diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 31865ff6347..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,3 +0,0 @@ -Unfortunately we are not accepting issues on GitHub yet. To report an issue, -please head over to https://trac.sagemath.org, log in with your GitHub account, -and create a ticket there :) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..f4ce719dcb8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,62 @@ +name: Bug Report +description: Report a bug +title: "" +labels: "t: bug" +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues for a bug report that matches the one I want to file, without success. + required: true + - type: checkboxes + attributes: + label: Did you read the documentation and troubleshoot guide? + description: Please read [README.md](https://github.com/sagemath/sage/blob/develop/README.md) and [the Troubleshooting section in the Installation Guide](https://doc.sagemath.org/html/en/installation/troubles.html). + options: + - label: I have read the documentation and troubleshoot guide + required: true + - type: textarea + attributes: + label: Environment + description: | + examples: + - **OS**: Ubuntu 20.04 + - Sage Version: 9.2 + value: | + - **OS**: + - **Sage Version**: + render: markdown + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: false + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Actual Behavior + description: A clear description of what actually happens. + validations: + required: true + - type: textarea + attributes: + label: Additional Information + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/failure_building_from_source.yml b/.github/ISSUE_TEMPLATE/failure_building_from_source.yml new file mode 100644 index 00000000000..afe651bf14d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/failure_building_from_source.yml @@ -0,0 +1,67 @@ +name: Failure building from source +description: Use this template when reporting a build failure +title: "<title>" +labels: ['c: build', 't: bug'] +assignees: [] +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues for a bug report that matches the one I want to file, without success. + required: true + - type: checkboxes + attributes: + label: Did you read the documentation and troubleshoot guide? + description: Please read [README.md](https://github.com/sagemath/sage/blob/develop/README.md) and [the Troubleshooting sectionin the Installation Guide](https://doc.sagemath.org/html/en/installation/troubles.html). + options: + - label: I have read the documentation and troubleshoot guide + required: true + - type: textarea + attributes: + label: Environment + description: | + examples: + - **OS**: Ubuntu 20.04 + - Sage Version: 9.2 + value: | + - **OS**: + - **Sage Version**: + render: markdown + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: false + - type: textarea + attributes: + label: Config log + description: | + Please attach `config.log`. + Tip: You can attach log files by clicking this area to highlight it and then dragging files in. + validations: + required: true + - type: textarea + attributes: + label: Package logs + description: | + Please attach ̀`logs/pkgs/SPKG.log` for failing packages. + Tip: You can attach log files by clicking this area to highlight it and then dragging files in. + validations: + required: false + - type: textarea + attributes: + label: Additional Information + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_report.yml b/.github/ISSUE_TEMPLATE/feature_report.yml new file mode 100644 index 00000000000..5505a45143a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_report.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest an idea +title: "<title>" +labels: "t: enhancement" +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues for a bug report that matches the one I want to file, without success. + required: true + - type: textarea + attributes: + label: Problem Description + description: Please add a clear and concise description of the problem you are seeking to solve with this feature request. + validations: + required: true + - type: textarea + attributes: + label: Proposed Solution + description: Describe the solution you'd like in a clear and concise manner. + validations: + required: true + - type: textarea + attributes: + label: Alternatives Considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + - type: textarea + attributes: + label: Additional Information + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 75508f8c77c..0ba5857b820 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,29 @@ -Thanks for contributing to Sage! Unfortunately we are not accepting pull -requests on GitHub yet. To propose a change to Sage, please log in with your -GitHub account at https://trac.sagemath.org, create a ticket, and push your -changes to that ticket as explained in our -[Developer's Guide](https://doc.sagemath.org/html/en/developer/manual_git.html). - -Please make sure to also have a look at our -[Code Style Conventions](https://doc.sagemath.org/html/en/developer/coding_basics.html). +<!-- ^^^^^ +Please provide a concise, informative and self-explanatory title. +Don't put issue numbers in there, do this in the PR body below. +For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" +--> +### 📚 Description + +<!-- Describe your changes here in detail --> +<!-- Why is this change required? What problem does it solve? --> +<!-- If it resolves an open issue, please link to the issue here. For example "Closes #1337" --> + +### 📝 Checklist + +<!-- Put an `x` in all the boxes that apply. --> +<!-- If your change requires a documentation PR, please link it appropriately --> +<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> + +- [ ] I have made sure that the title is self-explanatory and the description concisely explains the PR. +- [ ] I have linked an issue or discussion. +- [ ] I have created tests covering the changes. +- [ ] I have updated the documentation accordingly. + +### ⌛ Dependencies +<!-- List all open pull requests that this PR logically depends on --> +<!-- +- #xyz: short description why this is a dependency +- #abc: ... +--> + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eec448fed1f..814e410d29f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,7 @@ name: Build & Test on: + pull_request: push: workflow_dispatch: # Allow to run manually @@ -26,7 +27,7 @@ jobs: steps: - name: Checkout id: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Prepare id: prepare @@ -55,7 +56,7 @@ jobs: - name: Set up node to install pyright if: always() && steps.prepare.outcome == 'success' - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '12' diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml index 74e37b3a408..bc1f1c5a634 100644 --- a/.github/workflows/ci-conda.yml +++ b/.github/workflows/ci-conda.yml @@ -1,8 +1,6 @@ name: Build & Test using Conda on: - pull_request: - types: [opened, synchronize] push: tags: - '*' @@ -29,7 +27,7 @@ jobs: conda-env: [environment, environment-optional] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check for Miniconda id: check_conda diff --git a/.github/workflows/ci-cygwin-minimal.yml b/.github/workflows/ci-cygwin-minimal.yml new file mode 100644 index 00000000000..fa8f5db4f81 --- /dev/null +++ b/.github/workflows/ci-cygwin-minimal.yml @@ -0,0 +1,1042 @@ +name: CI cygwin-minimal + +on: + push: + tags: + - '*' + workflow_dispatch: + # Allow to run manually + +env: + MAKE: make -j8 + SAGE_NUM_THREADS: 3 + CYGWIN: winsymlinks:native + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + SAGE_LOCAL: /opt/sage-${{ github.sha }} + +jobs: + +############################################## stage-i ########################################## + + cygwin-stage-i-a: + env: + STAGE: i-a + # builds openblas + TARGETS: iml gsl + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-i-b: + env: + STAGE: i-b + TARGETS: cython setuptools_scm kiwisolver dateutil cycler pyparsing certifi pkgconfig pplpy + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-ii ########################################## + + cygwin-stage-ii-a: + env: + STAGE: ii-a + PREVIOUS_STAGES: i-* + TARGETS: cvxopt rpy2 + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-b: + env: + STAGE: ii-b + PREVIOUS_STAGES: i-* + TARGETS: singular maxima gap pari gfan palp flintqs arb ecm givaro + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-c: + env: + STAGE: ii-c + PREVIOUS_STAGES: i-* + TARGETS: cypari eclib fplll linbox giac + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-d: + env: + STAGE: ii-d + PREVIOUS_STAGES: i-* + TARGETS: ipython ipywidgets notebook + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-e: + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iii ########################################## + + cygwin-stage-iii-a: + env: + STAGE: iii-a + PREVIOUS_STAGES: ii-* + TARGETS: sagelib + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iii-b: + env: + STAGE: iii-b + PREVIOUS_STAGES: ii-* + TARGETS: networkx + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iv ########################################## + + cygwin-stage-iv: + env: + STAGE: iv + PREVIOUS_STAGES: iii-* + TARGETS: build + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iii-a, cygwin-stage-iii-b] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-v ########################################### + + cygwin-stage-v-a: + env: + STAGE: v-a + PREVIOUS_STAGES: iv + TARGETS: ptest-nodoc + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-b: + env: + STAGE: v-b + PREVIOUS_STAGES: iv + TARGETS: 4ti2 pynormaliz topcom lrslib latte_int cryptominisat + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-c: + env: + STAGE: v-c + PREVIOUS_STAGES: iv + TARGETS: sage_numerical_backends_coin + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-d: + env: + STAGE: v-d + PREVIOUS_STAGES: iv + TARGETS: qepcad barvinok isl qhull primecount plantri kenzo libsemigroups mcqd meataxe mpfrcx openssl p_group_cohomology rst2ipynb sirocco tdlib tides + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-e: + env: + STAGE: v-e + PREVIOUS_STAGES: iv + TARGETS: doc-html + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() diff --git a/.github/workflows/ci-cygwin-standard.yml b/.github/workflows/ci-cygwin-standard.yml index b1655e7696f..32efd7f8a4a 100644 --- a/.github/workflows/ci-cygwin-standard.yml +++ b/.github/workflows/ci-cygwin-standard.yml @@ -1,8 +1,6 @@ name: CI cygwin-standard on: - pull_request: - types: [opened, synchronize] push: tags: - '*' @@ -61,11 +59,13 @@ jobs: needs: [cygwin-stage-i-a, cygwin-stage-i-b] cygwin-stage-ii-e: - uses: ./.github/workflows/cygwin.yml - with: - stage: ii-e - previous_stages: i-* - targets: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs zn_poly sympow + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + needs: [cygwin-stage-i-a, cygwin-stage-i-b] ############################################## stage-iii ########################################## diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index d5935789219..bd9821c1e0c 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -18,8 +18,6 @@ name: CI Linux #on: [push, pull_request] on: - pull_request: - types: [opened, synchronize] push: tags: - '*' @@ -51,6 +49,7 @@ jobs: with: # Build incrementally from previous stage (pre) incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" docker_targets: "with-targets with-targets-optional" @@ -82,6 +81,7 @@ jobs: with: # Build incrementally from previous stage (pre) incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" docker_targets: "with-targets with-targets-optional" @@ -89,7 +89,7 @@ jobs: targets: build doc-html targets_optional: ptest tox_packages_factors: >- - ["minimal] + ["minimal"] docker_push_repository: ghcr.io/${{ github.repository }}/ maximal-pre: @@ -97,6 +97,7 @@ jobs: needs: [minimal] uses: ./.github/workflows/docker.yml with: + free_disk_space: true # Build from scratch docker_targets: "with-system-packages configured with-targets-pre" # FIXME: duplicated from env.TARGETS @@ -111,6 +112,7 @@ jobs: 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: >- @@ -125,6 +127,7 @@ jobs: 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: >- @@ -138,6 +141,7 @@ jobs: 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: >- @@ -151,6 +155,7 @@ jobs: 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: >- @@ -171,7 +176,7 @@ jobs: TOX_ENV: local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update @@ -185,7 +190,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index d2df2268199..015f1c8fb4f 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -18,8 +18,6 @@ name: CI macOS #on: [push, pull_request] on: - pull_request: - types: [opened, synchronize] push: tags: - '*' @@ -42,25 +40,21 @@ jobs: # python3_xcode is only accepted if enough packages are available from the system # --> to test "minimal", we would need https://trac.sagemath.org/ticket/30949 tox_env: [homebrew-macos-usrlocal-minimal, homebrew-macos-usrlocal-standard, homebrew-macos-usrlocal-maximal, homebrew-macos-usrlocal-python3_xcode-standard, conda-forge-macos-minimal, conda-forge-macos-standard, conda-forge-macos-maximal] - # As of 2021-12, default xcode - # - on macos-10.15: 12.4 - # - on macos-latest (= macos-11): 13.1 - # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode xcode_version_factor: [default] - os: [ macos-10.15, macos-latest ] + os: [ macos-11, macos-12 ] env: TOX_ENV: local-${{ matrix.tox_env }} LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-tox-local-${{ matrix.tox_env }}-${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_env }}--${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Select Xcode version run: | if [ ${{ matrix.xcode_version_factor }} != default ]; then sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version_factor }}.app; fi - name: Install test prerequisites run: | brew install tox - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: path: sage-local-artifact name: ${{ env.LOCAL_ARTIFACT_NAME }} @@ -96,7 +90,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} @@ -114,7 +108,7 @@ jobs: run: | mkdir -p sage-local-artifact && (cd .tox/$TOX_ENV && rm -f "local/lib64" && tar -cf - $(pwd)) > sage-local-artifact/sage-${{ env.TOX_ENV }}-${{ matrix.stage }}.tar if: contains(matrix.stage, '1') - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: sage-local-artifact/sage-${{ env.TOX_ENV }}-${{ matrix.stage }}.tar name: ${{ env.LOCAL_ARTIFACT_NAME }} @@ -124,7 +118,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 500 - name: fetch tags @@ -144,7 +138,7 @@ jobs: - name: make dist run: | ./configure --enable-download-from-upstream-url && make dist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: dist @@ -158,29 +152,17 @@ jobs: fail-fast: false max-parallel: 4 matrix: - os: [ macos-10.15, macos-11.0 ] - tox_system_factor: [macos-nobootstrap, macos-nobootstrap-python3_pythonorg] + os: [ macos-11, macos-12 ] + tox_system_factor: [macos-nobootstrap] tox_packages_factor: [minimal] - # As of 2021-03, default is 12.4 - # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode xcode_version_factor: [default] - include: - # Test xcode 11.7 only on macos-10.15 - - tox_system_factor: macos-nobootstrap - tox_packages_factor: minimal - xcode_version_factor: 11.7 - os: macos-10.15 - - tox_system_factor: macos-nobootstrap-python3_pythonorg - tox_packages_factor: minimal - xcode_version_factor: 11.7 - os: macos-10.15 env: TOX_ENV: local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}-xcode_${{ matrix.xcode_version_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 if: "!contains(matrix.tox_system_factor, 'nobootstrap')" - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: path: . name: dist @@ -198,13 +180,6 @@ jobs: - name: Install test prerequisites run: | sudo /usr/bin/python3 -m pip install tox - - name: Install python3 from python.org - # As of 2020-03-30 (https://github.com/actions/virtual-environments/blob/master/images/macos/macos-10.15-Readme.md), - # Python 3.7.7 is installed on GitHub Actions runners. But we install our own copy from the python.org binary package. - run: | - curl -o python3.pkg https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg - sudo installer -verbose -pkg python3.pkg -target / - if: contains(matrix.tox_system_factor, 'python3_pythonorg') - name: Build and test with tox # We use a high parallelization on purpose in order to catch possible parallelization bugs in the build scripts. # For doctesting, we use a lower parallelization to avoid timeouts. @@ -214,7 +189,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/ci-wsl.yml b/.github/workflows/ci-wsl.yml index 222af0695a5..4093af802b0 100644 --- a/.github/workflows/ci-wsl.yml +++ b/.github/workflows/ci-wsl.yml @@ -1,8 +1,6 @@ name: Build & Test WSL on: - pull_request: - types: [opened, synchronize] push: tags: - '*' @@ -19,7 +17,7 @@ jobs: steps: - name: Configure git run: git config --global core.symlinks true - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Ubuntu 20.04 (in WSL) run: | (New-Object System.Net.WebClient).DownloadFile("https://aka.ms/wslubuntu2004", "Ubuntu.appx") @@ -44,7 +42,7 @@ jobs: run: mkdir -p "artifacts/logs"; cp -r .tox/*/log "artifacts/logs" shell: bash if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: logs diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index e6f5cbb1391..0d57bd8440f 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -73,14 +73,14 @@ jobs: choco install git python39 python39-pip --source cygwin - name: Check out SageMath - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ inputs.sage_repo }} ref: ${{ inputs.sage_ref }} fetch-depth: 2000 - name: Check out git-trac-command - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sagemath/git-trac-command path: git-trac-command @@ -95,7 +95,7 @@ jobs: if: inputs.sage_trac_git != '' - name: Download upstream artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: path: upstream name: ${{ inputs.upstream_artifact }} @@ -106,7 +106,7 @@ jobs: if: inputs.upstream_artifact - name: Download sage-local artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: ${{ env.LOCAL_ARTIFACT_NAME }} path: C:\\tools\\cygwin\\tmp @@ -124,7 +124,7 @@ jobs: run: | C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/"$(basename "$a")".tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME"' if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} @@ -142,7 +142,7 @@ jobs: run: | C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ inputs.stage }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 # upload-artifact@v2 does not support whitespace in file names. # so we tar up the directory ourselves with: diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 5086ca60a7c..96aae8fbc1a 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -18,8 +18,9 @@ jobs: release_dist: - # This job, in contrast to "dist" in tox.yml, - # does not use "configure --enable-download-from-upstream-url". + # This job, in contrast to "dist" in ci-macos.yml, + # does not use "configure --enable-download-from-upstream-url" + # (the default since #32390). # # In this way, we check that all necessary package tarballs # have already been uploaded to the Sage server at the time @@ -31,15 +32,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install bootstrap prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) - name: make dist run: | - ./bootstrap -D && ./configure && make dist - - uses: actions/upload-artifact@v2 + ./bootstrap -D && ./configure --disable-download-from-upstream-url && make dist + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: release_dist @@ -50,7 +51,7 @@ jobs: env: CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install bootstrap prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update @@ -62,7 +63,7 @@ jobs: make pypi-sdists V=0 (mkdir dist && mv upstream/sage*.tar.gz dist/) ls -l dist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: dist @@ -105,9 +106,9 @@ jobs: # Use 'build', not 'pip wheel' CIBW_BUILD_FRONTEND: build steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: dist path: dist @@ -130,7 +131,7 @@ jobs: pipx run cibuildwheel==2.7.0 unpacked/$pkg* done - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: wheels path: ./wheelhouse/*.whl @@ -142,7 +143,7 @@ jobs: CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: wheels path: wheelhouse diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index 9d5934bd3af..1cc9cf8cd3f 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -1,6 +1,7 @@ name: Build documentation on: + pull_request: push: workflow_dispatch: # Allow to run manually @@ -14,13 +15,13 @@ jobs: build-docs: runs-on: ubuntu-latest container: ghcr.io/sagemath/sage/sage-docker-ubuntu-focal-standard-with-targets:dev - if: github.repository == 'sagemath/sagetrac-mirror' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Prepare run: | + apt-get update && apt-get install -y zip # Reuse built SAGE_LOCAL contained in the Docker image ./bootstrap ./configure --enable-build-as-root --prefix=/sage/local --with-sage-venv --enable-download-from-upstream-url @@ -38,35 +39,11 @@ jobs: # We also need to replace the symlinks because netlify is not following them mkdir -p ./docs cp -r -L /sage/local/share/doc/sage/html/en/* ./docs + # Zip everything for increased performance + zip -r docs.zip docs - - name: Deploy to Netlify preview - id: preview-netlify - if: github.ref != 'refs/heads/develop' - uses: netlify/actions/cli@master + - name: Upload docs + uses: actions/upload-artifact@v3 with: - args: deploy --dir=docs --alias="${NETLIFY_ALIAS}" - env: - # Set deployment url to commit hash to easily link from the trac. - # We could also set NETLIFY_ALIAS to the branch name. - # However, netlify currently doesn't support updates to a deployment with the same alias - # https://github.com/netlify/cli/issues/948 - # https://github.com/netlify/cli/issues/1984 - # Note that even if this feature is implemented, one would also need to first process the branch name - # to workaround the bug https://github.com/netlify/cli/issues/969. - NETLIFY_ALIAS: ${{ github.sha }} - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - - - name: Deploy to Netlify production - id: deploy-netlify - if: github.ref == 'refs/heads/develop' - uses: netlify/actions/cli@master - with: - args: deploy --dir=docs --prod - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - - - name: Report deployment url - run: | - echo "::notice::The documentation has being automatically deployed to Netlify. %0A ✅ Preview: ${{ steps.preview-netlify.outputs.NETLIFY_URL || steps.deploy-netlify.outputs.NETLIFY_URL }}" + name: docs + path: docs.zip diff --git a/.github/workflows/doc-publish.yml b/.github/workflows/doc-publish.yml new file mode 100644 index 00000000000..c7be4a46d3b --- /dev/null +++ b/.github/workflows/doc-publish.yml @@ -0,0 +1,95 @@ +# Triggers after the documentation build has finished, +# taking the artifact and uploading it to netlify +name: Publish documentation + +on: + workflow_run: + workflows: ["Build documentation"] + types: + - completed + +permissions: + statuses: write + checks: write + pull-requests: write + +jobs: + upload-docs: + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + steps: + - name: Get information about workflow origin + uses: potiuk/get-workflow-origin@v1_5 + id: source-run-info + with: + token: ${{ secrets.GITHUB_TOKEN }} + sourceRunId: ${{ github.event.workflow_run.id }} + + # Once https://github.com/actions/download-artifact/issues/172 and/or https://github.com/actions/download-artifact/issues/60 is implemented, we can use the official download-artifact action + # For now use the solution from https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow + - name: Download docs + uses: actions/github-script@v3.1.0 + with: + script: | + var artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "docs" + })[0]; + var download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/docs.zip', Buffer.from(download.data)); + + - name: Extract docs + run: unzip docs.zip -d docs && unzip docs/docs.zip -d docs/docs + + - name: Deploy to Netlify + id: deploy-netlify + uses: netlify/actions/cli@master + with: + args: deploy --dir=docs/docs/docs ${NETLIFY_PRODUCTION:+"--prod"} --message ${NETLIFY_MESSAGE} --alias ${NETLIFY_ALIAS} + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + NETLIFY_PRODUCTION: ${{ github.ref == 'refs/heads/develop' }} + NETLIFY_MESSAGE: ${{ steps.source-run-info.outputs.pullRequestNumber }} + NETLIFY_ALIAS: deploy-preview-${{ steps.source-run-info.outputs.pullRequestNumber }} + + # Add deployment as status check, PR comment and annotation + # we could use the nwtgck/actions-netlify action for that, except for that it is not (yet) working in workflow_run context: https://github.com/nwtgck/actions-netlify/issues/545 + - name: Add/Update deployment status PR comment + uses: marocchino/sticky-pull-request-comment@v2 + with: + number: ${{ steps.source-run-info.outputs.pullRequestNumber }} + header: preview-comment + recreate: true + message: | + [Documentation preview for this PR](${{ steps.deploy-netlify.outputs.NETLIFY_URL }}) is ready! :tada: + Built with commit: ${{ steps.source-run-info.outputs.sourceHeadSha }} + + - name: Update deployment status PR check + uses: myrotvorets/set-commit-status-action@1.1.6 + if: ${{ always() }} + env: + DEPLOY_SUCCESS: Successfully deployed preview. + DEPLOY_FAILURE: Failed to deploy preview. + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: ${{ job.status == 'success' && 'success' || 'failure' }} + sha: ${{ github.event.workflow_run.head_sha }} + context: Deploy Documentation + targetUrl: ${{ steps.deploy-netlify.outputs.NETLIFY_URL }} + description: ${{ job.status == 'success' && env.DEPLOY_SUCCESS || env.DEPLOY_FAILURE }} + + - name: Report deployment url + run: | + echo "::notice::The documentation has being automatically deployed to Netlify. %0A ✅ Preview: ${{ steps.deploy-netlify.outputs.NETLIFY_URL }}" + diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8eff13405f8..04c74659c1d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,22 +18,24 @@ on: default: >- ["ubuntu-trusty-toolchain-gcc_9", "ubuntu-xenial-toolchain-gcc_9", - "ubuntu-bionic-gcc_8", + "ubuntu-bionic-gcc_8-python3.8", "ubuntu-focal", "ubuntu-jammy", "ubuntu-kinetic", + "ubuntu-lunar", "debian-buster", "debian-bullseye", "debian-bookworm", "debian-sid", - "linuxmint-19-gcc_8", - "linuxmint-19.3-gcc_8", + "linuxmint-19-gcc_8-python3.8", + "linuxmint-19.3-gcc_8-python3.8", "linuxmint-20.1", "linuxmint-20.2", "linuxmint-20.3", "linuxmint-21", - "fedora-29", - "fedora-30", + "linuxmint-21.1", + "fedora-29-python3.8", + "fedora-30-python3.8", "fedora-31", "fedora-32", "fedora-33", @@ -41,18 +43,19 @@ on: "fedora-35", "fedora-36", "fedora-37", + "fedora-38", "centos-7-devtoolset-gcc_11", - "centos-stream-8", - "centos-stream-9", + "centos-stream-8-python3.9", + "centos-stream-9-python3.9", "gentoo-python3.9", "gentoo-python3.10", + "gentoo-python3.11", "archlinux-latest", - "opensuse-15.3-gcc_11", - "opensuse-15.4-gcc_11", - "opensuse-tumbleweed", + "opensuse-15.3-gcc_11-python3.9", + "opensuse-15.4-gcc_11-python3.10", + "opensuse-tumbleweed-python3.10", "conda-forge", - "ubuntu-bionic-i386", - "manylinux-2_24-i686", + "ubuntu-bionic-gcc_8-i386", "debian-buster-i386", ] tox_packages_factors: @@ -62,9 +65,16 @@ on: ["minimal", "standard", ] + extra_sage_packages: + description: 'Extra Sage packages to install as system packages' + type: string + default: "" max_parallel: type: number default: 24 + free_disk_space: + default: false + type: boolean # # Publishing to GitHub Packages # @@ -128,14 +138,15 @@ jobs: FROM_DOCKER_REPOSITORY: ${{ inputs.from_docker_repository }} FROM_DOCKER_TARGET: ${{ inputs.from_docker_target }} FROM_DOCKER_TAG: ${{ inputs.from_docker_tag }} - + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + EXTRA_SAGE_PACKAGES: ${{ inputs.extra_sage_packages }} steps: - name: Check out SageMath - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ inputs.sage_repo }} ref: ${{ inputs.sage_ref }} - fetch-depth: 2000 + fetch-depth: 10000 - name: fetch tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: free disk space @@ -149,8 +160,9 @@ jobs: dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 50 sudo apt-get --fix-broken --yes remove $(dpkg-query -f '${Package}\n' -W | grep -E '^(ghc-|google-cloud-sdk|google-chrome|firefox|mysql-server|dotnet-sdk|hhvm|mono)') || echo "(error ignored)" df -h + if: inputs.free_disk_space - name: Check out git-trac-command - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sagemath/git-trac-command path: git-trac-command @@ -165,7 +177,7 @@ jobs: if: inputs.sage_trac_git != '' - name: Download upstream artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: path: upstream name: ${{ inputs.upstream_artifact }} @@ -190,7 +202,7 @@ jobs: TOKEN="${{ secrets.GITHUB_TOKEN }}" fi if echo "$TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin; then - echo "DOCKER_PUSH_REPOSITORY=${{ inputs.docker_push_repository }}" >> $GITHUB_ENV + echo "DOCKER_PUSH_REPOSITORY=$(echo ${{ inputs.docker_push_repository }} | tr "[:upper:]" "[:lower:]")" >> $GITHUB_ENV echo "DOCKER_CONFIG_FILE=$HOME/.docker/config.json" >> $GITHUB_ENV fi # From the docker documentation via .ci/update-env.sh: @@ -217,7 +229,7 @@ jobs: if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 811a61cc928..883749e2bf7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install pycodestyle @@ -27,23 +27,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install relint run: pip install tox relint - name: Lint using relint - run: tox -e relint src/sage/ + run: tox -e relint -- src/sage/ lint-rst: name: Validate docstring markup as RST runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install tox diff --git a/.github/workflows/ticket.yaml b/.github/workflows/ticket.yaml deleted file mode 100644 index efbfa5bbdb0..00000000000 --- a/.github/workflows/ticket.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# CI Test For Individual Tickets - -name: Ticket CI - -on: - push: - branches: - # Once set to positive review, the branch from the trac ticket - # number 12345 will be copied to ticket/12345 - - 'ticket/**' - - -jobs: - ticket_ci: - - # TODO: this action is only a placeholder, github actions needs - # this in the master branch to enable it - - name: Ticket CI - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install pycodestyle - run: pip install tox pycodestyle - - name: Lint using pycodestyle - run: tox -e pycodestyle-minimal diff --git a/.gitignore b/.gitignore index 4440aee8f9a..e62fa805ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -131,9 +131,15 @@ __pycache__/ # Generated Cython files *.so **/*.so +/src/cython_debug +# Most C and C++ files are generated by Cython and should not +# be included in the sdist. /src/sage/**/*.c /src/sage/**/*.cpp +# C header generated by Cython /src/sage/modular/arithgroup/farey_symbol.h +# List of C and C++ files that are actual source files, +# NOT generated by Cython. The same list appears in src/MANIFEST.in !/src/sage/cpython/debugimpl.c !/src/sage/graphs/base/boost_interface.cpp !/src/sage/graphs/cliquer/cl.c @@ -151,12 +157,10 @@ __pycache__/ !/src/sage/rings/polynomial/weil/power_sums.c !/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp !/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp -!/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp !/src/sage/stats/distributions/dgs_bern.c !/src/sage/stats/distributions/dgs_gauss_dp.c !/src/sage/stats/distributions/dgs_gauss_mp.c !/src/sage/symbolic/ginac/*.cpp -/src/cython_debug # Temporary build files build/temp.*/ diff --git a/.gitpod-setup-trac-remote.sh b/.gitpod-setup-trac-remote.sh new file mode 100755 index 00000000000..4ca3b19f26d --- /dev/null +++ b/.gitpod-setup-trac-remote.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Exit on error +set -e + +# Setup trac as remote +## In order to push to trac, generate a new key with `ssh-keygen -f tempkey` and save the private key to gitpod `gp env PRIVATE_SSH_KEY="$(<tempkey)"` (or by following https://www.gitpod.io/docs/environment-variables#using-the-account-settings) +## then follow https://doc.sagemath.org/html/en/developer/trac.html#linking-your-public-key-to-your-trac-account to register the public key with trac. +## Afterwards, create a new gitpod workspace. +git remote remove trac 2> /dev/null || true # might still exists from a previous run/prebuild +if [[ -n "${PRIVATE_SSH_KEY}" ]]; then + # Setup ssh key for authentication with trac + mkdir -p ~/.ssh + echo $PRIVATE_SSH_KEY | sed 's/\(-----\(BEGIN\|END\) OPENSSH PRIVATE KEY-----\)/\n\1\n/g' > ~/.ssh/id_rsa + sed -i '/^$/d' ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + echo "PubkeyAcceptedKeyTypes +ssh-rsa" > ~/.ssh/config + ssh-keyscan -H trac.sagemath.org >> ~/.ssh/known_hosts + + # Setup trac repo + git remote add trac git@trac.sagemath.org:sage.git -t master -t develop -t $(git branch --show-current) + git remote set-url --push trac git@trac.sagemath.org:sage.git + git fetch trac + git branch -u trac/$(git branch --show-current) +else + # Fallback to sagemath mirror + git remote add trac https://github.com/sagemath/sagetrac-mirror.git -t master -t develop + git remote set-url --push trac pushing-needs-ssh-key +fi diff --git a/.gitpod.yml b/.gitpod.yml index fcd6027c88d..52ac8d7184c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -5,53 +5,24 @@ image: # Start up tasks. https://www.gitpod.io/docs/config-start-tasks/ tasks: - name: Setup - init: | - # Create conda environment + # Create conda environment, then configure and build sage + init: >- ./bootstrap-conda - mamba env create --file src/environment-dev.yml --prefix venv - conda config --append envs_dirs $(pwd) - conda activate $(pwd)/venv - - # Build sage - ./bootstrap - ./configure --enable-build-as-root --with-python=$CONDA_PREFIX/bin/python --prefix=$CONDA_PREFIX - pip install --no-build-isolation -v -v -e ./pkgs/sage-conf ./pkgs/sage-setup - pip install --no-build-isolation -v -v -e ./src - - command: | - # Activate conda environment - conda config --append envs_dirs $(pwd) - conda activate $(pwd)/venv - - # RestructuredText extension recommends python extension, although we have already installed it - ## So disable the recommendation dialog + && mamba env create --file src/environment-dev.yml --prefix venv + && conda config --append envs_dirs $(pwd) + && conda activate $(pwd)/venv + && ./bootstrap + && ./configure --enable-build-as-root --with-python=$CONDA_PREFIX/bin/python --prefix=$CONDA_PREFIX + && pip install --no-build-isolation -v -v -e ./pkgs/sage-conf ./pkgs/sage-setup + && pip install --no-build-isolation -v -v -e ./src + # Activate conda environment, set up Trac remote + # RestructuredText extension recommends python extension, although we have already installed it + # So disable the recommendation dialog + command: >- echo "{\"restructuredtext.pythonRecommendation.disabled\": true}" > /workspace/.vscode-remote/data/Machine/settings.json - - # Setup trac as remote - ## In order to push to trac, generate a new key with `ssh-keygen -f tempkey` and save the private key to gitpod `gp env PRIVATE_SSH_KEY="$(<tempkey)"` (or by following https://www.gitpod.io/docs/environment-variables#using-the-account-settings) - ## then follow https://doc.sagemath.org/html/en/developer/trac.html#linking-your-public-key-to-your-trac-account to register the public key with trac. - ## Afterwards, create a new gitpod workspace. - git remote remove trac 2> /dev/null # might still exists from a previous run/prebuild - if [[ -n "${PRIVATE_SSH_KEY}" ]]; then - # Setup ssh key for authentication with trac - mkdir -p ~/.ssh - echo $PRIVATE_SSH_KEY | sed 's/\(-----\(BEGIN\|END\) OPENSSH PRIVATE KEY-----\)/\n\1\n/g' > ~/.ssh/id_rsa - sed -i '/^$/d' ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "PubkeyAcceptedKeyTypes +ssh-rsa" > ~/.ssh/config - ssh-keyscan -H trac.sagemath.org >> ~/.ssh/known_hosts - - # Setup trac repo - git remote add trac git@trac.sagemath.org:sage.git -t master -t develop -t $(git branch --show-current) - git remote set-url --push trac git@trac.sagemath.org:sage.git - git fetch trac - git branch -u trac/$(git branch --show-current) - else - # Fallback to sagemath mirror - git remote add trac https://github.com/sagemath/sagetrac-mirror.git -t master -t develop - git remote set-url --push trac pushing-needs-ssh-key - fi - + && conda config --append envs_dirs $(pwd) + && conda activate $(pwd)/venv + && ./.gitpod-setup-trac-remote.sh env: SAGE_NUM_THREADS: 8 diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 0650ffa1eee..00000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,23 +0,0 @@ -queries: - - exclude: py/call/wrong-named-class-argument - - exclude: py/call/wrong-number-class-arguments - - exclude: py/similar-function - - exclude: py/unsafe-cyclic-import -path_classifiers: - imports_only: - - "**/all.py" - - "**/all_*.py" - - "**/*catalog*.py" - - "**/species/library.py" - - "**/categories/basic.py" - - "**/combinat/ribbon.py" - - "**/combinat/family.py" - - "**/interacts/geometry.py" - - "**/matroids/advanced.py" - - "**/matroids/named_matroids.py" - - "**/modular/congroup.py" - - "**/quadratic_forms/quadratic_form__mass.py" -extraction: - python: - python_setup: - version: 3 diff --git a/.vscode/settings.json b/.vscode/settings.json index 10822524b25..58c9bc7af2b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,11 +27,12 @@ "python.linting.enabled": true, // The following pycodestyle arguments are the same as the pycodestyle-minimal // tox environnment, see the file SAGE_ROOT/src/tox.ini - "python.linting.pycodestyleArgs": ["--select=E111,E306,E401,E701,E702,E703,W605,E711,E712,E713,E721,E722"], + "python.linting.pycodestyleArgs": ["--select=E111,E306,E401,E701,E702,E703,W291,W391,W605,E711,E712,E713,E721,E722"], "cSpell.words": [ "furo", "Conda", "sagemath", "Cython" - ] + ], + "editor.formatOnType": true } diff --git a/.zenodo.json b/.zenodo.json index 06bf86e4065..da76244bfa7 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.8.beta0", - "version": "9.8.beta0", + "title": "sagemath/sage: 10.0.beta0", + "version": "10.0.beta0", "upload_type": "software", - "publication_date": "2022-09-25", + "publication_date": "2023-02-12", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.8.beta0", + "identifier": "https://github.com/sagemath/sage/tree/10.0.beta0", "relation": "isSupplementTo" }, { diff --git a/COPYING.txt b/COPYING.txt index a3e9cc66458..a785477fb4f 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -130,7 +130,6 @@ tachyon Modified BSD threejs MIT License tornado Apache License zlib Custom (Modified BSD) -zn_poly GPLv2 or GPLv3 (no later versions, see below) CONTACT INFO: William Stein; wstein@gmail.com; @@ -1346,53 +1345,4 @@ Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler Jean-loup Gailly jloup@gzip.org Mark Adler madler@alumni.caltech.edu - -================================================================================ - -zn_poly: a library for polynomial arithmetic (version 0.9) - -Copyright (C) 2007, 2008, David Harvey - -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) version 3 of the License. - -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 (see gpl-2.0.txt and gpl-3.0.txt). If not, -see <http://www.gnu.org/licenses/>. - -Licensing notes: - -(1) -zn_poly is NOT released under the "GPL v2 or later" or "GPL v3 or later". -Both v2 and v3 are fine, but for now I am excluding later versions. If you -need zn_poly under a different license, ask me and I'll consider it. - -(2) -zn_poly incorporates small amounts of code from other projects: - - (2a) - The file "wide_arith.h" includes some assembly macros from the file - "longlong.h" in GMP 4.2.1; see http://gmplib.org/. The copyright to this - code is held by the Free Software Foundation, and it was released under - "LGPL v2.1 or later". - - (2b) - The file "wide_arith.h" also includes assembly macros from the file - "SPMM_ASM.h" in NTL 5.4.1; see http://www.shoup.net/ntl/. The copyright - to this code is held by Victor Shoup, and it was released under "GPL v2 or - later". - - (2c) - The filer "profiler.h" contains x86 cycle counting code from the file - "profiler.h" in FLINT 1.0; see http://www.flintlib.org/. The copyright - to this code is held by William Hart, and it was released under "GPL v2 or - later". - =============================================================================== diff --git a/Makefile b/Makefile index 004a6750920..414398ddf0d 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ pypi-wheels: rm -f venv/var/lib/sage/installed/$$a-*; \ done for a in $(PYPI_WHEEL_PACKAGES); do \ - $(MAKE) SAGE_EDITABLE=no $$a; \ + $(MAKE) SAGE_EDITABLE=no SAGE_WHEELS=yes $$a; \ done @echo "Built wheels are in venv/var/lib/sage/wheels/" @@ -112,7 +112,7 @@ wheels: rm -f venv/var/lib/sage/installed/$$a-*; \ done for a in $(WHEEL_PACKAGES); do \ - $(MAKE) SAGE_EDITABLE=no $$a; \ + $(MAKE) SAGE_EDITABLE=no SAGE_WHEELS=yes $$a; \ done @echo "Built wheels are in venv/var/lib/sage/wheels/" diff --git a/README.md b/README.md index 7a21328c9a4..1a0582d87c5 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ If your Mac uses the Apple Silicon (M1, arm64) architecture: https://brew.sh/ required because it provides a version of ``gfortran`` with necessary changes for this platform that are not in a released upstream version of GCC. (The ``gfortran`` package that comes with the Sage - distribution is not suitable for the M1.) + distribution is not suitable for the M1/M2.) If your Mac uses the Intel (x86_64) architecture: @@ -184,14 +184,20 @@ in the Installation Guide. - Compilers: `gcc`, `gfortran`, `g++` (GCC 8.x to 12.x and recent versions of Clang (LLVM) are supported). - See the Installation Manual for a discussion of suitable compilers. + See [build/pkgs/gcc/SPKG.rst](build/pkgs/gcc/SPKG.rst) and + [build/pkgs/gfortran/SPKG.rst](build/pkgs/gfortran/SPKG.rst) + for a discussion of suitable compilers. - Build tools: GNU `make`, GNU `m4`, `perl` (including ``ExtUtils::MakeMaker``), `ranlib`, `git`, `tar`, `bc`. + See [build/pkgs/_prereq/SPKG.rst](build/pkgs/_prereq/SPKG.rst) for + more details. - Python 3.4 or later, or Python 2.7, a full installation including `urllib`; but ideally version 3.8.x, 3.9.x, or 3.10.x, which will avoid having to build Sage's own copy of Python 3. + See [build/pkgs/python3/SPKG.rst](build/pkgs/python3/SPKG.rst) + for more details. We have collected lists of system packages that provide these build prerequisites. See, in the folder @@ -222,16 +228,37 @@ in the Installation Guide. (If the bootstrapping prerequisites are not installed, this command will download a package providing pre-built bootstrap output instead.) -6. [macOS with homebrew] Set required environment variables for the build: +6. Sanitize the build environment. Use the command - $ source ./.homebrew-build-env + $ env - This is to make some of Homebrew's packages (so-called keg-only packages) - available for the build. Run it once to apply the suggestions for the current - terminal session. You may need to repeat this command before you rebuild Sage - from a new terminal session, or after installing additional homebrew packages. - (You can also add it to your shell profile so that it gets run automatically - in all future sessions.) + to inspect the current environment variables, in particular `PATH`, + `PKG_CONFIG_PATH`, `LD_LIBRARY_PATH`, `CFLAGS`, `CPPFLAGS`, `CXXFLAGS`, + and `LDFLAGS` (if set). + + Remove items from these (colon-separated) environment variables + that Sage should not use for its own build. In particular, remove + items if they refer to a previous Sage installation. + + - [WSL] In particular, WSL imports many items from the Windows + `PATH` variable into the Linux environment, which can lead to + confusing build errors. These items typically start with `/mnt/c`. + It is best to remove all of them from the environment variables. + For example, you can set `PATH` using the command: + + $ export PATH=/usr/sbin/:/sbin/:/bin/:/usr/lib/wsl/lib/ + + - [macOS with homebrew] Set required environment variables for the build: + + $ source ./.homebrew-build-env + + This is to make some of Homebrew's packages (so-called keg-only + packages) available for the build. Run it once to apply the + suggestions for the current terminal session. You may need to + repeat this command before you rebuild Sage from a new terminal + session, or after installing additional homebrew packages. (You + can also add it to your shell profile so that it gets run + automatically in all future sessions.) 7. Optionally, decide on the installation prefix (`SAGE_LOCAL`): @@ -403,7 +430,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) │ └── pkgs Every package is a subdirectory here │ ├── 4ti2/ │ … -│ └── zn_poly/ +│ └── zlib/ ├── configure Top-level configure script ├── COPYING.txt Copyright information ├── pkgs Source trees of Python distribution packages @@ -450,7 +477,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) │ └── pkgs Build logs of individual packages │ ├── alabaster-0.7.12.log │ … -│ └── zn_poly-0.9.2.log +│ └── zlib-1.2.11.log ├── m4 M4 macros for generating the configure script │ └── *.m4 ├── Makefile Running "make" uses this file @@ -464,7 +491,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) ├── upstream Source tarballs of packages │ ├── Babel-2.9.1.tar.gz │ … -│ └── zn_poly-0.9.2.tar.gz +│ └── zlib-1.2.11.tar.gz ├── venv -> SAGE_VENV Convenience symlink to the virtual environment └── VERSION.txt ``` diff --git a/VERSION.txt b/VERSION.txt index 58778909bd8..ec675a255a2 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.8.beta0, Release Date: 2022-09-25 +SageMath version 10.0.beta0, Release Date: 2023-02-12 diff --git a/build/bin/sage-bootstrap-python b/build/bin/sage-bootstrap-python index f78e5e62a1d..db4cea35dfd 100755 --- a/build/bin/sage-bootstrap-python +++ b/build/bin/sage-bootstrap-python @@ -53,7 +53,7 @@ if [ "$LC_ALL" = "C" -o "$LANG" = "C" -o "$LC_CTYPE" = "C" ]; then export LANG fi -PYTHONS="python python3 python3.10 python3.9 python3.8 python3.7 python2.7 python3.6 python2" +PYTHONS="python python3 python3.12 python3.11 python3.10 python3.9 python3.8 python3.7 python2.7 python3.6 python2" # Trac #32405: Prefer a Python that provides ssl with SNI, which allows developers # to download from upstream URLs (configure --enable-download-from-upstream-url), # in particular from PyPI, which requires SNI. diff --git a/build/bin/sage-build-env b/build/bin/sage-build-env index ed999b703f0..87cd0fde5f3 100644 --- a/build/bin/sage-build-env +++ b/build/bin/sage-build-env @@ -31,6 +31,10 @@ if [ "x$SAGE_BUILD_ENV_SOURCED" = "x" ]; then if [ "x$SAGE_EDITABLE" = "x" ]; then export SAGE_EDITABLE="$CONFIGURED_SAGE_EDITABLE" fi + # Likewise for SAGE_WHEELS + if [ "x$SAGE_WHEELS" = "x" ]; then + export SAGE_WHEELS="$CONFIGURED_SAGE_WHEELS" + fi # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise if [ -n "$SAGE_GMP_PREFIX" ]; then diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 58d6bd5e5d7..b00fd2a3f99 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -58,3 +58,4 @@ export SAGE_SUITESPARSE_PREFIX="@SAGE_SUITESPARSE_PREFIX@" export SAGE_CONFIGURE_FFLAS_FFPACK="@SAGE_CONFIGURE_FFLAS_FFPACK@" export CONFIGURED_SAGE_EDITABLE="@SAGE_EDITABLE@" +export CONFIGURED_SAGE_WHEELS="@SAGE_WHEELS@" diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 339d06ce493..24769ebfffc 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -290,6 +290,8 @@ sdh_pip_install() { sdh_pip_editable_install() { echo "Installing $PKG_NAME (editable mode)" + # Until https://trac.sagemath.org/ticket/34209 switches us to PEP 660 editable wheels + export SETUPTOOLS_ENABLE_FEATURES=legacy-editable python3 -m pip install --verbose --no-deps --no-index --no-build-isolation --isolated --editable "$@" || \ sdh_die "Error installing $PKG_NAME" } diff --git a/build/bin/sage-package b/build/bin/sage-package index eeebbcd2c2e..ccb3ad194a7 100755 --- a/build/bin/sage-package +++ b/build/bin/sage-package @@ -18,7 +18,7 @@ # arb # autotools # [...] -# zn_poly +# zlib # # * Find the package name given a tarball filename # diff --git a/build/bin/sage-pip-install b/build/bin/sage-pip-install index 08978fe5717..71f436a4b47 100755 --- a/build/bin/sage-pip-install +++ b/build/bin/sage-pip-install @@ -28,7 +28,7 @@ PIP=pip3 # We should avoid running pip while installing a package because that # is prone to race conditions. Therefore, we use a lockfile while # running pip. This is implemented in the Python script sage-flock -LOCK="$SAGE_LOCAL/var/lock/$PIP.lock" +LOCK="$SAGE_VENV/var/lock/$PIP.lock" # Trac #33155: Pythons installed using the python.org macOS installers # for Python < 3.10 identify macOS Big Sur and newer as "10.16", causing diff --git a/build/bin/sage-pip-uninstall b/build/bin/sage-pip-uninstall index 3017627dbbc..616f86a065b 100755 --- a/build/bin/sage-pip-uninstall +++ b/build/bin/sage-pip-uninstall @@ -14,7 +14,7 @@ PIP=pip3 # We should avoid running pip while uninstalling a package because that # is prone to race conditions. Therefore, we use a lockfile while # running pip. This is implemented in the Python script sage-flock -LOCK="$SAGE_LOCAL/var/lock/$PIP.lock" +LOCK="$SAGE_VENV/var/lock/$PIP.lock" # --disable-pip-version-check: Don't periodically check PyPI to determine whether a new version of pip is available # --no-input: Disable prompting for input. diff --git a/build/bin/sage-site b/build/bin/sage-site index 0472f5652b3..f9d13d53047 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -98,7 +98,7 @@ fi if [ "$1" = '-package' -o "$1" = "--package" ]; then shift - exec sage-package $@ + exec sage-package "$@" fi if [ "$1" = '-optional' -o "$1" = "--optional" ]; then diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 42eb5f53686..14cbaf786eb 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -63,11 +63,15 @@ EOF RUN sed -i.bak $DIST_UPGRADE /etc/apt/sources.list && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade EOF fi - if [ -n "$EXTRA_REPOSITORY" ]; then + if [ -n "$EXTRA_REPOSITORIES" ]; then cat <<EOF RUN $UPDATE $INSTALL software-properties-common && ($INSTALL gpg gpg-agent || echo "(ignored)") -RUN $SUDO add-apt-repository $EXTRA_REPOSITORY EOF + for repo in $EXTRA_REPOSITORIES; do + cat <<EOF +RUN $SUDO add-apt-repository $repo +EOF + done fi esac ;; diff --git a/build/make/Makefile.in b/build/make/Makefile.in index ea54177f241..e971def2416 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -430,7 +430,7 @@ define SET_SAGE_CHECK $(eval SAGE_CHECK_$(1) := $(2)) endef # Set defaults -$(foreach pkgname, $(NORMAL_PACKAGES),\ +$(foreach pkgname, $(NORMAL_PACKAGES) $(SCRIPT_PACKAGES),\ $(eval $(call SET_SAGE_CHECK,$(pkgname),$(SAGE_CHECK)))) # Parsing the SAGE_CHECK_PACKAGES variable: @@ -443,8 +443,9 @@ $(foreach pkgname, $(NORMAL_PACKAGES),\ # # Since Python's self-tests seem to fail on all platforms, we disable # its test suite by default. +# meson_python 0.10.0 fails on some platforms, so we reduce it to warnings. # However, if SAGE_CHECK=warn, we do not do that. -SAGE_CHECK_PACKAGES_DEFAULT_yes := !python3 +SAGE_CHECK_PACKAGES_DEFAULT_yes := !python3,?meson_python SAGE_CHECK_PACKAGES_DEFAULT_warn := SAGE_CHECK_PACKAGES_DEFAULT_no := comma := , @@ -460,7 +461,7 @@ $(foreach clause, $(SAGE_CHECK_PACKAGES_sep), \ $(eval $(call SET_SAGE_CHECK,$(subst ?,,$(clause)),warn)), \ $(eval $(call SET_SAGE_CHECK,$(clause),yes))))) debug-check: - @echo $(foreach pkgname, $(NORMAL_PACKAGES), SAGE_CHECK_$(pkgname) = $(SAGE_CHECK_$(pkgname))) + @echo $(foreach pkgname, $(NORMAL_PACKAGES) $(SCRIPT_PACKAGES), SAGE_CHECK_$(pkgname) = $(SAGE_CHECK_$(pkgname))) #============================================================================== @@ -676,8 +677,8 @@ $(1)-$(4)-no-deps: . '$$(SAGE_ROOT)/build/bin/sage-build-env' && \ SAGE_SPKG_WHEELS=$$($(4))/var/lib/sage/wheels \ SAGE_INST_LOCAL=$$($(4)) \ - sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1)-$(2).log' && \ - rm -f "$$($(4))/$(SPKG_INST_RELDIR)/$(1)-*" && \ + sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) $$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1)-$(2).log' && \ + rm -f "$$($(4))/$(SPKG_INST_RELDIR)/$(1)"-* && \ touch "$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2)"; \ else ( \ echo; \ @@ -704,7 +705,7 @@ $(1)-$(4)-uninstall: . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env' && \ '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-uninstall' - -rm -f "$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2)" + -rm -f "$$($(4))/$(SPKG_INST_RELDIR)/$(1)"-* $(1)-uninstall: $(1)-$(4)-uninstall diff --git a/build/pkgs/_develop/dependencies b/build/pkgs/_develop/dependencies index f50a34b8495..f37c427ebcd 100644 --- a/build/pkgs/_develop/dependencies +++ b/build/pkgs/_develop/dependencies @@ -1 +1 @@ -_bootstrap git pytest pytest_xdist +_bootstrap git pytest pytest_xdist github_cli diff --git a/build/pkgs/_prereq/SPKG.rst b/build/pkgs/_prereq/SPKG.rst index a798c656ed0..2b64020a823 100644 --- a/build/pkgs/_prereq/SPKG.rst +++ b/build/pkgs/_prereq/SPKG.rst @@ -4,5 +4,49 @@ _prereq: Represents system packages required for installing SageMath from source Description ----------- -This script package represents the minimal requirements (system packages) +This dummy package represents the minimal requirements (system packages) for installing SageMath from source. + +In addition to standard :wikipedia:`POSIX <POSIX>` utilities +and the :wikipedia:`bash <Bash_(Unix_shell)>` shell, +the following standard command-line development tools must be installed on your +computer: + +- **make**: GNU make, version 3.80 or later. Version 3.82 or later is recommended. +- **m4**: GNU m4 1.4.2 or later (non-GNU or older versions might also work). +- **perl**: version 5.8.0 or later. +- **ar** and **ranlib**: can be obtained as part of GNU binutils. +- **tar**: GNU tar version 1.17 or later, or BSD tar (as provided on macOS). +- **python**: Python 3.4 or later, or Python 2.7. + (This range of versions is a minimal requirement for internal purposes of the SageMath + build system, which is referred to as ``sage-bootstrap-python``.) + +Other versions of these may work, but they are untested. + +On macOS, suitable versions of all of these tools are provided +by the Xcode Command Line Tools. To install them, open a terminal +window and run ``xcode-select --install``; then click "Install" in the +pop-up window. If the Xcode Command Line Tools are already installed, +you may want to check if they need to be updated by typing +``softwareupdate -l``. + +On Linux, ``ar`` and ``ranlib`` are in the `binutils +<https://www.gnu.org/software/binutils/>`_ package. The other +programs are usually located in packages with their respective names. + +On Redhat-derived systems not all perl components are installed by +default and you might have to install the ``perl-ExtUtils-MakeMaker`` +package. + +To check if you have the above prerequisites installed, for example ``perl``, +type:: + + $ command -v perl + +or:: + + $ which perl + +on the command line. If it gives an error (or returns nothing), then +either ``perl`` is not installed, or it is installed but not in your +:wikipedia:`PATH <PATH_%28variable%29>`. diff --git a/build/pkgs/_python3.10/distros/arch.txt b/build/pkgs/_python3.10/distros/arch.txt new file mode 100644 index 00000000000..92826c681b4 --- /dev/null +++ b/build/pkgs/_python3.10/distros/arch.txt @@ -0,0 +1 @@ +python310 diff --git a/build/pkgs/_python3.10/distros/cygwin.txt b/build/pkgs/_python3.10/distros/cygwin.txt new file mode 100644 index 00000000000..92826c681b4 --- /dev/null +++ b/build/pkgs/_python3.10/distros/cygwin.txt @@ -0,0 +1 @@ +python310 diff --git a/build/pkgs/_python3.10/distros/debian.txt b/build/pkgs/_python3.10/distros/debian.txt new file mode 100644 index 00000000000..a3e949f403c --- /dev/null +++ b/build/pkgs/_python3.10/distros/debian.txt @@ -0,0 +1,4 @@ +python3.10 +python3.10-dev +python3.10-distutils +python3.10-venv diff --git a/build/pkgs/_python3.10/distros/fedora.txt b/build/pkgs/_python3.10/distros/fedora.txt new file mode 100644 index 00000000000..90e42c91602 --- /dev/null +++ b/build/pkgs/_python3.10/distros/fedora.txt @@ -0,0 +1,2 @@ +python310 +python310-devel diff --git a/build/pkgs/_python3.10/distros/freebsd.txt b/build/pkgs/_python3.10/distros/freebsd.txt new file mode 100644 index 00000000000..92826c681b4 --- /dev/null +++ b/build/pkgs/_python3.10/distros/freebsd.txt @@ -0,0 +1 @@ +python310 diff --git a/build/pkgs/_python3.10/distros/homebrew.txt b/build/pkgs/_python3.10/distros/homebrew.txt new file mode 100644 index 00000000000..b669c26e698 --- /dev/null +++ b/build/pkgs/_python3.10/distros/homebrew.txt @@ -0,0 +1 @@ +python@3.10 diff --git a/build/pkgs/_python3.10/distros/macports.txt b/build/pkgs/_python3.10/distros/macports.txt new file mode 100644 index 00000000000..92826c681b4 --- /dev/null +++ b/build/pkgs/_python3.10/distros/macports.txt @@ -0,0 +1 @@ +python310 diff --git a/build/pkgs/_python3.10/distros/opensuse.txt b/build/pkgs/_python3.10/distros/opensuse.txt new file mode 100644 index 00000000000..90e42c91602 --- /dev/null +++ b/build/pkgs/_python3.10/distros/opensuse.txt @@ -0,0 +1,2 @@ +python310 +python310-devel diff --git a/build/pkgs/_python3.11/distros/arch.txt b/build/pkgs/_python3.11/distros/arch.txt new file mode 100644 index 00000000000..1d66f45569a --- /dev/null +++ b/build/pkgs/_python3.11/distros/arch.txt @@ -0,0 +1 @@ +python311 diff --git a/build/pkgs/_python3.11/distros/cygwin.txt b/build/pkgs/_python3.11/distros/cygwin.txt new file mode 100644 index 00000000000..1d66f45569a --- /dev/null +++ b/build/pkgs/_python3.11/distros/cygwin.txt @@ -0,0 +1 @@ +python311 diff --git a/build/pkgs/_python3.11/distros/debian.txt b/build/pkgs/_python3.11/distros/debian.txt new file mode 100644 index 00000000000..8877c2d2af0 --- /dev/null +++ b/build/pkgs/_python3.11/distros/debian.txt @@ -0,0 +1,4 @@ +python3.11 +python3.11-dev +python3.11-distutils +python3.11-venv diff --git a/build/pkgs/_python3.11/distros/fedora.txt b/build/pkgs/_python3.11/distros/fedora.txt new file mode 100644 index 00000000000..a478404f8fb --- /dev/null +++ b/build/pkgs/_python3.11/distros/fedora.txt @@ -0,0 +1,2 @@ +python311 +python311-devel diff --git a/build/pkgs/_python3.11/distros/freebsd.txt b/build/pkgs/_python3.11/distros/freebsd.txt new file mode 100644 index 00000000000..1d66f45569a --- /dev/null +++ b/build/pkgs/_python3.11/distros/freebsd.txt @@ -0,0 +1 @@ +python311 diff --git a/build/pkgs/_python3.11/distros/homebrew.txt b/build/pkgs/_python3.11/distros/homebrew.txt new file mode 100644 index 00000000000..f80da9b2e50 --- /dev/null +++ b/build/pkgs/_python3.11/distros/homebrew.txt @@ -0,0 +1 @@ +python@3.11 diff --git a/build/pkgs/_python3.11/distros/macports.txt b/build/pkgs/_python3.11/distros/macports.txt new file mode 100644 index 00000000000..1d66f45569a --- /dev/null +++ b/build/pkgs/_python3.11/distros/macports.txt @@ -0,0 +1 @@ +python311 diff --git a/build/pkgs/_python3.11/distros/opensuse.txt b/build/pkgs/_python3.11/distros/opensuse.txt new file mode 100644 index 00000000000..a478404f8fb --- /dev/null +++ b/build/pkgs/_python3.11/distros/opensuse.txt @@ -0,0 +1,2 @@ +python311 +python311-devel diff --git a/build/pkgs/_python3.12/distros/arch.txt b/build/pkgs/_python3.12/distros/arch.txt new file mode 100644 index 00000000000..a1bf1c64c82 --- /dev/null +++ b/build/pkgs/_python3.12/distros/arch.txt @@ -0,0 +1 @@ +python312 diff --git a/build/pkgs/_python3.12/distros/cygwin.txt b/build/pkgs/_python3.12/distros/cygwin.txt new file mode 100644 index 00000000000..a1bf1c64c82 --- /dev/null +++ b/build/pkgs/_python3.12/distros/cygwin.txt @@ -0,0 +1 @@ +python312 diff --git a/build/pkgs/_python3.12/distros/debian.txt b/build/pkgs/_python3.12/distros/debian.txt new file mode 100644 index 00000000000..20ac50641bf --- /dev/null +++ b/build/pkgs/_python3.12/distros/debian.txt @@ -0,0 +1,4 @@ +python3.12 +python3.12-dev +python3.12-distutils +python3.12-venv diff --git a/build/pkgs/_python3.12/distros/fedora.txt b/build/pkgs/_python3.12/distros/fedora.txt new file mode 100644 index 00000000000..b7ad10be81b --- /dev/null +++ b/build/pkgs/_python3.12/distros/fedora.txt @@ -0,0 +1,2 @@ +python312 +python312-devel diff --git a/build/pkgs/_python3.12/distros/freebsd.txt b/build/pkgs/_python3.12/distros/freebsd.txt new file mode 100644 index 00000000000..a1bf1c64c82 --- /dev/null +++ b/build/pkgs/_python3.12/distros/freebsd.txt @@ -0,0 +1 @@ +python312 diff --git a/build/pkgs/_python3.12/distros/homebrew.txt b/build/pkgs/_python3.12/distros/homebrew.txt new file mode 100644 index 00000000000..e3a91b82a27 --- /dev/null +++ b/build/pkgs/_python3.12/distros/homebrew.txt @@ -0,0 +1 @@ +python@3.12 diff --git a/build/pkgs/_python3.12/distros/macports.txt b/build/pkgs/_python3.12/distros/macports.txt new file mode 100644 index 00000000000..a1bf1c64c82 --- /dev/null +++ b/build/pkgs/_python3.12/distros/macports.txt @@ -0,0 +1 @@ +python312 diff --git a/build/pkgs/_python3.12/distros/opensuse.txt b/build/pkgs/_python3.12/distros/opensuse.txt new file mode 100644 index 00000000000..b7ad10be81b --- /dev/null +++ b/build/pkgs/_python3.12/distros/opensuse.txt @@ -0,0 +1,2 @@ +python312 +python312-devel diff --git a/build/pkgs/_python3.8/distros/arch.txt b/build/pkgs/_python3.8/distros/arch.txt new file mode 100644 index 00000000000..398ae3228b3 --- /dev/null +++ b/build/pkgs/_python3.8/distros/arch.txt @@ -0,0 +1 @@ +python38 diff --git a/build/pkgs/_python3.8/distros/cygwin.txt b/build/pkgs/_python3.8/distros/cygwin.txt new file mode 100644 index 00000000000..398ae3228b3 --- /dev/null +++ b/build/pkgs/_python3.8/distros/cygwin.txt @@ -0,0 +1 @@ +python38 diff --git a/build/pkgs/_python3.8/distros/debian.txt b/build/pkgs/_python3.8/distros/debian.txt new file mode 100644 index 00000000000..bf46e908ff6 --- /dev/null +++ b/build/pkgs/_python3.8/distros/debian.txt @@ -0,0 +1,4 @@ +python3.8 +python3.8-dev +python3.8-distutils +python3.8-venv diff --git a/build/pkgs/_python3.8/distros/fedora.txt b/build/pkgs/_python3.8/distros/fedora.txt new file mode 100644 index 00000000000..1f9ac08ba8e --- /dev/null +++ b/build/pkgs/_python3.8/distros/fedora.txt @@ -0,0 +1,2 @@ +python38 +python38-devel diff --git a/build/pkgs/_python3.8/distros/freebsd.txt b/build/pkgs/_python3.8/distros/freebsd.txt new file mode 100644 index 00000000000..398ae3228b3 --- /dev/null +++ b/build/pkgs/_python3.8/distros/freebsd.txt @@ -0,0 +1 @@ +python38 diff --git a/build/pkgs/_python3.8/distros/homebrew.txt b/build/pkgs/_python3.8/distros/homebrew.txt new file mode 100644 index 00000000000..ea9989e790c --- /dev/null +++ b/build/pkgs/_python3.8/distros/homebrew.txt @@ -0,0 +1 @@ +python@3.8 diff --git a/build/pkgs/_python3.8/distros/macports.txt b/build/pkgs/_python3.8/distros/macports.txt new file mode 100644 index 00000000000..398ae3228b3 --- /dev/null +++ b/build/pkgs/_python3.8/distros/macports.txt @@ -0,0 +1 @@ +python38 diff --git a/build/pkgs/_python3.8/distros/opensuse.txt b/build/pkgs/_python3.8/distros/opensuse.txt new file mode 100644 index 00000000000..1f9ac08ba8e --- /dev/null +++ b/build/pkgs/_python3.8/distros/opensuse.txt @@ -0,0 +1,2 @@ +python38 +python38-devel diff --git a/build/pkgs/_python3.9/distros/arch.txt b/build/pkgs/_python3.9/distros/arch.txt new file mode 100644 index 00000000000..6a2d05c5edb --- /dev/null +++ b/build/pkgs/_python3.9/distros/arch.txt @@ -0,0 +1 @@ +python39 diff --git a/build/pkgs/_python3.9/distros/cygwin.txt b/build/pkgs/_python3.9/distros/cygwin.txt new file mode 100644 index 00000000000..6a2d05c5edb --- /dev/null +++ b/build/pkgs/_python3.9/distros/cygwin.txt @@ -0,0 +1 @@ +python39 diff --git a/build/pkgs/_python3.9/distros/debian.txt b/build/pkgs/_python3.9/distros/debian.txt new file mode 100644 index 00000000000..014b90fd8ad --- /dev/null +++ b/build/pkgs/_python3.9/distros/debian.txt @@ -0,0 +1,4 @@ +python3.9 +python3.9-dev +python3.9-distutils +python3.9-venv diff --git a/build/pkgs/_python3.9/distros/fedora.txt b/build/pkgs/_python3.9/distros/fedora.txt new file mode 100644 index 00000000000..046ffc713b3 --- /dev/null +++ b/build/pkgs/_python3.9/distros/fedora.txt @@ -0,0 +1,2 @@ +python39 +python39-devel diff --git a/build/pkgs/_python3.9/distros/freebsd.txt b/build/pkgs/_python3.9/distros/freebsd.txt new file mode 100644 index 00000000000..6a2d05c5edb --- /dev/null +++ b/build/pkgs/_python3.9/distros/freebsd.txt @@ -0,0 +1 @@ +python39 diff --git a/build/pkgs/_python3.9/distros/homebrew.txt b/build/pkgs/_python3.9/distros/homebrew.txt new file mode 100644 index 00000000000..62a4a244e21 --- /dev/null +++ b/build/pkgs/_python3.9/distros/homebrew.txt @@ -0,0 +1 @@ +python@3.9 diff --git a/build/pkgs/_python3.9/distros/macports.txt b/build/pkgs/_python3.9/distros/macports.txt new file mode 100644 index 00000000000..6a2d05c5edb --- /dev/null +++ b/build/pkgs/_python3.9/distros/macports.txt @@ -0,0 +1 @@ +python39 diff --git a/build/pkgs/_python3.9/distros/opensuse.txt b/build/pkgs/_python3.9/distros/opensuse.txt new file mode 100644 index 00000000000..046ffc713b3 --- /dev/null +++ b/build/pkgs/_python3.9/distros/opensuse.txt @@ -0,0 +1,2 @@ +python39 +python39-devel diff --git a/build/pkgs/antic/SPKG.rst b/build/pkgs/antic/SPKG.rst new file mode 100644 index 00000000000..d6c32377957 --- /dev/null +++ b/build/pkgs/antic/SPKG.rst @@ -0,0 +1,18 @@ +antic: Algebraic Number Theory In C +=================================== + +Description +----------- + +Algebraic Number Theory In C + +License +------- + +LGPL 2.1 + +Upstream Contact +---------------- + +https://github.com/wbhart/antic + diff --git a/build/pkgs/antic/checksums.ini b/build/pkgs/antic/checksums.ini new file mode 100644 index 00000000000..fc8711ecd13 --- /dev/null +++ b/build/pkgs/antic/checksums.ini @@ -0,0 +1,5 @@ +tarball=antic-VERSION.tar.gz +sha1=940d8ea2c3512b9d49ee3101cf043f777764bd8f +md5=4e896420dd6344b53b307871efb2cbb4 +cksum=1938565125 +upstream_url=https://github.com/wbhart/antic/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/scipoptsuite/dependencies b/build/pkgs/antic/dependencies similarity index 63% rename from build/pkgs/scipoptsuite/dependencies rename to build/pkgs/antic/dependencies index dfc5ec2e844..c95d2836ce5 100644 --- a/build/pkgs/scipoptsuite/dependencies +++ b/build/pkgs/antic/dependencies @@ -1,4 +1,4 @@ -$(MP_LIBRARY) bliss readline | cmake +$(MP_LIBRARY) mpfr flint ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/antic/distros/arch.txt b/build/pkgs/antic/distros/arch.txt new file mode 100644 index 00000000000..83c7cab14e4 --- /dev/null +++ b/build/pkgs/antic/distros/arch.txt @@ -0,0 +1 @@ +antic diff --git a/build/pkgs/antic/distros/conda.txt b/build/pkgs/antic/distros/conda.txt new file mode 100644 index 00000000000..83c7cab14e4 --- /dev/null +++ b/build/pkgs/antic/distros/conda.txt @@ -0,0 +1 @@ +antic diff --git a/build/pkgs/antic/distros/debian.txt b/build/pkgs/antic/distros/debian.txt new file mode 100644 index 00000000000..8fdcd3e5721 --- /dev/null +++ b/build/pkgs/antic/distros/debian.txt @@ -0,0 +1 @@ +libantic-dev diff --git a/build/pkgs/antic/distros/fedora.txt b/build/pkgs/antic/distros/fedora.txt new file mode 100644 index 00000000000..1b16da9f64b --- /dev/null +++ b/build/pkgs/antic/distros/fedora.txt @@ -0,0 +1 @@ +antic-devel diff --git a/build/pkgs/antic/distros/freebsd.txt b/build/pkgs/antic/distros/freebsd.txt new file mode 100644 index 00000000000..116ff3a26f3 --- /dev/null +++ b/build/pkgs/antic/distros/freebsd.txt @@ -0,0 +1 @@ +math/antic diff --git a/build/pkgs/antic/distros/opensuse.txt b/build/pkgs/antic/distros/opensuse.txt new file mode 100644 index 00000000000..1b16da9f64b --- /dev/null +++ b/build/pkgs/antic/distros/opensuse.txt @@ -0,0 +1 @@ +antic-devel diff --git a/build/pkgs/antic/distros/repology.txt b/build/pkgs/antic/distros/repology.txt new file mode 100644 index 00000000000..83c7cab14e4 --- /dev/null +++ b/build/pkgs/antic/distros/repology.txt @@ -0,0 +1 @@ +antic diff --git a/build/pkgs/antic/package-version.txt b/build/pkgs/antic/package-version.txt new file mode 100644 index 00000000000..3a4036fb450 --- /dev/null +++ b/build/pkgs/antic/package-version.txt @@ -0,0 +1 @@ +0.2.5 diff --git a/build/pkgs/antic/spkg-install.in b/build/pkgs/antic/spkg-install.in new file mode 100644 index 00000000000..c57fa884a20 --- /dev/null +++ b/build/pkgs/antic/spkg-install.in @@ -0,0 +1,19 @@ +cd src + +# Copied from build/pkgs/flint/spkg-install.in: +# Trac #29607: We must always supply --with-gmp, --with-mpfr, +# --with-ntl because otherwise FLINT's configure script uses +# /usr/local, which is always wrong. +# This is why we do not use $SAGE_CONFIGURE_GMP etc. here. +# The value $SAGE_LOCAL is always a safe choice even if the library +# is coming from the system and is found using what is in +# LIBRARY_PATH or LDFLAGS etc. +./configure \ + --disable-static \ + --prefix="$SAGE_LOCAL" \ + --with-gmp="$SAGE_LOCAL" \ + --with-mpfr="$SAGE_LOCAL" \ + --with-flint="$SAGE_LOCAL" || sdh_die "Error: Failed to configure antic." + +sdh_make verbose +sdh_make_install diff --git a/build/pkgs/antic/type b/build/pkgs/antic/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/antic/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/argon2_cffi/checksums.ini b/build/pkgs/argon2_cffi/checksums.ini index 11705963d15..fa87877c2e6 100644 --- a/build/pkgs/argon2_cffi/checksums.ini +++ b/build/pkgs/argon2_cffi/checksums.ini @@ -1,5 +1,5 @@ tarball=argon2-cffi-VERSION.tar.gz -sha1=c79943104960db3024346731d5153392c187c0d7 -md5=e49ccb29351387fd853f31bf19b67f59 -cksum=3765128778 +sha1=c16c1506de0211bdfa23d4d51e780fb4aaff5222 +md5=b7843e8690c790f8e743d37bb75c25a8 +cksum=3700408796 upstream_url=https://pypi.io/packages/source/a/argon2_cffi/argon2-cffi-VERSION.tar.gz diff --git a/build/pkgs/argon2_cffi/package-version.txt b/build/pkgs/argon2_cffi/package-version.txt index e43bba47d76..54d3ad73646 100644 --- a/build/pkgs/argon2_cffi/package-version.txt +++ b/build/pkgs/argon2_cffi/package-version.txt @@ -1 +1 @@ -20.1.0 +21.3.0 diff --git a/build/pkgs/asttokens/checksums.ini b/build/pkgs/asttokens/checksums.ini index 2ebd46ce567..e69f1ecdfb3 100644 --- a/build/pkgs/asttokens/checksums.ini +++ b/build/pkgs/asttokens/checksums.ini @@ -1,5 +1,5 @@ tarball=asttokens-VERSION.tar.gz -sha1=8a9c1fc8752fedb52189441f9f874f5e1afd5866 -md5=0a2a057b9c9a220bffdb3e7512062f17 -cksum=2612374104 +sha1=cca6058c6c23195148be93bfa32c0a0ca9b2f873 +md5=67b269e359fcb404cd8626985f3676ae +cksum=3749309047 upstream_url=https://pypi.io/packages/source/a/asttokens/asttokens-VERSION.tar.gz diff --git a/build/pkgs/asttokens/package-version.txt b/build/pkgs/asttokens/package-version.txt index e01025862f7..7ec1d6db408 100644 --- a/build/pkgs/asttokens/package-version.txt +++ b/build/pkgs/asttokens/package-version.txt @@ -1 +1 @@ -2.0.5 +2.1.0 diff --git a/build/pkgs/attrs/checksums.ini b/build/pkgs/attrs/checksums.ini index f329e28cb08..291539baec1 100644 --- a/build/pkgs/attrs/checksums.ini +++ b/build/pkgs/attrs/checksums.ini @@ -1,5 +1,5 @@ tarball=attrs-VERSION.tar.gz -sha1=693de5a8890c6f7bad4edd6ade6971ab3eaf416b -md5=5a9b5e9ceebc380a13fb93235b11bbda -cksum=2935089723 +sha1=16d99f8e6f84309a4e399babc2e237da87b445ad +md5=0487081b7ead8753fc46cf7c6d1e28e3 +cksum=3993993002 upstream_url=https://pypi.io/packages/source/a/attrs/attrs-VERSION.tar.gz diff --git a/build/pkgs/attrs/package-version.txt b/build/pkgs/attrs/package-version.txt index 8bd85214a48..ee5c2446981 100644 --- a/build/pkgs/attrs/package-version.txt +++ b/build/pkgs/attrs/package-version.txt @@ -1 +1 @@ -21.4.0 +22.1.0 diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini index a98fa598a37..70beeda6613 100644 --- a/build/pkgs/babel/checksums.ini +++ b/build/pkgs/babel/checksums.ini @@ -1,5 +1,5 @@ tarball=Babel-VERSION.tar.gz -sha1=1ce15f82eba5184cabe6ac1491cb58262e27adfd -md5=7166099733d78aa857d74fa50d8ff58c -cksum=1695340328 +sha1=75baeb68d7481a67ba203191aa460c56b0221fda +md5=9ee7784fd452d456206ecd3a12694010 +cksum=227595701 upstream_url=https://pypi.io/packages/source/b/babel/Babel-VERSION.tar.gz diff --git a/build/pkgs/babel/package-version.txt b/build/pkgs/babel/package-version.txt index dedcc7d4335..46b81d815a2 100644 --- a/build/pkgs/babel/package-version.txt +++ b/build/pkgs/babel/package-version.txt @@ -1 +1 @@ -2.9.1 +2.11.0 diff --git a/build/pkgs/bleach/checksums.ini b/build/pkgs/bleach/checksums.ini index 1eb2d84effa..4d4855aab72 100644 --- a/build/pkgs/bleach/checksums.ini +++ b/build/pkgs/bleach/checksums.ini @@ -1,5 +1,5 @@ tarball=bleach-VERSION.tar.gz -sha1=8b4652eb5a4a1cd6dbf35905a25f389da512f940 -md5=97322e672e4b285e6354c40d07166fc4 -cksum=1632919602 +sha1=73c6b8fad993b318859ca65c365ac2191edd35fc +md5=03b5faa43c0d771a86a2c4cb2575d070 +cksum=4204308806 upstream_url=https://pypi.io/packages/source/b/bleach/bleach-VERSION.tar.gz diff --git a/build/pkgs/bleach/package-version.txt b/build/pkgs/bleach/package-version.txt index 0062ac97180..6b244dcd696 100644 --- a/build/pkgs/bleach/package-version.txt +++ b/build/pkgs/bleach/package-version.txt @@ -1 +1 @@ -5.0.0 +5.0.1 diff --git a/build/pkgs/cddlib/distros/homebrew.txt b/build/pkgs/cddlib/distros/homebrew.txt index 11ade1bfb9a..f9afcc0b330 100644 --- a/build/pkgs/cddlib/distros/homebrew.txt +++ b/build/pkgs/cddlib/distros/homebrew.txt @@ -1,3 +1 @@ -# Until https://trac.sagemath.org/ticket/29413 is done, we cannot use homebrew's cddlib, -# which already uses the new upstream include header locations. -#cddlib +cddlib diff --git a/build/pkgs/cddlib/spkg-configure.m4 b/build/pkgs/cddlib/spkg-configure.m4 index ad227f032ed..8508f28512d 100644 --- a/build/pkgs/cddlib/spkg-configure.m4 +++ b/build/pkgs/cddlib/spkg-configure.m4 @@ -40,15 +40,8 @@ EOF AC_MSG_RESULT([yes]) ]) ]) - dnl Recent versions (>= 0.94k) of cddlib put these headers in - dnl a "cddlib" subdirectory, and Debian currently relocates them - dnl under "cdd". But for now they're at the top-level, in e.g. - dnl /usr/include/cdd.h. The lattE and gfan packages within - dnl SageMath both look for them there, so that's where we have to - dnl check, passing up a chance to detect cddlib on Fedora and Debian - dnl for now. Once all of cddlib's consumers know about the new (or - dnl both) locations, we can update this check to support them. - dnl See https://trac.sagemath.org/ticket/29413 + dnl Recent versions (>= 0.94k) of cddlib put cddlib's headers in + dnl a "cddlib" subdirectory. AC_CHECK_HEADER([cddlib/cdd.h],[],[sage_spkg_install_cddlib=yes],[ #include <cddlib/setoper.h> #include <cddlib/cddmp.h> diff --git a/build/pkgs/certifi/checksums.ini b/build/pkgs/certifi/checksums.ini index 87d8378aa08..cab6201a644 100644 --- a/build/pkgs/certifi/checksums.ini +++ b/build/pkgs/certifi/checksums.ini @@ -1,5 +1,5 @@ tarball=certifi-VERSION.tar.gz -sha1=b13e22d55867e2ca5f92e5289cfdc21ba6e343aa -md5=880ed9e5d04aff8f46f5ff82a3a3e395 -cksum=613361382 +sha1=4a6fb9ae2afe62b33bab98ae21c0853d026d64c2 +md5=ff9c8d5c7e7fb083de6b874609c5ca68 +cksum=726096582 upstream_url=https://pypi.io/packages/source/c/certifi/certifi-VERSION.tar.gz diff --git a/build/pkgs/certifi/package-version.txt b/build/pkgs/certifi/package-version.txt index 6b1fb396ceb..e4b8493d095 100644 --- a/build/pkgs/certifi/package-version.txt +++ b/build/pkgs/certifi/package-version.txt @@ -1 +1 @@ -2021.10.8 +2022.9.24 diff --git a/build/pkgs/cffi/checksums.ini b/build/pkgs/cffi/checksums.ini index 9d2863a8496..5e9ebc003f4 100644 --- a/build/pkgs/cffi/checksums.ini +++ b/build/pkgs/cffi/checksums.ini @@ -1,5 +1,5 @@ tarball=cffi-VERSION.tar.gz -sha1=9c51c29e35510adf7f94542e1f8e05611930b07b -md5=f3a3f26cd3335fc597479c9475da0a0b -cksum=3482630007 +sha1=c42a46cd11f6153f299cf10e9c236e8b2a143c21 +md5=f493860a6e98cd0c4178149568a6b4f6 +cksum=585894851 upstream_url=https://pypi.io/packages/source/c/cffi/cffi-VERSION.tar.gz diff --git a/build/pkgs/cffi/package-version.txt b/build/pkgs/cffi/package-version.txt index 141f2e805be..ace44233b4a 100644 --- a/build/pkgs/cffi/package-version.txt +++ b/build/pkgs/cffi/package-version.txt @@ -1 +1 @@ -1.15.0 +1.15.1 diff --git a/build/pkgs/charset_normalizer/checksums.ini b/build/pkgs/charset_normalizer/checksums.ini index 90d2c47aba4..714de114bec 100644 --- a/build/pkgs/charset_normalizer/checksums.ini +++ b/build/pkgs/charset_normalizer/checksums.ini @@ -1,5 +1,5 @@ tarball=charset-normalizer-VERSION.tar.gz -sha1=6824bfae6dec62d93887b53468ea36124db5ecc8 -md5=f6664e0e90dbb3cc9cfc154a980f9864 -cksum=2680691552 +sha1=f976f0ee784273ee6bc06e996fbc192cbb718d18 +md5=a70f9fc85b6b8265c982eca6fe51381f +cksum=1911107732 upstream_url=https://pypi.io/packages/source/c/charset_normalizer/charset-normalizer-VERSION.tar.gz diff --git a/build/pkgs/charset_normalizer/package-version.txt b/build/pkgs/charset_normalizer/package-version.txt index 280a1e3368b..3e3c2f1e5ed 100644 --- a/build/pkgs/charset_normalizer/package-version.txt +++ b/build/pkgs/charset_normalizer/package-version.txt @@ -1 +1 @@ -2.0.12 +2.1.1 diff --git a/build/pkgs/cmake/checksums.ini b/build/pkgs/cmake/checksums.ini index 64ac39d871a..c89abdf4277 100644 --- a/build/pkgs/cmake/checksums.ini +++ b/build/pkgs/cmake/checksums.ini @@ -1,5 +1,5 @@ tarball=cmake-VERSION.tar.gz -sha1=abbeedb49c153be4103eabc95f4ffd94440f4d61 -md5=f616604606184e3c7b870a57e68a7c3b -cksum=2102786355 +sha1=256d6a57a57fa6ceaacd6a2daf708baefd33850c +md5=226dd564164372f9f7d1e21e38e6e8c5 +cksum=2080281918 upstream_url=https://github.com/Kitware/CMake/releases/download/vVERSION/cmake-VERSION.tar.gz diff --git a/build/pkgs/cmake/package-version.txt b/build/pkgs/cmake/package-version.txt index 6075c9a9ff9..693bd59e3e6 100644 --- a/build/pkgs/cmake/package-version.txt +++ b/build/pkgs/cmake/package-version.txt @@ -1 +1 @@ -3.21.0 +3.24.3 diff --git a/build/pkgs/cmake/spkg-configure.m4 b/build/pkgs/cmake/spkg-configure.m4 index bc50446d0ef..ce36e8aa0cc 100644 --- a/build/pkgs/cmake/spkg-configure.m4 +++ b/build/pkgs/cmake/spkg-configure.m4 @@ -1,11 +1,11 @@ SAGE_SPKG_CONFIGURE( [cmake], [ - AC_CACHE_CHECK([for cmake >= 3.4], [ac_cv_path_CMAKE], [ + AC_CACHE_CHECK([for cmake >= 3.11], [ac_cv_path_CMAKE], [ AC_PATH_PROGS_FEATURE_CHECK([CMAKE], [cmake], [ 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"], [ - AX_COMPARE_VERSION([$cmake_version], [ge], [3.4], [ + AX_COMPARE_VERSION([$cmake_version], [ge], [3.11], [ ac_cv_path_CMAKE="$ac_path_CMAKE" ac_path_CMAKE_found=: ]) diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index d7e7fc0fcc1..92c2ef9e108 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=c2e1c4db6762c4d97d5127f5f056e46fe3d5a94d -md5=70dcc35964e4234443c4e77beb2245d7 -cksum=2271434645 +sha1=fc9c71ad5be4e3197007ddb150cba47d15716d89 +md5=0a707bcc4316b8c3e3faca6330fc5dc2 +cksum=3972886119 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index abdbdc62467..61bc327050c 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -04fbc829e9850eedf555acc683666c55cb7052d7 +785ddef6338073b27082bb675446e3ae111b3b5d diff --git a/build/pkgs/contourpy/SPKG.rst b/build/pkgs/contourpy/SPKG.rst new file mode 100644 index 00000000000..f05a24b6922 --- /dev/null +++ b/build/pkgs/contourpy/SPKG.rst @@ -0,0 +1,18 @@ +contourpy: Python library for calculating contours of 2D quadrilateral grids +============================================================================ + +Description +----------- + +Python library for calculating contours of 2D quadrilateral grids + +License +------- + +BSD-3-Clause + +Upstream Contact +---------------- + +https://pypi.org/project/contourpy/ + diff --git a/build/pkgs/contourpy/checksums.ini b/build/pkgs/contourpy/checksums.ini new file mode 100644 index 00000000000..2b346a32ccc --- /dev/null +++ b/build/pkgs/contourpy/checksums.ini @@ -0,0 +1,5 @@ +tarball=contourpy-VERSION.tar.gz +sha1=f8dac7a79be96e2b8f085f79ba386dba54e99e99 +md5=0ed85863802b1323708b400ae7e7bbd7 +cksum=2680473500 +upstream_url=https://pypi.io/packages/source/c/contourpy/contourpy-VERSION.tar.gz diff --git a/build/pkgs/contourpy/dependencies b/build/pkgs/contourpy/dependencies new file mode 100644 index 00000000000..0740ab1d4a7 --- /dev/null +++ b/build/pkgs/contourpy/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) numpy | $(PYTHON_TOOLCHAIN) pybind11 + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/contourpy/install-requires.txt b/build/pkgs/contourpy/install-requires.txt new file mode 100644 index 00000000000..4c311115d71 --- /dev/null +++ b/build/pkgs/contourpy/install-requires.txt @@ -0,0 +1 @@ +contourpy diff --git a/build/pkgs/contourpy/package-version.txt b/build/pkgs/contourpy/package-version.txt new file mode 100644 index 00000000000..af0b7ddbffd --- /dev/null +++ b/build/pkgs/contourpy/package-version.txt @@ -0,0 +1 @@ +1.0.6 diff --git a/build/pkgs/contourpy/spkg-install.in b/build/pkgs/contourpy/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/contourpy/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/zn_poly/type b/build/pkgs/contourpy/type similarity index 100% rename from build/pkgs/zn_poly/type rename to build/pkgs/contourpy/type diff --git a/build/pkgs/cppy/checksums.ini b/build/pkgs/cppy/checksums.ini index 5c781671d43..613b3b3f5b8 100644 --- a/build/pkgs/cppy/checksums.ini +++ b/build/pkgs/cppy/checksums.ini @@ -1,5 +1,5 @@ tarball=cppy-VERSION.tar.gz -sha1=3af4f5f14ef1f9b49d7457e2fa5c241c721db29c -md5=2110891d75aa12551deebba1603428c6 -cksum=793876648 +sha1=c82ee7a4f38e302bfe4de2a695d2bdfefb69951f +md5=7c1f825c43dd66454440932a35b9969c +cksum=1879136901 upstream_url=https://files.pythonhosted.org/packages/source/c/cppy/cppy-VERSION.tar.gz diff --git a/build/pkgs/cppy/package-version.txt b/build/pkgs/cppy/package-version.txt index 9084fa2f716..6085e946503 100644 --- a/build/pkgs/cppy/package-version.txt +++ b/build/pkgs/cppy/package-version.txt @@ -1 +1 @@ -1.1.0 +1.2.1 diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index d81625b5867..998c8a5e962 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,5 +1,5 @@ tarball=cypari2-VERSION.tar.gz -sha1=2a3039aa6bd690206cb58d4c39aef21e736eacf1 -md5=6267c0dace847160763dc1777a390c0a -cksum=3639046443 +sha1=7208fd9d0b636ca7704d3b7d1167bc7c9af2637c +md5=605b157123bd8a498d53572e8096bb8c +cksum=2809951255 upstream_url=https://pypi.io/packages/source/c/cypari2/cypari2-VERSION.tar.gz diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index eca07e4c1a8..ac2cdeba013 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -2.1.2 +2.1.3 diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 6ca43241e34..babe4be9be1 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.29.32.p1 +0.29.32.p2 diff --git a/build/pkgs/cython/patches/trashcan.patch b/build/pkgs/cython/patches/trashcan.patch index 18e1b4a7052..e1e88ebe465 100644 --- a/build/pkgs/cython/patches/trashcan.patch +++ b/build/pkgs/cython/patches/trashcan.patch @@ -1,5 +1,7 @@ See https://github.com/cython/cython/pull/2842 +and https://github.com/cython/cython/pull/4475 + commit c47c4ef735c4b7f1863b21bbe6f112b06c4aad05 Author: Jeroen Demeyer <J.Demeyer@UGent.be> Date: Thu Feb 14 10:02:41 2019 +0100 @@ -128,7 +130,7 @@ diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c index 50d0e21..ca2adbe 100644 --- a/Cython/Utility/ExtensionTypes.c +++ b/Cython/Utility/ExtensionTypes.c -@@ -74,6 +74,49 @@ static int __Pyx_PyType_Ready(PyTypeObject *t) { +@@ -74,6 +74,54 @@ static int __Pyx_PyType_Ready(PyTypeObject *t) { return r; } @@ -140,7 +142,12 @@ index 50d0e21..ca2adbe 100644 + +// This requires CPython version >= 2.7.4 +// (or >= 3.2.4 but we don't support such old Python 3 versions anyway) -+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070400 ++#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03080000 ++// https://github.com/python/cpython/pull/11841 merged so Cython reimplementation ++// is no longer necessary ++#define __Pyx_TRASHCAN_BEGIN Py_TRASHCAN_BEGIN ++#define __Pyx_TRASHCAN_END Py_TRASHCAN_END ++#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070400 +#define __Pyx_TRASHCAN_BEGIN_CONDITION(op, cond) \ + do { \ + PyThreadState *_tstate = NULL; \ diff --git a/build/pkgs/database_knotinfo/checksums.ini b/build/pkgs/database_knotinfo/checksums.ini index 01afb61648f..9b521522202 100644 --- a/build/pkgs/database_knotinfo/checksums.ini +++ b/build/pkgs/database_knotinfo/checksums.ini @@ -1,5 +1,5 @@ tarball=database_knotinfo-VERSION.tar.gz -sha1=187e6b5ee2a935e3a50bc7648b181dfc7cb7bfa2 -md5=90822e09a1a84c8dbb84e20773c367f1 -cksum=1855405219 +sha1=16039d4e399efc78e4b1278527019f4bcdfdde13 +md5=3095993756f6b51d14c35adae5a75930 +cksum=2884062991 upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz diff --git a/build/pkgs/database_knotinfo/package-version.txt b/build/pkgs/database_knotinfo/package-version.txt index bb762ff812e..eef52011e7f 100644 --- a/build/pkgs/database_knotinfo/package-version.txt +++ b/build/pkgs/database_knotinfo/package-version.txt @@ -1 +1 @@ -2021.10.1 +2022.7.1 diff --git a/build/pkgs/debugpy/checksums.ini b/build/pkgs/debugpy/checksums.ini index 5872243e9f5..94e60c8de63 100644 --- a/build/pkgs/debugpy/checksums.ini +++ b/build/pkgs/debugpy/checksums.ini @@ -1,5 +1,5 @@ tarball=debugpy-VERSION.zip -sha1=5a0066e4641659c63ecc8d6ce35e96a2fd89b195 -md5=27a4789bfda161dc7de6a6860eeeff38 -cksum=708733483 +sha1=44ae7bfe2d355990604f83ee4c24eb81631b4433 +md5=a999f81d29db030bfacab544d5fb0976 +cksum=3944616380 upstream_url=https://pypi.io/packages/source/d/debugpy/debugpy-VERSION.zip diff --git a/build/pkgs/debugpy/package-version.txt b/build/pkgs/debugpy/package-version.txt index dc1e644a101..266146b87cb 100644 --- a/build/pkgs/debugpy/package-version.txt +++ b/build/pkgs/debugpy/package-version.txt @@ -1 +1 @@ -1.6.0 +1.6.3 diff --git a/build/pkgs/distlib/checksums.ini b/build/pkgs/distlib/checksums.ini index 9c739a93823..1d749b2f9a7 100644 --- a/build/pkgs/distlib/checksums.ini +++ b/build/pkgs/distlib/checksums.ini @@ -1,5 +1,5 @@ -tarball=distlib-VERSION.zip -sha1=e7927ebc964676c17d466ed6a345222c34167a85 -md5=c886b7d99b4085c5d960e7435dcbd397 -cksum=10374426 -upstream_url=https://pypi.io/packages/source/d/distlib/distlib-VERSION.zip +tarball=distlib-VERSION.tar.gz +sha1=3a86d49dc17320325004564d0dc86afa808624bc +md5=f60ba4e3f8e76c214d3d00b2227a16f7 +cksum=1543870863 +upstream_url=https://pypi.io/packages/source/d/distlib/distlib-VERSION.tar.gz diff --git a/build/pkgs/distlib/package-version.txt b/build/pkgs/distlib/package-version.txt index 42045acae20..449d7e73a96 100644 --- a/build/pkgs/distlib/package-version.txt +++ b/build/pkgs/distlib/package-version.txt @@ -1 +1 @@ -0.3.4 +0.3.6 diff --git a/build/pkgs/docutils/checksums.ini b/build/pkgs/docutils/checksums.ini index 7eb275b5d93..7ba721d2c06 100644 --- a/build/pkgs/docutils/checksums.ini +++ b/build/pkgs/docutils/checksums.ini @@ -1,5 +1,5 @@ tarball=docutils-VERSION.tar.gz -sha1=f423535c12fcd2a68d4fc52525fbe36020a58ab5 -md5=ed810564c25063e9dac10dd0893ead47 -cksum=3160620183 +sha1=c38c6ccd1547b4d651e39b64dd6be676be5f14d5 +md5=0afa992a6e93db892107c3f087d0d9df +cksum=658477137 upstream_url=https://pypi.io/packages/source/d/docutils/docutils-VERSION.tar.gz diff --git a/build/pkgs/docutils/package-version.txt b/build/pkgs/docutils/package-version.txt index 7cca7711a0d..caa4836d8e0 100644 --- a/build/pkgs/docutils/package-version.txt +++ b/build/pkgs/docutils/package-version.txt @@ -1 +1 @@ -0.17.1 +0.19 diff --git a/build/pkgs/dsdp/SPKG.rst b/build/pkgs/dsdp/SPKG.rst new file mode 100644 index 00000000000..8d7da090b77 --- /dev/null +++ b/build/pkgs/dsdp/SPKG.rst @@ -0,0 +1,27 @@ +dsdp: Semidefinite programming solver +===================================== + +Description +----------- + +Implementation of an interior-point method for semidefinite +programming. It provides primal and dual solutions, exploits low-rank +structure and sparsity in the data, and has relatively low memory +requirements for an interior-point method. It allows feasible and +infeasible starting points and provides approximate certificates of +infeasibility when no feasible solution exists. The dual-scaling +algorithm implemented in this package has a convergence proof and +worst-case polynomial complexity under mild assumptions on the data. + + +License +------- + +Permissive open source license +https://www.mcs.anl.gov/hs/software/DSDP/Copyright.txt + + +Upstream Contact +---------------- + +https://www.mcs.anl.gov/hs/software/DSDP/ diff --git a/build/pkgs/dsdp/checksums.ini b/build/pkgs/dsdp/checksums.ini new file mode 100644 index 00000000000..a82f4f651a8 --- /dev/null +++ b/build/pkgs/dsdp/checksums.ini @@ -0,0 +1,5 @@ +tarball=dsdp_VERSION.orig.tar.gz +sha1=d80b072acf5396561809266ff1c93e7927697aeb +md5=7f49a35f3fe7b5802d29ee2435fdb67e +cksum=955095458 +upstream_url=http://deb.debian.org/debian/pool/main/d/dsdp/dsdp_VERSION.orig.tar.gz diff --git a/build/pkgs/dsdp/dependencies b/build/pkgs/dsdp/dependencies new file mode 100644 index 00000000000..17d27e9459b --- /dev/null +++ b/build/pkgs/dsdp/dependencies @@ -0,0 +1 @@ +$(BLAS) | cmake diff --git a/build/pkgs/dsdp/distros/arch.txt b/build/pkgs/dsdp/distros/arch.txt new file mode 100644 index 00000000000..8e36bbf62f4 --- /dev/null +++ b/build/pkgs/dsdp/distros/arch.txt @@ -0,0 +1 @@ +dsdp diff --git a/build/pkgs/dsdp/distros/conda.txt b/build/pkgs/dsdp/distros/conda.txt new file mode 100644 index 00000000000..8e36bbf62f4 --- /dev/null +++ b/build/pkgs/dsdp/distros/conda.txt @@ -0,0 +1 @@ +dsdp diff --git a/build/pkgs/dsdp/distros/debian.txt b/build/pkgs/dsdp/distros/debian.txt new file mode 100644 index 00000000000..c6fc1af2af1 --- /dev/null +++ b/build/pkgs/dsdp/distros/debian.txt @@ -0,0 +1 @@ +libdsdp-dev diff --git a/build/pkgs/dsdp/distros/fedora.txt b/build/pkgs/dsdp/distros/fedora.txt new file mode 100644 index 00000000000..16a36663269 --- /dev/null +++ b/build/pkgs/dsdp/distros/fedora.txt @@ -0,0 +1 @@ +DSDP-devel diff --git a/build/pkgs/dsdp/distros/freebsd.txt b/build/pkgs/dsdp/distros/freebsd.txt new file mode 100644 index 00000000000..673daddb241 --- /dev/null +++ b/build/pkgs/dsdp/distros/freebsd.txt @@ -0,0 +1 @@ +math/dsdp diff --git a/build/pkgs/dsdp/distros/gentoo.txt b/build/pkgs/dsdp/distros/gentoo.txt new file mode 100644 index 00000000000..56fe1c1e1c3 --- /dev/null +++ b/build/pkgs/dsdp/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/dsdp diff --git a/build/pkgs/dsdp/distros/macports.txt b/build/pkgs/dsdp/distros/macports.txt new file mode 100644 index 00000000000..3df1e205292 --- /dev/null +++ b/build/pkgs/dsdp/distros/macports.txt @@ -0,0 +1 @@ +DSDP diff --git a/build/pkgs/dsdp/distros/repology.txt b/build/pkgs/dsdp/distros/repology.txt new file mode 100644 index 00000000000..8e36bbf62f4 --- /dev/null +++ b/build/pkgs/dsdp/distros/repology.txt @@ -0,0 +1 @@ +dsdp diff --git a/build/pkgs/dsdp/package-version.txt b/build/pkgs/dsdp/package-version.txt new file mode 100644 index 00000000000..3659ea2fa3a --- /dev/null +++ b/build/pkgs/dsdp/package-version.txt @@ -0,0 +1 @@ +5.8 diff --git a/build/pkgs/dsdp/patches/CMakeLists.txt b/build/pkgs/dsdp/patches/CMakeLists.txt new file mode 100644 index 00000000000..48f098aefac --- /dev/null +++ b/build/pkgs/dsdp/patches/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.0) +project (dsdp VERSION 5.8 LANGUAGES C) + +link_directories(${LIBRARY_PREFIX}/lib) + +find_package(LAPACK REQUIRED) + +include_directories(include src/vecmat src/solver src/sdp) +FILE (GLOB_RECURSE SRCS src/*.c) +if(WIN32) + list(APPEND SRCS dsdp.def) +endif() + +## DSDP shared library +add_library(dsdp ${SRCS}) +target_link_libraries(dsdp ${LAPACK_LIBRARIES}) +target_include_directories(dsdp PUBLIC include src/vecmat src/solver src/sdp) +target_compile_options(dsdp PUBLIC -O2) + +if(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + target_compile_definitions(dsdp PUBLIC DSDP_MS_TIME) +else() + target_link_libraries(dsdp m) + target_compile_definitions(dsdp PUBLIC DSDP_TIME) +endif() + +## DSDP5 executable +if(NOT WIN32) + add_executable(dsdp5 examples/readsdpa.c) + add_dependencies(dsdp5 dsdp) + target_link_libraries(dsdp5 dsdp) + target_include_directories(dsdp5 PUBLIC include) +endif() +if(APPLE) + target_compile_definitions(dsdp5 PUBLIC _FORTIFY_SOURCE=0) +endif() + +if(WIN32) +install(TARGETS dsdp + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) +else() +install(TARGETS dsdp5 dsdp + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) +endif() + +FILE (GLOB DSDP_HEADERS include/*.h) +INSTALL(FILES ${DSDP_HEADERS} DESTINATION include) diff --git a/build/pkgs/dsdp/patches/conda-forge-malloc.patch b/build/pkgs/dsdp/patches/conda-forge-malloc.patch new file mode 100644 index 00000000000..3c2b0ba1fbc --- /dev/null +++ b/build/pkgs/dsdp/patches/conda-forge-malloc.patch @@ -0,0 +1,11 @@ +diff -ruN DSDP5.8/src/sys/dsdploginfo.c DSDP5.8_patched/src/sys/dsdploginfo.c +--- DSDP5.8/src/sys/dsdploginfo.c 2005-10-21 21:31:15.000000000 +0200 ++++ DSDP5.8_patched/src/sys/dsdploginfo.c 2018-04-30 09:34:52.000000000 +0200 +@@ -6,7 +6,6 @@ + #include <stdarg.h> + #include <sys/types.h> + #include <stdlib.h> +-#include <malloc.h> + #include "dsdpsys.h" + #include "dsdpbasictypes.h" + diff --git a/build/pkgs/dsdp/patches/debian-type-mismatch.patch b/build/pkgs/dsdp/patches/debian-type-mismatch.patch new file mode 100644 index 00000000000..d698af2b60f --- /dev/null +++ b/build/pkgs/dsdp/patches/debian-type-mismatch.patch @@ -0,0 +1,39 @@ +Description: Use correct integer type for Fortran prototypes and variables + GNU Fortran's default integer width is 32-bit, the same as GCC, therefore use + int rather than long int when interfacing with Fortran. This was an issue on + 64-bit big-endian systems, since the upper 32 bits of the long would be set, + which would also be lost when truncating to a 32-bit integer. +Author: James Clarke <jrtc27@debian.org> +Bug-Debian: https://bugs.debian.org/857067 +Last-Update: 2017-03-28 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/include/dsdplapack.h ++++ b/include/dsdplapack.h +@@ -4,11 +4,11 @@ + \file dsdplapack.h + \brief DSDP uses BLAS and LAPACK for many of its operations. + */ +- +-typedef long int ffinteger; + /* +-typedef int ffinteger; ++typedef long int ffinteger; + */ ++typedef int ffinteger; ++ + /* + #define __DSDP_NONAMEMANGLING + #undef __DSDP_NONAMEMANGLING +--- a/src/vecmat/dtrsm2.c ++++ b/src/vecmat/dtrsm2.c +@@ -1,7 +1,7 @@ + #include "dsdplapack.h" + +-typedef long int integer; +-typedef long int logical; ++typedef int integer; ++typedef int logical; + + #define max(a,b) ((a) >= (b) ? (a) : (b)) + #define dmax(a,b) (double)max(a,b) diff --git a/build/pkgs/dsdp/spkg-install.in b/build/pkgs/dsdp/spkg-install.in new file mode 100644 index 00000000000..8c29005b9b1 --- /dev/null +++ b/build/pkgs/dsdp/spkg-install.in @@ -0,0 +1,9 @@ +cd src +cp ../patches/CMakeLists.txt . +sdh_cmake -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DBLA_VENDOR=OpenBLAS \ + -DBLAS_LIBRARIES="$(pkg-config --libs blas)" \ + -DLAPACK_LIBRARIES="$(pkg-config --libs lapack)" +sdh_make +sdh_make_install diff --git a/build/pkgs/dsdp/type b/build/pkgs/dsdp/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/dsdp/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/e_antic/checksums.ini b/build/pkgs/e_antic/checksums.ini index 15e0fcd9a9d..82757976c54 100644 --- a/build/pkgs/e_antic/checksums.ini +++ b/build/pkgs/e_antic/checksums.ini @@ -1,5 +1,5 @@ tarball=e-antic-VERSION.tar.gz -sha1=f51d90fcffb2c849eebc1013eb14984f9ad59719 -md5=84ab45f0e1eb3ddbbfb175927506b7bc -cksum=3161097188 -upstream_url=https://www.labri.fr/perso/vdelecro/e-antic/e-antic-VERSION.tar.gz +sha1=0fa6ba4a1f13e881f369f9185fe42c7f4bc10a18 +md5=5d77933d78dd08109b0a2c8403892eb6 +cksum=3304746077 +upstream_url=https://github.com/flatsurf/e-antic/releases/download/VERSION/e-antic-VERSION.tar.gz diff --git a/build/pkgs/e_antic/dependencies b/build/pkgs/e_antic/dependencies index ff67f31325b..fea1ffbda45 100644 --- a/build/pkgs/e_antic/dependencies +++ b/build/pkgs/e_antic/dependencies @@ -1,4 +1,4 @@ -$(MP_LIBRARY) flint arb +$(MP_LIBRARY) flint arb antic boost_cropped ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/e_antic/distros/arch.txt b/build/pkgs/e_antic/distros/arch.txt new file mode 100644 index 00000000000..31c9da4b82f --- /dev/null +++ b/build/pkgs/e_antic/distros/arch.txt @@ -0,0 +1 @@ +e-antic diff --git a/build/pkgs/e_antic/distros/debian.txt b/build/pkgs/e_antic/distros/debian.txt new file mode 100644 index 00000000000..43fbfebd54c --- /dev/null +++ b/build/pkgs/e_antic/distros/debian.txt @@ -0,0 +1 @@ +libeantic-dev diff --git a/build/pkgs/e_antic/distros/fedora.txt b/build/pkgs/e_antic/distros/fedora.txt new file mode 100644 index 00000000000..79e976c0953 --- /dev/null +++ b/build/pkgs/e_antic/distros/fedora.txt @@ -0,0 +1 @@ +e-antic-devel diff --git a/build/pkgs/e_antic/distros/freebsd.txt b/build/pkgs/e_antic/distros/freebsd.txt new file mode 100644 index 00000000000..647f5d36fac --- /dev/null +++ b/build/pkgs/e_antic/distros/freebsd.txt @@ -0,0 +1 @@ +math/e-antic diff --git a/build/pkgs/e_antic/distros/opensuse.txt b/build/pkgs/e_antic/distros/opensuse.txt new file mode 100644 index 00000000000..79e976c0953 --- /dev/null +++ b/build/pkgs/e_antic/distros/opensuse.txt @@ -0,0 +1 @@ +e-antic-devel diff --git a/build/pkgs/e_antic/package-version.txt b/build/pkgs/e_antic/package-version.txt index 1a030947e83..6085e946503 100644 --- a/build/pkgs/e_antic/package-version.txt +++ b/build/pkgs/e_antic/package-version.txt @@ -1 +1 @@ -0.1.9 +1.2.1 diff --git a/build/pkgs/e_antic/spkg-install.in b/build/pkgs/e_antic/spkg-install.in index 0c0f5d01a1f..fcaf76055b3 100644 --- a/build/pkgs/e_antic/spkg-install.in +++ b/build/pkgs/e_antic/spkg-install.in @@ -1,10 +1,6 @@ -############################################################################### -# -# e-antic Sage install script -# -############################################################################### cd src -sdh_configure +# Following https://github.com/Normaliz/Normaliz/blob/master/install_scripts_opt/install_nmz_e-antic.sh +sdh_configure --without-byexample --without-doc --without-benchmark --without-pyeantic sdh_make sdh_make_install diff --git a/build/pkgs/executing/checksums.ini b/build/pkgs/executing/checksums.ini index 660ea8654b9..71e107aafbe 100644 --- a/build/pkgs/executing/checksums.ini +++ b/build/pkgs/executing/checksums.ini @@ -1,5 +1,5 @@ tarball=executing-VERSION.tar.gz -sha1=9588832c2abca704a0a287d2c43690c046424697 -md5=43da806fc75eaba315e4947b329d2a90 -cksum=2140182428 +sha1=ac9b0cbedd1166bce7a3b9f8542f8d1fafdd8c73 +md5=e6fa9a6abf00555ccc8a6b3524729238 +cksum=1761713270 upstream_url=https://pypi.io/packages/source/e/executing/executing-VERSION.tar.gz diff --git a/build/pkgs/executing/package-version.txt b/build/pkgs/executing/package-version.txt index ee94dd834b5..26aaba0e866 100644 --- a/build/pkgs/executing/package-version.txt +++ b/build/pkgs/executing/package-version.txt @@ -1 +1 @@ -0.8.3 +1.2.0 diff --git a/build/pkgs/fastjsonschema/checksums.ini b/build/pkgs/fastjsonschema/checksums.ini index 94301089b1a..9b6be281643 100644 --- a/build/pkgs/fastjsonschema/checksums.ini +++ b/build/pkgs/fastjsonschema/checksums.ini @@ -1,5 +1,5 @@ tarball=fastjsonschema-VERSION.tar.gz -sha1=3634374e5004103a3789753f0c145bb798f90874 -md5=c371e5315f66bdd18b62e14c66f89543 -cksum=2483060937 +sha1=a6c53c1eed4f0fbb9c7eaf0fc21fc2c0be85bcd8 +md5=d7d76db7518e64b53a13a7a2315a1671 +cksum=1205433737 upstream_url=https://pypi.io/packages/source/f/fastjsonschema/fastjsonschema-VERSION.tar.gz diff --git a/build/pkgs/fastjsonschema/package-version.txt b/build/pkgs/fastjsonschema/package-version.txt index 6480dd5ed87..43c85e79255 100644 --- a/build/pkgs/fastjsonschema/package-version.txt +++ b/build/pkgs/fastjsonschema/package-version.txt @@ -1 +1 @@ -2.15.3 +2.16.2 diff --git a/build/pkgs/filelock/checksums.ini b/build/pkgs/filelock/checksums.ini index 24a5e470bef..48a18055930 100644 --- a/build/pkgs/filelock/checksums.ini +++ b/build/pkgs/filelock/checksums.ini @@ -1,5 +1,5 @@ tarball=filelock-VERSION.tar.gz -sha1=e0340015dcb7bbe19b5bf33ff8b9c94670994585 -md5=b1032075ddada92874377426337c38a6 -cksum=3903629392 +sha1=1de304add05b7e3e8874aa9f86202204f8042e30 +md5=9bd8d33d5d7dc95012981ccbfb2d2a0f +cksum=2335245752 upstream_url=https://pypi.io/packages/source/f/filelock/filelock-VERSION.tar.gz diff --git a/build/pkgs/filelock/package-version.txt b/build/pkgs/filelock/package-version.txt index 40c341bdcdb..19811903a7f 100644 --- a/build/pkgs/filelock/package-version.txt +++ b/build/pkgs/filelock/package-version.txt @@ -1 +1 @@ -3.6.0 +3.8.0 diff --git a/build/pkgs/flint/patches/0001-flint.h-On-GCC-4.9-do-not-use-_Thread_local.patch b/build/pkgs/flint/patches/0001-flint.h-On-GCC-4.9-do-not-use-_Thread_local.patch deleted file mode 100644 index f37e9c73476..00000000000 --- a/build/pkgs/flint/patches/0001-flint.h-On-GCC-4.9-do-not-use-_Thread_local.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 54e5a36901bcbe5dedadcf3fc670eb00a7ab9193 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Sun, 21 Nov 2021 11:33:59 -0800 -Subject: [PATCH] flint.h: On GCC < 4.9, do not use _Thread_local - ---- - flint.h | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/flint.h b/flint.h -index 2cd15fb29..9d082f7f0 100644 ---- a/flint.h -+++ b/flint.h -@@ -157,7 +157,10 @@ FLINT_DLL void flint_set_abort(FLINT_NORETURN void (*func)(void)); - #define flint_bitcnt_t ulong - - #if FLINT_USES_TLS --#if __STDC_VERSION__ >= 201112L -+#if defined(__GNUC__) && __STDC_VERSION__ >= 201112L && __GNUC__ == 4 && __GNUC_MINOR__ < 9 -+/* GCC 4.7, 4.8 with -std=gnu11 purport to support C11 via __STDC_VERSION__ but lack _Thread_local */ -+#define FLINT_TLS_PREFIX __thread -+#elif __STDC_VERSION__ >= 201112L - #define FLINT_TLS_PREFIX _Thread_local - #elif defined(_MSC_VER) - #define FLINT_TLS_PREFIX __declspec(thread) --- -2.33.0 - diff --git a/build/pkgs/fplll/checksums.ini b/build/pkgs/fplll/checksums.ini index 2d6b70504f8..22fba10e438 100644 --- a/build/pkgs/fplll/checksums.ini +++ b/build/pkgs/fplll/checksums.ini @@ -1,5 +1,5 @@ tarball=fplll-VERSION.tar.gz -sha1=d84ae04deee3a29033c6e28e40c67ed14f8fff32 -md5=9e8ed4e5ff7f3231f9ccf397dca9a117 -cksum=855019078 +sha1=8353a0db588d891951aa9760fbe490f4e308de8d +md5=7e333d7d0e535d27c591271340e28865 +cksum=2543682321 upstream_url=https://github.com/fplll/fplll/releases/download/VERSION/fplll-VERSION.tar.gz diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index 8ae03c11904..426c1c17944 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.4.2 +5.4.4 diff --git a/build/pkgs/fplll/spkg-configure.m4 b/build/pkgs/fplll/spkg-configure.m4 index c220aecc60c..4e58e636d79 100644 --- a/build/pkgs/fplll/spkg-configure.m4 +++ b/build/pkgs/fplll/spkg-configure.m4 @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([fplll], [ dnl Trac #31025: FPLLL/FPyLLL make no guarantee regarding compatibility dnl other than "whatever versions were released at the same time should work together" PKG_CHECK_MODULES([FPLLL], - [fplll >= 5.4.0 fplll <= 5.4.2], + [fplll >= 5.4.4 fplll <= 5.4.4], [ AC_MSG_CHECKING([whether BKZ default strategy JSON is installed]) AC_LANG_PUSH([C++]) diff --git a/build/pkgs/fpylll/checksums.ini b/build/pkgs/fpylll/checksums.ini index 5a3fc413fe2..0172ce17065 100644 --- a/build/pkgs/fpylll/checksums.ini +++ b/build/pkgs/fpylll/checksums.ini @@ -1,5 +1,5 @@ tarball=fpylll-VERSION.tar.gz -sha1=9c4951f4ec50f36805129df4b821e5ea18b7ad30 -md5=d802205f818a9ae5846f8eaa34db7b5c -cksum=3615125514 +sha1=2dcc29155ee11b4460fc79c2d933b6b8230c89f6 +md5=828b3a382594d34d8788e9ff041125bd +cksum=921330875 upstream_url=https://github.com/fplll/fpylll/releases/download/VERSION/fpylll-VERSION.tar.gz diff --git a/build/pkgs/fpylll/install-requires.txt b/build/pkgs/fpylll/install-requires.txt index c1a9bf38bbb..c97d0c2c71e 100644 --- a/build/pkgs/fpylll/install-requires.txt +++ b/build/pkgs/fpylll/install-requires.txt @@ -1 +1 @@ -fpylll >=0.5.6, <=0.5.7 +fpylll >=0.5.9, <=0.5.9 diff --git a/build/pkgs/fpylll/package-version.txt b/build/pkgs/fpylll/package-version.txt index d3532a107ee..416bfb0a221 100644 --- a/build/pkgs/fpylll/package-version.txt +++ b/build/pkgs/fpylll/package-version.txt @@ -1 +1 @@ -0.5.7 +0.5.9 diff --git a/build/pkgs/furo/checksums.ini b/build/pkgs/furo/checksums.ini index 2ba1678635d..b2f2ee12484 100644 --- a/build/pkgs/furo/checksums.ini +++ b/build/pkgs/furo/checksums.ini @@ -1,5 +1,5 @@ tarball=furo-VERSION-py3-none-any.whl -sha1=b9261dbe404cc13d399d50db0122fe48d7daeb23 -md5=fb331872d4d8a7d33f56aeb5df1f333f -cksum=3430203884 +sha1=c27ec5ecd6eb2bd30741d632a29fc1bbcc26e170 +md5=43edbca958fcdcb9df2683a81852a4e6 +cksum=1356193603 upstream_url=https://pypi.io/packages/py3/f/furo/furo-VERSION-py3-none-any.whl diff --git a/build/pkgs/furo/package-version.txt b/build/pkgs/furo/package-version.txt index 95f879eb816..b7fb725c655 100644 --- a/build/pkgs/furo/package-version.txt +++ b/build/pkgs/furo/package-version.txt @@ -1 +1 @@ -2022.6.21 +2022.9.29 diff --git a/build/pkgs/gcc/SPKG.rst b/build/pkgs/gcc/SPKG.rst index 1f5684b86b2..75feee2d6d8 100644 --- a/build/pkgs/gcc/SPKG.rst +++ b/build/pkgs/gcc/SPKG.rst @@ -1,10 +1,70 @@ -gcc: The GNU Compiler Collection, including the C, C++ and Fortran compiler -=========================================================================== +gcc: The GNU Compiler Collection or other suitable C and C++ compilers +====================================================================== Description ----------- -The GNU Compiler Collection, including the C, C++ and Fortran compiler. +This package represents the required C and C++ compilers. + +- GCC (GNU Compiler Collection) versions 8.x to 12.x are supported. + +- Clang (LLVM) is also supported. + +The required Fortran compiler is represented by the package ``gfortran``. + +You can pass the names of compilers to use to ``./configure`` using +the environment variables :envvar:`CC`, :envvar:`CXX`, and +:envvar:`FC`, for C, C++, and Fortran compilers, respectively. + +For example, if your C compiler is ``clang``, your C++ compiler is +``clang++``, and your Fortran compiler is ``flang``, then you would +need to run:: + + $ ./configure CC=clang CXX=clang++ FC=flang + +Vendor and versions of the C and C++ compilers should match. + +Users of older Linux distributions (in particular, ``ubuntu-xenial`` +or older, ``debian-stretch`` or older, ``linuxmint-18`` or older) +should upgrade their systems before attempting to install Sage from +source. Users of ``ubuntu-bionic``, ``linuxmint-19.x``, and +``opensuse-15.x`` can install a versioned ``gcc`` system package +and then use:: + + $ ./configure CC=gcc-8 CXX=g++-8 FC=gfortran-8 + +or similar. Users on ``ubuntu`` can also install a modern compiler +toolchain `using the ubuntu-toolchain-r ppa +<https://askubuntu.com/questions/1140183/install-gcc-9-on-ubuntu-18-04/1149383#1149383>`_. +On ``ubuntu-trusty``, also the package ``binutils-2.26`` is required; +after installing it, make it available using ``export +PATH="/usr/lib/binutils-2.26/bin:$PATH"``. Instead of upgrading their +distribution, users of ``centos-7`` can install a modern compiler +toolchain `using Redhat's devtoolset +<https://stackoverflow.com/a/67212990/557937>`_. + +This package uses the non-standard default +``configure --with-system-gcc=force``, giving an error at ``configure`` +time when no suitable system compilers are configured. + +You can override this using ``./configure --without-system-gcc``. In +this case, Sage builds and installs the GNU Compiler Collection, +including the C, C++ and Fortran compiler. This is not recommended. +You will need suitable C and C++ compilers from which GCC can +bootstrap itself. There are some known problems with old assemblers, +in particular when building the ``ecm`` and ``fflas_ffpack`` +packages. You should ensure that your assembler understands all +instructions for your processor. On Linux, this means you need a +recent version of ``binutils`` (not provided by an SPKG); on macOS +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 +will likely fail. License ------- diff --git a/build/pkgs/gcc/build-gcc b/build/pkgs/gcc/build-gcc index 1b3c72b1298..a185603d453 100755 --- a/build/pkgs/gcc/build-gcc +++ b/build/pkgs/gcc/build-gcc @@ -54,9 +54,11 @@ fi if [ -n "$AS" -a "$AS" != "as" ]; then CONFIGURE_AS="--with-as=$AS" fi +unset AS if [ -n "$LD" -a "$LD" != "ld" ]; then CONFIGURE_LD="--with-ld=$LD" fi +unset LD # Use SAGE_CXX_WITHOUT_STD instead of CXX. # This fixes #29162 (gfortran 9.2.0 compile error on debian-jessie with gcc 4.9.2) diff --git a/build/pkgs/gcc/checksums.ini b/build/pkgs/gcc/checksums.ini index 2ea976e85c1..996f8360f45 100644 --- a/build/pkgs/gcc/checksums.ini +++ b/build/pkgs/gcc/checksums.ini @@ -1,5 +1,5 @@ tarball=gcc-VERSION.tar.xz -sha1=cf86a48278f9a6f4b03d4390550577b20353b4e9 -md5=4ee3e8c4c99e7b3444eb79f00f5f7a7e -cksum=215110545 +sha1=5dce6dc0091b8049b530d1587513a07201691760 +md5=73bafd0af874439dcdb9fc063b6fb069 +cksum=2807184004 upstream_url=https://mirrors.kernel.org/gnu/gcc/gcc-VERSION/gcc-VERSION.tar.xz diff --git a/build/pkgs/gcc/package-version.txt b/build/pkgs/gcc/package-version.txt index f628d2eafc5..685332623b2 100644 --- a/build/pkgs/gcc/package-version.txt +++ b/build/pkgs/gcc/package-version.txt @@ -1 +1 @@ -11.3.0 +12.2.0 diff --git a/build/pkgs/gcc/patches/gcc-12.2.0-arm.patch b/build/pkgs/gcc/patches/gcc-12.2.0-arm.patch new file mode 100644 index 00000000000..bcaade28c15 --- /dev/null +++ b/build/pkgs/gcc/patches/gcc-12.2.0-arm.patch @@ -0,0 +1,14243 @@ +diff --git a/Makefile.def b/Makefile.def +index 72d58549645..25b8563a808 100644 +--- a/Makefile.def ++++ b/Makefile.def +@@ -47,7 +47,8 @@ host_modules= { module= fixincludes; bootstrap=true; + host_modules= { module= flex; no_check_cross= true; }; + host_modules= { module= gas; bootstrap=true; }; + host_modules= { module= gcc; bootstrap=true; +- extra_make_flags="$(EXTRA_GCC_FLAGS)"; }; ++ extra_make_flags="$(EXTRA_GCC_FLAGS)"; ++ extra_configure_flags='--enable-pie-tools=@enable_pie_tools@'; }; + host_modules= { module= gmp; lib_path=.libs; bootstrap=true; + // Work around in-tree gmp configure bug with missing flex. + extra_configure_flags='--disable-shared LEX="touch lex.yy.c"'; +diff --git a/Makefile.in b/Makefile.in +index 593495e1650..807c5947895 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -112,6 +112,9 @@ GCC_SHLIB_SUBDIR = @GCC_SHLIB_SUBDIR@ + # If the build should make suitable code for shared host resources. + host_shared = @host_shared@ + ++# If we should build compilers and supporting tools as PIE. ++enable_pie_tools = @enable_pie_tools@ ++ + # Build programs are put under this directory. + BUILD_SUBDIR = @build_subdir@ + # This is set by the configure script to the arguments to use when configuring +@@ -12012,7 +12015,7 @@ configure-gcc: + $$s/$$module_srcdir/configure \ + --srcdir=$${topdir}/$$module_srcdir \ + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ +- --target=${target_alias} \ ++ --target=${target_alias} --enable-pie-tools=@enable_pie_tools@ \ + || exit 1 + @endif gcc + +@@ -12047,7 +12050,8 @@ configure-stage1-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + \ +- $(STAGE1_CONFIGURE_FLAGS) ++ $(STAGE1_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stage2-gcc maybe-configure-stage2-gcc +@@ -12080,7 +12084,8 @@ configure-stage2-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGE2_CONFIGURE_FLAGS) ++ $(STAGE2_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stage3-gcc maybe-configure-stage3-gcc +@@ -12113,7 +12118,8 @@ configure-stage3-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGE3_CONFIGURE_FLAGS) ++ $(STAGE3_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stage4-gcc maybe-configure-stage4-gcc +@@ -12146,7 +12152,8 @@ configure-stage4-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGE4_CONFIGURE_FLAGS) ++ $(STAGE4_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stageprofile-gcc maybe-configure-stageprofile-gcc +@@ -12179,7 +12186,8 @@ configure-stageprofile-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGEprofile_CONFIGURE_FLAGS) ++ $(STAGEprofile_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stagetrain-gcc maybe-configure-stagetrain-gcc +@@ -12212,7 +12220,8 @@ configure-stagetrain-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGEtrain_CONFIGURE_FLAGS) ++ $(STAGEtrain_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stagefeedback-gcc maybe-configure-stagefeedback-gcc +@@ -12245,7 +12254,8 @@ configure-stagefeedback-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGEfeedback_CONFIGURE_FLAGS) ++ $(STAGEfeedback_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stageautoprofile-gcc maybe-configure-stageautoprofile-gcc +@@ -12278,7 +12288,8 @@ configure-stageautoprofile-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGEautoprofile_CONFIGURE_FLAGS) ++ $(STAGEautoprofile_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + .PHONY: configure-stageautofeedback-gcc maybe-configure-stageautofeedback-gcc +@@ -12311,7 +12322,8 @@ configure-stageautofeedback-gcc: + $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \ + --target=${target_alias} \ + --with-build-libsubdir=$(HOST_SUBDIR) \ +- $(STAGEautofeedback_CONFIGURE_FLAGS) ++ $(STAGEautofeedback_CONFIGURE_FLAGS) \ ++ --enable-pie-tools=@enable_pie_tools@ + @endif gcc-bootstrap + + +diff --git a/Makefile.tpl b/Makefile.tpl +index ef58fac2b9a..925da105c18 100644 +--- a/Makefile.tpl ++++ b/Makefile.tpl +@@ -115,6 +115,9 @@ GCC_SHLIB_SUBDIR = @GCC_SHLIB_SUBDIR@ + # If the build should make suitable code for shared host resources. + host_shared = @host_shared@ + ++# If we should build compilers and supporting tools as PIE. ++enable_pie_tools = @enable_pie_tools@ ++ + # Build programs are put under this directory. + BUILD_SUBDIR = @build_subdir@ + # This is set by the configure script to the arguments to use when configuring +diff --git a/config/mh-darwin b/config/mh-darwin +index b72835ae953..bb4112773c9 100644 +--- a/config/mh-darwin ++++ b/config/mh-darwin +@@ -11,7 +11,8 @@ + # non-bootstrapped compiler), later stages will be built by GCC which supports + # the required flags. + +-# We cannot use mdynamic-no-pic when building shared host resources. ++# We cannot use mdynamic-no-pic when building shared host resources, or for PIE ++# tool executables, which also enables host-shared. + + ifeq (${host_shared},no) + BOOTSTRAP_TOOL_CAN_USE_MDYNAMIC_NO_PIC := $(shell \ +diff --git a/configure b/configure +index 5dcaab14ae9..c690bbec82b 100755 +--- a/configure ++++ b/configure +@@ -685,6 +685,7 @@ get_gcc_base_ver + extra_host_zlib_configure_flags + extra_host_libiberty_configure_flags + stage1_languages ++enable_pie_tools + host_shared + extra_linker_plugin_flags + extra_linker_plugin_configure_flags +@@ -830,6 +831,7 @@ enable_lto + enable_linker_plugin_configure_flags + enable_linker_plugin_flags + enable_host_shared ++enable_pie_tools + enable_stage1_languages + enable_objc_gc + with_target_bdw_gc +@@ -1558,6 +1560,8 @@ Optional Features: + additional flags for configuring and building linker + plugins [none] + --enable-host-shared build host code as shared libraries ++ --enable-pie-tools build Position Independent Executables for the ++ compilers and other tools + --enable-stage1-languages[=all] + choose additional languages to build during stage1. + Mostly useful for compiler development +@@ -8410,6 +8414,20 @@ else + fi + fi + ++case $target in ++ *-darwin2* | *-darwin1[56789]*) ++ # For these versions, we default to using embedded rpaths. ++ if test "x$enable_darwin_at_rpath" != "xno"; then ++ poststage1_ldflags="$poststage1_ldflags -nodefaultrpaths" ++ fi ++ ;; ++ *-darwin*) ++ # For these versions, we only use embedded rpaths on demand. ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ poststage1_ldflags="$poststage1_ldflags -nodefaultrpaths" ++ fi ++ ;; ++esac + + + # GCC GRAPHITE dependency isl. +@@ -8663,6 +8681,42 @@ else + fi + + ++# Check whether --enable-pie-tools was given. ++# Checked early because it can affect host make fragments. ++# Check whether --enable-pie-tools was given. ++if test "${enable_pie_tools+set}" = set; then : ++ enableval=$enable_pie_tools; enable_pie_tools=$enableval ++ case $target in ++ aarch64-*-darwin1[1-9]*) ++ if test x$enable_pie_tools != xyes ; then ++ echo configure.ac: warning: aarch64-darwin must use PIE, pie-tools setting ignored. 1>&2 ++ enable_pie_tools=yes ++ host_shared=yes ++ fi ;; ++ *) ;; ++ esac ++else ++ case $target in ++ # PIE is the default for macOS 10.7+ so reflect that in the configure. ++ # However, we build 32b toolchains mdynamic-no-pic by default which is ++ # not compatible with PIE. ++ x86_64-*-darwin1[1-9]* | *-*-darwin2*) enable_pie_tools=yes ;; ++ *) enable_pie_tools=no ;; ++ esac ++fi ++ ++ ++case $target in ++ *-*-darwin*) ++ if test x$enable_pie_tools = xyes && test x$host_shared != xyes ; then ++ echo configure.ac: warning: for Darwin PIE requires PIC code, switching host-shared on 1>&2 ++ host_shared=yes ++ fi ;; ++ *) ;; ++esac ++ ++ ++ + + # By default, C and C++ are the only stage 1 languages. + stage1_languages=,c, +diff --git a/configure.ac b/configure.ac +index 85977482aee..72bd20fda66 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1827,6 +1827,20 @@ AC_ARG_WITH(boot-ldflags, + if test "$poststage1_libs" = ""; then + poststage1_ldflags="-static-libstdc++ -static-libgcc" + fi]) ++case $target in ++ *-darwin2* | *-darwin1[[56789]]*) ++ # For these versions, we default to using embedded rpaths. ++ if test "x$enable_darwin_at_rpath" != "xno"; then ++ poststage1_ldflags="$poststage1_ldflags -nodefaultrpaths" ++ fi ++ ;; ++ *-darwin*) ++ # For these versions, we only use embedded rpaths on demand. ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ poststage1_ldflags="$poststage1_ldflags -nodefaultrpaths" ++ fi ++ ;; ++esac + AC_SUBST(poststage1_ldflags) + + # GCC GRAPHITE dependency isl. +@@ -1931,7 +1945,41 @@ AC_ARG_ENABLE(host-shared, + x86_64-*-darwin* | aarch64-*-darwin*) host_shared=yes ;; + *) host_shared=no ;; + esac]) ++ ++# Check whether --enable-pie-tools was given. ++# Checked early because it can affect host make fragments. ++AC_ARG_ENABLE(pie-tools, ++[AS_HELP_STRING([--enable-pie-tools], ++ [build Position Independent Executables for the compilers and other tools])], ++[enable_pie_tools=$enableval ++ case $target in ++ aarch64-*-darwin1[[1-9]]*) ++ if test x$enable_pie_tools != xyes ; then ++ echo configure.ac: warning: aarch64-darwin must use PIE, pie-tools setting ignored. 1>&2 ++ enable_pie_tools=yes ++ host_shared=yes ++ fi ;; ++ *) ;; ++ esac], ++[case $target in ++ # PIE is the default for macOS 10.7+ so reflect that in the configure. ++ # However, we build 32b toolchains mdynamic-no-pic by default which is ++ # not compatible with PIE. ++ x86_64-*-darwin1[[1-9]]* | *-*-darwin2*) enable_pie_tools=yes ;; ++ *) enable_pie_tools=no ;; ++ esac]) ++ ++case $target in ++ *-*-darwin*) ++ if test x$enable_pie_tools = xyes && test x$host_shared != xyes ; then ++ echo configure.ac: warning: for Darwin PIE requires PIC code, switching host-shared on 1>&2 ++ host_shared=yes ++ fi ;; ++ *) ;; ++esac ++ + AC_SUBST(host_shared) ++AC_SUBST([enable_pie_tools]) + + # By default, C and C++ are the only stage 1 languages. + stage1_languages=,c, +diff --git a/contrib/compare-debug b/contrib/compare-debug +index cf80ae32695..678a897c931 100755 +--- a/contrib/compare-debug ++++ b/contrib/compare-debug +@@ -60,9 +60,19 @@ trap 'rm -f "$1.$suf1" "$2.$suf2"' 0 1 2 15 + case `uname -s` in + Darwin) + # The strip command on darwin does not remove all debug info. +- # Fortunately, we can use ld to do it instead. +- ld -S -r -no_uuid "$1" -o "$1.$suf1" +- ld -S -r -no_uuid "$2" -o "$2.$suf2" ++ # Fortunately, we can use ld to do it instead, but even ld on earlier ++ # system versions can be fussy about what it finds - make sure we use ++ # a ld that understands coalesced sections. ++ case `uname -r` in ++ 8*) ++ ld64 -S -r -no_uuid "$1" -o "$1.$suf1" ++ ld64 -S -r -no_uuid "$2" -o "$2.$suf2" ++ ;; ++ *) ++ ld -S -r -no_uuid "$1" -o "$1.$suf1" ++ ld -S -r -no_uuid "$2" -o "$2.$suf2" ++ ;; ++ esac + ;; + *) + cp "$1" "$1.$suf1" +diff --git a/fixincludes/configure b/fixincludes/configure +index 6e2d67b655b..b3bca666a4d 100755 +--- a/fixincludes/configure ++++ b/fixincludes/configure +@@ -2644,7 +2644,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + # _LT_DARWIN_LINKER_FEATURES + # -------------------------- +-# Checks for linker and compiler features on darwin ++# Checks for linker and compiler features on Darwin / macOS / iOS + + + # _LT_SYS_MODULE_PATH_AIX +diff --git a/gcc/Makefile.in b/gcc/Makefile.in +index 31ff95500c9..255a10c2ce8 100644 +--- a/gcc/Makefile.in ++++ b/gcc/Makefile.in +@@ -270,11 +270,15 @@ COMPILER += $(CET_HOST_FLAGS) + NO_PIE_CFLAGS = @NO_PIE_CFLAGS@ + NO_PIE_FLAG = @NO_PIE_FLAG@ + +-# We don't want to compile the compilers with -fPIE, it make PCH fail. ++ifneq (@enable_pie_tools@,yes) ++# Build and link the compilers and tools without PIE. + COMPILER += $(NO_PIE_CFLAGS) +- +-# Link with -no-pie since we compile the compiler with -fno-PIE. + LINKER += $(NO_PIE_FLAG) ++else ++# FIXME these need to be configured. ++COMPILER += -fPIE ++LINKER += -pie ++endif + + # Like LINKER, but use a mutex for serializing front end links. + ifeq (@DO_LINK_MUTEX@,true) +@@ -407,6 +411,7 @@ ifeq ($(enable_plugin),yes) + endif + + enable_host_shared = @enable_host_shared@ ++enable_default_pie = @enable_default_pie@ + + enable_as_accelerator = @enable_as_accelerator@ + +@@ -1153,6 +1158,8 @@ LANG_MAKEFRAGS = @all_lang_makefrags@ + # Used by gcc/jit/Make-lang.in + LD_VERSION_SCRIPT_OPTION = @ld_version_script_option@ + LD_SONAME_OPTION = @ld_soname_option@ ++@ENABLE_DARWIN_AT_RPATH_TRUE@DARWIN_RPATH = @rpath ++@ENABLE_DARWIN_AT_RPATH_FALSE@DARWIN_RPATH = ${libdir} + + # Flags to pass to recursive makes. + # CC is set by configure. +@@ -1942,9 +1949,12 @@ cs-tconfig.h: Makefile + $(SHELL) $(srcdir)/mkconfig.sh tconfig.h + + cs-tm.h: Makefile +- TARGET_CPU_DEFAULT="$(target_cpu_default)" \ +- HEADERS="$(tm_include_list)" DEFINES="$(tm_defines)" \ +- $(SHELL) $(srcdir)/mkconfig.sh tm.h ++@ENABLE_DARWIN_AT_RPATH_FALSE@ TARGET_CPU_DEFAULT="$(target_cpu_default)" \ ++@ENABLE_DARWIN_AT_RPATH_FALSE@ HEADERS="$(tm_include_list)" DEFINES="$(tm_defines)" \ ++@ENABLE_DARWIN_AT_RPATH_FALSE@ $(SHELL) $(srcdir)/mkconfig.sh tm.h ++@ENABLE_DARWIN_AT_RPATH_TRUE@ TARGET_CPU_DEFAULT="$(target_cpu_default)" \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ HEADERS="$(tm_include_list)" DEFINES="$(tm_defines) DARWIN_AT_RPATH=1" \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ $(SHELL) $(srcdir)/mkconfig.sh tm.h + + cs-tm_p.h: Makefile + TARGET_CPU_DEFAULT="" \ +@@ -4116,6 +4126,9 @@ site.exp: ./config.status Makefile + echo "set COMPAT_OPTIONS \"$(COMPAT_OPTIONS)\"" >> ./site.tmp; \ + else true; \ + fi ++ @if test "x@enable_darwin_at_rpath@" = "xyes" ; then \ ++ echo "set ENABLE_DARWIN_AT_RPATH 1" >> ./site.tmp; \ ++ fi + @echo "## All variables above are generated by configure. Do Not Edit ##" >> ./site.tmp + @cat ./site.tmp > site.exp + @cat site.bak | sed \ +diff --git a/gcc/aclocal.m4 b/gcc/aclocal.m4 +index 6be36df5190..126e09bbcd1 100644 +--- a/gcc/aclocal.m4 ++++ b/gcc/aclocal.m4 +@@ -12,6 +12,56 @@ + # PARTICULAR PURPOSE. + + m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) ++# AM_CONDITIONAL -*- Autoconf -*- ++ ++# Copyright (C) 1997-2017 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# AM_CONDITIONAL(NAME, SHELL-CONDITION) ++# ------------------------------------- ++# Define a conditional. ++AC_DEFUN([AM_CONDITIONAL], ++[AC_PREREQ([2.52])dnl ++ m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], ++ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl ++AC_SUBST([$1_TRUE])dnl ++AC_SUBST([$1_FALSE])dnl ++_AM_SUBST_NOTMAKE([$1_TRUE])dnl ++_AM_SUBST_NOTMAKE([$1_FALSE])dnl ++m4_define([_AM_COND_VALUE_$1], [$2])dnl ++if $2; then ++ $1_TRUE= ++ $1_FALSE='#' ++else ++ $1_TRUE='#' ++ $1_FALSE= ++fi ++AC_CONFIG_COMMANDS_PRE( ++[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then ++ AC_MSG_ERROR([[conditional "$1" was never defined. ++Usually this means the macro was only invoked conditionally.]]) ++fi])]) ++ ++# Copyright (C) 2006-2017 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# _AM_SUBST_NOTMAKE(VARIABLE) ++# --------------------------- ++# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. ++# This macro is traced by Automake. ++AC_DEFUN([_AM_SUBST_NOTMAKE]) ++ ++# AM_SUBST_NOTMAKE(VARIABLE) ++# -------------------------- ++# Public sister of _AM_SUBST_NOTMAKE. ++AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) ++ + m4_include([../libtool.m4]) + m4_include([../ltoptions.m4]) + m4_include([../ltsugar.m4]) +diff --git a/gcc/ada/Makefile.rtl b/gcc/ada/Makefile.rtl +index aaf853e3a2a..b20218310f3 100644 +--- a/gcc/ada/Makefile.rtl ++++ b/gcc/ada/Makefile.rtl +@@ -2822,6 +2822,15 @@ ifeq ($(strip $(filter-out darwin%,$(target_os))),) + TOOLS_TARGET_PAIRS = indepsw.adb<indepsw-darwin.adb + + EH_MECHANISM=-gcc ++ # Darwin 8 does not support rpaths / @rpath. ++ ifeq ($(strip $(filter-out darwin8%,$(target_os))),) ++ GNATLIB_INSTALL_PREFIX = '$(ADA_RTL_DSO_DIR)' ++ GNATLIB_ADD_RPATHS = ++ else ++ GNATLIB_INSTALL_PREFIX = '@rpath' ++ GNATLIB_ADD_RPATHS = -nodefaultrpaths -Wl,-rpath,@loader_path/,-rpath,@loader_path/.. \ ++ -Wl,-rpath,@loader_path/../../../../ ++ endif + GNATLIB_SHARED = gnatlib-shared-darwin + GMEM_LIB = gmemlib + LIBRARY_VERSION := $(LIB_VERSION) +diff --git a/gcc/ada/gcc-interface/Makefile.in b/gcc/ada/gcc-interface/Makefile.in +index 1e9801a8b96..0ab39609539 100644 +--- a/gcc/ada/gcc-interface/Makefile.in ++++ b/gcc/ada/gcc-interface/Makefile.in +@@ -795,14 +795,15 @@ gnatlib-shared-darwin: + -o libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) \ + $(GNATRTL_NONTASKING_OBJS) $(LIBGNAT_OBJS) \ + $(SO_OPTS) \ +- -Wl,-install_name,@rpath/libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) \ +- $(MISCLIB) ++ -Wl,-install_name,$(GNATLIB_INSTALL_PREFIX)/libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) \ ++ $(GNATLIB_ADD_RPATHS) $(MISCLIB) + cd $(RTSDIR); `echo "$(GCC_FOR_TARGET)" \ + | sed -e 's,\./xgcc,../../xgcc,' -e 's,-B\./,-B../../,'` -dynamiclib $(PICFLAG_FOR_TARGET) \ + -o libgnarl$(hyphen)$(LIBRARY_VERSION)$(soext) \ + $(GNATRTL_TASKING_OBJS) \ + $(SO_OPTS) \ +- -Wl,-install_name,@rpath/libgnarl$(hyphen)$(LIBRARY_VERSION)$(soext) \ ++ -Wl,-install_name,$(GNATLIB_INSTALL_PREFIX)/libgnarl$(hyphen)$(LIBRARY_VERSION)$(soext) \ ++ $(GNATLIB_ADD_RPATHS) \ + $(THREADSLIB) -Wl,libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) + cd $(RTSDIR); $(LN_S) libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) \ + libgnat$(soext) +@@ -811,6 +812,7 @@ gnatlib-shared-darwin: + cd $(RTSDIR); $(DSYMUTIL_FOR_TARGET) libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) + cd $(RTSDIR); $(DSYMUTIL_FOR_TARGET) libgnarl$(hyphen)$(LIBRARY_VERSION)$(soext) + ++ + gnatlib-shared: + $(MAKE) $(FLAGS_TO_PASS) \ + GNATLIBFLAGS="$(GNATLIBFLAGS)" \ +diff --git a/gcc/builtins.def b/gcc/builtins.def +index 005976f34e9..e2cd65eed7b 100644 +--- a/gcc/builtins.def ++++ b/gcc/builtins.def +@@ -950,6 +950,8 @@ DEF_BUILTIN_STUB (BUILT_IN_ADJUST_TRAMPOLINE, "__builtin_adjust_trampoline") + DEF_BUILTIN_STUB (BUILT_IN_INIT_DESCRIPTOR, "__builtin_init_descriptor") + DEF_BUILTIN_STUB (BUILT_IN_ADJUST_DESCRIPTOR, "__builtin_adjust_descriptor") + DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, "__builtin_nonlocal_goto") ++DEF_BUILTIN_STUB (BUILT_IN_NESTED_PTR_CREATED, "__builtin_nested_func_ptr_created") ++DEF_BUILTIN_STUB (BUILT_IN_NESTED_PTR_DELETED, "__builtin_nested_func_ptr_deleted") + + /* Implementing __builtin_setjmp. */ + DEF_BUILTIN_STUB (BUILT_IN_SETJMP_SETUP, "__builtin_setjmp_setup") +diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc +index a341a061758..b584a05c935 100644 +--- a/gcc/c-family/c-opts.cc ++++ b/gcc/c-family/c-opts.cc +@@ -1068,7 +1068,7 @@ c_common_post_options (const char **pfilename) + + if (flag_extern_tls_init) + { +- if (!TARGET_SUPPORTS_ALIASES || !SUPPORTS_WEAK) ++ if (!SUPPORTS_WEAK) + { + /* Lazy TLS initialization for a variable in another TU requires + alias and weak reference support. */ +diff --git a/gcc/calls.cc b/gcc/calls.cc +index 4d0bc45be28..208648a7439 100644 +--- a/gcc/calls.cc ++++ b/gcc/calls.cc +@@ -1355,7 +1355,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 +@@ -1528,6 +1529,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, + #endif + reg_parm_stack_space, + args[i].pass_on_stack ? 0 : args[i].partial, ++ args_so_far, + fndecl, args_size, &args[i].locate); + #ifdef BLOCK_REG_PADDING + else +@@ -4215,6 +4217,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, + argvec[count].reg != 0, + #endif + reg_parm_stack_space, 0, ++ args_so_far, + NULL_TREE, &args_size, &argvec[count].locate); + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +@@ -4306,6 +4309,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, + argvec[count].reg != 0, + #endif + reg_parm_stack_space, argvec[count].partial, ++ args_so_far, + NULL_TREE, &args_size, &argvec[count].locate); + 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 fd7836e481d..a2a4f85e23e 100644 +--- a/gcc/calls.h ++++ b/gcc/calls.h +@@ -35,24 +35,43 @@ class function_arg_info + { + public: + function_arg_info () +- : type (NULL_TREE), mode (VOIDmode), named (false), ++ : type (NULL_TREE), mode (VOIDmode), named (false), last_named (false), + pass_by_reference (false) + {} + + /* Initialize an argument of mode MODE, either before or after promotion. */ + function_arg_info (machine_mode mode, bool named) +- : type (NULL_TREE), mode (mode), named (named), pass_by_reference (false) ++ : type (NULL_TREE), mode (mode), named (named), last_named (false), ++ pass_by_reference (false) ++ {} ++ ++ function_arg_info (machine_mode mode, bool named, bool last_named) ++ : type (NULL_TREE), mode (mode), named (named), last_named (last_named), ++ pass_by_reference (false) + {} + + /* Initialize an unpromoted argument of type TYPE. */ + function_arg_info (tree type, bool named) +- : type (type), mode (TYPE_MODE (type)), named (named), ++ : type (type), mode (TYPE_MODE (type)), named (named), last_named (false), + pass_by_reference (false) + {} + ++ /* Initialize an unpromoted argument of type TYPE. */ ++ function_arg_info (tree type, bool named, bool last_named) ++ : type (type), mode (TYPE_MODE (type)), named (named), ++ last_named (last_named), pass_by_reference (false) ++ {} ++ + /* Initialize an argument with explicit properties. */ + function_arg_info (tree type, machine_mode mode, bool named) +- : type (type), mode (mode), named (named), pass_by_reference (false) ++ : type (type), mode (mode), named (named), last_named (false), ++ pass_by_reference (false) ++ {} ++ ++ /* Initialize an argument with explicit properties. */ ++ function_arg_info (tree type, machine_mode mode, bool named, bool last_named) ++ : type (type), mode (mode), named (named), last_named (last_named), ++ pass_by_reference (false) + {} + + /* Return true if the gimple-level type is an aggregate. */ +@@ -105,6 +124,9 @@ public: + "..."). See also TARGET_STRICT_ARGUMENT_NAMING. */ + unsigned int named : 1; + ++ /* True if this is the last named argument. */ ++ unsigned int last_named : 1; ++ + /* True if we have decided to pass the argument by reference, in which case + the function_arg_info describes a pointer to the original argument. */ + unsigned int pass_by_reference : 1; +diff --git a/gcc/common.opt b/gcc/common.opt +index 8a0dafc522d..7feb4635656 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -2173,6 +2173,10 @@ foffload-abi= + Common Joined RejectNegative Enum(offload_abi) + -foffload-abi=[lp64|ilp32] Set the ABI to use in an offload compiler. + ++foff-stack-trampolines ++Common RejectNegative Var(flag_off_stack_trampolines) Init(OFF_STACK_TRAMPOLINES_INIT) ++Generate trampolines in executable memory rather than executable stack. ++ + Enum + Name(offload_abi) Type(enum offload_abi) UnknownError(unknown offload ABI %qs) + +@@ -2733,6 +2737,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. +@@ -2801,7 +2809,7 @@ Common Var(flag_tracer) Optimization + Perform superblock formation via tail duplication. + + ftrampolines +-Common Var(flag_trampolines) Init(0) ++Common Var(flag_trampolines) Init(OFF_STACK_TRAMPOLINES_INIT) + For targets that normally need trampolines for nested functions, always + generate them instead of using descriptors. + +diff --git a/gcc/config.gcc b/gcc/config.gcc +index c5064dd3766..1562a7bea4b 100644 +--- a/gcc/config.gcc ++++ b/gcc/config.gcc +@@ -1086,6 +1086,23 @@ case ${target} in + ;; + esac + ++# Defaults that need fixing. ++case ${target} in ++aarch64*-*-darwin2*) ++ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=1" ++ tm_defines="$tm_defines OFF_STACK_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 OFF_STACK_TRAMPOLINES_INIT=1" ++ ;; ++*) ++ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=0" ++ tm_defines="$tm_defines OFF_STACK_TRAMPOLINES_INIT=0" ++ ;; ++esac ++ + case ${target} in + aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*) + tm_file="${tm_file} dbxelf.h elfos.h newlib-stdint.h" +@@ -1124,6 +1141,11 @@ 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" ++ ;; + aarch64*-*-freebsd*) + tm_file="${tm_file} dbxelf.h elfos.h ${fbsd_tm_file}" + tm_file="${tm_file} aarch64/aarch64-elf.h aarch64/aarch64-errata.h aarch64/aarch64-freebsd.h" +diff --git a/gcc/config.in b/gcc/config.in +index 64c27c9cfac..5cd26cac57a 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 +@@ -224,6 +237,13 @@ + #endif + + ++/* Define if you build Position Independent Executables for the compilers and ++ other tools. */ ++#ifndef USED_FOR_TARGET ++#undef ENABLE_PIE_TOOLS ++#endif ++ ++ + /* Define to enable plugin support. */ + #ifndef USED_FOR_TARGET + #undef ENABLE_PLUGIN +@@ -2208,6 +2228,12 @@ + #endif + + ++/* Define which stat syscall is able to handle 64bit indodes. */ ++#ifndef USED_FOR_TARGET ++#undef HOST_STAT_FOR_64BIT_INODES ++#endif ++ ++ + /* Define as const if the declaration of iconv() needs const. */ + #ifndef USED_FOR_TARGET + #undef ICONV_CONST +diff --git a/gcc/config/aarch64/aarch64-builtins.cc b/gcc/config/aarch64/aarch64-builtins.cc +index 1b0db677f34..549c9d2c8c2 100644 +--- a/gcc/config/aarch64/aarch64-builtins.cc ++++ b/gcc/config/aarch64/aarch64-builtins.cc +@@ -619,6 +619,10 @@ enum aarch64_builtins + AARCH64_RBIT, + AARCH64_RBITL, + AARCH64_RBITLL, ++ /* OS-specific */ ++ AARCH64_BUILTIN_CFSTRING, ++ AARCH64_BUILTIN_HUGE_VALQ, ++ AARCH64_BUILTIN_INFQ, + AARCH64_BUILTIN_MAX + }; + +@@ -738,6 +742,9 @@ tree aarch64_fp16_ptr_type_node = NULL_TREE; + tree aarch64_bf16_type_node = NULL_TREE; + tree aarch64_bf16_ptr_type_node = NULL_TREE; + ++/* Pointer to __float128 on Mach-O, where the 128b float is not long double. */ ++tree aarch64_float128_ptr_type_node = NULL_TREE; ++ + /* Wrapper around add_builtin_function. NAME is the name of the built-in + function, TYPE is the function type, CODE is the function subcode + (relative to AARCH64_BUILTIN_GENERAL), and ATTRS is the function +@@ -1480,6 +1487,40 @@ aarch64_init_bf16_types (void) + aarch64_bf16_ptr_type_node = build_pointer_type (aarch64_bf16_type_node); + } + ++/* Initialize the backend REAL_TYPE type supporting __float128 on Mach-O, ++ as well as the related built-ins. */ ++static void ++aarch64_init_float128_types (void) ++{ ++ tree ftype, fndecl; ++ ++ /* Populate the float128 node if it is not already done so that the FEs ++ know it is available. */ ++ if (float128_type_node == NULL_TREE) ++ { ++ float128_type_node = make_node (REAL_TYPE); ++ TYPE_PRECISION (float128_type_node) = 128; ++ SET_TYPE_MODE (float128_type_node, TFmode); ++ layout_type (float128_type_node); ++ } ++ ++ lang_hooks.types.register_builtin_type (float128_type_node, "__float128"); ++ aarch64_float128_ptr_type_node = build_pointer_type (float128_type_node); ++ ++ ftype = build_function_type_list (float128_type_node, NULL_TREE); ++ ++ fndecl = aarch64_general_add_builtin ("__builtin_huge_valq", ftype, ++ AARCH64_BUILTIN_HUGE_VALQ); ++ TREE_READONLY (fndecl) = 1; ++ aarch64_builtin_decls[AARCH64_BUILTIN_HUGE_VALQ] = fndecl; ++ ++ fndecl = aarch64_general_add_builtin ("__builtin_infq", ftype, ++ AARCH64_BUILTIN_INFQ); ++ TREE_READONLY (fndecl) = 1; ++ aarch64_builtin_decls[AARCH64_BUILTIN_INFQ] = fndecl; ++} ++ ++ + /* Pointer authentication builtins that will become NOP on legacy platform. + Currently, these builtins are for internal use only (libgcc EH unwinder). */ + +@@ -1767,8 +1808,9 @@ aarch64_general_init_builtins (void) + aarch64_init_fpsr_fpcr_builtins (); + + aarch64_init_fp16_types (); +- + aarch64_init_bf16_types (); ++ if (TARGET_MACHO) ++ aarch64_init_float128_types (); + + { + aarch64_simd_switcher simd; +@@ -1802,6 +1844,14 @@ aarch64_general_init_builtins (void) + aarch64_init_memtag_builtins (); + } + ++void ++aarch64_init_subtarget_builtins (void) ++{ ++#ifdef SUBTARGET_INIT_BUILTINS ++ SUBTARGET_INIT_BUILTINS; ++#endif ++} ++ + /* Implement TARGET_BUILTIN_DECL for the AARCH64_BUILTIN_GENERAL group. */ + tree + aarch64_general_builtin_decl (unsigned code, bool) +@@ -2801,6 +2851,15 @@ aarch64_general_fold_builtin (unsigned int fcode, tree type, + if (aarch64_fold_builtin_lane_check (args[0], args[1], args[2])) + return void_node; + break; ++ case AARCH64_BUILTIN_HUGE_VALQ: ++ case AARCH64_BUILTIN_INFQ: ++ { ++ gcc_assert (n_args == 0); ++ REAL_VALUE_TYPE inf; ++ real_inf (&inf); ++ return build_real (type, inf); ++ } ++ break; + default: + break; + } +diff --git a/gcc/config/aarch64/aarch64-c.cc b/gcc/config/aarch64/aarch64-c.cc +index 767ee0c763c..55ffdbd2f4d 100644 +--- a/gcc/config/aarch64/aarch64-c.cc ++++ b/gcc/config/aarch64/aarch64-c.cc +@@ -369,4 +369,8 @@ aarch64_register_pragmas (void) + targetm.check_builtin_call = aarch64_check_builtin_call; + + c_register_pragma ("GCC", "aarch64", aarch64_pragma_aarch64); ++ ++#ifdef REGISTER_SUBTARGET_PRAGMAS ++ REGISTER_SUBTARGET_PRAGMAS (); ++#endif + } +diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h +index df311812e8d..b84bc8ed68e 100644 +--- a/gcc/config/aarch64/aarch64-protos.h ++++ b/gcc/config/aarch64/aarch64-protos.h +@@ -108,6 +108,14 @@ enum aarch64_symbol_type + SYMBOL_TLSLE24, + SYMBOL_TLSLE32, + SYMBOL_TLSLE48, ++ SYMBOL_MO_SMALL_ABS, ++ SYMBOL_MO_SMALL_PCR, ++ SYMBOL_MO_SMALL_GOT, ++ SYMBOL_MO_SMALL_TLS, ++ SYMBOL_MO_LARGE_ABS, ++ SYMBOL_MO_LARGE_PCR, ++ SYMBOL_MO_LARGE_GOT, ++ SYMBOL_MO_LARGE_TLS, + SYMBOL_FORCE_TO_MEM + }; + +@@ -763,6 +771,7 @@ void aarch64_post_cfi_startproc (void); + poly_int64 aarch64_initial_elimination_offset (unsigned, unsigned); + int aarch64_get_condition_code (rtx); + bool aarch64_address_valid_for_prefetch_p (rtx, bool); ++bool aarch64_address_valid_for_unscaled_prefetch_p (rtx, bool); + bool aarch64_bitmask_imm (HOST_WIDE_INT val, machine_mode); + unsigned HOST_WIDE_INT aarch64_and_split_imm1 (HOST_WIDE_INT val_in); + unsigned HOST_WIDE_INT aarch64_and_split_imm2 (HOST_WIDE_INT val_in); +@@ -914,6 +923,7 @@ void aarch64_expand_vector_init (rtx, rtx); + void aarch64_sve_expand_vector_init (rtx, rtx); + void aarch64_init_cumulative_args (CUMULATIVE_ARGS *, const_tree, rtx, + const_tree, unsigned, bool = false); ++void aarch64_init_cumulative_incoming_args (CUMULATIVE_ARGS *, const_tree, rtx); + void aarch64_init_expanders (void); + void aarch64_init_simd_builtins (void); + void aarch64_emit_call_insn (rtx); +@@ -988,6 +998,7 @@ void aarch64_override_options_internal (struct gcc_options *); + + const char *aarch64_general_mangle_builtin_type (const_tree); + void aarch64_general_init_builtins (void); ++void aarch64_init_subtarget_builtins (void); + tree aarch64_general_fold_builtin (unsigned int, tree, unsigned int, tree *); + gimple *aarch64_general_gimple_fold_builtin (unsigned int, gcall *, + gimple_stmt_iterator *); +diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc +index 5c9e7791a12..91258c925a8 100644 +--- a/gcc/config/aarch64/aarch64.cc ++++ b/gcc/config/aarch64/aarch64.cc +@@ -292,8 +292,10 @@ static bool aarch64_vfp_is_call_or_return_candidate (machine_mode, + const_tree, + machine_mode *, int *, + bool *, bool); ++#if !TARGET_MACHO + static void aarch64_elf_asm_constructor (rtx, int) ATTRIBUTE_UNUSED; + static void aarch64_elf_asm_destructor (rtx, int) ATTRIBUTE_UNUSED; ++#endif + static void aarch64_override_options_after_change (void); + static bool aarch64_vector_mode_supported_p (machine_mode); + static int aarch64_address_cost (rtx, machine_mode, addr_space_t, bool); +@@ -2757,6 +2759,9 @@ static const struct attribute_spec aarch64_attribute_table[] = + { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL }, + { "SVE type", 3, 3, false, true, false, true, NULL, NULL }, + { "SVE sizeless type", 0, 0, false, true, false, true, NULL, NULL }, ++#ifdef SUBTARGET_ATTRIBUTE_TABLE ++ SUBTARGET_ATTRIBUTE_TABLE, ++#endif + { NULL, 0, 0, false, false, false, false, NULL, NULL } + }; + +@@ -3973,7 +3978,7 @@ aarch64_hard_regno_mode_ok (unsigned regno, machine_mode mode) + if (known_le (GET_MODE_SIZE (mode), 8)) + return true; + if (known_le (GET_MODE_SIZE (mode), 16)) +- return (regno & 1) == 0; ++ return (regno & 1) == 0 || TARGET_MACHO; /* darwinpcs D.4 */ + } + else if (FP_REGNUM_P (regno)) + { +@@ -4019,8 +4024,10 @@ static bool + aarch64_takes_arguments_in_sve_regs_p (const_tree fntype) + { + CUMULATIVE_ARGS args_so_far_v; ++ /* This does not apply to variadic functions, so all the (currently ++ uncounted) arguments must be named. */ + aarch64_init_cumulative_args (&args_so_far_v, NULL_TREE, NULL_RTX, +- NULL_TREE, 0, true); ++ NULL_TREE, -1, true); + cumulative_args_t args_so_far = pack_cumulative_args (&args_so_far_v); + + for (tree chain = TYPE_ARG_TYPES (fntype); +@@ -4505,6 +4512,7 @@ aarch64_load_symref_appropriately (rtx dest, rtx imm, + switch (type) + { + case SYMBOL_SMALL_ABSOLUTE: ++ case SYMBOL_MO_SMALL_PCR: + { + /* In ILP32, the mode of dest can be either SImode or DImode. */ + rtx tmp_reg = dest; +@@ -4515,6 +4523,21 @@ aarch64_load_symref_appropriately (rtx dest, rtx imm, + if (can_create_pseudo_p ()) + tmp_reg = gen_reg_rtx (mode); + ++ if (TARGET_MACHO) ++ { ++ rtx sym, off; ++ split_const (imm, &sym, &off); ++ /* Negative offsets don't work, whether by intention is TBD. */ ++ if (INTVAL (off) < 0 || INTVAL (off) > 8 * 1024 * 1024) ++ { ++ emit_move_insn (tmp_reg, gen_rtx_HIGH (mode, sym)); ++ emit_insn (gen_add_losym (dest, tmp_reg, sym)); ++ /* FIXME: add the SI option if/when we support ilp32. */ ++ emit_insn (gen_adddi3 (dest, dest, off)); ++ return; ++ } ++ /* else small enough positive offset is OK. */ ++ } + emit_move_insn (tmp_reg, gen_rtx_HIGH (mode, copy_rtx (imm))); + emit_insn (gen_add_losym (dest, tmp_reg, imm)); + return; +@@ -4598,6 +4621,7 @@ aarch64_load_symref_appropriately (rtx dest, rtx imm, + return; + } + ++ case SYMBOL_MO_SMALL_GOT: + case SYMBOL_SMALL_GOT_4G: + emit_insn (gen_rtx_SET (dest, imm)); + return; +@@ -6659,6 +6683,7 @@ aarch64_expand_mov_immediate (rtx dest, rtx imm) + case SYMBOL_SMALL_TLSIE: + case SYMBOL_SMALL_GOT_28K: + case SYMBOL_SMALL_GOT_4G: ++ case SYMBOL_MO_SMALL_GOT: + case SYMBOL_TINY_GOT: + case SYMBOL_TINY_TLSIE: + if (const_offset != 0) +@@ -6672,6 +6697,7 @@ aarch64_expand_mov_immediate (rtx dest, rtx imm) + /* FALLTHRU */ + + case SYMBOL_SMALL_ABSOLUTE: ++ case SYMBOL_MO_SMALL_PCR: + case SYMBOL_TINY_ABSOLUTE: + case SYMBOL_TLSLE12: + case SYMBOL_TLSLE24: +@@ -7251,6 +7277,7 @@ aarch64_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED) + gcc_unreachable (); + } + ++#if !TARGET_MACHO + static bool + aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode, + const_tree type, int *nregs) +@@ -7260,6 +7287,7 @@ aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode, + &pcum->aapcs_vfp_rmode, + nregs, NULL, pcum->silent_p); + } ++#endif + + /* Given MODE and TYPE of a function argument, return the alignment in + bits. The idea is to suppress any stronger alignment requested by +@@ -7343,6 +7371,13 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + return; + + pcum->aapcs_arg_processed = true; ++ if (TARGET_MACHO) ++ { ++ /* Set suitable defaults for queries. */ ++ pcum->darwinpcs_arg_boundary ++ = aarch64_function_arg_alignment (mode, type, &abi_break); ++ pcum->darwinpcs_arg_padding = BITS_PER_UNIT; ++ } + + pure_scalable_type_info pst_info; + if (type && pst_info.analyze_registers (type)) +@@ -7399,13 +7434,29 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + /* No frontends can create types with variable-sized modes, so we + shouldn't be asked to pass or return them. */ + size = GET_MODE_SIZE (mode).to_constant (); ++ ++ if (TARGET_MACHO) ++ /* Since we can pack things on the stack, we need the unrounded size. */ ++ pcum->darwinpcs_stack_bytes = size; ++ + size = ROUND_UP (size, UNITS_PER_WORD); + + allocate_ncrn = (type) ? !(FLOAT_TYPE_P (type)) : !FLOAT_MODE_P (mode); ++ bool is_ha = false; ++#if !TARGET_MACHO + allocate_nvrn = aarch64_vfp_is_call_candidate (pcum_v, + mode, + type, + &nregs); ++#else ++ /* We care if the value is a homogenous aggregate when laying out the stack, ++ so use this call directly. */ ++ allocate_nvrn ++ = aarch64_vfp_is_call_or_return_candidate (mode, type, ++ &pcum->aapcs_vfp_rmode, ++ &nregs, &is_ha, ++ pcum->silent_p); ++#endif + gcc_assert (!sve_p || !allocate_nvrn); + + /* allocate_ncrn may be false-positive, but allocate_nvrn is quite reliable. +@@ -7420,7 +7471,13 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + if (!pcum->silent_p && !TARGET_FLOAT) + aarch64_err_no_fpadvsimd (mode); + +- if (nvrn + nregs <= NUM_FP_ARG_REGS) ++ if (TARGET_MACHO ++ && !arg.named) ++ { ++ pcum->aapcs_nextnvrn = NUM_FP_ARG_REGS; ++ goto on_stack; ++ } ++ else if (nvrn + nregs <= NUM_FP_ARG_REGS) + { + pcum->aapcs_nextnvrn = nvrn + nregs; + if (!aarch64_composite_type_p (type, mode)) +@@ -7450,6 +7507,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + } + pcum->aapcs_reg = par; + } ++ pcum->darwinpcs_stack_bytes = 0; + return; + } + else +@@ -7466,10 +7524,18 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + /* C6 - C9. though the sign and zero extension semantics are + handled elsewhere. This is the case where the argument fits + entirely general registers. */ ++ + if (allocate_ncrn && (ncrn + nregs <= NUM_ARG_REGS)) + { + gcc_assert (nregs == 0 || nregs == 1 || nregs == 2); + ++ if (TARGET_MACHO ++ && !arg.named) ++ { ++ pcum->aapcs_nextncrn = NUM_ARG_REGS; ++ goto on_stack; ++ } ++ + /* C.8 if the argument has an alignment of 16 then the NGRN is + rounded up to the next even number. */ + if (nregs == 2 +@@ -7479,7 +7545,9 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + alignment nregs should be > 2 and therefore it should be + passed by reference rather than value. */ + && (aarch64_function_arg_alignment (mode, type, &abi_break) +- == 16 * BITS_PER_UNIT)) ++ == 16 * BITS_PER_UNIT) ++ /* Darwin PCS deletes rule C.8. */ ++ && !TARGET_MACHO) + { + if (abi_break && warn_psabi && currently_expanding_gimple_stmt) + inform (input_location, "parameter passing for argument of type " +@@ -7525,8 +7593,8 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + } + pcum->aapcs_reg = par; + } +- + pcum->aapcs_nextncrn = ncrn + nregs; ++ pcum->darwinpcs_stack_bytes = 0; + return; + } + +@@ -7536,10 +7604,87 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) + /* The argument is passed on stack; record the needed number of words for + this argument and align the total size if necessary. */ + on_stack: +- pcum->aapcs_stack_words = size / UNITS_PER_WORD; + +- if (aarch64_function_arg_alignment (mode, type, &abi_break) +- == 16 * BITS_PER_UNIT) ++ unsigned int align = aarch64_function_arg_alignment (mode, type, &abi_break); ++ ++ if (TARGET_MACHO) ++ { ++ /* Darwin does not round up the allocation for smaller entities to 8 ++ bytes. It only requires the natural alignment for these. ++ ++ but we don't do this for: ++ * unnamed parms in variadic functions ++ * complex types ++ * unions ++ * aggregates (except for homogeneous ones which are handles as the ++ enclosed type). ++ each entry starts a new slot. ++ ++ 16 byte entities are naturally aligned on the stack. ++ There was no darwinpcs for GCC 9, so neither the implementation ++ change nor the warning should fire here (i.e. we do not need to check ++ if 16byte entities alter the stack size). */ ++ ++gcc_checking_assert (arg.named == pcum->named_p); ++ pcum->darwinpcs_arg_padding = BITS_PER_UNIT; ++ if (!pcum->named_p ++ || TREE_CODE (type) == COMPLEX_TYPE ++ || (TREE_CODE (type) == RECORD_TYPE ++ && !is_ha && !SCALAR_FLOAT_MODE_P (pcum->aapcs_vfp_rmode)) ++ || TREE_CODE (type) == UNION_TYPE) ++ { ++ pcum->aapcs_stack_words = size / UNITS_PER_WORD; ++ pcum->darwinpcs_sub_word_offset = 0; ++ pcum->darwinpcs_sub_word_pos = 0; ++ pcum->darwinpcs_arg_boundary = MAX (align, PARM_BOUNDARY); ++ if (!pcum->named_p) ++ pcum->darwinpcs_arg_padding = PARM_BOUNDARY; ++ return; ++ } ++ ++ /* Updated sub-word offset aligned for the new object. ++ We are looking for the case that the new object will fit after some ++ existing object(s) in the same stack slot. In that case, we do not ++ need to add any more stack space for it. */ ++ int new_off ++ = ROUND_UP (pcum->darwinpcs_sub_word_pos, align / BITS_PER_UNIT); ++ ++ if (new_off >= UNITS_PER_WORD) ++ { ++ /* That exceeds a stack slot, start a new one. */ ++ pcum->darwinpcs_sub_word_offset = 0; ++ pcum->darwinpcs_sub_word_pos = 0; ++ new_off = 0; ++ } ++ /* This is the end of the new object. */ ++ int new_pos = new_off + pcum->darwinpcs_stack_bytes; ++ ++ if (pcum->darwinpcs_sub_word_pos == 0) ++ /* New stack slot, just allocate one or more words, and note where ++ the next arg will start. */ ++ pcum->aapcs_stack_words = size / UNITS_PER_WORD; ++ else if (new_pos <= UNITS_PER_WORD) ++ /* Old stack slot, object starts at new_off and goes to new_pos, we do ++ not add any stack space. */ ++ pcum->darwinpcs_sub_word_offset = new_off; ++ pcum->darwinpcs_sub_word_pos = new_pos; ++ pcum->darwinpcs_arg_boundary = align; ++ if (pcum->last_named_p && new_pos > 0) ++ { ++ /* Round the last named arg to the start of the next stack slot. */ ++ if (new_pos <= 4) ++ pcum->darwinpcs_arg_padding = PARM_BOUNDARY; ++ else if (new_pos <= 6) ++ pcum->darwinpcs_arg_padding = 4 * BITS_PER_UNIT; ++ else if (pcum->darwinpcs_sub_word_pos <= 7) ++ pcum->darwinpcs_arg_padding = 2 * BITS_PER_UNIT; ++ } ++ return; ++ } ++ ++ /* size was already rounded up to PARM_BOUNDARY. */ ++ pcum->aapcs_stack_words = size / UNITS_PER_WORD; ++ if (align == 16 * BITS_PER_UNIT) + { + int new_size = ROUND_UP (pcum->aapcs_stack_size, 16 / UNITS_PER_WORD); + if (pcum->aapcs_stack_size != new_size) +@@ -7592,7 +7737,28 @@ aarch64_init_cumulative_args (CUMULATIVE_ARGS *pcum, + pcum->aapcs_arg_processed = false; + pcum->aapcs_stack_words = 0; + pcum->aapcs_stack_size = 0; ++ pcum->darwinpcs_stack_bytes = 0; ++ pcum->darwinpcs_sub_word_offset = 0; ++ pcum->darwinpcs_sub_word_pos = 0; ++ pcum->darwinpcs_arg_boundary = BITS_PER_UNIT; ++ pcum->darwinpcs_arg_padding = BITS_PER_UNIT; ++ /* If we have been invoked for incoming args, then n_named will have been ++ set to -1, but we should have a function decl - so pick up the named ++ count from that. If that fails, and we end up with -1, this effectively ++ corresponds to assuming that there is an arbitrary number of named ++ args. */ ++ pcum->darwinpcs_n_named = n_named; ++ if (n_named == (unsigned)-1 && fndecl) ++ { ++ tree fnt = TREE_TYPE (fndecl); ++ if (fnt && TYPE_ARG_TYPES (fnt)) ++ pcum->darwinpcs_n_named = list_length (TYPE_ARG_TYPES (fnt)); ++ } ++ pcum->darwinpcs_n_args_processed = 0; ++ pcum->named_p = pcum->darwinpcs_n_named != 0; ++ pcum->last_named_p = pcum->darwinpcs_n_named == 1; + pcum->silent_p = silent_p; ++ pcum->aapcs_vfp_rmode = VOIDmode; + + if (!silent_p + && !TARGET_FLOAT +@@ -7631,8 +7797,10 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v, + || pcum->pcs_variant == ARM_PCS_SVE) + { + aarch64_layout_arg (pcum_v, arg); +- gcc_assert ((pcum->aapcs_reg != NULL_RTX) +- != (pcum->aapcs_stack_words != 0)); ++ pcum->darwinpcs_n_args_processed++; ++ gcc_assert (TARGET_MACHO ++ || (pcum->aapcs_reg != NULL_RTX) ++ != (pcum->aapcs_stack_words != 0)); + pcum->aapcs_arg_processed = false; + pcum->aapcs_ncrn = pcum->aapcs_nextncrn; + pcum->aapcs_nvrn = pcum->aapcs_nextnvrn; +@@ -7640,6 +7808,12 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v, + pcum->aapcs_stack_size += pcum->aapcs_stack_words; + pcum->aapcs_stack_words = 0; + pcum->aapcs_reg = NULL_RTX; ++ pcum->darwinpcs_arg_boundary = BITS_PER_UNIT; ++ pcum->darwinpcs_arg_padding = BITS_PER_UNIT; ++ pcum->named_p ++ = pcum->darwinpcs_n_args_processed < pcum->darwinpcs_n_named; ++ pcum->last_named_p ++ = pcum->darwinpcs_n_args_processed + 1 == pcum->darwinpcs_n_named; + } + } + +@@ -7650,12 +7824,15 @@ aarch64_function_arg_regno_p (unsigned regno) + || (FP_REGNUM_P (regno) && regno < V0_REGNUM + NUM_FP_ARG_REGS)); + } + +-/* Implement FUNCTION_ARG_BOUNDARY. Every parameter gets at least +- PARM_BOUNDARY bits of alignment, but will be given anything up +- to STACK_BOUNDARY bits if the type requires it. This makes sure +- that both before and after the layout of each argument, the Next +- Stacked Argument Address (NSAA) will have a minimum alignment of +- 8 bytes. */ ++/* Implement FUNCTION_ARG_BOUNDARY. ++ For AAPCS64, Every parameter gets at least PARM_BOUNDARY bits of ++ alignment, but will be given anything up to STACK_BOUNDARY bits ++ if the type requires it. This makes sure that both before and after ++ the layout of each argument, the Next Stacked Argument Address (NSAA) ++ will have a minimum alignment of 8 bytes. ++ ++ For darwinpcs, this is only called to lower va_arg entries which are ++ always aligned as for AAPCS64. */ + + static unsigned int + aarch64_function_arg_boundary (machine_mode mode, const_tree type) +@@ -7663,6 +7840,71 @@ aarch64_function_arg_boundary (machine_mode mode, const_tree type) + unsigned int abi_break; + unsigned int alignment = aarch64_function_arg_alignment (mode, type, + &abi_break); ++#if TARGET_MACHO ++ /* This can only work for unnamed args. */ ++ machine_mode comp_mode = VOIDmode; ++ int nregs; ++ bool is_ha; ++ aarch64_vfp_is_call_or_return_candidate (mode, type, &comp_mode, &nregs, ++ &is_ha, /*silent*/true); ++ if (TREE_CODE (type) == COMPLEX_TYPE ++ || (TREE_CODE (type) == RECORD_TYPE ++ && !is_ha && !SCALAR_FLOAT_MODE_P (comp_mode)) ++ || TREE_CODE (type) == UNION_TYPE) ++ return MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY); ++ return MIN (alignment, STACK_BOUNDARY); ++#else ++ alignment = MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY); ++ if (abi_break & warn_psabi) ++ { ++ abi_break = MIN (MAX (abi_break, PARM_BOUNDARY), STACK_BOUNDARY); ++ if (alignment != abi_break && !TARGET_MACHO) ++ inform (input_location, "parameter passing for argument of type " ++ "%qT changed in GCC 9.1", type); ++ } ++ ++ return alignment; ++#endif ++} ++ ++/* For Darwin, we want to use the arg boundary computed when laying out the ++ function arg, to cope with items packed on the stack and the different ++ rules applied to unnamed parms. */ ++ ++static unsigned int ++aarch64_function_arg_boundary_ca (machine_mode mode ATTRIBUTE_UNUSED, ++ const_tree type ATTRIBUTE_UNUSED, ++ cumulative_args_t ca ATTRIBUTE_UNUSED) ++{ ++ unsigned int abi_break; ++ unsigned int alignment = aarch64_function_arg_alignment (mode, type, ++ &abi_break); ++#if TARGET_MACHO ++ CUMULATIVE_ARGS *pcum = get_cumulative_args (ca); ++gcc_checking_assert (pcum->aapcs_arg_processed); ++ ++ bool named_p = pcum->darwinpcs_n_args_processed < pcum->darwinpcs_n_named; ++gcc_checking_assert (named_p == pcum->named_p); ++ machine_mode comp_mode = VOIDmode; ++ int nregs; ++ bool is_ha; ++ aarch64_vfp_is_call_or_return_candidate (mode, type, &comp_mode, &nregs, ++ &is_ha, /*silent*/true); ++ bool no_pack = (TREE_CODE (type) == COMPLEX_TYPE ++ || (TREE_CODE (type) == RECORD_TYPE ++ && !is_ha && !SCALAR_FLOAT_MODE_P (comp_mode)) ++ || TREE_CODE (type) == UNION_TYPE); ++ ++ bool in_regs = (pcum->aapcs_reg != NULL_RTX); ++ ++ if ((named_p && !no_pack) || in_regs) ++ ; /* Leave the alignment as natural. */ ++ else ++ alignment = MAX (alignment, PARM_BOUNDARY); ++gcc_checking_assert (alignment == pcum->darwinpcs_arg_boundary); ++ return MIN (alignment, STACK_BOUNDARY); ++ ++#else + alignment = MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY); + if (abi_break & warn_psabi) + { +@@ -7673,6 +7915,44 @@ aarch64_function_arg_boundary (machine_mode mode, const_tree type) + } + + return alignment; ++#endif ++} ++ ++/* Implement TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA for darwinpcs which allows ++ non-standard passing of byte-aligned items [D.2]. This is done by pulling ++ the values out of the cumulative args struct. */ ++ ++static unsigned int ++aarch64_function_arg_round_boundary_ca (machine_mode mode ATTRIBUTE_UNUSED, ++ const_tree type ATTRIBUTE_UNUSED, ++ cumulative_args_t ca) ++{ ++ CUMULATIVE_ARGS *pcum = get_cumulative_args (ca); ++gcc_checking_assert (pcum->aapcs_arg_processed); ++ bool named_p = pcum->darwinpcs_n_args_processed < pcum->darwinpcs_n_named; ++gcc_checking_assert (named_p == pcum->named_p); ++ bool last_named_p = pcum->darwinpcs_n_args_processed + 1 == pcum->darwinpcs_n_named; ++gcc_checking_assert (last_named_p == pcum->last_named_p); ++ ++ unsigned boundary = BITS_PER_UNIT; ++ if (last_named_p && pcum->darwinpcs_sub_word_pos > 0) ++ { ++ /* Round the last named arg to the start of the next stack slot. */ ++ if (pcum->darwinpcs_sub_word_pos <= 4) ++ boundary = PARM_BOUNDARY; ++ else if (pcum->darwinpcs_sub_word_pos <= 6) ++ boundary = 4 * BITS_PER_UNIT; ++ else if (pcum->darwinpcs_sub_word_pos <= 7) ++ boundary = 2 * BITS_PER_UNIT; ++ } ++ else if (named_p) ++ /* Named args are naturally aligned, but with no rounding. */ ++ ; ++ else ++ /* un-named args are rounded to fill slots. */ ++ boundary = PARM_BOUNDARY; ++gcc_checking_assert (boundary == pcum->darwinpcs_arg_padding); ++ return boundary; + } + + /* Implement TARGET_GET_RAW_RESULT_MODE and TARGET_GET_RAW_ARG_MODE. */ +@@ -10848,6 +11128,7 @@ aarch64_classify_address (struct aarch64_address_info *info, + /* load literal: pc-relative constant pool entry. Only supported + for SI mode or larger. */ + info->type = ADDRESS_SYMBOLIC; ++ info->offset = NULL_RTX; + + if (!load_store_pair_p + && GET_MODE_SIZE (mode).is_constant (&const_size) +@@ -10855,6 +11136,7 @@ aarch64_classify_address (struct aarch64_address_info *info, + { + poly_int64 offset; + rtx sym = strip_offset_and_salt (x, &offset); ++ + return ((LABEL_REF_P (sym) + || (SYMBOL_REF_P (sym) + && CONSTANT_POOL_ADDRESS_P (sym) +@@ -10872,10 +11154,13 @@ aarch64_classify_address (struct aarch64_address_info *info, + poly_int64 offset; + HOST_WIDE_INT const_offset; + rtx sym = strip_offset_and_salt (info->offset, &offset); ++ + if (SYMBOL_REF_P (sym) + && offset.is_constant (&const_offset) + && (aarch64_classify_symbol (sym, const_offset) +- == SYMBOL_SMALL_ABSOLUTE)) ++ == SYMBOL_SMALL_ABSOLUTE ++ || aarch64_classify_symbol (sym, const_offset) ++ == SYMBOL_MO_SMALL_PCR)) + { + /* The symbol and offset must be aligned to the access size. */ + unsigned int align; +@@ -10925,6 +11210,55 @@ aarch64_address_valid_for_prefetch_p (rtx x, bool strict_p) + if (!res) + return false; + ++ /* For ELF targets using GAS, we emit prfm unconditionally; GAS will alter ++ the instruction to pick the prfum form where possible (i.e. when the ++ offset is in the range -256..255) and fall back to prfm otherwise. ++ We can reject cases where the offset exceeds the range usable by both ++ insns [-256..32760], or for offsets > 255 when the value is not divisible ++ by 8. ++ For Mach-O (Darwin) where the assembler uses the LLVM back end, that does ++ not yet do the substitution, so we must reject all prfum cases. */ ++ if (addr.offset) ++ { ++ HOST_WIDE_INT offs = INTVAL (addr.offset); ++ if (offs < -256) /* Out of range for both prfum and prfm. */ ++ return false; ++ if (offs > 32760) /* Out of range for prfm. */ ++ return false; ++ if (offs & 0x07) /* We cannot use prfm. */ ++ { ++ if (offs > 255) /* Out of range for prfum. */ ++ return false; ++ if (TARGET_MACHO) ++ return false; ++ } ++ if (TARGET_MACHO && offs < 0) ++ return false; ++ } ++ ++ /* ... except writeback forms. */ ++ return addr.type != ADDRESS_REG_WB; ++} ++ ++/* Return true if the address X is valid for a PRFUM instruction. ++ STRICT_P is true if we should do strict checking with ++ aarch64_classify_address. */ ++ ++bool ++aarch64_address_valid_for_unscaled_prefetch_p (rtx x, bool strict_p) ++{ ++ struct aarch64_address_info addr; ++ ++ /* PRFUM accepts the same addresses as DImode, but constrained to a range ++ -256..255. */ ++ bool res = aarch64_classify_address (&addr, x, DImode, strict_p); ++ if (!res) ++ return false; ++ ++ if (addr.offset && ((INTVAL (addr.offset) > 255) ++ || (INTVAL (addr.offset) < -256))) ++ return false; ++ + /* ... except writeback forms. */ + return addr.type != ADDRESS_REG_WB; + } +@@ -11609,6 +11943,144 @@ sizetochar (int size) + } + } + ++static void ++output_macho_postfix_expr (FILE *file, rtx x, const char *postfix) ++{ ++ char buf[256]; ++ ++ restart: ++ switch (GET_CODE (x)) ++ { ++ case PC: ++ putc ('.', file); ++ break; ++ ++ case SYMBOL_REF: ++ if (SYMBOL_REF_DECL (x)) ++ assemble_external (SYMBOL_REF_DECL (x)); ++ assemble_name (file, XSTR (x, 0)); ++ fprintf (file, "@%s", postfix); ++ break; ++ ++ case LABEL_REF: ++ x = label_ref_label (x); ++ /* Fall through. */ ++ case CODE_LABEL: ++ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); ++ assemble_name (file, buf); ++ fprintf (file, "@%s", postfix); ++ break; ++ ++ case CONST_INT: ++ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); ++ break; ++ ++ case CONST: ++ /* This used to output parentheses around the expression, ++ but that does not work on the 386 (either ATT or BSD assembler). */ ++ output_macho_postfix_expr (file, XEXP (x, 0), postfix); ++ break; ++ ++ case CONST_WIDE_INT: ++ /* We do not know the mode here so we have to use a round about ++ way to build a wide-int to get it printed properly. */ ++ { ++ wide_int w = wide_int::from_array (&CONST_WIDE_INT_ELT (x, 0), ++ CONST_WIDE_INT_NUNITS (x), ++ CONST_WIDE_INT_NUNITS (x) ++ * HOST_BITS_PER_WIDE_INT, ++ false); ++ print_decs (w, file); ++ } ++ break; ++ ++ case CONST_DOUBLE: ++ if (CONST_DOUBLE_AS_INT_P (x)) ++ { ++ /* We can use %d if the number is one word and positive. */ ++ if (CONST_DOUBLE_HIGH (x)) ++ fprintf (file, HOST_WIDE_INT_PRINT_DOUBLE_HEX, ++ (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (x), ++ (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (x)); ++ else if (CONST_DOUBLE_LOW (x) < 0) ++ fprintf (file, HOST_WIDE_INT_PRINT_HEX, ++ (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (x)); ++ else ++ fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x)); ++ } ++ else ++ /* We can't handle floating point constants; ++ PRINT_OPERAND must handle them. */ ++ output_operand_lossage ("floating constant misused"); ++ break; ++ ++ case CONST_FIXED: ++ fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_FIXED_VALUE_LOW (x)); ++ break; ++ ++ case PLUS: ++ /* Some assemblers need integer constants to appear last (eg masm). */ ++ if (CONST_INT_P (XEXP (x, 0))) ++ { ++ output_macho_postfix_expr (file, XEXP (x, 1), postfix); ++ if (INTVAL (XEXP (x, 0)) >= 0) ++ fprintf (file, "+"); ++ output_addr_const (file, XEXP (x, 0)); ++ } ++ else ++ { ++ output_macho_postfix_expr (file, XEXP (x, 0), postfix); ++ if (!CONST_INT_P (XEXP (x, 1)) ++ || INTVAL (XEXP (x, 1)) >= 0) ++ fprintf (file, "+"); ++ output_addr_const (file, XEXP (x, 1)); ++ } ++ break; ++ ++ case MINUS: ++ /* Avoid outputting things like x-x or x+5-x, ++ since some assemblers can't handle that. */ ++ x = simplify_subtraction (x); ++ if (GET_CODE (x) != MINUS) ++ goto restart; ++ ++ output_macho_postfix_expr (file, XEXP (x, 0), postfix); ++ fprintf (file, "-"); ++ if ((CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) >= 0) ++ || GET_CODE (XEXP (x, 1)) == PC ++ || GET_CODE (XEXP (x, 1)) == SYMBOL_REF) ++ output_addr_const (file, XEXP (x, 1)); ++ else ++ { ++ fputs (targetm.asm_out.open_paren, file); ++ output_addr_const (file, XEXP (x, 1)); ++ fputs (targetm.asm_out.close_paren, file); ++ } ++ break; ++ ++ case ZERO_EXTEND: ++ case SIGN_EXTEND: ++ case SUBREG: ++ case TRUNCATE: ++ output_addr_const (file, XEXP (x, 0)); ++ break; ++ ++ case UNSPEC: ++ if (XINT (x, 1) == UNSPEC_SALT_ADDR) ++ { ++ output_macho_postfix_expr (file, XVECEXP (x, 0, 0), postfix); ++ break; ++ } ++ /* FALLTHROUGH */ ++ default: ++ if (targetm.asm_out.output_addr_const_extra (file, x)) ++ break; ++ ++ output_operand_lossage ("invalid expression as operand"); ++ } ++ ++} ++ + /* Print operand X to file F in a target specific manner according to CODE. + The acceptable formatting commands given by CODE are: + 'c': An integer or symbol address without a preceding # +@@ -11677,6 +12149,12 @@ aarch64_print_operand (FILE *f, rtx x, int code) + } + break; + ++ case 'K': ++ output_macho_postfix_expr (f, x, "PAGEOFF"); ++ break; ++ case 'O': ++ output_macho_postfix_expr (f, x, "GOTPAGEOFF"); ++ break; + case 'e': + { + x = unwrap_const_vec_duplicate (x); +@@ -12000,7 +12478,7 @@ aarch64_print_operand (FILE *f, rtx x, int code) + case 'A': + if (GET_CODE (x) == HIGH) + x = XEXP (x, 0); +- ++#if !TARGET_MACHO + switch (aarch64_classify_symbolic_expression (x)) + { + case SYMBOL_SMALL_GOT_4G: +@@ -12031,9 +12509,29 @@ aarch64_print_operand (FILE *f, rtx x, int code) + break; + } + output_addr_const (asm_out_file, x); ++#endif ++#if TARGET_MACHO ++ // FIXME update classify symbolic expression to handle macho. ++ switch (aarch64_classify_symbolic_expression (x)) ++ { ++ case SYMBOL_MO_SMALL_PCR: ++ output_macho_postfix_expr (asm_out_file, x, "PAGE"); ++// asm_fprintf (asm_out_file, "@PAGE;mopcr"); ++ break; ++ case SYMBOL_MO_SMALL_GOT: ++ output_macho_postfix_expr (asm_out_file, x, "GOTPAGE"); ++// asm_fprintf (asm_out_file, "@GOTPAGE;mosg"); ++ break; ++ default: ++ output_macho_postfix_expr (asm_out_file, x, "BLEAH"); ++// asm_fprintf (asm_out_file, "@BLEAH"); ++ break; ++ } ++#endif + break; + + case 'L': ++#if !TARGET_MACHO + switch (aarch64_classify_symbolic_expression (x)) + { + case SYMBOL_SMALL_GOT_4G: +@@ -12071,10 +12569,12 @@ aarch64_print_operand (FILE *f, rtx x, int code) + default: + break; + } ++#endif + output_addr_const (asm_out_file, x); + break; + + case 'G': ++#if !TARGET_MACHO + switch (aarch64_classify_symbolic_expression (x)) + { + case SYMBOL_TLSLE24: +@@ -12083,6 +12583,7 @@ aarch64_print_operand (FILE *f, rtx x, int code) + default: + break; + } ++#endif + output_addr_const (asm_out_file, x); + break; + +@@ -12232,8 +12733,14 @@ aarch64_print_address_internal (FILE *f, machine_mode mode, rtx x, + break; + + case ADDRESS_LO_SUM: ++#if TARGET_MACHO ++ asm_fprintf (f, "[%s, #", reg_names [REGNO (addr.base)]); ++ output_macho_postfix_expr (f, addr.offset, "PAGEOFF"); ++// output_addr_const (f, addr.offset); ++#else + asm_fprintf (f, "[%s, #:lo12:", reg_names [REGNO (addr.base)]); + output_addr_const (f, addr.offset); ++#endif + asm_fprintf (f, "]"); + return true; + +@@ -12703,6 +13210,8 @@ aarch64_asm_output_labelref (FILE* f, const char *name) + asm_fprintf (f, "%U%s", name); + } + ++#if !TARGET_MACHO ++ + static void + aarch64_elf_asm_constructor (rtx symbol, int priority) + { +@@ -12742,6 +13251,7 @@ aarch64_elf_asm_destructor (rtx symbol, int priority) + assemble_aligned_integer (POINTER_BYTES, symbol); + } + } ++#endif + + const char* + aarch64_output_casesi (rtx *operands) +@@ -15048,15 +15558,17 @@ aarch64_init_builtins () + { + aarch64_general_init_builtins (); + aarch64_sve::init_builtins (); +-#ifdef SUBTARGET_INIT_BUILTINS +- SUBTARGET_INIT_BUILTINS; +-#endif ++ aarch64_init_subtarget_builtins (); + } + + /* Implement TARGET_FOLD_BUILTIN. */ + static tree + aarch64_fold_builtin (tree fndecl, int nargs, tree *args, bool) + { ++#ifdef SUBTARGET_FOLD_BUILTIN ++ if (tree res = SUBTARGET_FOLD_BUILTIN (fndecl, nargs, args, false)) ++ return res; ++#endif + unsigned int code = DECL_MD_FUNCTION_CODE (fndecl); + unsigned int subcode = code >> AARCH64_BUILTIN_SHIFT; + tree type = TREE_TYPE (TREE_TYPE (fndecl)); +@@ -18326,10 +18838,14 @@ initialize_aarch64_code_model (struct gcc_options *opts) + } + break; + case AARCH64_CMODEL_LARGE: +- if (opts->x_flag_pic) ++ if (TARGET_MACHO) ++ /* We need to implement fPIC here (arm64_32 also accepts the large ++ model). */ ++ ; ++ else if (opts->x_flag_pic) + sorry ("code model %qs with %<-f%s%>", "large", + opts->x_flag_pic > 1 ? "PIC" : "pic"); +- if (opts->x_aarch64_abi == AARCH64_ABI_ILP32) ++ else if (opts->x_aarch64_abi == AARCH64_ABI_ILP32) + sorry ("code model %qs not supported in ilp32 mode", "large"); + break; + case AARCH64_CMODEL_TINY_PIC: +@@ -19252,7 +19768,9 @@ aarch64_classify_symbol (rtx x, HOST_WIDE_INT offset) + case AARCH64_CMODEL_SMALL_SPIC: + case AARCH64_CMODEL_SMALL_PIC: + case AARCH64_CMODEL_SMALL: +- return SYMBOL_SMALL_ABSOLUTE; ++ return TARGET_MACHO ++ ? SYMBOL_MO_SMALL_PCR ++ : SYMBOL_SMALL_ABSOLUTE; + + default: + gcc_unreachable (); +@@ -19288,10 +19806,22 @@ aarch64_classify_symbol (rtx x, HOST_WIDE_INT offset) + + return SYMBOL_TINY_ABSOLUTE; + +- + case AARCH64_CMODEL_SMALL_SPIC: + case AARCH64_CMODEL_SMALL_PIC: + case AARCH64_CMODEL_SMALL: ++#if TARGET_MACHO ++ if (TARGET_MACHO) ++ { ++ /* Constant pool addresses are always TU-local and PC- ++ relative. We indirect common, external and weak ++ symbols (but weak only if not hidden). */ ++ if (!CONSTANT_POOL_ADDRESS_P (x) ++ && (MACHO_SYMBOL_MUST_INDIRECT_P (x) ++ || !aarch64_symbol_binds_local_p (x))) ++ return SYMBOL_MO_SMALL_GOT; ++ } ++ else ++#endif + if ((flag_pic || SYMBOL_REF_WEAK (x)) + && !aarch64_symbol_binds_local_p (x)) + return aarch64_cmodel == AARCH64_CMODEL_SMALL_SPIC +@@ -19303,7 +19833,8 @@ aarch64_classify_symbol (rtx x, HOST_WIDE_INT offset) + || offset_within_block_p (x, offset))) + return SYMBOL_FORCE_TO_MEM; + +- return SYMBOL_SMALL_ABSOLUTE; ++ return TARGET_MACHO ? SYMBOL_MO_SMALL_PCR ++ : SYMBOL_SMALL_ABSOLUTE; + + case AARCH64_CMODEL_LARGE: + /* This is alright even in PIC code as the constant +@@ -19433,7 +19964,10 @@ static GTY(()) tree va_list_type; + void *__vr_top; + int __gr_offs; + int __vr_offs; +- }; */ ++ }; ++ ++ darwinpcs uses 'char *' for the va_list (in common with other platform ++ ports). */ + + static tree + aarch64_build_builtin_va_list (void) +@@ -19441,6 +19975,13 @@ aarch64_build_builtin_va_list (void) + tree va_list_name; + tree f_stack, f_grtop, f_vrtop, f_groff, f_vroff; + ++ /* darwinpcs uses a simple char * for this. */ ++ if (TARGET_MACHO) ++ { ++ va_list_type = build_pointer_type (char_type_node); ++ return va_list_type; ++ } ++ + /* Create the type. */ + va_list_type = lang_hooks.types.make_type (RECORD_TYPE); + /* Give it the required name. */ +@@ -19512,6 +20053,13 @@ aarch64_expand_builtin_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) + int vr_save_area_size = cfun->va_list_fpr_size; + int vr_offset; + ++ /* darwinpcs uses the default, char * va_list impl. */ ++ if (TARGET_MACHO) ++ { ++ std_expand_builtin_va_start (valist, nextarg); ++ return; ++ } ++ + cum = &crtl->args.info; + if (cfun->va_list_gpr_size) + gr_save_area_size = MIN ((NUM_ARG_REGS - cum->aapcs_ncrn) * UNITS_PER_WORD, +@@ -19602,6 +20150,9 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, + HOST_WIDE_INT size, rsize, adjust, align; + tree t, u, cond1, cond2; + ++ if (TARGET_MACHO) ++ return std_gimplify_va_arg_expr (valist, type, pre_p, post_p); ++ + indirect_p = pass_va_arg_by_reference (type); + if (indirect_p) + type = build_pointer_type (type); +@@ -19786,8 +20337,18 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, + field_ptr_t = double_ptr_type_node; + break; + case E_TFmode: +- field_t = long_double_type_node; +- field_ptr_t = long_double_ptr_type_node; ++ if (TARGET_MACHO) ++ { ++ /* Darwin has __float128, and long double is the same as ++ double. */ ++ field_t = float128_type_node; ++ field_ptr_t = aarch64_float128_ptr_type_node; ++ } ++ else ++ { ++ field_t = long_double_type_node; ++ field_ptr_t = long_double_ptr_type_node; ++ } + break; + case E_HFmode: + field_t = aarch64_fp16_type_node; +@@ -19858,6 +20419,9 @@ aarch64_setup_incoming_varargs (cumulative_args_t cum_v, + int gr_saved = cfun->va_list_gpr_size; + int vr_saved = cfun->va_list_fpr_size; + ++ if (TARGET_MACHO) ++ return default_setup_incoming_varargs (cum_v, arg, pretend_size, no_rtl); ++ + /* The caller has advanced CUM up to, but not beyond, the last named + argument. Advance a local copy of CUM past the last "real" named + argument, to find out how many registers are left over. */ +@@ -20685,6 +21249,12 @@ aarch64_autovectorize_vector_modes (vector_modes *modes, bool) + static const char * + aarch64_mangle_type (const_tree type) + { ++ /* The darwinpcs ABI documents say that "__va_list" has to be ++ mangled as char *. */ ++ if (TARGET_MACHO ++ && lang_hooks.types_compatible_p (CONST_CAST_TREE (type), va_list_type)) ++ return "Pc"; ++ + /* The AArch64 ABI documents say that "__va_list" has to be + mangled as if it is in the "std" namespace. */ + if (lang_hooks.types_compatible_p (CONST_CAST_TREE (type), va_list_type)) +@@ -20699,6 +21269,10 @@ aarch64_mangle_type (const_tree type) + return "Dh"; + } + ++ /* TFmode is __float128 for Darwin. */ ++ if (TARGET_MACHO && TYPE_MODE (type) == TFmode) ++ return "g"; ++ + /* Mangle AArch64-specific internal types. TYPE_NAME is non-NULL_TREE for + builtin types. */ + if (TYPE_NAME (type) != NULL) +@@ -21389,7 +21963,8 @@ aarch64_mov_operand_p (rtx x, machine_mode mode) + + /* GOT accesses are valid moves. */ + if (SYMBOL_REF_P (x) +- && aarch64_classify_symbolic_expression (x) == SYMBOL_SMALL_GOT_4G) ++ && (aarch64_classify_symbolic_expression (x) == SYMBOL_SMALL_GOT_4G ++ || aarch64_classify_symbolic_expression (x) == SYMBOL_MO_SMALL_GOT)) + return true; + + if (SYMBOL_REF_P (x) && mode == DImode && CONSTANT_ADDRESS_P (x)) +@@ -22549,7 +23124,9 @@ aarch64_declare_function_name (FILE *stream, const char* name, + aarch64_asm_output_variant_pcs (stream, fndecl, name); + + /* Don't forget the type directive for ELF. */ ++#ifdef ASM_OUTPUT_TYPE_DIRECTIVE + ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "function"); ++#endif + ASM_OUTPUT_LABEL (stream, name); + + cfun->machine->label_is_assembled = true; +@@ -22584,12 +23161,17 @@ aarch64_print_patchable_function_entry (FILE *file, + /* Implement ASM_OUTPUT_DEF_FROM_DECLS. Output .variant_pcs for aliases. */ + + void +-aarch64_asm_output_alias (FILE *stream, const tree decl, const tree target) ++aarch64_asm_output_alias (FILE *stream, const tree decl, ++ const tree target ATTRIBUTE_UNUSED) + { + const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0); ++#ifdef ASM_OUTPUT_DEF + const char *value = IDENTIFIER_POINTER (target); ++#endif + aarch64_asm_output_variant_pcs (stream, decl, name); ++#ifdef ASM_OUTPUT_DEF + ASM_OUTPUT_DEF (stream, name, value); ++#endif + } + + /* Implement ASM_OUTPUT_EXTERNAL. Output .variant_pcs for undefined +@@ -23213,6 +23795,16 @@ aarch64_output_simd_mov_immediate (rtx const_vector, unsigned width, + } + + gcc_assert (CONST_INT_P (info.u.mov.value)); ++ unsigned HOST_WIDE_INT value = UINTVAL (info.u.mov.value); ++ ++ /* We have signed chars which can result in a sign-extended 8bit value ++ which is then emitted as an unsigned hex value, and the LLVM back end ++ assembler rejects that as being too big. */ ++ if (TARGET_MACHO && (known_eq (GET_MODE_BITSIZE (info.elt_mode), 8))) ++ { ++ unsigned HOST_WIDE_INT mask = (1U << GET_MODE_BITSIZE (info.elt_mode))-1; ++ value &= mask; ++ } + + if (which == AARCH64_CHECK_MOV) + { +@@ -23221,16 +23813,16 @@ aarch64_output_simd_mov_immediate (rtx const_vector, unsigned width, + ? "msl" : "lsl"); + if (lane_count == 1) + snprintf (templ, sizeof (templ), "%s\t%%d0, " HOST_WIDE_INT_PRINT_HEX, +- mnemonic, UINTVAL (info.u.mov.value)); ++ mnemonic, value); + else if (info.u.mov.shift) + snprintf (templ, sizeof (templ), "%s\t%%0.%d%c, " + HOST_WIDE_INT_PRINT_HEX ", %s %d", mnemonic, lane_count, +- element_char, UINTVAL (info.u.mov.value), shift_op, ++ element_char, value, shift_op, + info.u.mov.shift); + else + snprintf (templ, sizeof (templ), "%s\t%%0.%d%c, " + HOST_WIDE_INT_PRINT_HEX, mnemonic, lane_count, +- element_char, UINTVAL (info.u.mov.value)); ++ element_char, value); + } + else + { +@@ -23239,12 +23831,12 @@ aarch64_output_simd_mov_immediate (rtx const_vector, unsigned width, + if (info.u.mov.shift) + snprintf (templ, sizeof (templ), "%s\t%%0.%d%c, #" + HOST_WIDE_INT_PRINT_DEC ", %s #%d", mnemonic, lane_count, +- element_char, UINTVAL (info.u.mov.value), "lsl", ++ element_char, value, "lsl", + info.u.mov.shift); + else + snprintf (templ, sizeof (templ), "%s\t%%0.%d%c, #" + HOST_WIDE_INT_PRINT_DEC, mnemonic, lane_count, +- element_char, UINTVAL (info.u.mov.value)); ++ element_char, value); + } + return templ; + } +@@ -26355,12 +26947,12 @@ aarch64_libgcc_floating_mode_supported_p (scalar_float_mode mode) + } + + /* Implement TARGET_SCALAR_MODE_SUPPORTED_P - return TRUE +- if MODE is HFmode, and punt to the generic implementation otherwise. */ ++ if MODE is HFmode, or TFmode on Mach-O, and punt to the generic implementation otherwise. */ + + static bool + aarch64_scalar_mode_supported_p (scalar_mode mode) + { +- return (mode == HFmode ++ return (mode == HFmode || (mode == TFmode && TARGET_MACHO) + ? true + : default_scalar_mode_supported_p (mode)); + } +@@ -27118,19 +27710,37 @@ aarch64_sls_emit_shared_blr_thunks (FILE *out_file) + continue; + + const char *name = indirect_symbol_names[regnum]; +- switch_to_section (get_named_section (decl, NULL, 0)); ++ /* If the target uses a unique section for this switch to it. */ ++ if (DECL_SECTION_NAME (decl)) ++ switch_to_section (get_named_section (decl, NULL, 0)); ++ else ++ switch_to_section (text_section); + ASM_OUTPUT_ALIGN (out_file, 2); +- targetm.asm_out.globalize_label (out_file, name); ++ if (!TARGET_MACHO) ++ targetm.asm_out.globalize_label (out_file, name); ++#ifdef ASM_OUTPUT_TYPE_DIRECTIVE ++ ASM_OUTPUT_TYPE_DIRECTIVE (out_file, name, "function"); ++#endif ++ if (TARGET_MACHO) ++ { ++#ifdef ASM_WEAKEN_DECL ++ if (DECL_WEAK (decl)) ++ ASM_WEAKEN_DECL (out_file, decl, name, 0); ++ else ++#endif ++ targetm.asm_out.globalize_decl_name (out_file, decl); ++ } + /* Only emits if the compiler is configured for an assembler that can + handle visibility directives. */ + targetm.asm_out.assemble_visibility (decl, VISIBILITY_HIDDEN); +- ASM_OUTPUT_TYPE_DIRECTIVE (out_file, name, "function"); + ASM_OUTPUT_LABEL (out_file, name); + aarch64_sls_emit_function_stub (out_file, regnum); + /* Use the most conservative target to ensure it can always be used by any + function in the translation unit. */ + asm_fprintf (out_file, "\tdsb\tsy\n\tisb\n"); ++#ifdef ASM_DECLARE_FUNCTION_SIZE + ASM_DECLARE_FUNCTION_SIZE (out_file, name, decl); ++#endif + } + } + +@@ -27323,6 +27933,15 @@ aarch64_run_selftests (void) + #undef TARGET_ASM_ALIGNED_SI_OP + #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" + ++#if TARGET_MACHO ++#undef TARGET_ASM_UNALIGNED_HI_OP ++#define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t" ++#undef TARGET_ASM_UNALIGNED_SI_OP ++#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t" ++#undef TARGET_ASM_UNALIGNED_DI_OP ++#define TARGET_ASM_UNALIGNED_DI_OP "\t.quad\t" ++#endif ++ + #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK + #define TARGET_ASM_CAN_OUTPUT_MI_THUNK \ + hook_bool_const_tree_hwi_hwi_const_tree_true +@@ -27409,6 +28028,12 @@ aarch64_run_selftests (void) + #undef TARGET_FUNCTION_ARG_BOUNDARY + #define TARGET_FUNCTION_ARG_BOUNDARY aarch64_function_arg_boundary + ++#undef TARGET_FUNCTION_ARG_BOUNDARY_CA ++#define TARGET_FUNCTION_ARG_BOUNDARY_CA aarch64_function_arg_boundary_ca ++ ++#undef TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA ++#define TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA aarch64_function_arg_round_boundary_ca ++ + #undef TARGET_FUNCTION_ARG_PADDING + #define TARGET_FUNCTION_ARG_PADDING aarch64_function_arg_padding + +@@ -27736,7 +28361,7 @@ aarch64_libgcc_floating_mode_supported_p + + /* The architecture reserves bits 0 and 1 so use bit 2 for descriptors. */ + #undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS +-#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 4 ++#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS AARCH64_CUSTOM_FUNCTION_TEST + + #undef TARGET_HARD_REGNO_NREGS + #define TARGET_HARD_REGNO_NREGS aarch64_hard_regno_nregs +diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h +index 359b6e8561f..db5fe441b36 100644 +--- a/gcc/config/aarch64/aarch64.h ++++ b/gcc/config/aarch64/aarch64.h +@@ -58,6 +58,10 @@ + #define TARGET_SIMD (!TARGET_GENERAL_REGS_ONLY && AARCH64_ISA_SIMD) + #define TARGET_FLOAT (!TARGET_GENERAL_REGS_ONLY && AARCH64_ISA_FP) + ++/* If this is non-zero then generated code of the object format, ABI and ++ assembler syntax used by Darwin (Mach-O) platforms. */ ++#define TARGET_MACHO 0 ++ + #define UNITS_PER_WORD 8 + + #define UNITS_PER_VREG 16 +@@ -135,6 +139,12 @@ + /* Heap alignment (same as BIGGEST_ALIGNMENT and STACK_BOUNDARY). */ + #define MALLOC_ABI_ALIGNMENT 128 + ++/* We will and with this value to test if a custom function descriptor needs ++ a static chain. The function boundary must the adjusted so that the bit ++ this represents is no longer part of the address. 0 Disables the custom ++ function descriptors. */ ++#define AARCH64_CUSTOM_FUNCTION_TEST 4 ++ + /* Defined by the ABI */ + #define WCHAR_TYPE "unsigned int" + #define WCHAR_TYPE_SIZE 32 +@@ -1025,6 +1035,24 @@ typedef struct + aapcs_reg == NULL_RTX. */ + int aapcs_stack_size; /* The total size (in words, per 8 byte) of the + stack arg area so far. */ ++ ++ /* In the darwinpcs, items smaller than one word are packed onto the stack ++ naturally aligned. Unnamed parameters passed in a variadic call are, ++ however, aligned the same way as the AAPCS64. This means that we need to ++ pad the last named arg to the next parm boundary (and hence notice when ++ we are processing that arg). */ ++ int darwinpcs_stack_bytes; /* If the argument is passed on the stack, this ++ the byte-size. */ ++ int darwinpcs_sub_word_offset;/* This is the offset of this arg within a word ++ when placing smaller items for darwinpcs. */ ++ int darwinpcs_sub_word_pos; /* The next byte available within the word for ++ darwinpcs. */ ++ unsigned darwinpcs_arg_boundary; /* The computed argument boundary. */ ++ unsigned darwinpcs_arg_padding; /* The computed argument padding. */ ++ unsigned darwinpcs_n_named; /* Number of named arguments. */ ++ unsigned darwinpcs_n_args_processed; /* Processed so far. */ ++ bool named_p; /* Is this arg named? */ ++ bool last_named_p; /* Is this the last named arg? */ + bool silent_p; /* True if we should act silently, rather than + raise an error for invalid calls. */ + } CUMULATIVE_ARGS; +@@ -1309,8 +1337,13 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); + #define ASM_CPU_SPEC \ + MCPU_TO_MARCH_SPEC + ++#ifndef SUBTARGET_EXTRA_SPECS ++#define SUBTARGET_EXTRA_SPECS ++#endif ++ + #define EXTRA_SPECS \ +- { "asm_cpu_spec", ASM_CPU_SPEC } ++ { "asm_cpu_spec", ASM_CPU_SPEC }, \ ++ SUBTARGET_EXTRA_SPECS + + #define ASM_OUTPUT_POOL_EPILOGUE aarch64_asm_output_pool_epilogue + +@@ -1324,6 +1357,10 @@ extern GTY(()) tree aarch64_fp16_ptr_type_node; + extern GTY(()) tree aarch64_bf16_type_node; + extern GTY(()) tree aarch64_bf16_ptr_type_node; + ++/* A pointer to the user-visible __float128 (on Mach-O). Defined in ++ aarch64-builtins.c. */ ++extern GTY(()) tree aarch64_float128_ptr_type_node; ++ + /* The generic unwind code in libgcc does not initialize the frame pointer. + 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 34b8059b45b..c356d3048b5 100644 +--- a/gcc/config/aarch64/aarch64.md ++++ b/gcc/config/aarch64/aarch64.md +@@ -303,6 +303,7 @@ + UNSPEC_TAG_SPACE ; Translate address to MTE tag address space. + UNSPEC_LD1RO + UNSPEC_SALT_ADDR ++ UNSPEC_MACHOPIC_OFFSET + ]) + + (define_c_enum "unspecv" [ +@@ -849,6 +850,37 @@ + [(set_attr "type" "load_4")] + ) + ++(define_insn "prefetch_unscaled" ++ [(prefetch (match_operand:DI 0 "aarch64_unscaled_prefetch_operand" "Du") ++ (match_operand:QI 1 "const_int_operand" "") ++ (match_operand:QI 2 "const_int_operand" ""))] ++ "" ++ { ++ const char * pftype[2][4] = ++ { ++ {"prfum\\tPLDL1STRM, %0", ++ "prfum\\tPLDL3KEEP, %0", ++ "prfum\\tPLDL2KEEP, %0", ++ "prfum\\tPLDL1KEEP, %0"}, ++ {"prfum\\tPSTL1STRM, %0", ++ "prfum\\tPSTL3KEEP, %0", ++ "prfum\\tPSTL2KEEP, %0", ++ "prfum\\tPSTL1KEEP, %0"}, ++ }; ++ ++ int locality = INTVAL (operands[2]); ++ ++ gcc_assert (IN_RANGE (locality, 0, 3)); ++ ++ /* PRFUM accepts the same addresses as a 64-bit LDR so wrap ++ the address into a DImode MEM so that aarch64_print_operand knows ++ how to print it. */ ++ operands[0] = gen_rtx_MEM (DImode, operands[0]); ++ return pftype[INTVAL(operands[1])][locality]; ++ } ++ [(set_attr "type" "load_4")] ++) ++ + (define_insn "trap" + [(trap_if (const_int 1) (const_int 8))] + "" +@@ -1286,7 +1318,7 @@ + ldr\\t%s0, %1 + str\\t%w1, %0 + str\\t%s1, %0 +- adrp\\t%x0, %A1\;ldr\\t%w0, [%x0, %L1] ++ * return TARGET_MACHO ? \"adrp\\t%x0, %A1\;ldr\\t%w0, [%x0, %O1]\" : \"adrp\\t%x0, %A1\;ldr\\t%w0, [%x0, %L1]\"; + adr\\t%x0, %c1 + adrp\\t%x0, %A1 + fmov\\t%s0, %w1 +@@ -1325,7 +1357,7 @@ + ldr\\t%d0, %1 + str\\t%x1, %0 + str\\t%d1, %0 +- * return TARGET_ILP32 ? \"adrp\\t%0, %A1\;ldr\\t%w0, [%0, %L1]\" : \"adrp\\t%0, %A1\;ldr\\t%0, [%0, %L1]\"; ++ * return TARGET_ILP32 ? (TARGET_MACHO ? \"adrp\\t%0, %A1\;ldr\\t%w0, [%0, %O1]\" : \"adrp\\t%0, %A1\;ldr\\t%w0, [%0, %L1]\") : (TARGET_MACHO ? \"adrp\\t%0, %A1\;ldr\\t%0, [%0, %O1]\" : \"adrp\\t%0, %A1\;ldr\\t%0, [%0, %L1]\"); + adr\\t%x0, %c1 + adrp\\t%x0, %A1 + fmov\\t%d0, %x1 +@@ -6857,7 +6889,10 @@ + (lo_sum:P (match_operand:P 1 "register_operand" "r") + (match_operand 2 "aarch64_valid_symref" "S")))] + "" +- "add\\t%<w>0, %<w>1, :lo12:%c2" ++ { return TARGET_MACHO ++ ? "add\\t%<w>0, %<w>1, %K2;momd" ++ : "add\\t%<w>0, %<w>1, :lo12:%c2"; ++ } + [(set_attr "type" "alu_imm")] + ) + +diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt +index 92220b26ee2..15ec719ca2d 100644 +--- a/gcc/config/aarch64/aarch64.opt ++++ b/gcc/config/aarch64/aarch64.opt +@@ -152,6 +152,13 @@ Enum(aarch64_abi) String(ilp32) Value(AARCH64_ABI_ILP32) + EnumValue + Enum(aarch64_abi) String(lp64) Value(AARCH64_ABI_LP64) + ++EnumValue ++Enum(aarch64_abi) String(darwinpcs) Value(AARCH64_ABI_LP64) ++ ++m64 ++Target RejectNegative Alias(mabi=, darwinpcs) ++On Darwin for compatibility with other platform variants. ++ + mpc-relative-literal-loads + 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 ee7587cca16..cb73a2daae8 100644 +--- a/gcc/config/aarch64/constraints.md ++++ b/gcc/config/aarch64/constraints.md +@@ -158,7 +158,9 @@ + A constraint that matches a small GOT access." + (and (match_code "const,symbol_ref") + (match_test "aarch64_classify_symbolic_expression (op) +- == SYMBOL_SMALL_GOT_4G"))) ++ == SYMBOL_SMALL_GOT_4G ++ || aarch64_classify_symbolic_expression (op) ++ == SYMBOL_MO_SMALL_GOT"))) + + (define_constraint "Uss" + "@internal +@@ -490,6 +492,11 @@ + An address valid for a prefetch instruction." + (match_test "aarch64_address_valid_for_prefetch_p (op, true)")) + ++(define_address_constraint "Du" ++ "@internal ++ An address valid for a prefetch instruction with an unscaled offset." ++ (match_test "aarch64_address_valid_for_unscaled_prefetch_p (op, true)")) ++ + (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 +new file mode 100644 +index 00000000000..2a855c11efa +--- /dev/null ++++ b/gcc/config/aarch64/darwin.h +@@ -0,0 +1,280 @@ ++/* Target definitions for Arm64/Aarch64 running on macOS/iOS. ++ ++Copyright The GNU Toolchain Authors. ++Contributed by Iain Sandoe. ++ ++This file is part of GCC. ++ ++GCC 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 3, or (at your option) ++any later version. ++ ++GCC 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 GCC; see the file COPYING3. If not see ++<http://www.gnu.org/licenses/>. */ ++ ++/* Enable Mach-O bits in generic Aarch64 code. */ ++#undef TARGET_MACHO ++#define TARGET_MACHO 1 ++ ++#undef DARWIN_ARM64 ++#define DARWIN_ARM64 1 ++ ++/* FIXME FIXME FIXME - these are mostly guesses right now. */ ++ ++/* FIXME: this is only used in generic code in darwin.c. */ ++#undef TARGET_64BIT ++#define TARGET_64BIT 1 ++ ++#undef PTRDIFF_TYPE ++#define PTRDIFF_TYPE "long int" ++ ++/* NOTE that arm64_32 is a valid thing and corresponds to darwinpcs ++ and TARGET_ILP32, but we are not implementing that for now. */ ++#define TARGET_OS_CPP_BUILTINS() \ ++ do { \ ++ builtin_define ("__LITTLE_ENDIAN__"); \ ++ builtin_define ("__arm64"); \ ++ builtin_define ("__arm64__"); \ ++ darwin_cpp_builtins (pfile); \ ++ } while (0) ++ ++/* In Darwin's arm64 ABI, chars are signed, for consistency with other Darwin ++ architectures. */ ++ ++#undef DEFAULT_SIGNED_CHAR ++#define DEFAULT_SIGNED_CHAR 1 ++ ++#undef LONG_DOUBLE_TYPE_SIZE ++#define LONG_DOUBLE_TYPE_SIZE 64 ++ ++/* Disable custom function descriptors on Darwin, it breaks ABI. */ ++#undef AARCH64_CUSTOM_FUNCTION_TEST ++#define AARCH64_CUSTOM_FUNCTION_TEST 0 ++ ++/* Non-PIE executables are forbidden by the aarch64-darwin security model; ++ remove the option from link-lines since they just produce a warning from ++ ld64 and are then ignored anyway. */ ++#undef DARWIN_NOPIE_SPEC ++#define DARWIN_NOPIE_SPEC \ ++" %<no-pie %<fno-pie %<fno-PIE " ++ ++/* Hack alert - we want the exported cas etc. */ ++#undef LIB_SPEC ++#define LIB_SPEC "%{!static:-lSystem} -lgcc" ++ ++/* Force the default endianness and ABI flags onto the command line ++ in order to make the other specs easier to write. Match clang in ++ silently ignoring mdynamic-no-pic */ ++#undef DRIVER_SELF_SPECS ++#define DRIVER_SELF_SPECS \ ++"%{mbig-endian:%eDarwin platforms do not support big-endian arm64}" \ ++"%{!mlittle-endian:-mlittle-endian} " \ ++"%{mabi=ilp32:%eSorry, support for Darwin ilp32 arm64 is not implemented} " \ ++"%{!mabi=*:-mabi=lp64} " \ ++" %<mdynamic-no-pic* " \ ++ MCPU_MTUNE_NATIVE_SPECS \ ++ SUBTARGET_DRIVER_SELF_SPECS ++ ++/* We want -fPIC by default, unless we're using -static to compile for ++ the kernel or some such. */ ++ ++#undef CC1_SPEC ++#define CC1_SPEC \ ++"%{!mkernel:%{!static:-fPIC}} " DARWIN_CC1_SPEC ++ ++#undef ASM_SPEC ++#define ASM_SPEC "-arch %(darwin_arch) "\ ++ ASM_OPTIONS " %{static} " ASM_MMACOSX_VERSION_MIN_SPEC ++ ++#undef ENDFILE_SPEC ++#define ENDFILE_SPEC \ ++ " " TM_DESTRUCTOR ++ ++/* The arch is known as 'arm64' by the system tools. */ ++#define DARWIN_ARCH_SPEC "arm64" ++ ++#undef SUBTARGET_EXTRA_SPECS ++#define SUBTARGET_EXTRA_SPECS \ ++ DARWIN_EXTRA_SPECS \ ++ { "darwin_arch", DARWIN_ARCH_SPEC }, \ ++ { "darwin_crt2", "" }, \ ++ { "darwin_subarch", DARWIN_ARCH_SPEC }, ++ ++#undef TARGET_ASM_FILE_END ++#define TARGET_ASM_FILE_END darwin_file_end ++ ++/* For now, we do not give global entities any extra alignment ++ TODO: determine if we should for some optimisation level. */ ++#undef DATA_ALIGNMENT ++#define DATA_ALIGNMENT(EXP, ALIGN) \ ++ AARCH64_EXPAND_ALIGNMENT (false, EXP, ALIGN) ++ ++/* Darwin binds locally for PIC code (the default) without which ++ we lose many in-lineing opportunities. */ ++#undef TARGET_BINDS_LOCAL_P ++#define TARGET_BINDS_LOCAL_P darwin_binds_local_p ++ ++/* Define the syntax of pseudo-ops, labels and comments. */ ++ ++#ifdef HAVE_GAS_MAX_SKIP_P2ALIGN ++/* Support for -falign-* switches. Use .p2align to ensure that code ++ sections are padded with NOP instructions, rather than zeros. */ ++#define ASM_OUTPUT_MAX_SKIP_ALIGN(FILE, LOG, MAX_SKIP) \ ++ do \ ++ { \ ++ if ((LOG) != 0) \ ++ { \ ++ if ((MAX_SKIP) == 0) \ ++ fprintf ((FILE), "\t.p2align %d\n", (int) (LOG)); \ ++ else \ ++ fprintf ((FILE), "\t.p2align %d,,%d\n", \ ++ (int) (LOG), (int) (MAX_SKIP)); \ ++ } \ ++ } while (0) ++ ++#endif /* HAVE_GAS_MAX_SKIP_P2ALIGN */ ++ ++/* String containing the assembler's comment-starter. */ ++ ++#define ASM_COMMENT_START ";" ++ ++/* Define the syntax of pseudo-ops, labels and comments. */ ++ ++#define LPREFIX "L" ++ ++/* Assembler pseudos to introduce constants of various size. */ ++ ++#define ASM_BYTE "\t.byte\t" ++#define ASM_SHORT "\t.word\t" ++#define ASM_LONG "\t.long\t" ++#define ASM_QUAD "\t.quad\t" ++ ++/* darwinpcs reserves X18. */ ++ ++#undef FIXED_REGISTERS ++#define FIXED_REGISTERS \ ++ { \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* R0 - R7 */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* R8 - R15 */ \ ++ 0, 0, 1, 0, 0, 0, 0, 0, /* R16 - R23 */ \ ++ 0, 0, 0, 0, 0, 1, 0, 1, /* R24 - R30, SP */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* V0 - V7 */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* V8 - V15 */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* V16 - V23 */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* V24 - V31 */ \ ++ 1, 1, 1, 1, /* SFP, AP, CC, VG */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* P0 - P7 */ \ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* P8 - P15 */ \ ++ 1, 1 /* FFR and FFRT */ \ ++ } ++ ++/* Although we cannot use executable stack, we still need to assign ++ a static chain regnum. At the moment using R16 (IP0) is available. */ ++#undef STATIC_CHAIN_REGNUM ++#define STATIC_CHAIN_REGNUM R16_REGNUM ++ ++#define SUBTARGET_ENCODE_SECTION_INFO darwin_encode_section_info ++ ++#undef ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX ++#define ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX(FILE, ENCODING, SIZE, ADDR, DONE) \ ++ if (TARGET_64BIT) \ ++ { \ ++ if ((SIZE) == 4 && ((ENCODING) & 0x70) == DW_EH_PE_pcrel) \ ++ { \ ++ fputs (ASM_LONG, FILE); \ ++ assemble_name (FILE, XSTR (ADDR, 0)); \ ++ fputs ("@GOT-.", FILE); \ ++ goto DONE; \ ++ } \ ++ } \ ++ else \ ++ { \ ++ if (ENCODING == ASM_PREFERRED_EH_DATA_FORMAT (2, 1)) \ ++ { \ ++ gcc_unreachable (); /* no 32b support yet.*/ \ ++ /*darwin_non_lazy_pcrel (FILE, ADDR);*/ \ ++ goto DONE; \ ++ } \ ++ } ++ ++/* Darwin x86 assemblers support the .ident directive. */ ++ ++#undef TARGET_ASM_OUTPUT_IDENT ++#define TARGET_ASM_OUTPUT_IDENT default_asm_output_ident_directive ++ ++/* Darwin has experimental support for section anchors on aarch64*; it is ++ not enabled by default (the -fsection-anchors is required). */ ++ ++#undef TARGET_ASM_OUTPUT_ANCHOR ++#define TARGET_ASM_OUTPUT_ANCHOR darwin_asm_output_anchor ++ ++#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P ++#define TARGET_USE_ANCHORS_FOR_SYMBOL_P darwin_use_anchors_for_symbol_p ++ ++#undef DARWIN_SECTION_ANCHORS ++#define DARWIN_SECTION_ANCHORS 1 ++ ++/* Pull in the stuff common to all Darwin-based platforms. */ ++#define C_COMMON_OVERRIDE_OPTIONS \ ++ do { \ ++ SUBTARGET_C_COMMON_OVERRIDE_OPTIONS; \ ++ } while (0) ++ ++/* We do not have a definition for a tiny (or large) code model so ++ far. ++ Section anchors are (probably) not useful with ld64 atom model so ++ default them off - this can be overridden by the user at present. ++ mdynamic-no-pic is silently ignored by clang (and not applicable ++ to this port). */ ++#undef SUBTARGET_OVERRIDE_OPTIONS ++#define SUBTARGET_OVERRIDE_OPTIONS \ ++ do { \ ++ if (global_options.x_aarch64_cmodel_var == AARCH64_CMODEL_TINY) \ ++ sorry ("code model %qs is not supported on Darwin platforms", \ ++ "tiny"); \ ++ if (!global_options_set.x_flag_section_anchors) \ ++ flag_section_anchors = 0; \ ++ target_flags &= ~MASK_MACHO_DYNAMIC_NO_PIC; \ ++ } while (0); \ ++ SUBSUBTARGET_OVERRIDE_OPTIONS ++ ++#undef SUBTARGET_INIT_BUILTINS ++#define SUBTARGET_INIT_BUILTINS \ ++ do { \ ++ aarch64_builtin_decls[AARCH64_BUILTIN_CFSTRING] \ ++ = darwin_init_cfstring_builtins ((AARCH64_BUILTIN_CFSTRING << AARCH64_BUILTIN_SHIFT) | AARCH64_BUILTIN_GENERAL); \ ++ } while(0) ++ ++/* Darwin on Arm64 uses dwarf-2. */ ++#ifndef DARWIN_PREFER_DWARF ++# undef PREFERRED_DEBUGGING_TYPE ++# define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG ++#endif ++ ++#undef REGISTER_SUBTARGET_PRAGMAS ++#define REGISTER_SUBTARGET_PRAGMAS() DARWIN_REGISTER_TARGET_PRAGMAS() ++ ++#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES ++#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES darwin_set_default_type_attributes ++ ++/* FIXME: CHECK Define the shadow offset for asan. */ ++#undef SUBTARGET_SHADOW_OFFSET ++#define SUBTARGET_SHADOW_OFFSET (HOST_WIDE_INT_1 << 44) ++ ++/* First available SYMBOL flag bit for use by subtargets. */ ++#define SYMBOL_FLAG_SUBT_DEP (SYMBOL_FLAG_MACH_DEP) ++ ++#undef ASM_OUTPUT_DEF_FROM_DECLS ++ ++#undef CLEAR_INSN_CACHE ++#define CLEAR_INSN_CACHE(beg, end) \ ++ extern void sys_icache_invalidate(void *start, size_t len); \ ++ sys_icache_invalidate ((beg), (size_t)((end)-(beg))) ++ +diff --git a/gcc/config/aarch64/falkor-tag-collision-avoidance.cc b/gcc/config/aarch64/falkor-tag-collision-avoidance.cc +index 5f198290568..610838049e5 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, + void *unused ATTRIBUTE_UNUSED) + { + gcc_assert (dump_file); +- fprintf (dump_file, "Tag 0x%lx ::\n", INTVAL (t)); ++ fprintf (dump_file, "Tag 0x%lx ::\n", (long unsigned int)INTVAL (t)); + + for (unsigned i = 0; i < insn_info.length (); i++) + dump_insn_slim (dump_file, insn_info[i]->insn); +diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md +index c308015ac2c..cd88f93ca2d 100644 +--- a/gcc/config/aarch64/predicates.md ++++ b/gcc/config/aarch64/predicates.md +@@ -261,9 +261,24 @@ + (define_predicate "aarch64_prefetch_operand" + (match_test "aarch64_address_valid_for_prefetch_p (op, false)")) + ++(define_predicate "aarch64_unscaled_prefetch_operand" ++ (match_test "aarch64_address_valid_for_unscaled_prefetch_p (op, false)")) ++ + (define_predicate "aarch64_valid_symref" + (match_code "const, symbol_ref, label_ref") + { ++ if (TARGET_MACHO) ++ { ++ rtx x = op; ++ rtx offset; ++ split_const (x, &x, &offset); ++ if (GET_CODE (x) == CONST) ++ x = XEXP (x, 0); ++ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_SALT_ADDR) ++ x = XVECEXP (x, 0, 0); ++ if (SYMBOL_REF_P (x) && INTVAL (offset) < 0) ++ return false; ++ } + 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 +new file mode 100644 +index 00000000000..a8bfcffad78 +--- /dev/null ++++ b/gcc/config/aarch64/t-aarch64-darwin +@@ -0,0 +1,25 @@ ++# Machine description for AArch64 architecture. ++# Copyright (C) 2020 Free Software Foundation, Inc. ++# ++# This file is part of GCC. ++# ++# GCC 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 3, or (at your option) ++# any later version. ++# ++# GCC 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 GCC; see the file COPYING3. If not see ++# <http://www.gnu.org/licenses/>. ++ ++LIB1ASMSRC = aarch64/lib1funcs.asm ++LIB1ASMFUNCS = _aarch64_sync_cache_range ++ ++# FIXME - figure out what multilib provisions we should make for ++# a) arm64e ++# b) arm64_32 +diff --git a/gcc/config/darwin-driver.cc b/gcc/config/darwin-driver.cc +index 00287f3d5ec..6df85f57bb4 100644 +--- a/gcc/config/darwin-driver.cc ++++ b/gcc/config/darwin-driver.cc +@@ -268,14 +268,21 @@ darwin_driver_init (unsigned int *decoded_options_count, + bool seenX86_64 = false; + bool seenPPC = false; + bool seenPPC64 = false; ++#if !DARWIN_ARM64 ++ bool seenArm64 = false; + bool seenM32 = false; + bool seenM64 = false; + bool appendM32 = false; + bool appendM64 = false; ++#endif + const char *vers_string = NULL; + bool seen_version_min = false; + bool seen_sysroot_p = false; + bool noexport_p = true; ++#ifdef RPATH_SETS_NODEFAULT ++ bool seen_rpath_p = false; ++ bool seen_nodefaultrpaths_p = false; ++#endif + + for (i = 1; i < *decoded_options_count; i++) + { +@@ -296,6 +303,12 @@ darwin_driver_init (unsigned int *decoded_options_count, + seenPPC = true; + else if (!strcmp ((*decoded_options)[i].arg, "ppc64")) + seenPPC64 = true; ++ else if (!strcmp ((*decoded_options)[i].arg, "arm64")) ++#if !DARWIN_ARM64 ++ seenArm64 = true; ++#else ++ ; /* We accept the option, but don't need to act on it. */ ++#endif + else + error ("this compiler does not support %qs", + (*decoded_options)[i].arg); +@@ -309,7 +322,7 @@ darwin_driver_init (unsigned int *decoded_options_count, + --i; + --*decoded_options_count; + break; +- ++#if !DARWIN_ARM64 + case OPT_m32: + seenM32 = true; + break; +@@ -317,6 +330,7 @@ darwin_driver_init (unsigned int *decoded_options_count, + case OPT_m64: + seenM64 = true; + break; ++#endif + + case OPT_mmacosx_version_min_: + seen_version_min = true; +@@ -349,8 +363,16 @@ darwin_driver_init (unsigned int *decoded_options_count, + gcc_checking_assert ((*decoded_options)[i].arg); + if (startswith ((*decoded_options)[i].arg, "-exported_symbol")) + noexport_p = false; ++#ifdef RPATH_SETS_NODEFAULT ++ else if (strncmp ((*decoded_options)[i].arg, "-rpath", 6) == 0) ++ seen_rpath_p = true; ++#endif + break; + ++#ifdef RPATH_SETS_NODEFAULT ++ case OPT_nodefaultrpaths: ++ seen_nodefaultrpaths_p = true; ++#endif + default: + break; + } +@@ -366,6 +388,9 @@ darwin_driver_init (unsigned int *decoded_options_count, + if (seenPPC || seenPPC64) + warning (0, "this compiler does not support PowerPC" + " (%<-arch%> option ignored)"); ++ else if (seenArm64) ++ warning (0, "this compiler does not support Arm64" ++ " (%<-arch%> option ignored)"); + if (seenX86) + { + if (seenX86_64 || seenM64) +@@ -389,6 +414,9 @@ darwin_driver_init (unsigned int *decoded_options_count, + if (seenX86 || seenX86_64) + warning (0, "this compiler does not support x86" + " (%<-arch%> option ignored)"); ++ else if (seenArm64) ++ warning (0, "this compiler does not support Arm64" ++ " (%<-arch%> option ignored)"); + if (seenPPC) + { + if (seenPPC64 || seenM64) +@@ -408,12 +436,20 @@ darwin_driver_init (unsigned int *decoded_options_count, + if (! seenM64) /* Add -m64 if the User didn't. */ + appendM64 = true; + } ++#elif DARWIN_ARM64 ++ if (seenPPC || seenPPC64) ++ warning (0, "this compiler does not support PowerPC" ++ " (%<-arch%> option ignored)"); ++ if (seenX86 || seenX86_64) ++ warning (0, "this compiler does not support x86" ++ " (%<-arch%> option ignored)"); + #endif + + /* If there is nothing else on the command line, do not add sysroot etc. */ + if (*decoded_options_count <= 1) + return; + ++#if !DARWIN_ARM64 + if (appendM32 || appendM64) + { + ++*decoded_options_count; +@@ -423,6 +459,7 @@ darwin_driver_init (unsigned int *decoded_options_count, + generate_option (appendM32 ? OPT_m32 : OPT_m64, NULL, 1, CL_DRIVER, + &(*decoded_options)[*decoded_options_count - 1]); + } ++#endif + + if (!seen_sysroot_p) + { +@@ -490,4 +527,16 @@ darwin_driver_init (unsigned int *decoded_options_count, + generate_option (OPT_nodefaultexport, NULL, 1, CL_DRIVER, + &(*decoded_options)[*decoded_options_count - 1]); + } ++ ++#ifdef RPATH_SETS_NODEFAULT ++ if (seen_rpath_p && !seen_nodefaultrpaths_p) ++ { ++ ++*decoded_options_count; ++ *decoded_options = XRESIZEVEC (struct cl_decoded_option, ++ *decoded_options, ++ *decoded_options_count); ++ generate_option (OPT_nodefaultrpaths, NULL, 1, CL_DRIVER, ++ &(*decoded_options)[*decoded_options_count - 1]); ++ } ++#endif + } +diff --git a/gcc/config/darwin.cc b/gcc/config/darwin.cc +index f065a13d73d..7cd684c6abe 100644 +--- a/gcc/config/darwin.cc ++++ b/gcc/config/darwin.cc +@@ -118,7 +118,7 @@ static bool ld_init_term_start_labels = false; + section * darwin_sections[NUM_DARWIN_SECTIONS]; + + /* While we transition to using in-tests instead of ifdef'd code. */ +-#if !HAVE_lo_sum ++#if !HAVE_lo_sum || DARWIN_ARM64 + #define gen_macho_high(m,a,b) (a) + #define gen_macho_low(m,a,b,c) (a) + #endif +@@ -1052,6 +1052,7 @@ machopic_legitimize_pic_address (rtx orig, machine_mode mode, rtx reg) + return pic_ref; + } + ++#if !DARWIN_ARM64 + /* Callbacks to output the stub or non-lazy pointers. + Each works on the item in *SLOT,if it has been used. + DATA is the FILE* for assembly output. +@@ -1207,6 +1208,7 @@ machopic_finish (FILE *out_file) + machopic_indirections->traverse_noresize + <FILE *, machopic_output_indirection> (out_file); + } ++#endif + + int + machopic_operand_p (rtx op) +@@ -1936,6 +1938,8 @@ darwin_label_is_anonymous_local_objc_name (const char *name) + } + else if (startswith ((const char *)p, "ClassMethods")) + return false; ++ else if (startswith ((const char *)p, "ClassProtocols")) ++ return false; + else if (startswith ((const char *)p, "Instance")) + { + if (p[8] == 'I' || p[8] == 'M') +@@ -2238,6 +2242,8 @@ darwin_emit_except_table_label (FILE *file) + rtx + darwin_make_eh_symbol_indirect (rtx orig, bool ARG_UNUSED (pubvis)) + { ++ if (DARWIN_ARM64) ++ return orig; + if (DARWIN_PPC == 0 && TARGET_64BIT) + return orig; + +@@ -3058,7 +3064,12 @@ darwin_file_end (void) + fprintf (asm_out_file, "\t.long\t0\n\t.long\t%u\n", flags); + } + ++#if !DARWIN_ARM64 + machopic_finish (asm_out_file); ++#else ++ gcc_checking_assert (!machopic_indirections); ++#endif ++ + if (flag_apple_kext) + { + /* These sections are only used for kernel code. */ +diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h +index 51e257dc698..13ba6e61474 100644 +--- a/gcc/config/darwin.h ++++ b/gcc/config/darwin.h +@@ -42,6 +42,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + + #define DARWIN_X86 0 + #define DARWIN_PPC 0 ++#define DARWIN_ARM64 0 + + #define OBJECT_FORMAT_MACHO 1 + +@@ -296,6 +297,31 @@ extern GTY(()) int darwin_ms_struct; + #define DARWIN_CC1_SPEC \ + "%<dynamic %<dynamiclib %<force_cpusubtype_ALL " + ++/* When we are using embedded runpaths DARWIN_AT_RPATH is set. */ ++#if DARWIN_AT_RPATH ++# define DARWIN_RPATH_LINK \ ++"%{!r:%{!nostdlib:%{!nodefaultrpaths:%(darwin_rpaths)}}}" ++# define DARWIN_SHARED_LIBGCC "-lgcc_s.1.1" ++#else ++# define DARWIN_RPATH_LINK "" ++# define DARWIN_SHARED_LIBGCC \ ++"%:version-compare(!> 10.11 mmacosx-version-min= -lgcc_s.1.1) \ ++ %:version-compare(>= 10.11 mmacosx-version-min= -lemutls_w) " ++#endif ++ ++/* We might elect to add a path even when this compiler does not use embedded ++ run paths, so that we can use libraries from an alternate compiler that is ++ using embedded runpaths. */ ++#if DARWIN_DO_EXTRA_RPATH ++# define DARWIN_EXTRA_RPATH \ ++"%{!r:%{!nostdlib:%{!nodefaultrpaths:\ ++ %:version-compare(>= 10.5 mmacosx-version-min= -rpath) \ ++ %:version-compare(>= 10.5 mmacosx-version-min= " DARWIN_ADD_RPATH ") \ ++ }}}" ++#else ++# define DARWIN_EXTRA_RPATH "" ++#endif ++ + #define SUBSUBTARGET_OVERRIDE_OPTIONS \ + do { \ + darwin_override_options (); \ +@@ -387,7 +413,9 @@ extern GTY(()) int darwin_ms_struct; + DARWIN_NOPIE_SPEC \ + DARWIN_RDYNAMIC \ + DARWIN_NOCOMPACT_UNWIND \ +- "}}}}}}} %<pie %<no-pie %<rdynamic %<X %<rpath " ++ DARWIN_EXTRA_RPATH \ ++ DARWIN_RPATH_LINK \ ++ "}}}}}}} %<pie %<no-pie %<rdynamic %<X %<rpath %<nodefaultrpaths " + + /* Spec that controls whether the debug linker is run automatically for + a link step. This needs to be done if there is a source file on the +@@ -465,34 +493,19 @@ extern GTY(()) int darwin_ms_struct; + This is because,in general, we need to unwind through system libraries that + are linked with the shared unwinder in libunwind (or libgcc_s for 10.4/5). + +- For -static-libgcc: < 10.6, use the unwinder in libgcc_eh (and find +- the emultls impl. there too). +- +- For -static-libgcc: >= 10.6, the unwinder *still* comes from libSystem and +- we find the emutls impl from lemutls_w. In either case, the builtins etc. +- are linked from -lgcc. +- +- When we have specified shared-libgcc or any case that might require +- exceptions, we pull the libgcc content (including emulated tls) from +- -lgcc_s.1 in GCC and the unwinder from /usr/lib/libgcc_s.1 for < 10.6 and +- libSystem for >= 10.6 respectively. +- Otherwise, we just link the emutls/builtins from convenience libs. +- +- If we need exceptions, prior to 10.3.9, then we have to link the static +- eh lib, since there's no shared version on the system. +- +- In all cases, libgcc_s.1 will be installed with the compiler, or any app +- built using it, so we can link the builtins and emutls shared on all. +- + We have to work around that DYLD_XXXX are disabled in macOS 10.11+ which + means that any bootstrap trying to use a shared libgcc with a bumped SO- + name will fail. This means that we do not accept shared libgcc for these +- versions. ++ versions, unless we have embedded run paths enabled, in which case the ++ compiler will add the appropriate path to find the library. ++ ++ For -static-libgcc: < 10.6, use the unwinder in libgcc_eh (and find ++ the emultls impl. there too). + + For -static-libgcc: >= 10.6, the unwinder *still* comes from libSystem and + we find the emutls impl from lemutls_w. In either case, the builtins etc. + are linked from -lgcc. +-> ++ + Otherwise, we just link the shared version of gcc_s.1.1 and pick up + exceptions: + * Prior to 10.3.9, then we have to link the static eh lib, since there +@@ -502,6 +515,10 @@ extern GTY(()) int darwin_ms_struct; + + In all cases, libgcc_s.1.1 will be installed with the compiler, or any app + built using it, so we can link the builtins and emutls shared on all. ++ ++ On most Darwin systems (other than Arm64) we will also install a legacy ++ support libgcc_s.1.dylib to support executables linked with libgcc_ext by ++ earlier GCC versions. + */ + #undef REAL_LIBGCC_SPEC + #define REAL_LIBGCC_SPEC \ +@@ -509,8 +526,7 @@ extern GTY(()) int darwin_ms_struct; + %:version-compare(!> 10.6 mmacosx-version-min= -lgcc_eh) \ + %:version-compare(>= 10.6 mmacosx-version-min= -lemutls_w); \ + shared-libgcc|fexceptions|fobjc-exceptions|fgnu-runtime: \ +- %:version-compare(!> 10.11 mmacosx-version-min= -lgcc_s.1.1) \ +- %:version-compare(>= 10.11 mmacosx-version-min= -lemutls_w) \ ++ " DARWIN_SHARED_LIBGCC " \ + %:version-compare(!> 10.3.9 mmacosx-version-min= -lgcc_eh) \ + %:version-compare(>< 10.3.9 10.5 mmacosx-version-min= -lgcc_s.10.4) \ + %:version-compare(>< 10.5 10.6 mmacosx-version-min= -lgcc_s.10.5); \ +@@ -545,7 +561,8 @@ extern GTY(()) int darwin_ms_struct; + { "darwin_crt2", DARWIN_CRT2_SPEC }, \ + { "darwin_crt3", DARWIN_CRT3_SPEC }, \ + { "darwin_dylib1", DARWIN_DYLIB1_SPEC }, \ +- { "darwin_bundle1", DARWIN_BUNDLE1_SPEC }, ++ { "darwin_bundle1", DARWIN_BUNDLE1_SPEC }, \ ++ { "darwin_rpaths", DARWIN_RPATH_SPEC }, + + #define DARWIN_CRT1_SPEC \ + "%:version-compare(!> 10.5 mmacosx-version-min= -lcrt1.o) \ +@@ -571,6 +588,17 @@ extern GTY(()) int darwin_ms_struct; + "%{!static:%:version-compare(< 10.6 mmacosx-version-min= -lbundle1.o) \ + %{fgnu-tm: -lcrttms.o}}" + ++#if DARWIN_AT_RPATH ++/* A default rpath, that picks up dependent libraries installed in the same ++ director as one being loaded. */ ++#define DARWIN_RPATH_SPEC \ ++ "%:version-compare(>= 10.5 mmacosx-version-min= -rpath) \ ++ %:version-compare(>= 10.5 mmacosx-version-min= @loader_path) \ ++ %P " ++#else ++#define DARWIN_RPATH_SPEC "" ++#endif ++ + #ifdef HAVE_AS_MMACOSX_VERSION_MIN_OPTION + /* Emit macosx version (but only major). */ + #define ASM_MMACOSX_VERSION_MIN_SPEC \ +diff --git a/gcc/config/darwin.opt b/gcc/config/darwin.opt +index cc7d14c2e4d..b1cb8464d57 100644 +--- a/gcc/config/darwin.opt ++++ b/gcc/config/darwin.opt +@@ -237,6 +237,10 @@ nodefaultexport + Driver RejectNegative + Do not add a default symbol exports to modules or dynamic libraries. + ++nodefaultrpaths ++Driver RejectNegative ++Do not add default run paths (for the compiler library directories) to executables, modules or dynamic libraries. ++ + nofixprebinding + 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 a55f6b2b874..36a32867281 100644 +--- a/gcc/config/i386/darwin.h ++++ b/gcc/config/i386/darwin.h +@@ -308,3 +308,10 @@ along with GCC; see the file COPYING3. If not see + #define CLEAR_INSN_CACHE(beg, end) \ + extern void sys_icache_invalidate(void *start, size_t len); \ + sys_icache_invalidate ((beg), (size_t)((end)-(beg))) ++ ++/* Disable custom function descriptors for Darwin when we have off-stack ++ trampolines. */ ++#undef X86_CUSTOM_FUNCTION_TEST ++#define X86_CUSTOM_FUNCTION_TEST \ ++ (!flag_off_stack_trampolines && !flag_trampolines) ? 1 : 0 ++ +diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc +index 9dd9fa68722..5cd4b5c0592 100644 +--- a/gcc/config/i386/i386.cc ++++ b/gcc/config/i386/i386.cc +@@ -24698,7 +24698,7 @@ ix86_libgcc_floating_mode_supported_p + #define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok + + #undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS +-#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 ++#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS X86_CUSTOM_FUNCTION_TEST + + #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 363082ba47b..5f56d7abf65 100644 +--- a/gcc/config/i386/i386.h ++++ b/gcc/config/i386/i386.h +@@ -746,6 +746,12 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); + /* Minimum allocation boundary for the code of a function. */ + #define FUNCTION_BOUNDARY 8 + ++/* We will and with this value to test if a custom function descriptor needs ++ a static chain. The function boundary must the adjusted so that the bit ++ this represents is no longer part of the address. 0 Disables the custom ++ function descriptors. */ ++#define X86_CUSTOM_FUNCTION_TEST 1 ++ + /* C++ stores the virtual bit in the lowest bit of function pointers. */ + #define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_pfn + +diff --git a/gcc/configure b/gcc/configure +index 5ce0557719a..dbc127c59cb 100755 +--- a/gcc/configure ++++ b/gcc/configure +@@ -634,6 +634,7 @@ LIBOBJS + CET_HOST_FLAGS + NO_PIE_FLAG + NO_PIE_CFLAGS ++enable_pie_tools + enable_default_pie + PICFLAG + enable_host_shared +@@ -740,6 +741,8 @@ ORIGINAL_PLUGIN_LD_FOR_TARGET + gcc_cv_ld + ORIGINAL_AS_FOR_TARGET + gcc_cv_as ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_fast_install + objdir + OTOOL64 +@@ -1000,6 +1003,8 @@ enable_static + with_pic + enable_fast_install + enable_libtool_lock ++enable_darwin_at_rpath ++with_darwin_extra_rpath + enable_ld + enable_gold + with_plugin_ld +@@ -1030,6 +1035,7 @@ with_linker_hash_style + with_diagnostics_color + with_diagnostics_urls + enable_default_pie ++enable_pie_tools + enable_cet + enable_s390_excess_float_precision + ' +@@ -1733,6 +1739,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-ld[=ARG] build ld [ARG={default,yes,no}] + --enable-gold[=ARG] build gold [ARG={default,yes,no}] + --enable-gnu-indirect-function +@@ -1790,6 +1798,8 @@ Optional Features: + --disable-libquadmath-support + disable libquadmath support for Fortran + --enable-default-pie enable Position Independent Executable as default ++ --enable-pie-tools build Position Independent Executables for the ++ compilers and other tools + --enable-cet enable Intel CET in host libraries [default=auto] + --enable-s390-excess-float-precision + on s390 targets, evaluate float with double +@@ -1850,6 +1860,9 @@ Optional Packages: + --with-pic try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] ++ --with-darwin-extra-rpath=[ARG] ++ Specify a runpath directory, additional to those ++ provided by the compiler + --with-plugin-ld=[ARG] specify the plugin linker + --with-glibc-version=M.N + assume GCC used with glibc version M.N or later +@@ -3766,15 +3779,24 @@ if test x${gcc_gxx_libcxx_include_dir} != x; then + $as_echo "#define ENABLE_STDLIB_OPTION 1" >>confdefs.h + + else +- $as_echo "#define ENABLE_STDLIB_OPTION 0" >>confdefs.h ++ case $target in ++ *-darwin1[1-9]* | *-darwin2*) ++ # Default this on for Darwin versions which default to libcxx. ++ $as_echo "#define ENABLE_STDLIB_OPTION 1" >>confdefs.h + ++ ;; ++ *) ++ $as_echo "#define ENABLE_STDLIB_OPTION 0" >>confdefs.h ++ ++ ;; ++ esac + fi +-# ??? This logic must match libstdc++-v3/acinclude.m4:GLIBCXX_EXPORT_INSTALL_INFO. ++ + if test x${gcc_gxx_libcxx_include_dir} = x; then ++ libcxx_incdir='include/c++/v1' + if test x${enable_version_specific_runtime_libs} = xyes; then +- gcc_gxx_libcxx_include_dir='${libsubdir}/libc++_include/c++/v1' ++ gcc_gxx_libcxx_include_dir='${libsubdir}/$libcxx_incdir' + else +- libcxx_incdir='libc++_include/c++/$(version)/v1' + if test x$host != x$target; then + libcxx_incdir="$target_alias/$libcxx_incdir" + fi +@@ -17867,6 +17889,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -17884,10 +17947,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -19673,7 +19745,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 19676 "configure" ++#line 19748 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -19779,7 +19851,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 19782 "configure" ++#line 19854 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -20655,6 +20727,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -20672,12 +20785,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -23028,6 +23154,35 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++DARWIN_DO_EXTRA_RPATH=0 ++ ++# Check whether --with-darwin-extra-rpath was given. ++if test "${with_darwin_extra_rpath+set}" = set; then : ++ withval=$with_darwin_extra_rpath; if test x"$withval" != x; then ++ DARWIN_ADD_RPATH="$withval" ++ DARWIN_DO_EXTRA_RPATH=1 ++ fi ++fi ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define DARWIN_DO_EXTRA_RPATH $DARWIN_DO_EXTRA_RPATH ++_ACEOF ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define DARWIN_ADD_RPATH "$DARWIN_ADD_RPATH" ++_ACEOF ++ ++ + # Identify the assembler which will work hand-in-glove with the newly + # built GCC, so that we can examine its features. This is the assembler + # which will be driven by the driver program. +@@ -32429,6 +32584,22 @@ $as_echo "#define ENABLE_DEFAULT_PIE 1" >>confdefs.h + fi + + ++# Check whether --enable-pie-tools was given; this is passed automatically ++# from the top level where it has already been validated. ++# Check whether --enable-pie-tools was given. ++if test "${enable_pie_tools+set}" = set; then : ++ enableval=$enable_pie_tools; enable_pie_tools=$enableval ++else ++ enable_pie_tools=no ++fi ++ ++if test x$enable_pie_tools = xyes ; then ++ ++$as_echo "#define ENABLE_PIE_TOOLS 1" >>confdefs.h ++ ++fi ++ ++ + # Check if -fno-PIE works. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -fno-PIE option" >&5 + $as_echo_n "checking for -fno-PIE option... " >&6; } +@@ -32966,6 +33137,10 @@ LTLIBOBJS=$ac_ltlibobjs + + + ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/gcc/configure.ac b/gcc/configure.ac +index 23bee7010a3..2d9159989e4 100644 +--- a/gcc/configure.ac ++++ b/gcc/configure.ac +@@ -249,14 +249,22 @@ if test x${gcc_gxx_libcxx_include_dir} != x; then + AC_DEFINE(ENABLE_STDLIB_OPTION, 1, + [Define if the -stdlib= option should be enabled.]) + else +- AC_DEFINE(ENABLE_STDLIB_OPTION, 0) ++ case $target in ++ *-darwin1[[1-9]]* | *-darwin2*) ++ # Default this on for Darwin versions which default to libcxx. ++ AC_DEFINE(ENABLE_STDLIB_OPTION, 1) ++ ;; ++ *) ++ AC_DEFINE(ENABLE_STDLIB_OPTION, 0) ++ ;; ++ esac + fi +-# ??? This logic must match libstdc++-v3/acinclude.m4:GLIBCXX_EXPORT_INSTALL_INFO. ++ + if test x${gcc_gxx_libcxx_include_dir} = x; then ++ libcxx_incdir='include/c++/v1' + if test x${enable_version_specific_runtime_libs} = xyes; then +- gcc_gxx_libcxx_include_dir='${libsubdir}/libc++_include/c++/v1' ++ gcc_gxx_libcxx_include_dir='${libsubdir}/$libcxx_incdir' + else +- libcxx_incdir='libc++_include/c++/$(version)/v1' + if test x$host != x$target; then + libcxx_incdir="$target_alias/$libcxx_incdir" + fi +@@ -2559,6 +2567,21 @@ AC_PROG_LIBTOOL + AC_SUBST(objdir) + AC_SUBST(enable_fast_install) + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++DARWIN_DO_EXTRA_RPATH=0 ++AC_ARG_WITH(darwin-extra-rpath, ++[AS_HELP_STRING( ++ [[--with-darwin-extra-rpath=[ARG]]], ++ [Specify a runpath directory, additional to those provided by the compiler])], ++[if test x"$withval" != x; then ++ DARWIN_ADD_RPATH="$withval" ++ DARWIN_DO_EXTRA_RPATH=1 ++ fi]) ++AC_DEFINE_UNQUOTED(DARWIN_DO_EXTRA_RPATH, $DARWIN_DO_EXTRA_RPATH, ++ [Should add an extra runpath directory]) ++AC_DEFINE_UNQUOTED(DARWIN_ADD_RPATH, "$DARWIN_ADD_RPATH", ++ [Specify a runpath directory, additional to those provided by the compiler]) ++ + # Identify the assembler which will work hand-in-glove with the newly + # built GCC, so that we can examine its features. This is the assembler + # which will be driven by the driver program. +@@ -7646,6 +7669,19 @@ if test x$enable_default_pie = xyes ; then + fi + AC_SUBST([enable_default_pie]) + ++# Check whether --enable-pie-tools was given; this is passed automatically ++# from the top level where it has already been validated. ++AC_ARG_ENABLE(pie-tools, ++[AS_HELP_STRING([--enable-pie-tools], ++ [build Position Independent Executables for the compilers and other tools])], ++[enable_pie_tools=$enableval], ++[enable_pie_tools=no]) ++if test x$enable_pie_tools = xyes ; then ++ AC_DEFINE(ENABLE_PIE_TOOLS, 1, ++ [Define if you build Position Independent Executables for the compilers and other tools.]) ++fi ++AC_SUBST([enable_pie_tools]) ++ + # Check if -fno-PIE works. + AC_CACHE_CHECK([for -fno-PIE option], + [gcc_cv_c_no_fpie], +diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc +index d7e9980ff1e..ff2a1f6665f 100644 +--- a/gcc/cp/decl2.cc ++++ b/gcc/cp/decl2.cc +@@ -3653,9 +3653,8 @@ get_tls_init_fn (tree var) + if (!flag_extern_tls_init && DECL_EXTERNAL (var)) + return NULL_TREE; + +- /* If the variable is internal, or if we can't generate aliases, +- call the local init function directly. */ +- if (!TREE_PUBLIC (var) || !TARGET_SUPPORTS_ALIASES) ++ /* If the variable is internal call the local init function directly. */ ++ if (!TREE_PUBLIC (var)) + return get_local_tls_init_fn (DECL_SOURCE_LOCATION (var)); + + tree sname = mangle_tls_init_fn (var); +@@ -4801,22 +4800,24 @@ handle_tls_init (void) + finish_expr_stmt (cp_build_modify_expr (loc, guard, NOP_EXPR, + boolean_true_node, + tf_warning_or_error)); ++ auto_vec<tree> direct_calls; + for (; vars; vars = TREE_CHAIN (vars)) + { + tree var = TREE_VALUE (vars); + tree init = TREE_PURPOSE (vars); + one_static_initialization_or_destruction (var, init, true); + +- /* Output init aliases even with -fno-extern-tls-init. */ +- if (TARGET_SUPPORTS_ALIASES && TREE_PUBLIC (var)) ++ /* Output inits even with -fno-extern-tls-init. ++ We save the list here and output either an alias or a stub function ++ below. */ ++ if (TREE_PUBLIC (var)) + { +- tree single_init_fn = get_tls_init_fn (var); ++ tree single_init_fn = get_tls_init_fn (var); + if (single_init_fn == NULL_TREE) + continue; +- cgraph_node *alias +- = cgraph_node::get_create (fn)->create_same_body_alias +- (single_init_fn, fn); +- gcc_assert (alias != NULL); ++ if (single_init_fn == fn) ++ continue; ++ direct_calls.safe_push (single_init_fn); + } + } + +@@ -4824,6 +4825,30 @@ handle_tls_init (void) + finish_if_stmt (if_stmt); + finish_function_body (body); + expand_or_defer_fn (finish_function (/*inline_p=*/false)); ++ ++ /* For each TLS var that we have an init function, we either emit an alias ++ between that and the tls_init, or a stub function that just calls the ++ tls_init. */ ++ while (!direct_calls.is_empty()) ++ { ++ tree single_init_fn = direct_calls.pop (); ++ if (TARGET_SUPPORTS_ALIASES) ++ { ++ cgraph_node *alias ++ = cgraph_node::get_create (fn)->create_same_body_alias ++ (single_init_fn, fn); ++ gcc_assert (alias != NULL); ++ } ++ else ++ { ++ start_preparsed_function (single_init_fn, NULL_TREE, SF_PRE_PARSED); ++ tree body = begin_function_body (); ++ tree r = build_call_expr (fn, 0); ++ finish_expr_stmt (r); ++ finish_function_body (body); ++ expand_or_defer_fn (finish_function (/*inline_p=*/false)); ++ } ++ } + } + + /* We're at the end of compilation, so generate any mangling aliases that +diff --git a/gcc/cp/g++spec.cc b/gcc/cp/g++spec.cc +index 8174d652776..2e1e06e6ac9 100644 +--- a/gcc/cp/g++spec.cc ++++ b/gcc/cp/g++spec.cc +@@ -222,7 +222,12 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, + + case OPT_static_libstdc__: + library = library >= 0 ? 2 : library; ++#ifdef HAVE_LD_STATIC_DYNAMIC ++ /* Remove -static-libstdc++ from the command only if target supports ++ LD_STATIC_DYNAMIC. When not supported, it is left in so that a ++ back-end target can use outfile substitution. */ + args[i] |= SKIPOPT; ++#endif + break; + + case OPT_stdlib_: +diff --git a/gcc/cumulative-args.h b/gcc/cumulative-args.h +new file mode 100644 +index 00000000000..b60928e37f9 +--- /dev/null ++++ b/gcc/cumulative-args.h +@@ -0,0 +1,20 @@ ++#ifndef GCC_CUMULATIVE_ARGS_H ++#define GCC_CUMULATIVE_ARGS_H ++ ++#if CHECKING_P ++ ++struct cumulative_args_t { void *magic; void *p; }; ++ ++#else /* !CHECKING_P */ ++ ++/* When using a GCC build compiler, we could use ++ __attribute__((transparent_union)) to get cumulative_args_t function ++ arguments passed like scalars where the ABI would mandate a less ++ efficient way of argument passing otherwise. However, that would come ++ at the cost of less type-safe !CHECKING_P compilation. */ ++ ++union cumulative_args_t { void *p; }; ++ ++#endif /* !CHECKING_P */ ++ ++#endif /* GCC_CUMULATIVE_ARGS_H */ +diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi +index ff6c338bedb..55c8ba8969f 100644 +--- a/gcc/doc/invoke.texi ++++ b/gcc/doc/invoke.texi +@@ -670,6 +670,7 @@ Objective-C and Objective-C++ Dialects}. + @gccoptlist{-fcall-saved-@var{reg} -fcall-used-@var{reg} @gol + -ffixed-@var{reg} -fexceptions @gol + -fnon-call-exceptions -fdelete-dead-exceptions -funwind-tables @gol ++-foff-stack-trampolines @gol + -fasynchronous-unwind-tables @gol + -fno-gnu-unique @gol + -finhibit-size-directive -fcommon -fno-ident @gol +@@ -680,6 +681,7 @@ Objective-C and Objective-C++ Dialects}. + -fverbose-asm -fpack-struct[=@var{n}] @gol + -fleading-underscore -ftls-model=@var{model} @gol + -fstack-reuse=@var{reuse_level} @gol ++-fstack-use-cumulative-args @gol + -ftrampolines -ftrapv -fwrapv @gol + -fvisibility=@r{[}default@r{|}internal@r{|}hidden@r{|}protected@r{]} @gol + -fstrict-volatile-bitfields -fsync-libcalls} +@@ -17071,6 +17073,17 @@ the behavior of older compilers in which temporaries' stack space is + not reused, the aggressive stack reuse can lead to runtime errors. This + option is used to control the temporary stack reuse optimization. + ++@item -fstack-use-cumulative-args ++@opindex fstack_use_cumulative_args ++This option instructs the compiler to use the ++@code{cumulative_args_t}-based stack layout target hooks, ++@code{TARGET_FUNCTION_ARG_BOUNDARY_CA} and ++@code{TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA}. If a given target does ++not define these hooks, the default behaviour is to fallback to using ++the standard non-@code{_CA} variants instead. Certain targets (such as ++AArch64 Darwin) require using the more advanced @code{_CA}-based ++hooks: For these targets this option should be enabled by default. ++ + @item -ftrapv + @opindex ftrapv + This option generates traps for signed overflow on addition, subtraction, +@@ -17129,6 +17142,19 @@ instructions. It does not allow exceptions to be thrown from + arbitrary signal handlers such as @code{SIGALRM}. This enables + @option{-fexceptions}. + ++@item -foff-stack-trampolines ++@opindex foff-stack-trampolines ++Certain platforms (such as the Apple M1) do not permit an executable ++stack. Generate calls to @code{__builtin_nested_func_ptr_created} and ++@code{__builtin_nested_func_ptr_deleted} in order to allocate and ++deallocate trampoline space on the executable heap. Please note that ++these functions are implemented in libgcc, and will not be compiled in ++unless you provide @option{--enable-off-stack-trampolines} when ++building gcc. @emph{PLEASE NOTE}: The trampolines are @emph{not} ++guaranteed to be correctly deallocated if you @code{setjmp}, ++instantiate nested functions, and then @code{longjmp} back to a state ++prior to having allocated those nested functions. ++ + @item -fdelete-dead-exceptions + @opindex fdelete-dead-exceptions + Consider that instructions that may throw exceptions but don't otherwise +diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi +index c5006afc00d..366360612e3 100644 +--- a/gcc/doc/tm.texi ++++ b/gcc/doc/tm.texi +@@ -4322,6 +4322,16 @@ with the specified mode and type. The default hook returns + @code{PARM_BOUNDARY} for all arguments. + @end deftypefn + ++@deftypefn {Target Hook} {unsigned int} TARGET_FUNCTION_ARG_BOUNDARY_CA (machine_mode @var{mode}, const_tree @var{type}, cumulative_args_t @var{ca}) ++This is the @code{cumulative_args_t}-based version of ++@code{TARGET_FUNCTION_ARG_BOUNDARY}. Define this hook if you need more ++fine-grained control over argument alignment, e.g. depending on whether ++it is a named argument or not, or any other criteria that you choose to ++place in the @var{ca} structure. ++ ++The default hook will call @code{TARGET_FUNCTION_ARG_BOUNDARY}. ++@end deftypefn ++ + @deftypefn {Target Hook} {unsigned int} TARGET_FUNCTION_ARG_ROUND_BOUNDARY (machine_mode @var{mode}, const_tree @var{type}) + Normally, the size of an argument is rounded up to @code{PARM_BOUNDARY}, + which is the default value for this hook. You can define this hook to +@@ -4329,6 +4339,16 @@ return a different value if an argument size must be rounded to a larger + value. + @end deftypefn + ++@deftypefn {Target Hook} {unsigned int} TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA (machine_mode @var{mode}, const_tree @var{type}, cumulative_args_t @var{ca}) ++This is the @code{cumulative_args_t}-based version of ++@code{TARGET_FUNCTION_ARG_ROUND_BOUNDARY}. Define this hook if you need more ++fine-grained control over argument size rounding, e.g. depending on whether ++it is a named argument or not, or any other criteria that you choose to ++place in the @var{ca} structure. ++ ++The default hook will call @code{TARGET_FUNCTION_ARG_ROUND_BOUNDARY}. ++@end deftypefn ++ + @defmac FUNCTION_ARG_REGNO_P (@var{regno}) + A C expression that is nonzero if @var{regno} is the number of a hard + register in which function arguments are sometimes passed. This does +diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in +index f869ddd5e5b..2c9f0f0bf14 100644 +--- a/gcc/doc/tm.texi.in ++++ b/gcc/doc/tm.texi.in +@@ -3330,8 +3330,12 @@ required. + + @hook TARGET_FUNCTION_ARG_BOUNDARY + ++@hook TARGET_FUNCTION_ARG_BOUNDARY_CA ++ + @hook TARGET_FUNCTION_ARG_ROUND_BOUNDARY + ++@hook TARGET_FUNCTION_ARG_ROUND_BOUNDARY_CA ++ + @defmac FUNCTION_ARG_REGNO_P (@var{regno}) + A C expression that is nonzero if @var{regno} is the number of a hard + register in which function arguments are sometimes passed. This does +diff --git a/gcc/function.cc b/gcc/function.cc +index ad0096a43ef..e5044a60741 100644 +--- a/gcc/function.cc ++++ b/gcc/function.cc +@@ -2445,7 +2445,10 @@ assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm, + else if (DECL_CHAIN (parm)) + data->arg.named = 1; /* Not the last non-variadic parm. */ + else if (targetm.calls.strict_argument_naming (all->args_so_far)) +- data->arg.named = 1; /* Only variadic ones are unnamed. */ ++ { ++ data->arg.named = 1; /* Only variadic ones are unnamed. */ ++ data->arg.last_named = 1; ++ } + else + data->arg.named = 0; /* Treat as variadic. */ + +@@ -2502,6 +2505,7 @@ assign_parms_setup_varargs (struct assign_parm_data_all *all, + + function_arg_info last_named_arg = data->arg; + last_named_arg.named = true; ++ last_named_arg.last_named = true; + targetm.calls.setup_incoming_varargs (all->args_so_far, last_named_arg, + &varargs_pretend_bytes, no_rtl); + +@@ -2610,7 +2614,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, + + locate_and_pad_parm (data->arg.mode, data->arg.type, in_regs, + all->reg_parm_stack_space, +- entry_parm ? data->partial : 0, current_function_decl, ++ entry_parm ? data->partial : 0, ++ all->args_so_far, ++ current_function_decl, + &all->stack_args_size, &data->locate); + + /* Update parm_stack_boundary if this parameter is passed in the +@@ -3924,7 +3930,8 @@ gimplify_parameters (gimple_seq *cleanup) + if (data.arg.pass_by_reference) + { + tree type = TREE_TYPE (data.arg.type); +- function_arg_info orig_arg (type, data.arg.named); ++ function_arg_info orig_arg (type, data.arg.named, ++ data.arg.last_named); + if (reference_callee_copied (&all.args_so_far_v, orig_arg)) + { + tree local, t; +@@ -4027,6 +4034,7 @@ gimplify_parameters (gimple_seq *cleanup) + void + locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs, + int reg_parm_stack_space, int partial, ++ cumulative_args_t ca, + tree fndecl ATTRIBUTE_UNUSED, + struct args_size *initial_offset_ptr, + struct locate_and_pad_arg_data *locate) +@@ -4064,9 +4072,23 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs, + ? arg_size_in_bytes (type) + : size_int (GET_MODE_SIZE (passed_mode))); + where_pad = targetm.calls.function_arg_padding (passed_mode, type); +- boundary = targetm.calls.function_arg_boundary (passed_mode, type); +- round_boundary = targetm.calls.function_arg_round_boundary (passed_mode, +- type); ++ ++ if (flag_stack_use_cumulative_args) ++ { ++ boundary = targetm.calls.function_arg_boundary_ca (passed_mode, ++ type, ++ ca); ++ round_boundary = targetm.calls.function_arg_round_boundary_ca ++ (passed_mode, type, ca); ++ } ++ else ++ { ++ boundary = targetm.calls.function_arg_boundary (passed_mode, ++ type); ++ round_boundary = targetm.calls.function_arg_round_boundary ++ (passed_mode, type); ++ } ++ + locate->where_pad = where_pad; + + /* Alignment can't exceed MAX_SUPPORTED_STACK_ALIGNMENT. */ +diff --git a/gcc/function.h b/gcc/function.h +index 098613766be..009a9dc1c44 100644 +--- a/gcc/function.h ++++ b/gcc/function.h +@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see + #ifndef GCC_FUNCTION_H + #define GCC_FUNCTION_H + ++#include "cumulative-args.h" + + /* Stack of pending (incomplete) sequences saved by `start_sequence'. + Each element describes one pending sequence. +@@ -661,6 +662,7 @@ extern int aggregate_value_p (const_tree, const_tree); + extern bool use_register_for_decl (const_tree); + extern gimple_seq gimplify_parameters (gimple_seq *); + extern void locate_and_pad_parm (machine_mode, tree, int, int, int, ++ cumulative_args_t, + tree, struct args_size *, + struct locate_and_pad_arg_data *); + extern void generate_setjmp_warnings (void); +diff --git a/gcc/gcc.cc b/gcc/gcc.cc +index bb07cc244e3..a16c1e4372b 100644 +--- a/gcc/gcc.cc ++++ b/gcc/gcc.cc +@@ -572,6 +572,7 @@ or with constant text in a single argument. + %l process LINK_SPEC as a spec. + %L process LIB_SPEC as a spec. + %M Output multilib_os_dir. ++ %P Output a RUNPATH_OPTION for each directory in startfile_prefixes. + %G process LIBGCC_SPEC as a spec. + %R Output the concatenation of target_system_root and + target_sysroot_suffix. +@@ -1191,6 +1192,10 @@ proper position among the other output files. */ + # define SYSROOT_HEADERS_SUFFIX_SPEC "" + #endif + ++#ifndef RUNPATH_OPTION ++# define RUNPATH_OPTION "-rpath" ++#endif ++ + static const char *asm_debug = ASM_DEBUG_SPEC; + static const char *asm_debug_option = ASM_DEBUG_OPTION_SPEC; + static const char *cpp_spec = CPP_SPEC; +@@ -5895,6 +5900,7 @@ struct spec_path_info { + size_t append_len; + bool omit_relative; + bool separate_options; ++ bool realpaths; + }; + + static void * +@@ -5904,6 +5910,16 @@ spec_path (char *path, void *data) + size_t len = 0; + char save = 0; + ++ /* The path must exist; we want to resolve it to the realpath so that this ++ can be embedded as a runpath. */ ++ if (info->realpaths) ++ path = lrealpath (path); ++ ++ /* However, if we failed to resolve it - perhaps because there was a bogus ++ -B option on the command line, then punt on this entry. */ ++ if (!path) ++ return NULL; ++ + if (info->omit_relative && !IS_ABSOLUTE_PATH (path)) + return NULL; + +@@ -6135,6 +6151,22 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) + info.omit_relative = false; + #endif + info.separate_options = false; ++ info.realpaths = false; ++ ++ for_each_path (&startfile_prefixes, true, 0, spec_path, &info); ++ } ++ break; ++ ++ case 'P': ++ { ++ struct spec_path_info info; ++ ++ info.option = RUNPATH_OPTION; ++ info.append_len = 0; ++ info.omit_relative = false; ++ info.separate_options = true; ++ /* We want to embed the actual paths that have the libraries. */ ++ info.realpaths = true; + + for_each_path (&startfile_prefixes, true, 0, spec_path, &info); + } +@@ -6461,6 +6493,7 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) + info.append_len = strlen (info.append); + info.omit_relative = false; + info.separate_options = true; ++ info.realpaths = false; + + 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 79e296d4a66..a9caa0467ba 100644 +--- a/gcc/ginclude/stddef.h ++++ b/gcc/ginclude/stddef.h +@@ -427,9 +427,8 @@ typedef struct { + /* _Float128 is defined as a basic type, so max_align_t must be + sufficiently aligned for it. This code must work in C++, so we + use __float128 here; that is only available on some +- architectures, but only on i386 is extra alignment needed for +- __float128. */ +-#ifdef __i386__ ++ architectures. */ ++#if defined(__i386__) || (__APPLE__ && __aarch64__) + __float128 __max_align_f128 __attribute__((__aligned__(__alignof(__float128)))); + #endif + } max_align_t; +diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in +index 6e10abfd0ac..5a39342068d 100644 +--- a/gcc/jit/Make-lang.in ++++ b/gcc/jit/Make-lang.in +@@ -43,6 +43,7 @@ + LIBGCCJIT_VERSION_NUM = 0 + LIBGCCJIT_MINOR_NUM = 0 + LIBGCCJIT_RELEASE_NUM = 1 ++COMMA := , + + ifneq (,$(findstring mingw,$(target))) + LIBGCCJIT_FILENAME = libgccjit-$(LIBGCCJIT_VERSION_NUM).dll +@@ -59,22 +60,18 @@ LIBGCCJIT_AGE = 1 + LIBGCCJIT_BASENAME = libgccjit + + LIBGCCJIT_SONAME = \ +- ${libdir}/$(LIBGCCJIT_BASENAME).$(LIBGCCJIT_VERSION_NUM).dylib ++ $(DARWIN_RPATH)/$(LIBGCCJIT_BASENAME).$(LIBGCCJIT_VERSION_NUM).dylib + LIBGCCJIT_FILENAME = $(LIBGCCJIT_BASENAME).$(LIBGCCJIT_VERSION_NUM).dylib + LIBGCCJIT_LINKER_NAME = $(LIBGCCJIT_BASENAME).dylib + +-# Conditionalize the use of the LD_VERSION_SCRIPT_OPTION and +-# LD_SONAME_OPTION depending if configure found them, using $(if) +-# We have to define a COMMA here, otherwise the commas in the "true" +-# result are treated as separators by the $(if). +-COMMA := , +-LIBGCCJIT_VERSION_SCRIPT_OPTION = \ +- $(if $(LD_VERSION_SCRIPT_OPTION),\ +- -Wl$(COMMA)$(LD_VERSION_SCRIPT_OPTION)$(COMMA)$(srcdir)/jit/libgccjit.map) ++# TODO: translate the libgccjit.map into a form usable by Darwin's linker and ++# then check for linker support for -exported_symbols_list=. Omitting this ++# means that all symbols in the libgccjit library will be visible. ++LIBGCCJIT_VERSION_SCRIPT_OPTION = + +-LIBGCCJIT_SONAME_OPTION = \ +- $(if $(LD_SONAME_OPTION), \ +- -Wl$(COMMA)$(LD_SONAME_OPTION)$(COMMA)$(LIBGCCJIT_SONAME)) ++# This is a work-around fix for cross-compilation where the target linker ++# is ld and the host is ld64. ++LIBGCCJIT_SONAME_OPTION = -Wl,-install_name,$(LIBGCCJIT_SONAME) + + LIBGCCJIT_SONAME_SYMLINK = $(LIBGCCJIT_FILENAME) + LIBGCCJIT_LINKER_NAME_SYMLINK = $(LIBGCCJIT_LINKER_NAME) +@@ -98,7 +95,6 @@ LIBGCCJIT_SONAME_SYMLINK = $(LIBGCCJIT_SONAME) + # LD_SONAME_OPTION depending if configure found them, using $(if) + # We have to define a COMMA here, otherwise the commas in the "true" + # result are treated as separators by the $(if). +-COMMA := , + LIBGCCJIT_VERSION_SCRIPT_OPTION = \ + $(if $(LD_VERSION_SCRIPT_OPTION),\ + -Wl$(COMMA)$(LD_VERSION_SCRIPT_OPTION)$(COMMA)$(srcdir)/jit/libgccjit.map) +diff --git a/gcc/objc/objc-next-runtime-abi-02.cc b/gcc/objc/objc-next-runtime-abi-02.cc +index e50ca6e89f5..9ea63b189c7 100644 +--- a/gcc/objc/objc-next-runtime-abi-02.cc ++++ b/gcc/objc/objc-next-runtime-abi-02.cc +@@ -1033,6 +1033,7 @@ next_runtime_abi_02_protocol_decl (tree p) + else + decl = start_var_decl (objc_v2_protocol_template, buf); + OBJCMETA (decl, objc_meta, meta_protocol); ++ DECL_PRESERVE_P (decl) = 1; + return decl; + } + +@@ -2115,8 +2116,8 @@ build_v2_classrefs_table (void) + expr = convert (objc_class_type, build_fold_addr_expr (expr)); + } + /* The runtime wants this, even if it appears unused, so we must force the +- output. +- DECL_PRESERVE_P (decl) = 1; */ ++ output. */ ++ DECL_PRESERVE_P (decl) = 1; + finish_var_decl (decl, expr); + } + } +@@ -2318,6 +2319,7 @@ build_v2_protocol_list_address_table (void) + expr = convert (objc_protocol_type, build_fold_addr_expr (ref->refdecl)); + OBJCMETA (decl, objc_meta, meta_label_protocollist); + finish_var_decl (decl, expr); ++ DECL_PRESERVE_P (decl) = 1; + } + + /* TODO: delete the vec. */ +diff --git a/gcc/target.def b/gcc/target.def +index d85adf36a39..5eb1fdce24e 100644 +--- a/gcc/target.def ++++ b/gcc/target.def +@@ -4967,6 +4967,18 @@ with the specified mode and type. The default hook returns\n\ + unsigned int, (machine_mode mode, const_tree type), + default_function_arg_boundary) + ++DEFHOOK ++(function_arg_boundary_ca, ++ "This is the @code{cumulative_args_t}-based version of\n\ ++@code{TARGET_FUNCTION_ARG_BOUNDARY}. Define this hook if you need more\n\ ++fine-grained control over argument alignment, e.g. depending on whether\n\ ++it is a named argument or not, or any other criteria that you choose to\n\ ++place in the @var{ca} structure.\n\ ++\n\ ++The default hook will call @code{TARGET_FUNCTION_ARG_BOUNDARY}.", ++ unsigned int, (machine_mode mode, const_tree type, cumulative_args_t ca), ++ default_function_arg_boundary_ca) ++ + DEFHOOK + (function_arg_round_boundary, + "Normally, the size of an argument is rounded up to @code{PARM_BOUNDARY},\n\ +@@ -4976,6 +4988,18 @@ value.", + unsigned int, (machine_mode mode, const_tree type), + default_function_arg_round_boundary) + ++DEFHOOK ++(function_arg_round_boundary_ca, ++ "This is the @code{cumulative_args_t}-based version of\n\ ++@code{TARGET_FUNCTION_ARG_ROUND_BOUNDARY}. Define this hook if you need more\n\ ++fine-grained control over argument size rounding, e.g. depending on whether\n\ ++it is a named argument or not, or any other criteria that you choose to\n\ ++place in the @var{ca} structure.\n\ ++\n\ ++The default hook will call @code{TARGET_FUNCTION_ARG_ROUND_BOUNDARY}.", ++ unsigned int, (machine_mode mode, const_tree type, cumulative_args_t ca), ++ default_function_arg_round_boundary_ca) ++ + /* Return the diagnostic message string if function without a prototype + is not allowed for this 'val' argument; NULL otherwise. */ + DEFHOOK +diff --git a/gcc/target.h b/gcc/target.h +index d6fa6931499..40c3da87656 100644 +--- a/gcc/target.h ++++ b/gcc/target.h +@@ -51,22 +51,7 @@ + #include "insn-codes.h" + #include "tm.h" + #include "hard-reg-set.h" +- +-#if CHECKING_P +- +-struct cumulative_args_t { void *magic; void *p; }; +- +-#else /* !CHECKING_P */ +- +-/* When using a GCC build compiler, we could use +- __attribute__((transparent_union)) to get cumulative_args_t function +- arguments passed like scalars where the ABI would mandate a less +- efficient way of argument passing otherwise. However, that would come +- at the cost of less type-safe !CHECKING_P compilation. */ +- +-union cumulative_args_t { void *p; }; +- +-#endif /* !CHECKING_P */ ++#include "cumulative-args.h" + + /* 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 399d6f874dc..9d554adcb45 100644 +--- a/gcc/targhooks.cc ++++ b/gcc/targhooks.cc +@@ -850,6 +850,14 @@ default_function_arg_boundary (machine_mode mode ATTRIBUTE_UNUSED, + return PARM_BOUNDARY; + } + ++unsigned int ++default_function_arg_boundary_ca (machine_mode mode ATTRIBUTE_UNUSED, ++ const_tree type ATTRIBUTE_UNUSED, ++ cumulative_args_t ca ATTRIBUTE_UNUSED) ++{ ++ return default_function_arg_boundary (mode, type); ++} ++ + unsigned int + default_function_arg_round_boundary (machine_mode mode ATTRIBUTE_UNUSED, + const_tree type ATTRIBUTE_UNUSED) +@@ -857,6 +865,14 @@ default_function_arg_round_boundary (machine_mode mode ATTRIBUTE_UNUSED, + return PARM_BOUNDARY; + } + ++unsigned int ++default_function_arg_round_boundary_ca (machine_mode mode ATTRIBUTE_UNUSED, ++ const_tree type ATTRIBUTE_UNUSED, ++ cumulative_args_t ca ATTRIBUTE_UNUSED) ++{ ++ return default_function_arg_round_boundary (mode, type); ++} ++ + void + hook_void_bitmap (bitmap regs ATTRIBUTE_UNUSED) + { +diff --git a/gcc/targhooks.h b/gcc/targhooks.h +index ecce55ebe79..ba110ade58b 100644 +--- a/gcc/targhooks.h ++++ b/gcc/targhooks.h +@@ -154,6 +154,12 @@ extern unsigned int default_function_arg_boundary (machine_mode, + const_tree); + extern unsigned int default_function_arg_round_boundary (machine_mode, + const_tree); ++extern unsigned int default_function_arg_boundary_ca (machine_mode, ++ const_tree, ++ cumulative_args_t ca); ++extern unsigned int default_function_arg_round_boundary_ca (machine_mode, ++ const_tree, ++ cumulative_args_t ca); + extern bool hook_bool_const_rtx_commutative_p (const_rtx, int); + 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/testsuite/g++.dg/abi/aarch64_guard1.C b/gcc/testsuite/g++.dg/abi/aarch64_guard1.C +index e2669a89fbf..52be32decc6 100644 +--- a/gcc/testsuite/g++.dg/abi/aarch64_guard1.C ++++ b/gcc/testsuite/g++.dg/abi/aarch64_guard1.C +@@ -12,5 +12,6 @@ int *foo () + return &x; + } + +-// { dg-final { scan-assembler _ZGVZ3foovE1x,8,8 } } ++// { dg-final { scan-assembler _ZGVZ3foovE1x,8,8 { target { ! *-*-darwin* } } } } ++// { dg-final { scan-assembler __DATA,__bss,__ZGVZ3foovE1x,8,3 { target *-*-darwin* } } } + // { dg-final { scan-tree-dump "& 1" "original" } } +diff --git a/gcc/testsuite/g++.dg/abi/arm_va_list.C b/gcc/testsuite/g++.dg/abi/arm_va_list.C +index 4f6f3a46da4..ff9fd8bcf0d 100644 +--- a/gcc/testsuite/g++.dg/abi/arm_va_list.C ++++ b/gcc/testsuite/g++.dg/abi/arm_va_list.C +@@ -8,8 +8,10 @@ + // #include <stdarg.h> + typedef __builtin_va_list va_list; + +-// { dg-final { scan-assembler "\n_Z1fPSt9__va_list:" } } ++// { dg-final { scan-assembler "\n_Z1fPSt9__va_list:" { target { ! *-*-darwin* } } } } ++// { dg-final { scan-assembler "\n__Z1fPPc:" { target *-*-darwin* } } } + void f(va_list*) {} + +-// { dg-final { scan-assembler "\n_Z1gSt9__va_listS_:" } } ++// { dg-final { scan-assembler "\n_Z1gSt9__va_listS_:" { target { ! *-*-darwin* } } } } ++// { dg-final { scan-assembler "\n__Z1gPcS_:" { target *-*-darwin* } } } + void g(va_list, va_list) {} +diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc b/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc +new file mode 100644 +index 00000000000..4f581694177 +--- /dev/null ++++ b/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc +@@ -0,0 +1,17 @@ ++// PR c++/106435 ++#include "pr106435.h" ++ ++//#include <iostream> ++ ++Foo::Foo() { ++ ++num_calls; ++// std::cout << "Foo::Foo(this=" << this << ")\n"; ++} ++ ++int Foo::func() { ++// std::cout << "Foo::func(this=" << this << ")\n"; ++ return num_calls; ++} ++ ++thread_local Foo Bar::foo; ++thread_local Foo Bar::baz; +diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435.C b/gcc/testsuite/g++.dg/cpp0x/pr106435.C +new file mode 100644 +index 00000000000..d600976f9f9 +--- /dev/null ++++ b/gcc/testsuite/g++.dg/cpp0x/pr106435.C +@@ -0,0 +1,20 @@ ++// PR c++/106435 ++// { dg-do run { target c++11 } } ++// { dg-additional-sources "pr106435-b.cc" } ++ ++#include "pr106435.h" ++ ++int num_calls = 0; ++ ++extern "C" __attribute__((__noreturn__)) void abort(); ++ ++thread_local Foo Bar::bat; ++ ++int main() { ++ int v = Bar::foo.func(); ++ if (v != 2) ++ abort(); ++ v = Bar::bat.func(); ++ if (v != 3) ++ abort(); ++} +diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435.h b/gcc/testsuite/g++.dg/cpp0x/pr106435.h +new file mode 100644 +index 00000000000..240de1ee9a9 +--- /dev/null ++++ b/gcc/testsuite/g++.dg/cpp0x/pr106435.h +@@ -0,0 +1,14 @@ ++// PR c++/106435 ++#pragma once ++ ++extern int num_calls; ++struct Foo { ++ Foo(); ++ int func(); ++}; ++ ++struct Bar { ++ thread_local static Foo foo; ++ thread_local static Foo baz; ++ thread_local static Foo bat; ++}; +diff --git a/gcc/testsuite/g++.dg/ext/arm-bf16/bf16-mangle-aarch64-1.C b/gcc/testsuite/g++.dg/ext/arm-bf16/bf16-mangle-aarch64-1.C +index 5426a1814b8..a017ce8ce5f 100644 +--- a/gcc/testsuite/g++.dg/ext/arm-bf16/bf16-mangle-aarch64-1.C ++++ b/gcc/testsuite/g++.dg/ext/arm-bf16/bf16-mangle-aarch64-1.C +@@ -2,12 +2,12 @@ + + /* Test mangling */ + +-/* { dg-final { scan-assembler "\t.global\t_Z1fPu6__bf16" } } */ ++/* { dg-final { scan-assembler {\t.globa?l[ \t]_?_Z1fPu6__bf16} } } */ + void f (__bf16 *x) { } + +-/* { dg-final { scan-assembler "\t.global\t_Z1gPu6__bf16S_" } } */ ++/* { dg-final { scan-assembler {\t.globa?l[ \t]_?_Z1gPu6__bf16S_} } } */ + void g (__bf16 *x, __bf16 *y) { } + +-/* { dg-final { scan-assembler "\t.global\t_ZN1SIu6__bf16u6__bf16E1iE" } } */ ++/* { dg-final { scan-assembler {\t.globa?l[ \t]_?_ZN1SIu6__bf16u6__bf16E1iE} } } */ + template <typename T, typename U> struct S { static int i; }; + template <> int S<__bf16, __bf16>::i = 3; +diff --git a/gcc/testsuite/g++.dg/torture/darwin-cfstring-3.C b/gcc/testsuite/g++.dg/torture/darwin-cfstring-3.C +index ee4b385b17f..eabb3b517a4 100644 +--- a/gcc/testsuite/g++.dg/torture/darwin-cfstring-3.C ++++ b/gcc/testsuite/g++.dg/torture/darwin-cfstring-3.C +@@ -26,5 +26,5 @@ void foo(void) { + + /* { dg-final { scan-assembler "\\.long\[ \\t\]+___CFConstantStringClassReference\n\[ \\t\]*\\.long\[ \\t\]+1992\n\[ \\t\]*\\.long\[ \\t\]+\[lL\]C.*\n\[ \\t\]*\\.long\[ \\t\]+4\n" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler "\\.long\[ \\t\]+___CFConstantStringClassReference\n\[ \\t\]*\\.long\[ \\t\]+1992\n\[ \\t\]*\\.long\[ \\t\]+\[lL\]C.*\n\[ \\t\]*\\.long\[ \\t\]+10\n" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t4\n" { target { *-*-darwin* && { lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t10\n" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t4\n} { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t10\n} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C b/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C +index 5fc68ea5d6d..5faf915fa54 100644 +--- a/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C ++++ b/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C +@@ -1,5 +1,5 @@ + /* { dg-options "-std=c++11 -O -foptimize-sibling-calls -fpeephole2" } */ +-/* { dg-final { check-function-bodies "**" "" "" { target lp64 } } } */ ++/* { dg-final { check-function-bodies "**" "" "" { target { lp64 && { ! aarch64*-*-darwin* } } } } } */ + + struct X { }; + struct Y { int : 0; }; +diff --git a/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C b/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C +index f0717133ccd..322ec127c79 100644 +--- a/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C ++++ b/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C +@@ -1,5 +1,5 @@ + /* { dg-options "-std=c++17 -O -foptimize-sibling-calls -fpeephole2" } */ +-/* { dg-final { check-function-bodies "**" "" "" { target lp64 } } } */ ++/* { dg-final { check-function-bodies "**" "" "" { target { lp64 && { ! aarch64*-*-darwin* } } } } } */ + + struct X { }; + struct Y { int : 0; }; +diff --git a/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp b/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp +index 03a6537a53e..d4c2052dc59 100644 +--- a/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp ++++ b/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } then { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib g++-dg.exp + +diff --git a/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp b/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp +index 38140413a97..559e1f37c68 100644 +--- a/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp ++++ b/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp +@@ -24,6 +24,11 @@ if { ![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib g++-dg.exp + +diff --git a/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle.exp b/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle.exp +index d1887eb8087..c9fee945c52 100644 +--- a/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle.exp ++++ b/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib g++-dg.exp + +diff --git a/gcc/testsuite/g++.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp b/gcc/testsuite/g++.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp +index 78e8ecae729..e22ef5f0876 100644 +--- a/gcc/testsuite/g++.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp ++++ b/gcc/testsuite/g++.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp +@@ -24,6 +24,11 @@ if { ![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib g++-dg.exp + +diff --git a/gcc/testsuite/gcc.dg/builtin-apply2.c b/gcc/testsuite/gcc.dg/builtin-apply2.c +index 0f350f4ac16..d1e70b3a3e5 100644 +--- a/gcc/testsuite/gcc.dg/builtin-apply2.c ++++ b/gcc/testsuite/gcc.dg/builtin-apply2.c +@@ -1,7 +1,7 @@ + /* { dg-do run } */ + /* { dg-require-effective-target untyped_assembly } */ + /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { "avr-*-* nds32*-*-* amdgcn-*-*" } } */ +-/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "csky*-*-* riscv*-*-* or1k*-*-* msp430-*-* pru-*-* loongarch*-*-*" } } */ ++/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "csky*-*-* riscv*-*-* or1k*-*-* msp430-*-* pru-*-* loongarch*-*-* aarch64-apple-darwin*" } } */ + /* { dg-skip-if "Variadic funcs use Base AAPCS. Normal funcs use VFP variant." { arm*-*-* && arm_hf_eabi } } */ + + /* PR target/12503 */ +diff --git a/gcc/testsuite/gcc.dg/cwsc1.c b/gcc/testsuite/gcc.dg/cwsc1.c +index e793e26116a..7d8b472bdf6 100644 +--- a/gcc/testsuite/gcc.dg/cwsc1.c ++++ b/gcc/testsuite/gcc.dg/cwsc1.c +@@ -6,7 +6,11 @@ + #elif defined(__i386__) + # define CHAIN "%ecx" + #elif defined(__aarch64__) +-# define CHAIN "x18" ++# if defined(__APPLE__) ++# define CHAIN "x16" ++# else ++# define CHAIN "x18" ++# endif + #elif defined(__alpha__) + # define CHAIN "$1" + #elif defined(__arm__) +diff --git a/gcc/testsuite/gcc.dg/darwin-segaddr.c b/gcc/testsuite/gcc.dg/darwin-segaddr.c +index 526db77bd9c..fcc324b3031 100644 +--- a/gcc/testsuite/gcc.dg/darwin-segaddr.c ++++ b/gcc/testsuite/gcc.dg/darwin-segaddr.c +@@ -1,7 +1,8 @@ + /* Check that -segaddr gets through and works. */ + /* { dg-do run { target *-*-darwin* } } */ + /* { dg-options "-O0 -segaddr __TEST 0x200000 -fno-pie" { target { *-*-darwin* && { ! lp64 } } } } */ +-/* { dg-options "-O0 -segaddr __TEST 0x110000000 -fno-pie" { target { *-*-darwin* && lp64 } } } */ ++/* { dg-options "-O0 -segaddr __TEST 0x110000000 -fno-pie" { target { *-*-darwin[1456789]* && lp64 } } } */ ++/* { dg-options "-O0 -segaddr __TEST 0x110000000 " { target { *-*-darwin2* && lp64 } } } */ + + extern void abort (); + +diff --git a/gcc/testsuite/gcc.dg/pr26427.c b/gcc/testsuite/gcc.dg/pr26427.c +index add13ca209e..2c09f28195d 100644 +--- a/gcc/testsuite/gcc.dg/pr26427.c ++++ b/gcc/testsuite/gcc.dg/pr26427.c +@@ -1,4 +1,4 @@ +-/* { dg-warning "this target does not support" "" {target *86*-*-darwin* } 0 } */ ++/* { dg-warning "this target does not support" "" {target *86*-*-darwin* aarch64-*-darwin* } 0 } */ + /* { dg-do run { target { *-*-darwin* } } } */ + /* { dg-options { -fsection-anchors -O } } */ + /* PR target/26427 */ +diff --git a/gcc/testsuite/gcc.dg/pubtypes-2.c b/gcc/testsuite/gcc.dg/pubtypes-2.c +index 116e3489bc0..b3d1231ad44 100644 +--- a/gcc/testsuite/gcc.dg/pubtypes-2.c ++++ b/gcc/testsuite/gcc.dg/pubtypes-2.c +@@ -2,7 +2,8 @@ + /* { dg-options "-O0 -gdwarf-2 -dA" } */ + /* { dg-skip-if "Unmatchable assembly" { mmix-*-* } } */ + /* { dg-final { scan-assembler "__debug_pubtypes" } } */ +-/* { dg-final { scan-assembler {long+[ \t]+0x14d+[ \t]+[#;]+[ \t]+Pub Info Length} } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x14d+[ \t]+[#;]+[ \t]+Pub Info Length} { target { ! aarch64-*-darwin* } } } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x163+[ \t]+[#;]+[ \t]+Pub Info Length} { target aarch64-*-darwin* } } } */ + /* { dg-final { scan-assembler "used_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + /* { dg-final { scan-assembler-not "unused_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + +diff --git a/gcc/testsuite/gcc.dg/pubtypes-3.c b/gcc/testsuite/gcc.dg/pubtypes-3.c +index 3fb3468fb00..950a9ba72fc 100644 +--- a/gcc/testsuite/gcc.dg/pubtypes-3.c ++++ b/gcc/testsuite/gcc.dg/pubtypes-3.c +@@ -2,7 +2,8 @@ + /* { dg-options "-O0 -gdwarf-2 -dA" } */ + /* { dg-skip-if "Unmatchable assembly" { mmix-*-* } } */ + /* { dg-final { scan-assembler "__debug_pubtypes" } } */ +-/* { dg-final { scan-assembler {long+[ \t]+0x14d+[ \t]+[#;]+[ \t]+Pub Info Length} } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x14d+[ \t]+[#;]+[ \t]+Pub Info Length} { target { ! aarch64-*-darwin* } } } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x163+[ \t]+[#;]+[ \t]+Pub Info Length} { target aarch64-*-darwin* } } } */ + /* { dg-final { scan-assembler "used_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + /* { dg-final { scan-assembler-not "unused_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + /* { dg-final { scan-assembler-not "\"list_name_type\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ +diff --git a/gcc/testsuite/gcc.dg/pubtypes-4.c b/gcc/testsuite/gcc.dg/pubtypes-4.c +index 83fba8dfabc..7250771587b 100644 +--- a/gcc/testsuite/gcc.dg/pubtypes-4.c ++++ b/gcc/testsuite/gcc.dg/pubtypes-4.c +@@ -2,7 +2,8 @@ + /* { dg-options "-O0 -gdwarf-2 -dA" } */ + /* { dg-skip-if "Unmatchable assembly" { mmix-*-* } } */ + /* { dg-final { scan-assembler "__debug_pubtypes" } } */ +-/* { dg-final { scan-assembler {long+[ \t]+0x184+[ \t]+[#;]+[ \t]+Pub Info Length} } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x184+[ \t]+[#;]+[ \t]+Pub Info Length} { target { ! aarch64-*-darwin* } } } } */ ++/* { dg-final { scan-assembler {long+[ \t]+0x19a+[ \t]+[#;]+[ \t]+Pub Info Length} { target aarch64-*-darwin* } } } */ + /* { dg-final { scan-assembler "used_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + /* { dg-final { scan-assembler-not "unused_struct\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ + /* { dg-final { scan-assembler "\"list_name_type\\\\0\"+\[ \t\]+\[#;]+\[ \t\]+external name" } } */ +diff --git a/gcc/testsuite/gcc.dg/rtl/aarch64/big-endian-cse-1.c b/gcc/testsuite/gcc.dg/rtl/aarch64/big-endian-cse-1.c +index 1559a489f25..aa2da0cbca5 100644 +--- a/gcc/testsuite/gcc.dg/rtl/aarch64/big-endian-cse-1.c ++++ b/gcc/testsuite/gcc.dg/rtl/aarch64/big-endian-cse-1.c +@@ -1,4 +1,5 @@ + /* { dg-do compile { target aarch64*-*-* } } */ ++/* { dg-skip-if "Darwin platforms do not support big-endian arm64" *-*-darwin* } */ + /* { dg-require-effective-target lp64 } */ + /* { dg-options "-O3 -mbig-endian" } */ + +diff --git a/gcc/testsuite/gcc.dg/tls/pr78796.c b/gcc/testsuite/gcc.dg/tls/pr78796.c +index 038e5366e41..31e03dd419c 100644 +--- a/gcc/testsuite/gcc.dg/tls/pr78796.c ++++ b/gcc/testsuite/gcc.dg/tls/pr78796.c +@@ -1,7 +1,7 @@ + /* PR target/78796 */ + /* { dg-do run } */ + /* { dg-options "-O2" } */ +-/* { dg-additional-options "-mcmodel=large" { target aarch64-*-* } } */ ++/* { dg-additional-options "-mcmodel=large" { target { { aarch64-*-* } && { ! aarch64-*-darwin* } } } } */ + /* { dg-require-effective-target tls_runtime } */ + /* { dg-add-options tls } */ + +diff --git a/gcc/testsuite/gcc.dg/torture/darwin-cfstring-3.c b/gcc/testsuite/gcc.dg/torture/darwin-cfstring-3.c +index ee4b385b17f..eabb3b517a4 100644 +--- a/gcc/testsuite/gcc.dg/torture/darwin-cfstring-3.c ++++ b/gcc/testsuite/gcc.dg/torture/darwin-cfstring-3.c +@@ -26,5 +26,5 @@ void foo(void) { + + /* { dg-final { scan-assembler "\\.long\[ \\t\]+___CFConstantStringClassReference\n\[ \\t\]*\\.long\[ \\t\]+1992\n\[ \\t\]*\\.long\[ \\t\]+\[lL\]C.*\n\[ \\t\]*\\.long\[ \\t\]+4\n" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler "\\.long\[ \\t\]+___CFConstantStringClassReference\n\[ \\t\]*\\.long\[ \\t\]+1992\n\[ \\t\]*\\.long\[ \\t\]+\[lL\]C.*\n\[ \\t\]*\\.long\[ \\t\]+10\n" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t4\n" { target { *-*-darwin* && { lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t10\n" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t4\n} { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t10\n} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c +index 552ca1433f4..16643ceb198 100644 +--- a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c ++++ b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c +@@ -9,7 +9,7 @@ + /* arm_hf_eabi: Variadic funcs use Base AAPCS. Normal funcs use VFP variant. + avr: Variadic funcs don't pass arguments in registers, while normal funcs + do. */ +-/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { csky*-*-* avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* loongarch*-*-* } } } */ ++/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { csky*-*-* avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* loongarch*-*-* aarch64-apple-darwin* } } } */ + /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { nds32*-*-* } { v850*-*-* } } */ + /* { dg-require-effective-target untyped_assembly } */ + +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c +index 0224997f18a..3684cffdc64 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-2.c +@@ -25,9 +25,9 @@ f1 (int i, ...) + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -46,9 +46,9 @@ f2 (int i, ...) + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 8 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 1 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -62,10 +62,10 @@ f3 (int i, ...) + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and 1 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and 16 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and 16 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[1-9\]\[0-9\]* GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[1-9\]\[0-9\]* GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[1-9\]\[0-9\]* GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[1-9\]\[0-9\]* GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -81,9 +81,9 @@ f4 (int i, ...) + /* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -100,9 +100,9 @@ f5 (int i, ...) + /* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -121,9 +121,9 @@ f6 (int i, ...) + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 3 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -139,9 +139,9 @@ f7 (int i, ...) + /* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -159,9 +159,9 @@ f8 (int i, ...) + /* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f8: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -177,9 +177,9 @@ f9 (int i, ...) + /* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f9: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -197,9 +197,9 @@ f10 (int i, ...) + /* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f10: va_list escapes 1, needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -218,9 +218,9 @@ f11 (int i, ...) + /* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save 3 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f11: va_list escapes 0, needs to save (3|12|24) GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -239,9 +239,9 @@ f12 (int i, ...) + /* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 24 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 0 GPR units and 3 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 0 GPR units and 48 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save 0 GPR units and 48 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f12: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -260,9 +260,9 @@ f13 (int i, ...) + /* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 24 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 0 GPR units and 3 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 0 GPR units and 48 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save 0 GPR units and 48 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f13: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -281,9 +281,9 @@ f14 (int i, ...) + /* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save 24 GPR units and 3" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save 1 GPR units and 2 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f14: va_list escapes 0, needs to save \[1-9]\[0-9\]* GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + inline void __attribute__((always_inline)) +@@ -305,11 +305,11 @@ f15 (int i, ...) + /* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save \[148\] GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f15: va_list escapes 0, needs to save 1 GPR units and 2 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f15: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f15: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + + /* We may be able to improve upon this after fixing PR66010/PR66013. */ + /* { dg-final { scan-tree-dump "f15: va_list escapes 1, needs to save all GPR units and all FPR units" "stdarg" { target alpha*-*-linux* } } } */ + + /* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump-not "f15: va_list escapes 0, needs to save 0 GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c +index 1a637d6efe4..77cdf384df4 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-4.c +@@ -27,9 +27,9 @@ f1 (int i, ...) + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f1: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -45,9 +45,9 @@ f2 (int i, ...) + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 0 GPR units and all FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 0 GPR units and all FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 0 GPR units and all FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save 0 GPR units and all FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f2: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes \[01\], needs to save all GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + /* Here va_arg can be executed at most as many times as va_start. +@@ -69,9 +69,9 @@ f3 (int i, ...) + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units and 0 FPR units" "stdarg" { target { powerpc*-*-linux* && ilp32 } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 1 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ + + void +@@ -91,7 +91,7 @@ f4 (int i, ...) + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 0 GPR units and \[1-9\]\[0-9\]* FPR units" "stdarg" { target { powerpc*-*-linux* && { powerpc_fprs && ilp32 } } } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 8 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 0 GPR units and 1 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 0 GPR units and 16 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 0 GPR units and 16 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */ +-/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target ia64-*-* aarch64-apple-darwin* } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save \[148\] GPR units" "stdarg" { target { powerpc*-*-* && lp64 } } } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c +index c8ad4fe320d..b0484f2f053 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/stdarg-5.c +@@ -25,7 +25,8 @@ f1 (int i, ...) + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f1: va_list escapes 0, needs to save all GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + void + f2 (int i, ...) +@@ -39,7 +40,8 @@ f2 (int i, ...) + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and all FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f2: va_list escapes 0, needs to save all GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + /* Here va_arg can be executed at most as many times as va_start. */ + void +@@ -58,7 +60,8 @@ f3 (int i, ...) + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 0 GPR units and 0 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 32 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 1 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f3: va_list escapes 0, needs to save 8 GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + void + f4 (int i, ...) +@@ -77,7 +80,8 @@ f4 (int i, ...) + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 16 GPR units and 16 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 24 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 2 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 24 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f4: va_list escapes 0, needs to save 24 GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + void + f5 (int i, ...) +@@ -92,7 +96,8 @@ f5 (int i, ...) + /* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 16 GPR units and 0 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 32 GPR units and 1" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save (4|2) GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 16 GPR units and 0 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 16 GPR units and 0 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f5: va_list escapes 0, needs to save 16 GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + void + f6 (int i, ...) +@@ -107,7 +112,8 @@ f6 (int i, ...) + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 32 GPR units and 3" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save (3|2) GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 8 GPR units and 32 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f6: va_list escapes 0, needs to save 24 GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ + + void + f7 (int i, ...) +@@ -122,4 +128,5 @@ f7 (int i, ...) + /* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 0 GPR units and 64 FPR units" "stdarg" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 || llp64 } } } } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 32 GPR units and 2" "stdarg" { target alpha*-*-linux* } } } */ + /* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 2 GPR units and 0 FPR units" "stdarg" { target s390*-*-linux* } } } */ +-/* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 0 GPR units and 64 FPR units" "stdarg" { target aarch64*-*-* } } } */ ++/* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 0 GPR units and 64 FPR units" "stdarg" { target { { aarch64*-*-* } && { ! aarch64-apple-darwin* } } } } } */ ++/* { dg-final { scan-tree-dump "f7: va_list escapes 0, needs to save 32 GPR units" "stdarg" { target aarch64-apple-darwin* } } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp b/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp +index 3e652c483c7..34907929bda 100644 +--- a/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp ++++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp +@@ -25,6 +25,11 @@ if { ![istarget aarch64*-*-*] } then { + return + } + ++if { [istarget *-*-darwin*] } then { ++ # darwinpcs and mach-o will need different test mechanisms. ++ return ++} ++ + torture-init + set-torture-options $C_TORTURE_OPTIONS + set additional_flags "-W -Wall -Wno-abi" +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_dup.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_dup.c +index c42c7acbbe9..76917a6ff5b 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_dup.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_dup.c +@@ -1,4 +1,5 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-options "-O2" } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_get.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_get.c +index 2193753ffbb..d29b222b032 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_get.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_get.c +@@ -1,4 +1,5 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_reinterpret.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_reinterpret.c +index f5adf40c648..4e3a3d94416 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_reinterpret.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bf16_reinterpret.c +@@ -1,4 +1,5 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-compile.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-compile.c +index 47af7c494d9..a2f415f67b7 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-compile.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-compile.c +@@ -1,4 +1,5 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-nosimd.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-nosimd.c +index a914680937d..c6b2ef3e444 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-nosimd.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvt-nosimd.c +@@ -2,7 +2,7 @@ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + /* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-additional-options "-save-temps -march=armv8.2-a+bf16+nosimd" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + + #include <arm_neon.h> + +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvtnq2-untied.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvtnq2-untied.c +index 4b730e39d4e..fd2abadb457 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvtnq2-untied.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfcvtnq2-untied.c +@@ -1,8 +1,9 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + + #include <arm_neon.h> +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-1.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-1.c +index ad51507731b..e57053d2193 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-1.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-1.c +@@ -1,8 +1,9 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + + #include <arm_neon.h> +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-2.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-2.c +index ae0a953f7b4..9f5669a8974 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfdot-2.c +@@ -3,7 +3,7 @@ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-mbig-endian --save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + + #include <arm_neon.h> +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmlalbt-compile.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmlalbt-compile.c +index 9810e4ba374..315cabd464b 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmlalbt-compile.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmlalbt-compile.c +@@ -1,8 +1,9 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */ ++/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" { target { ! aarch64*-*-darwin* } } } } */ + + #include <arm_neon.h> + +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmmla-compile.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmmla-compile.c +index 0aaa69f0037..ddc391b1332 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmmla-compile.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/bfmmla-compile.c +@@ -1,8 +1,9 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target aarch64_asm_bf16_ok } */ + /* { dg-require-effective-target arm_v8_2a_bf16_neon_ok } */ + /* { dg-add-options arm_v8_2a_bf16_neon } */ + /* { dg-additional-options "-save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */ ++/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" { target { ! aarch64*-*-darwin* } } } } */ + + #include <arm_neon.h> + +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-1.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-1.c +index ac4f821e771..978eac29815 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-1.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-1.c +@@ -1,8 +1,9 @@ + /* { dg-do assemble { target { aarch64*-*-* } } } */ ++/* { dg-require-effective-target arm_v8_2a_i8mm_neon_hw } */ + /* { dg-require-effective-target arm_v8_2a_i8mm_ok } */ + /* { dg-add-options arm_v8_2a_i8mm } */ + /* { dg-additional-options "-save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + + #include <arm_neon.h> +diff --git a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-2.c b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-2.c +index 61c7c51f5ec..f84ed68e2f7 100644 +--- a/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vdot-3-2.c +@@ -3,7 +3,7 @@ + /* { dg-require-effective-target arm_v8_2a_i8mm_ok } */ + /* { dg-add-options arm_v8_2a_i8mm } */ + /* { dg-additional-options "-mbig-endian -save-temps" } */ +-/* { dg-final { check-function-bodies "**" "" {-O[^0]} } } */ ++/* { dg-final { check-function-bodies "**" "" {-O[^0]} { target { ! aarch64*-*-darwin* } } } } */ + /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */ + + #include <arm_neon.h> +diff --git a/gcc/testsuite/gcc.target/aarch64/arm_align_max_pwr.c b/gcc/testsuite/gcc.target/aarch64/arm_align_max_pwr.c +index ffa4d229922..38b9ef01eb7 100644 +--- a/gcc/testsuite/gcc.target/aarch64/arm_align_max_pwr.c ++++ b/gcc/testsuite/gcc.target/aarch64/arm_align_max_pwr.c +@@ -19,5 +19,7 @@ dummy () + return result; + } + +-/* { dg-final { scan-assembler-times "zero\t4" 2 } } */ +-/* { dg-final { scan-assembler "zero\t268435452" } } */ ++/* { dg-final { scan-assembler-times "zero\t4" 2 { target { ! *-*-darwin* } } } } */ ++/* { dg-final { scan-assembler "zero\t268435452" { target { ! *-*-darwin*} } } } */ ++/* { dg-final { scan-assembler-times ".zerofill __DATA,__bss,_y,4,28" 1 { target { *-*-darwin* } } } } */ ++/* { dg-final { scan-assembler-times ".zerofill __DATA,__bss,_x,4,28" 1 { target { *-*-darwin* } } } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-2.c b/gcc/testsuite/gcc.target/aarch64/auto-init-2.c +index 375befd325b..3a0387a5952 100644 +--- a/gcc/testsuite/gcc.target/aarch64/auto-init-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-2.c +@@ -12,11 +12,11 @@ enum E { + N3 + }; + +-extern void bar (char, short, int, enum E, long, long long, int *, bool); ++extern void bar (unsigned char, short, int, enum E, long, long long, int *, bool); + + void foo() + { +- char temp1; ++ unsigned char temp1; + short temp2; + int temp3; + enum E temp4; +diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-3.c b/gcc/testsuite/gcc.target/aarch64/auto-init-3.c +index 7008f76b294..85a4e4daeb6 100644 +--- a/gcc/testsuite/gcc.target/aarch64/auto-init-3.c ++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-3.c +@@ -2,13 +2,19 @@ + /* { dg-do compile } */ + /* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */ + +-long double result; ++#ifdef __APPLE__ ++# define TYPE _Float128 ++#else ++# define TYPE long double ++#endif + +-long double foo() ++TYPE result; ++ ++TYPE foo() + { + float temp1; + double temp2; +- long double temp3; ++ TYPE temp3; + + result = temp1 + temp2 + temp3; + return result; +diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-4.c b/gcc/testsuite/gcc.target/aarch64/auto-init-4.c +index 10197045b4c..0c6840ba224 100644 +--- a/gcc/testsuite/gcc.target/aarch64/auto-init-4.c ++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-4.c +@@ -2,13 +2,19 @@ + /* { dg-do compile } */ + /* { dg-options "-O -ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */ + +-long double result; ++#ifdef __APPLE__ ++# define TYPE _Float128 ++#else ++# define TYPE long double ++#endif + +-long double foo() ++TYPE result; ++ ++TYPE foo() + { + float temp1; + double temp2; +- long double temp3; ++ TYPE temp3; + + result = temp1 + temp2 + temp3; + return result; +diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-5.c b/gcc/testsuite/gcc.target/aarch64/auto-init-5.c +index ac69ac3df82..0dda3c201d3 100644 +--- a/gcc/testsuite/gcc.target/aarch64/auto-init-5.c ++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-5.c +@@ -2,14 +2,19 @@ + /* { dg-do compile } */ + /* { dg-options "-ftrivial-auto-var-init=zero" } */ + ++#ifdef __APPLE__ ++# define TYPE _Float128 ++#else ++# define TYPE long double ++#endif + +-_Complex long double result; ++_Complex TYPE result; + +-_Complex long double foo() ++_Complex TYPE foo() + { + _Complex float temp1; + _Complex double temp2; +- _Complex long double temp3; ++ _Complex TYPE temp3; + + result = temp1 + temp2 + temp3; + return result; +diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-6.c b/gcc/testsuite/gcc.target/aarch64/auto-init-6.c +index 0456c66f496..23323115a11 100644 +--- a/gcc/testsuite/gcc.target/aarch64/auto-init-6.c ++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-6.c +@@ -2,14 +2,19 @@ + /* { dg-do compile } */ + /* { dg-options "-ftrivial-auto-var-init=pattern" } */ + ++#ifdef __APPLE__ ++# define TYPE _Float128 ++#else ++# define TYPE long double ++#endif + +-_Complex long double result; ++_Complex TYPE result; + +-_Complex long double foo() ++_Complex TYPE foo() + { + _Complex float temp1; + _Complex double temp2; +- _Complex long double temp3; ++ _Complex TYPE temp3; + + result = temp1 + temp2 + temp3; + return result; +diff --git a/gcc/testsuite/gcc.target/aarch64/c-output-template-2.c b/gcc/testsuite/gcc.target/aarch64/c-output-template-2.c +index ced96d04542..86e4f5fa82c 100644 +--- a/gcc/testsuite/gcc.target/aarch64/c-output-template-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/c-output-template-2.c +@@ -6,4 +6,4 @@ test (void) + __asm__ ("@ %c0" : : "S" (test)); + } + +-/* { dg-final { scan-assembler "@ test" } } */ ++/* { dg-final { scan-assembler "@ _?test" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/c-output-template-3.c b/gcc/testsuite/gcc.target/aarch64/c-output-template-3.c +index 8bde4cbeb0c..4531a381518 100644 +--- a/gcc/testsuite/gcc.target/aarch64/c-output-template-3.c ++++ b/gcc/testsuite/gcc.target/aarch64/c-output-template-3.c +@@ -7,4 +7,4 @@ test (void) + __asm__ ("@ %c0" : : "S" (&test + 4)); + } + +-/* { dg-final { scan-assembler "@ test\\+4" } } */ ++/* { dg-final { scan-assembler "@ _?test\\+4" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/c-output-template-4.c b/gcc/testsuite/gcc.target/aarch64/c-output-template-4.c +index c5a93915af1..800d52bfab8 100644 +--- a/gcc/testsuite/gcc.target/aarch64/c-output-template-4.c ++++ b/gcc/testsuite/gcc.target/aarch64/c-output-template-4.c +@@ -7,4 +7,4 @@ test (void) + __asm__ ("@ %c0" : : "S" (&test + 4)); + } + +-/* { dg-final { scan-assembler "@ test\\+4" } } */ ++/* { dg-final { scan-assembler "@ _?test\\+4" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/cpymem-size.c b/gcc/testsuite/gcc.target/aarch64/cpymem-size.c +index 4a6f2495d22..b8ef4745c6d 100644 +--- a/gcc/testsuite/gcc.target/aarch64/cpymem-size.c ++++ b/gcc/testsuite/gcc.target/aarch64/cpymem-size.c +@@ -6,7 +6,7 @@ + /* + ** cpy_127: + ** mov (w|x)2, 127 +-** b memcpy ++** b _?memcpy + */ + void + cpy_127 (char *out, char *in) +@@ -17,7 +17,7 @@ cpy_127 (char *out, char *in) + /* + ** cpy_128: + ** mov (w|x)2, 128 +-** b memcpy ++** b _?memcpy + */ + void + cpy_128 (char *out, char *in) +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/aarch64-darwin.exp b/gcc/testsuite/gcc.target/aarch64/darwin/aarch64-darwin.exp +new file mode 100644 +index 00000000000..b0b7f49aede +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/aarch64-darwin.exp +@@ -0,0 +1,46 @@ ++# Specific tests for the darwinpcs and codegen. ++# Copyright (C) GNU Toolchain Authors ++# Contributed by Iain Sandoe ++# ++# This file is part of GCC. ++# ++# GCC 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 3, or (at your option) ++# any later version. ++# ++# GCC 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 GCC; see the file COPYING3. If not see ++# <http://www.gnu.org/licenses/>. */ ++ ++# GCC testsuite that uses the `dg.exp' driver. ++ ++# Exit immediately if this isn't aarch64-darwin. ++ ++if { ![istarget aarch64*-*-darwin*] } then { ++ return ++} ++ ++# Load support procs. ++load_lib gcc-dg.exp ++ ++# If a testcase doesn't have special options, use these. ++global DEFAULT_CFLAGS ++if ![info exists DEFAULT_CFLAGS] then { ++ set DEFAULT_CFLAGS " -ansi -pedantic-errors" ++} ++ ++# Initialize `dg'. ++dg-init ++ ++# Main loop. ++dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ ++ "" $DEFAULT_CFLAGS ++ ++# All done. ++dg-finish +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/complex-in-regs.c b/gcc/testsuite/gcc.target/aarch64/darwin/complex-in-regs.c +new file mode 100644 +index 00000000000..974f02ca2ec +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/complex-in-regs.c +@@ -0,0 +1,103 @@ ++/* { dg-do compile } */ ++/* we need this for complex and gnu initializers. */ ++/* { dg-options "-std=gnu99 " } */ ++/* We use the sections anchors to make the code easier to match. */ ++/* { dg-additional-options " -O -fsection-anchors -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++ ++__attribute__((__noinline__)) ++_Complex char ++cc_regs_fun (_Complex char r0, _Complex char r1, ++ _Complex char r2, _Complex char r3, ++ _Complex char r4, _Complex char r5, ++ _Complex char r6, _Complex char r7); ++ ++/* ++**call_cc_regs_fun: ++** ... ++** ldrh w7, \[x0\] ++** ldrh w6, \[x0, 2\] ++** ldrh w5, \[x0, 4\] ++** ldrh w4, \[x0, 6\] ++** ldrh w3, \[x0, 8\] ++** ldrh w2, \[x0, 10\] ++** ldrh w1, \[x0, 12\] ++** ldrh w0, \[x0, 14]\ ++** bl _cc_regs_fun ++** ... ++*/ ++ ++_Complex char ++call_cc_regs_fun (void) ++{ ++ return cc_regs_fun ((_Complex char) (1 + 1i), (_Complex char) (2 + 2i), ++ (_Complex char) (3 + 3i), (_Complex char) (4 + 4i), ++ (_Complex char) (5 + 5i), (_Complex char) (6 + 6i), ++ (_Complex char) (7 + 7i), (_Complex char) (8 + 8i)); ++} ++ ++ ++__attribute__((__noinline__)) ++_Complex short ++cs_regs_fun (_Complex short r0, _Complex short r1, ++ _Complex short r2, _Complex short r3, ++ _Complex short r4, _Complex short r5, ++ _Complex short r6, _Complex short r7); ++ ++/* ++**call_cs_regs_fun: ++** ... ++** ldr w7, \[x0, 16\] ++** ldr w6, \[x0, 20\] ++** ldr w5, \[x0, 24\] ++** ldr w4, \[x0, 28\] ++** ldr w3, \[x0, 32\] ++** ldr w2, \[x0, 36\] ++** ldr w1, \[x0, 40\] ++** ldr w0, \[x0, 44\] ++** bl _cs_regs_fun ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Complex short ++call_cs_regs_fun (void) ++{ ++ return cs_regs_fun ((_Complex short) (1 + 1i), (_Complex short) (2 + 2i), ++ (_Complex short) (3 + 3i), (_Complex short) (4 + 4i), ++ (_Complex short) (5 + 5i), (_Complex short) (6 + 6i), ++ (_Complex short) (7 + 7i), (_Complex short) (8 + 8i)); ++} ++ ++__attribute__((__noinline__)) ++_Complex int ++ci_regs_fun (_Complex int r0, _Complex int r1, ++ _Complex int r2, _Complex int r3, ++ _Complex int r4, _Complex int r5, ++ _Complex int r6, _Complex int r7); ++ ++/* ++**call_ci_regs_fun: ++** ... ++** ldr x7, \[x0, 48\] ++** ldr x6, \[x0, 56\] ++** ldr x5, \[x0, 64\] ++** ldr x4, \[x0, 72\] ++** ldr x3, \[x0, 80\] ++** ldr x2, \[x0, 88\] ++** ldr x1, \[x0, 96\] ++** ldr x0, \[x0, 104\] ++** bl _ci_regs_fun ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Complex int ++call_ci_regs_fun (void) ++{ ++ return ci_regs_fun ((_Complex int) (1 + 1i), (_Complex int) (2 + 2i), ++ (_Complex int) (3 + 3i), (_Complex int) (4 + 4i), ++ (_Complex int) (5 + 5i), (_Complex int) (6 + 6i), ++ (_Complex int) (7 + 7i), (_Complex int) (8 + 8i)); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d1.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d1.c +new file mode 100644 +index 00000000000..e2dd574fac7 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d1.c +@@ -0,0 +1,54 @@ ++/* { dg-do compile } */ ++/* we need this for the empty struct. */ ++/* { dg-options "-std=gnu99 " } */ ++/* { dg-additional-options "-O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* Make sure we do no consume any registers in passing zero-sized entities */ ++ ++typedef struct es {} Empty; ++ ++__attribute__((__noinline__)) void ++use_no_regs (int a, Empty b, int c, Empty d, Empty e, int f); ++ ++/* ++**call_use_no_regs: ++** ... ++** mov w2, 3 ++** mov w1, 2 ++** mov w0, 1 ++** bl _use_no_regs ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++call_use_no_regs (void) ++{ ++ Empty e; ++ use_no_regs (1, e, 2, e, e, 3); ++} ++ ++/* Make sure we consume no stack in passing zero-sized entities. */ ++ ++/* ++**call_use_no_stack: ++** ... ++** mov w[0-9]+, 108 ++** strb w[0-9]+, \[sp, 1\] ++** mov w[0-9]+, 106 ++** strb w[0-9]+, \[sp\] ++** ... ++** bl _use_no_stack ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++use_no_stack (int a, int b, int c, int d, int e, int f, int g, int h, ++ Empty i, char j, Empty k, char l); ++ ++void ++call_use_no_stack (void) ++{ ++ Empty e; ++ use_no_stack (0, 1, 2, 3, 4, 5, 6, 7, e, 'j', e, 'l'); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-00.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-00.c +new file mode 100644 +index 00000000000..bd76856308b +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-00.c +@@ -0,0 +1,126 @@ ++/* { dg-do compile } */ ++/* { dg-additional-options " -O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* In each case we consume the parm registers with 8 ints, forcing ++ the test values to be spilled to the stack. */ ++ ++/* The important thing here is that the chars are assigned to the stack ++ * with no padding - so that they occupy bytes 0-8. */ ++ ++/* ++**call_char_packing: ++** ... ++** mov w[0-9]+, 113 ++** strb w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 112 ++** strb w[0-9]+, \[sp, 7\] ++** mov w[0-9]+, 111 ++** strb w[0-9]+, \[sp, 6\] ++** mov w[0-9]+, 110 ++** strb w[0-9]+, \[sp, 5\] ++** mov w[0-9]+, 109 ++** strb w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 108 ++** strb w[0-9]+, \[sp, 3\] ++** mov w[0-9]+, 107 ++** strb w[0-9]+, \[sp, 2\] ++** mov w[0-9]+, 106 ++** strb w[0-9]+, \[sp, 1\] ++** mov w[0-9]+, 105 ++** strb w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _char_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++char_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ char i, char j, char k, char l, ++ char m, char n, char o, char p, ++ char q); ++ ++void call_char_packing (void) ++{ ++ char_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q'); ++} ++ ++/* Here we should occupy the first 7 short words on the stack. */ ++ ++/* ++**call_short_packing: ++** ... ++** mov w[0-9]+, 12 ++** strh w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 11 ++** strh w[0-9]+, \[sp, 6\] ++** mov w[0-9]+, 10 ++** strh w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 9 ++** strh w[0-9]+, \[sp, 2\] ++** mov w[0-9]+, 8 ++** strh w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _short_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++short_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ short i, short j, short k, short l, ++ short m); ++ ++void call_short_packing (void) ++{ ++ short_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 8, 9, 10, 11, 12); ++} ++ ++/* Here we should occupy the first 3 ints on the stack. */ ++ ++/* ++**call_int_packing: ++** ... ++** mov w[0-9]+, 10 ++** str w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 9 ++** str w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 8 ++** str w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _int_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++int_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ int i, int j, int k); ++ ++void call_int_packing (void) ++{ ++ int_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 8, 9, 10); ++} ++ +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-01.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-01.c +new file mode 100644 +index 00000000000..d21fd551b4a +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-01.c +@@ -0,0 +1,115 @@ ++/* { dg-do compile } */ ++/* { dg-additional-options " -O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* In each case we consume the parm registers with 8 ints, forcing ++ the test values to be spilled to the stack. */ ++ ++/* char short char short - everything on 2byte boundaries */ ++ ++/* ++**call_c_s_packing: ++** ... ++** mov w[0-9]+, 109 ++** strb w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 9 ++** strh w[0-9]+, \[sp, 6\] ++** mov w[0-9]+, 107 ++** strb w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 8 ++** strh w[0-9]+, \[sp, 2\] ++** mov w[0-9]+, 105 ++** strb w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _c_s_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++c_s_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ char i, short j, char k, short l, ++ char m); ++ ++void call_c_s_packing (void) ++{ ++ c_s_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 'i', 8 , 'k', 9, 'm'); ++} ++ ++/* ++**call_s_c_packing: ++** ... ++** mov w[0-9]+, 109 ++** strb w[0-9]+, \[sp, 7\] ++** mov w[0-9]+, 108 ++** strb w[0-9]+, \[sp, 6\] ++** mov w[0-9]+, 9 ++** strh w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 106 ++** strb w[0-9]+, \[sp, 2\] ++** mov w[0-9]+, 8 ++** strh w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _s_c_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++s_c_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ short i, char j, short k, char l, ++ char m); ++ ++void call_s_c_packing (void) ++{ ++ s_c_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 8, 'j' , 9, 'l', 'm'); ++} ++ ++/* 0, 2, 4, 0 */ ++ ++/* ++**call_csi_packing: ++** ... ++** mov w[0-9]+, 108 ++** strb w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 9 ++** str w[0-9]+, \[sp, 4\] ++** mov w[0-9]+, 8 ++** strh w[0-9]+, \[sp, 2\] ++** mov w[0-9]+, 105 ++** strb w[0-9]+, \[sp\] ++** mov w7, 7 ++** mov w6, 6 ++** mov w5, 5 ++** mov w4, 4 ++** mov w3, 3 ++** mov w2, 2 ++** mov w1, 1 ++** mov w0, 0 ++** bl _csi_packing ++** ... ++*/ ++ ++__attribute__((__noinline__)) void ++csi_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ char i, short j, int k, char l); ++ ++void call_csi_packing (void) ++{ ++ csi_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ 'i', 8 , 9, 'l'); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-02.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-02.c +new file mode 100644 +index 00000000000..55e5acdaf41 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-02.c +@@ -0,0 +1,75 @@ ++/* { dg-do compile } */ ++/* we need this for complex literals. */ ++/* { dg-options "-std=gnu99 " } */ ++/* { dg-additional-options "-O -fsection-anchors -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++ ++__attribute__((__noinline__)) void ++c_cc_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ _Complex char i, _Complex char j); ++ ++/* We check that these values are not packed on the stack. ++**call_c_cc_packing: ++** ... ++** ldrh w[0-9]+, \[x[0-9]+\] ++** strh w[0-9]+, \[sp, 8\] ++** ldrh w[0-9]+, \[x[0-9]+, 2\] ++** strh w[0-9]+, \[sp\] ++** ... ++** bl _c_cc_packing ++** ... ++*/ ++ ++void ++call_c_cc_packing (void) ++{ ++ c_cc_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ (_Complex char) (1 + 1i),(_Complex char) (2 + 2i)); ++} ++ ++ ++__attribute__((__noinline__)) void ++c_cs_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ _Complex short i, _Complex short j); ++ ++/* ++**call_c_cs_packing: ++** ... ++** ldr w[0-9]+, \[x[0-9]+, 4\] ++** str w[0-9]+, \[sp, 8\] ++** ldr w[0-9]+, \[x[0-9]+, 8\] ++** str w[0-9]+, \[sp\] ++** ... ++** bl _c_cs_packing ++** ... ++*/ ++ ++void ++call_c_cs_packing (void) ++{ ++ c_cs_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ (_Complex short) (1 + 1i),(_Complex short) (2 + 2i)); ++} ++ ++void c_ci_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ _Complex int i, _Complex int j); ++ ++/* ++**call_c_ci_packing: ++** ... ++** ldr x[0-9]+, \[x[0-9]+, 12\] ++** str x[0-9]+, \[sp, 8\] ++** ldr x[0-9]+, \[x[0-9]+, 20\] ++** str x[0-9]+, \[sp\] ++** ... ++** bl _c_ci_packing ++** ... ++*/ ++ ++void ++call_c_ci_packing (void) ++{ ++ c_ci_packing (0, 1, 2, 3, 4, 5, 6, 7, ++ (_Complex int) (1 + 1i),(_Complex int) (2 + 2i)); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-03.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-03.c +new file mode 100644 +index 00000000000..b0d2593dfd7 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-03.c +@@ -0,0 +1,67 @@ ++/* { dg-do compile } */ ++ ++/* { dg-additional-options "-O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++typedef union u { char a; short b; } U; ++typedef struct sf { float a; float b; float c;} SF; ++ ++__attribute__((__noinline__)) void ++u_packing (int a, int b, int c, int d, int e, int f, int g, int h, ++ U i, U j); ++ ++/* We check that these values are not packed on the stack. ++**call_u_packing: ++** ... ++** strh w[0-9]+, \[sp, 8\] ++** strh w[0-9]+, \[sp\] ++** ... ++** bl _u_packing ++** ... ++*/ ++ ++void ++call_u_packing (void) ++{ ++ U x = { 'a' }; ++ u_packing (0, 1, 2, 3, 4, 5, 6, 7, x, x); ++} ++ ++/* But a homogeneous float aggregate is treated as if it were the contained ++ floats. */ ++ ++__attribute__((__noinline__)) void ++sf_packing (float a, float b, float c, float d, ++ float e, float f, float g, float h, ++ SF i, SF j); ++ ++/* So the stores to sp+12 and 20 pack the floats onto the stack. ++**call_sf_packing: ++** ... ++** fmov s1, 1.0e\+0 ++** str s1, \[sp, 48\] ++** fmov s2, 2.0e\+0 ++** str s2, \[sp, 52\] ++** mov w[0-9]+, 1077936128 ++** ldr x[0-9]+, \[sp, 48\] ++** str x[0-9]+, \[sp, 12\] ++** str w[0-9]+, \[sp, 20\] ++** str x[0-9]+, \[sp\] ++** str w[0-9]+, \[sp, 8\] ++** fmov s7, 7.0e\+0 ++** fmov s6, 6.0e\+0 ++** fmov s5, 5.0e\+0 ++** fmov s4, 4.0e\+0 ++** fmov s3, 3.0e\+0 ++** movi v0.2s, #0 ++** bl _sf_packing ++** ... ++*/ ++ ++void ++call_sf_packing (void) ++{ ++ SF A = {1.0F, 2.0F, 3.0F}; ++ sf_packing (0.0F, 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F, 7.0F, ++ A, A); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-04.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-04.c +new file mode 100644 +index 00000000000..33c60c69b78 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d2-04.c +@@ -0,0 +1,66 @@ ++/* { dg-do compile } */ ++ ++/* { dg-additional-options "-O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++typedef short v2hi __attribute__ ((vector_size (4))); ++typedef int v4si __attribute__ ((vector_size (16))); ++ ++v4si t; ++int al = __alignof__ (t); ++ ++__attribute__((__noinline__)) void ++v2hi_packing (v2hi a, v2hi b, v2hi c, v2hi d, v2hi e, v2hi f, v2hi g, v2hi h, ++ v2hi i, v2hi j); ++ ++/* We check that v2hi is packed on the stack. ++**call_v2hi_packing: ++** ... ++** mov w[0-9]+, 1 ++** movk w[0-9]+, 0x2, lsl 16 ++** str w[0-9]+, \[sp, 4\] ++** str w[0-9]+, \[sp\] ++** mov w7, w[0-9]+ ++** mov w6, w[0-9]+ ++** mov w5, w[0-9]+ ++** mov w4, w[0-9]+ ++** mov w3, w[0-9]+ ++** mov w2, w[0-9]+ ++** mov w1, w[0-9]+ ++** bl _v2hi_packing ++** ... ++*/ ++ ++void ++call_v2hi_packing (void) ++{ ++ v2hi x = {1,2}; ++ v2hi_packing (x, x, x, x, x, x, x, x, x, x); ++} ++ ++ ++__attribute__((__noinline__)) void ++v4si_packing (int r0, int r1, int r2, int r3, int r4, int r5, int r6, int r7, ++ v4si a, v4si b, v4si c, v4si d, v4si e, v4si f, v4si g, v4si h, ++ int stack, v4si i, v4si j); ++ ++/* Test that we align a 16b vector on the stack. ++**call_v4si_packing: ++** ... ++** adrp x0, lC0@PAGE ++** ldr q[0-9]+, \[x[0-9]+, #lC0@PAGEOFF\] ++** str q[0-9]+, \[sp, 32\] ++** str q[0-9]+, \[sp, 16\] ++** mov w[0-9]+, 42 ++** str w[0-9]+, \[sp\] ++** ... ++** bl _v4si_packing ++** ... ++*/ ++ ++void ++call_v4si_packing (void) ++{ ++ v4si x = {3,1,2,4}; ++ v4si_packing (0, 1, 2, 3, 4, 5, 6, 7, x, x, x, x, x, x, x, x, 42, x, x); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d3.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d3.c +new file mode 100644 +index 00000000000..21c6b696b7c +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d3.c +@@ -0,0 +1,40 @@ ++/* { dg-do compile } */ ++/* { dg-additional-options "-O " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* This will fail, because of issue #74 ++**foo: ++** cmp w0, w1 ++** cset w0, eq ++** ret ++*/ ++ ++__attribute__((__noinline__)) ++int ++foo (char a, unsigned char b) ++{ ++ return a == b ? 1 : 0; ++} ++ ++__attribute__((__noinline__)) ++int ++bar (short a, unsigned short b) ++{ ++ return a == b ? 1 : 0; ++} ++ ++void pop (char *, unsigned char *, short *, unsigned short *); ++ ++int main () ++{ ++ char a; ++ unsigned char b; ++ short c; ++ unsigned short d; ++ int result; ++ pop (&a, &b, &c, &d); ++ ++ result = foo (a, b); ++ result += bar (c, d); ++ return result; ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d4.c b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d4.c +new file mode 100644 +index 00000000000..2aab48260f4 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d4.c +@@ -0,0 +1,62 @@ ++/* { dg-do compile } */ ++/* we need this for __int128. */ ++/* { dg-options "-std=gnu99 " } */ ++/* { dg-additional-options "-O -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* we should use x0, x1 and x2 - not skip x1. ++**foo: ++** eor x0, x0, x1 ++** orr x0, x0, x2 ++** cmp x0, 0 ++** cset w0, eq ++** ret ++*/ ++ ++__attribute__((__noinline__)) ++int ++foo (unsigned long long x,unsigned __int128 y) ++{ ++ return x == y ? 1 : 0; ++} ++ ++/* we should use x0, x1 and x2. ++**bar: ++** eor x2, x2, x0 ++** orr x2, x2, x1 ++** cmp x2, 0 ++** cset w0, eq ++** ret ++*/ ++ ++__attribute__((__noinline__)) ++int ++bar (unsigned __int128 y, unsigned long long x) ++{ ++ return x == y ? 1 : 0; ++} ++ ++int fooo (unsigned long long x, unsigned __int128 y); ++int baro (unsigned __int128 y, unsigned long long x); ++ ++/* we should use x0, x1 and x2 in both calls. ++**main: ++** ... ++** mov x1, 25 ++** mov x2, 0 ++** mov x0, 10 ++** bl _fooo ++** mov x2, 10 ++** mov x0, 25 ++** mov x1, 0 ++** bl _baro ++** ... ++*/ ++ ++int main () ++{ ++ unsigned long long x = 10; ++ unsigned __int128 y = 25; ++ int r = fooo (x, y); ++ r += baro (y, x); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/float128-00.c b/gcc/testsuite/gcc.target/aarch64/darwin/float128-00.c +new file mode 100644 +index 00000000000..29aec80fbaa +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/float128-00.c +@@ -0,0 +1,38 @@ ++ ++/* we need this for _Float128. */ ++/* { dg-options "-std=gnu99 " } */ ++/* We use the sections anchors to make the code easier to match. */ ++/* { dg-additional-options " -O2 -fsection-anchors " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* we should just pass q0 and q1 through ++**foo: ++** ... ++** bl ___addtf3 ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++foo (_Float128 a, _Float128 b) ++{ ++ return a + b; ++} ++ ++ ++/* we should just load q0 and q1 ++**call_foo: ++** ... ++** ldr q1, \[x[0-9]+\] ++** ... ++** ldr q0, \[x[0-9]+\] ++** b _foo ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++call_foo (void) ++{ ++ return foo (1.0, 2.0); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/homogeneous-aggr.c b/gcc/testsuite/gcc.target/aarch64/darwin/homogeneous-aggr.c +new file mode 100644 +index 00000000000..bee97557a4d +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/homogeneous-aggr.c +@@ -0,0 +1,25 @@ ++/* { dg-do compile } */ ++/* { dg-additional-options "-O " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++typedef struct sf { float a; float b; float c;} SF; ++ ++__attribute__((__noinline__)) void ++hmg_f (SF a); ++ ++/* we should use registers for each item ++**call_hmg_f: ++** ... ++** fmov s0, 1.0e\+0 ++** fmov s1, 2.0e\+0 ++** fmov s2, 3.0e\+0 ++** bl _hmg_f ++** ... ++*/ ++ ++void ++call_hmg_f (void) ++{ ++ SF A = { 1.0F, 2.0F, 3.0F }; ++ hmg_f (A); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/k+r-00.c b/gcc/testsuite/gcc.target/aarch64/darwin/k+r-00.c +new file mode 100644 +index 00000000000..443fb968811 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/k+r-00.c +@@ -0,0 +1,28 @@ ++/* { dg-do compile } */ ++ ++/* { dg-options "-std=gnu99 " } */ ++/* { dg-additional-options "-O2 -fsection-anchors" } */ ++ ++ ++/* What we care about here is that we get int loads from sp, sp+4 and sp+8. ++ * This code will change when we implement darwinpcs d.3 - since the ++ * promotions will no longer be needed (although they are harmless). ++**test_k_r00: ++** ldrsb w[0-9]+, \[sp, 4\] ++** ldr x[0-9]+, \[sp, 8\] ++** ... ++** ldrsb w[0-9]+, \[sp\] ++** ... ++*/ ++ ++const char * ++test_k_r00 (r0, r1, r2, r3, r4, r5, r6, r7, a, b, c) ++ char r0, r1, r2, r3, r4, r5, r6, r7; ++ char a; ++ char b; ++ const char *c; ++{ ++ if (a > 10 && b < 100) ++ return c; ++ return (char *)0; ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/tu-accesses-0.c b/gcc/testsuite/gcc.target/aarch64/darwin/tu-accesses-0.c +new file mode 100644 +index 00000000000..ba5cc493bc9 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/tu-accesses-0.c +@@ -0,0 +1,82 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O -fcommon -mno-pc-relative-literal-loads" } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++/* This checks that we perform the correct accesses for file-scope vars ++ including GOT indirections. */ ++ ++double gd = 1.0; ++ ++__attribute__((__weak__)) ++double wd = 2.0; ++ ++__attribute__((__visibility__("hidden"))) ++double hd = 3.0; ++ ++__attribute__((__weak__, __visibility__("hidden"))) ++double whd = 4.0; ++ ++extern double ed; ++ ++double cd; ++ ++static double sd = 5.0; ++ ++struct { ++ double a; ++ double b; ++} two_dbls = { 1.0, 42.0 }; ++ ++double arr[3] = { 6.0, 7.0, 8.0 }; ++ ++/* ++**test: ++** adrp x[0-9]+, _gd@PAGE ++** ldr d[0-9]+, \[x[0-9]+, #_gd@PAGEOFF\] ++** adrp x[0-9]+, lC0@PAGE ++** ldr d[0-9]+, \[x[0-9]+, #lC0@PAGEOFF\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _wd@GOTPAGE ++** ldr x[0-9]+, \[x[0-9]+, _wd@GOTPAGEOFF\] ++** ldr d[0-9]+, \[x[0-9]+\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _hd@PAGE ++** ldr d[0-9]+, \[x[0-9]+, #_hd@PAGEOFF\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _whd@PAGE ++** ldr d[0-9]+, \[x[0-9]+, #_whd@PAGEOFF\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _ed@GOTPAGE ++** ldr x[0-9]+, \[x[0-9]+, _ed@GOTPAGEOFF\] ++** ldr d[0-9]+, \[x[0-9]+\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _cd@GOTPAGE ++** ldr x[0-9]+, \[x[0-9]+, _cd@GOTPAGEOFF\] ++** ldr d[0-9]+, \[x[0-9]+\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** fmov d[0-9]+, 5.0e\+0 ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _two_dbls@PAGE\+8 ++** ldr d[0-9]+, \[x[0-9]+, #_two_dbls@PAGEOFF\+8\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** adrp x[0-9]+, _arr@PAGE\+16 ++** ldr d[0-9]+, \[x[0-9]+, #_arr@PAGEOFF\+16\] ++** fadd d[0-9]+, d[0-9]+, d[0-9]+ ++** ret ++*/ ++ ++double test (void) ++{ ++ double x = 123456123456123456.0; ++ x += gd; ++ x += wd; ++ x += hd; ++ x += whd; ++ x += ed; ++ x += cd; ++ x += sd; ++ x += two_dbls.b; ++ x += arr[2]; ++ ++ return x; ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/variadic-00.c b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-00.c +new file mode 100644 +index 00000000000..6420fca11d5 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-00.c +@@ -0,0 +1,91 @@ ++/* { dg-do compile } */ ++ ++/* We use the sections anchors to make the code easier to match. */ ++/* { dg-additional-options " -O -fsection-anchors -fno-schedule-insns -fno-schedule-insns2 " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++#include <stdarg.h> ++ ++/* What we care about here is that the load of w0 is from the incoming [SP] ++**fooi: ++** sub sp, sp, #16 ++** add x[0-9]+, sp, 24 ++** str x[0-9]+, \[sp, 8\] ++** ldr w0, \[sp, 16\] ++** ... ++*/ ++ ++__attribute__((__noinline__)) int ++fooi (int a, ...) ++{ ++ int x; ++ va_list ap; ++ va_start(ap, a); ++ x = va_arg(ap, int); ++ va_end(ap); ++ return x; ++} ++ ++__attribute__((__noinline__)) int ++fooo (char a, ...); ++ ++/* ++**call_foo: ++** ... ++** mov w[0-9]+, 42 ++** str w[0-9]+, \[sp\] ++** mov w0, 1 ++** bl _fooo ++** ... ++*/ ++ ++__attribute__((__noinline__)) int ++call_foo (void) ++{ ++ int y = fooo (1, 42); ++ return y; ++} ++ ++/* What we care about here is that the load of w0 is from the incoming [SP+8] ++**bari: ++** sub sp, sp, #16 ++** add x[0-9]+, sp, 32 ++** str x[0-9]+, \[sp, 8\] ++** ldr w0, \[sp, 24\] ++** ... ++*/ ++ ++__attribute__((__noinline__)) int ++bari (int a, int b, int c, int d, int e, int f, int g, int h, ++ int i, ...) ++{ ++ int x; ++ va_list ap; ++ va_start(ap, i); ++ x = va_arg(ap, int); ++ va_end(ap); ++ return x; ++} ++ ++/* ++**call_bar: ++** ... ++** mov w[0-9]+, 42 ++** str w[0-9]+, \[sp, 8\] ++** mov w[0-9]+, 9 ++** str w[0-9]+, \[sp\] ++** ... ++ bl _baro ++** ... ++*/ ++ ++__attribute__((__noinline__)) int ++baro (int a, int b, int c, int d, int e, int f, int g, int h, ++ int i, ...); ++ ++__attribute__((__noinline__)) int ++call_bar (void) ++{ ++ int y = baro (1, 2, 3, 4, 5, 6, 7, 8, 9, 42); ++ return y; ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/variadic-01.c b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-01.c +new file mode 100644 +index 00000000000..c055aeae580 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-01.c +@@ -0,0 +1,102 @@ ++/* { dg-do compile } */ ++ ++/* we need this for _Float128. */ ++/* { dg-options "-std=gnu99 " } */ ++/* We use the sections anchors to make the code easier to match. */ ++/* { dg-additional-options " -O2 -fsection-anchors " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++#include <stdarg.h> ++ ++/* What we care about here is that q0 and q1 are loaded from incoming sp and ++ sp+16. ++**foo: ++** ... ++** ldr q1, \[sp, 32\] ++** ldr q0, \[sp, 48\] ++** ... ++** bl ___addtf3 ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++foo (int n, ...) ++{ ++ _Float128 a, b; ++ va_list ap; ++ ++ va_start(ap, n); ++ a = va_arg(ap, _Float128); ++ b = va_arg(ap, _Float128); ++ va_end(ap); ++ return a + b; ++} ++ ++/* ++**call_foo: ++** ... ++** str q[0-9]+, \[sp, 16\] ++** ... ++** mov w0, 2 ++** str q[0-9]+, \[sp\] ++** bl _foo ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++call_foo (void) ++{ ++ return foo (2, (_Float128)1.0, (_Float128)2.0); ++} ++ ++/* What we care about here is that q0 and q1 are loaded from incoming sp and ++ sp+32 (with the int at sp+16). ++**bar: ++** ... ++** ldr w[0-9]+, \[x[0-9]+, 16\] ++** ldr q0, \[x[0-9]+\] ++** ... ++** ldr q1, \[x[0-9]+, 32\] ++** bl ___addtf3 ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++bar (int n, ...) ++{ ++ _Float128 a, b; ++ va_list ap; ++ ++ va_start(ap, n); ++ a = va_arg(ap, _Float128); ++ n = va_arg(ap, int); ++ if (n != 42) ++ __builtin_abort (); ++ b = va_arg(ap, _Float128); ++ va_end(ap); ++ return a + b; ++} ++ ++/* ++**call_bar: ++** ... ++** str q[0-9]+, \[sp, 32\] ++** ... ++** mov w[0-9]+, 42 ++** str w[0-9]+, \[sp, 16\] ++** mov w0, 2 ++** str q[0-9]+, \[sp\] ++** bl _bar ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++_Float128 ++call_bar (void) ++{ ++ return bar (2, (_Float128)1.0, ++ 42, (_Float128)2.0); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/darwin/variadic-02.c b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-02.c +new file mode 100644 +index 00000000000..9d796bfc07f +--- /dev/null ++++ b/gcc/testsuite/gcc.target/aarch64/darwin/variadic-02.c +@@ -0,0 +1,104 @@ ++/* { dg-do compile } */ ++ ++/* we need this for __int128. */ ++/* { dg-options "-std=gnu99 " } */ ++/* We use the sections anchors to make the code easier to match. */ ++/* { dg-additional-options " -O2 -fsection-anchors " } */ ++/* { dg-final { check-function-bodies "**" "" "" { target *-*-darwin* } } } */ ++ ++#include <stdarg.h> ++ ++/* What we care about here is that we load the values from incoming sp and ++ sp + 16. ++**foo: ++** sub sp, sp, #16 ++** ... ++** ldp x[0-9]+, x[0-9]+, \[sp, 16\] ++** ... ++** ldr x[0-9]+, \[sp, 32\] ++** ldr x[0-9]+, \[sp, 40\] ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++__int128 ++foo (int n, ...) ++{ ++ __int128 a, b; ++ va_list ap; ++ ++ va_start(ap, n); ++ a = va_arg(ap, __int128); ++ b = va_arg(ap, __int128); ++ va_end(ap); ++ return a + b; ++} ++ ++/* ++**call_foo: ++** ... ++** stp x[0-9]+, x[0-9]+, \[sp\] ++** mov w0, 2 ++** stp x[0-9]+, x[0-9]+, \[sp, 16\] ++** bl _foo ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++__int128 ++call_foo (void) ++{ ++ return foo (2, (__int128)1, (__int128)2); ++} ++ ++ ++/* sp = one int128, sp+16 = int sp + 32 = other int128 ++**bar: ++** ... ++** sub sp, sp, #16 ++** ... ++** ldp x[0-9]+, x[0-9]+, \[sp, 16\] ++** ... ++** ldr x[0-9]+, \[sp, 48\] ++** ldr x[0-9]+, \[sp, 56\] ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++__int128 ++bar (int n, ...) ++{ ++ __int128 a, b; ++ va_list ap; ++ ++ va_start(ap, n); ++ a = va_arg(ap, __int128); ++ n = va_arg(ap, int); ++ b = va_arg(ap, __int128); ++ va_end(ap); ++ return a + b; ++} ++ ++__attribute__((__noinline__)) ++__int128 ++baro (int n, ...); ++ ++/* ++**call_bar: ++** ... ++** mov w[0-9]+, 42 ++** ... ++** mov w0, 2 ++** stp x[0-9]+, x[0-9]+, \[sp\] ++** str w[0-9]+, \[sp, 16\] ++** stp x[0-9]+, x[0-9]+, \[sp, 32\] ++** bl _baro ++** ... ++*/ ++ ++__attribute__((__noinline__)) ++__int128 ++call_bar (void) ++{ ++ return baro (2, (__int128)1, 42, (__int128)2); ++} +diff --git a/gcc/testsuite/gcc.target/aarch64/dbl_mov_immediate_1.c b/gcc/testsuite/gcc.target/aarch64/dbl_mov_immediate_1.c +index ba6a230457b..cc30dd546f4 100644 +--- a/gcc/testsuite/gcc.target/aarch64/dbl_mov_immediate_1.c ++++ b/gcc/testsuite/gcc.target/aarch64/dbl_mov_immediate_1.c +@@ -41,8 +41,10 @@ double d4(void) + + /* { dg-final { scan-assembler-times "movi\td\[0-9\]+, #?0" 1 } } */ + +-/* { dg-final { scan-assembler-times "adrp\tx\[0-9\]+, \.LC\[0-9\]" 2 } } */ +-/* { dg-final { scan-assembler-times "ldr\td\[0-9\]+, \\\[x\[0-9\], #:lo12:\.LC\[0-9\]\\\]" 2 } } */ ++/* { dg-final { scan-assembler-times "adrp\tx\[0-9\]+, \.LC\[0-9\]" 2 { target { ! *-*-darwin* } } } } */ ++/* { dg-final { scan-assembler-times "ldr\td\[0-9\]+, \\\[x\[0-9\], #:lo12:\.LC\[0-9\]\\\]" 2 { target { ! *-*-darwin* } } } } */ ++/* { dg-final { scan-assembler-times "adrp\tx\[0-9\]+, lC\[0-9\]@PAGE" 2 { target *-*-darwin* } } } */ ++/* { dg-final { scan-assembler-times "ldr\td\[0-9\]+, \\\[x\[0-9\], #lC\[0-9\]@PAGEOFF\\\]" 2 { target *-*-darwin* } } } */ + + /* { dg-final { scan-assembler-times "fmov\td\[0-9\]+, 1\\\.5e\\\+0" 1 } } */ + +diff --git a/gcc/testsuite/gcc.target/aarch64/dwarf-cfa-reg.c b/gcc/testsuite/gcc.target/aarch64/dwarf-cfa-reg.c +index ae5b3797021..8a691add222 100644 +--- a/gcc/testsuite/gcc.target/aarch64/dwarf-cfa-reg.c ++++ b/gcc/testsuite/gcc.target/aarch64/dwarf-cfa-reg.c +@@ -1,5 +1,6 @@ + /* Verify that CFA register is restored to SP after FP is restored. */ + /* { dg-do compile } */ ++/* { dg-skip-if "no cfi insn support yet" *-*-darwin* } */ + /* { dg-options "-O0 -gdwarf-2" } */ + /* { dg-final { scan-assembler ".cfi_restore 30" } } */ + /* { dg-final { scan-assembler ".cfi_restore 29" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/inline-lrint_1.c b/gcc/testsuite/gcc.target/aarch64/inline-lrint_1.c +index 478875ff874..9a2b2e44893 100644 +--- a/gcc/testsuite/gcc.target/aarch64/inline-lrint_1.c ++++ b/gcc/testsuite/gcc.target/aarch64/inline-lrint_1.c +@@ -15,4 +15,4 @@ TEST (fllf, float , long long, l) + + /* { dg-final { scan-assembler-times "frintx\t\[d,s\]\[0-9\]+, \[d,s\]\[0-9\]+" 6 } } */ + /* { dg-final { scan-assembler-times "fcvtzs\tx\[0-9\]+, \[d,s\]\[0-9\]+" 6 } } */ +-/* { dg-final { scan-assembler-not "bl" } } */ ++/* { dg-final { scan-assembler-not "bl\t" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/ldp_stp_13.c b/gcc/testsuite/gcc.target/aarch64/ldp_stp_13.c +index 9cc3942f153..52c90a92114 100644 +--- a/gcc/testsuite/gcc.target/aarch64/ldp_stp_13.c ++++ b/gcc/testsuite/gcc.target/aarch64/ldp_stp_13.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-require-effective-target arm_mabi_ilp32 } */ + /* { dg-options "-O2 -mabi=ilp32" } */ + + long long +diff --git a/gcc/testsuite/gcc.target/aarch64/memset-corner-cases-2.c b/gcc/testsuite/gcc.target/aarch64/memset-corner-cases-2.c +index 9ee96f33255..dec73f98506 100644 +--- a/gcc/testsuite/gcc.target/aarch64/memset-corner-cases-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/memset-corner-cases-2.c +@@ -7,7 +7,7 @@ + /* 127 bytes should use libcall for size. + **set127byte: + ** mov x2, 127 +-** b memset ++** b _?memset + */ + void __attribute__((__noinline__)) + set127byte (int64_t *src, int c) +@@ -18,7 +18,7 @@ set127byte (int64_t *src, int c) + /* 128 bytes should use libcall for size. + **set128byte: + ** mov x2, 128 +-** b memset ++** b _?memset + */ + void __attribute__((__noinline__)) + set128byte (int64_t *src, int c) +diff --git a/gcc/testsuite/gcc.target/aarch64/memset-corner-cases.c b/gcc/testsuite/gcc.target/aarch64/memset-corner-cases.c +index c43f0199adc..733a11e585a 100644 +--- a/gcc/testsuite/gcc.target/aarch64/memset-corner-cases.c ++++ b/gcc/testsuite/gcc.target/aarch64/memset-corner-cases.c +@@ -77,7 +77,7 @@ set256byte (int64_t *src, char c) + **set257byte: + ** mov x2, 257 + ** mov w1, 99 +-** b memset ++** b _?memset + */ + void __attribute__((__noinline__)) + set257byte (int64_t *src) +diff --git a/gcc/testsuite/gcc.target/aarch64/no-inline-lrint_1.c b/gcc/testsuite/gcc.target/aarch64/no-inline-lrint_1.c +index d5e9200562c..7f504ad687f 100644 +--- a/gcc/testsuite/gcc.target/aarch64/no-inline-lrint_1.c ++++ b/gcc/testsuite/gcc.target/aarch64/no-inline-lrint_1.c +@@ -14,6 +14,6 @@ TEST (dlld, double, long long, l) + TEST (fllf, float , long long, l) + + /* { dg-final { scan-assembler-times "frintx\t\[d,s\]\[0-9\]+, \[d,s\]\[0-9\]+" 6 } } */ +-/* { dg-final { scan-assembler-times "bl\tlrint" 4 } } */ +-/* { dg-final { scan-assembler-times "bl\tllrint" 2 } } */ ++/* { dg-final { scan-assembler-times "bl\t_*lrint" 4 } } */ ++/* { dg-final { scan-assembler-times "bl\t_*llrint" 2 } } */ + /* { dg-final { scan-assembler-not "fcvtzs" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/pr100518.c b/gcc/testsuite/gcc.target/aarch64/pr100518.c +index 5ca599f5d2e..4e7d6bc7d90 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr100518.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr100518.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-require-effective-target arm_mabi_ilp32 } */ + /* { dg-options "-mabi=ilp32 -mstrict-align -O2" } */ + + int unsigned_range_min, unsigned_range_max, a11___trans_tmp_1; +diff --git a/gcc/testsuite/gcc.target/aarch64/pr62308.c b/gcc/testsuite/gcc.target/aarch64/pr62308.c +index 1cf6e212dca..4c1a733e84d 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr62308.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr62308.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-skip-if "Darwin platforms do not support big-endian arm64" *-*-darwin* } */ + /* { dg-options "-mbig-endian" } */ + + typedef int __attribute__((vector_size(16))) v4si; +diff --git a/gcc/testsuite/gcc.target/aarch64/pr78255.c b/gcc/testsuite/gcc.target/aarch64/pr78255.c +index b078cf3e1c1..fc5d859ee68 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr78255.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr78255.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-skip-if "Darwin platforms do not support tiny" *-*-darwin* } */ + /* { dg-options "-O2 -mcmodel=tiny" } */ + + extern int bar (void *); +diff --git a/gcc/testsuite/gcc.target/aarch64/pr78561.c b/gcc/testsuite/gcc.target/aarch64/pr78561.c +index 048d2d7969f..635214edde1 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr78561.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr78561.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-skip-if "Darwin platforms do not support tiny" *-*-darwin* } */ + /* { dg-options "-Og -O3 -mcmodel=tiny" } */ + + int +diff --git a/gcc/testsuite/gcc.target/aarch64/pr80295.c b/gcc/testsuite/gcc.target/aarch64/pr80295.c +index b3866d8d6a9..7a7f127b65f 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr80295.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr80295.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-require-effective-target arm_mabi_ilp32 } */ + /* { dg-options "-mabi=ilp32" } */ + + void f (void *b) +diff --git a/gcc/testsuite/gcc.target/aarch64/pr87305.c b/gcc/testsuite/gcc.target/aarch64/pr87305.c +index 8beaa9176e0..c3f98e8eaec 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr87305.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr87305.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-skip-if "Darwin platforms do not support big-endian arm64" *-*-darwin* } */ + /* { dg-options "-Ofast -mbig-endian -w" } */ + + int cc; +diff --git a/gcc/testsuite/gcc.target/aarch64/pr92424-1.c b/gcc/testsuite/gcc.target/aarch64/pr92424-1.c +index c413a2c306e..59f7435dc83 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr92424-1.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr92424-1.c +@@ -1,6 +1,7 @@ + /* { dg-do "compile" } */ + /* { dg-options "-O1" } */ + /* { dg-final { check-function-bodies "**" "" } } */ ++/* { dg-skip-if "unimplemented patchable function entry" *-*-darwin* } */ + + /* Note: this test only checks the instructions in the function bodies, + not the placement of the patch label or nops before the futncion. */ +diff --git a/gcc/testsuite/gcc.target/aarch64/pr94201.c b/gcc/testsuite/gcc.target/aarch64/pr94201.c +index 69176169186..051c742e98e 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr94201.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr94201.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-require-effective-target arm_mabi_ilp32 } */ + /* { dg-options "-mcmodel=tiny -mabi=ilp32 -fPIC" } */ + + extern int bar (void *); +diff --git a/gcc/testsuite/gcc.target/aarch64/pr94577.c b/gcc/testsuite/gcc.target/aarch64/pr94577.c +index 6f2d3612c26..6a52e52dc39 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr94577.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr94577.c +@@ -1,4 +1,5 @@ + /* { dg-do compile } */ ++/* { dg-require-effective-target arm_mabi_ilp32 } */ + /* { dg-options "-mcmodel=large -mabi=ilp32" } */ + + void +diff --git a/gcc/testsuite/gcc.target/aarch64/pr97535.c b/gcc/testsuite/gcc.target/aarch64/pr97535.c +index 7d4db485f1f..6f1ee8035eb 100644 +--- a/gcc/testsuite/gcc.target/aarch64/pr97535.c ++++ b/gcc/testsuite/gcc.target/aarch64/pr97535.c +@@ -13,4 +13,4 @@ void setRaw(const void *raw) + + /* At any optimization level this should be a function call + and not inlined. */ +-/* { dg-final { scan-assembler "bl\tmemcpy" } } */ ++/* { dg-final { scan-assembler "bl\t_*memcpy" } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-2.c b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-2.c +index 8eec6824f37..193c65717ed 100644 +--- a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-2.c +@@ -1,5 +1,6 @@ + /* { dg-do compile } */ + /* { dg-options "-Ofast" } */ ++/* { dg-skip-if "no system variant_pcs support" *-*-darwin* } */ + + __attribute__ ((__simd__ ("notinbranch"))) + __attribute__ ((__nothrow__ , __leaf__ , __const__)) +@@ -12,5 +13,5 @@ void bar(double * f, int n) + f[i] = foo(f[i]); + } + +-/* { dg-final { scan-assembler-not {\.variant_pcs\tfoo} } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnN2v_foo} 1 } } */ ++/* { dg-final { scan-assembler-not {\.variant_pcs\t_?foo} } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnN2v_foo} 1 } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-3.c b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-3.c +index 95f6a6803e8..6fd57735b3a 100644 +--- a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-3.c ++++ b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute-3.c +@@ -1,5 +1,6 @@ + /* { dg-do compile } */ + /* { dg-options "-Ofast" } */ ++/* { dg-skip-if "no system variant_pcs support" *-*-darwin* } */ + + __attribute__ ((__simd__)) + __attribute__ ((__nothrow__ , __leaf__ , __const__)) +@@ -17,8 +18,8 @@ double foo(double x) + return x * x / 3.0; + } + +-/* { dg-final { scan-assembler-not {\.variant_pcs\tfoo} } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnM1v_foo} 1 } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnM2v_foo} 1 } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnN1v_foo} 1 } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnN2v_foo} 1 } } */ ++/* { dg-final { scan-assembler-not {\.variant_pcs\t_?foo} } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnM1v_foo} 1 } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnM2v_foo} 1 } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnN1v_foo} 1 } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnN2v_foo} 1 } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute.c b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute.c +index eddcef3597c..62ee482a892 100644 +--- a/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute.c ++++ b/gcc/testsuite/gcc.target/aarch64/simd_pcs_attribute.c +@@ -1,5 +1,6 @@ + /* { dg-do compile } */ + /* { dg-options "-Ofast" } */ ++/* { dg-skip-if "no system variant_pcs support" *-*-darwin* } */ + + __attribute__ ((__simd__ ("notinbranch"))) + __attribute__ ((__nothrow__ , __leaf__ , __const__)) +@@ -12,5 +13,5 @@ void foo(double *f, int n) + f[i] = log(f[i]); + } + +-/* { dg-final { scan-assembler-not {\.variant_pcs\tlog} } } */ +-/* { dg-final { scan-assembler-times {\.variant_pcs\t_ZGVnN2v_log} 1 } } */ ++/* { dg-final { scan-assembler-not {\.variant_pcs\t_?log} } } */ ++/* { dg-final { scan-assembler-times {\.variant_pcs\t_?_ZGVnN2v_log} 1 } } */ +diff --git a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-1.c b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-1.c +index 6885894a97e..ebba23c9d02 100644 +--- a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-1.c ++++ b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-1.c +@@ -1,6 +1,7 @@ + /* { dg-do compile } */ + /* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables" } */ + /* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "no cfi insn support yet" *-*-darwin* } */ + + #define SIZE 128*1024 + #include "stack-check-prologue.h" +diff --git a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-2.c b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-2.c +index 5796a53be06..e15fbd6196c 100644 +--- a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-2.c ++++ b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-2.c +@@ -1,6 +1,7 @@ + /* { dg-do compile } */ + /* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables" } */ + /* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "no cfi insn support yet" *-*-darwin* } */ + + #define SIZE 1280*1024 + 512 + #include "stack-check-prologue.h" +diff --git a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-3.c b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-3.c +index c4b7bb601c4..ccaf2e6f8cf 100644 +--- a/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-3.c ++++ b/gcc/testsuite/gcc.target/aarch64/stack-check-cfa-3.c +@@ -1,6 +1,7 @@ + /* { dg-do compile } */ + /* { dg-options "-O3 -fopenmp-simd -march=armv8-a+sve -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables" } */ + /* { dg-require-effective-target supports_stack_clash_protection } */ ++/* { dg-skip-if "no cfi insn support yet" *-*-darwin* } */ + + #include "stack-check-prologue-16.c" + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve/aarch64-sve.exp b/gcc/testsuite/gcc.target/aarch64/sve/aarch64-sve.exp +index 71dd6687c6b..7a62814edbb 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve/aarch64-sve.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve/aarch64-sve.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } then { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp b/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp +index a271f1793f4..2da5720d1f3 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp +@@ -24,6 +24,11 @@ if {![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle.exp b/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle.exp +index ce71c9c1fd4..c7bd136f202 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pcs/aarch64-sve-pcs.exp b/gcc/testsuite/gcc.target/aarch64/sve/pcs/aarch64-sve-pcs.exp +index 83786733f35..9ab68902def 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve/pcs/aarch64-sve-pcs.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve/pcs/aarch64-sve-pcs.exp +@@ -25,6 +25,10 @@ if {![istarget aarch64*-*-*] } then { + return + } + ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/aarch64-sve2.exp b/gcc/testsuite/gcc.target/aarch64/sve2/aarch64-sve2.exp +index 60652dbf8fb..010e32132cb 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve2/aarch64-sve2.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve2/aarch64-sve2.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } then { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp b/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp +index e08cd612190..a7e5f364770 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp +@@ -24,6 +24,11 @@ if {![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle.exp b/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle.exp +index f0255967c9d..82092e337f6 100644 +--- a/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle.exp ++++ b/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle.exp +@@ -25,6 +25,11 @@ if {![istarget aarch64*-*-*] } { + return + } + ++# Darwin doesn't support sve ++if { [istarget *-*-darwin*] } then { ++ return ++} ++ + # Load support procs. + load_lib gcc-dg.exp + +diff --git a/gcc/testsuite/gcc.target/aarch64/symbol-range-tiny.c b/gcc/testsuite/gcc.target/aarch64/symbol-range-tiny.c +index fc6a4f3ec78..2d9e94bc625 100644 +--- a/gcc/testsuite/gcc.target/aarch64/symbol-range-tiny.c ++++ b/gcc/testsuite/gcc.target/aarch64/symbol-range-tiny.c +@@ -1,4 +1,5 @@ + /* { dg-do link } */ ++/* { dg-skip-if "no mcmodel tiny" *-*-darwin* } */ + /* { dg-options "-O3 -save-temps -mcmodel=tiny" } */ + + char fixed_regs[0x00080000]; +diff --git a/gcc/testsuite/gcc.target/aarch64/uaddw-3.c b/gcc/testsuite/gcc.target/aarch64/uaddw-3.c +index 39cbd6b6cc2..b4ed187bd2c 100644 +--- a/gcc/testsuite/gcc.target/aarch64/uaddw-3.c ++++ b/gcc/testsuite/gcc.target/aarch64/uaddw-3.c +@@ -1,10 +1,11 @@ + /* { dg-do compile } */ + /* { dg-options "-O3" } */ ++/* { dg-additional-options "-fno-signed-char" { target *-*-darwin* } } */ + + #pragma GCC target "+nosve" + + int +-t6(int len, void * dummy, char * __restrict x) ++t6(int len, void * dummy, unsigned char * __restrict x) + { + len = len & ~31; + unsigned short result = 0; +diff --git a/gcc/testsuite/gcc.target/aarch64/vect-cse-codegen.c b/gcc/testsuite/gcc.target/aarch64/vect-cse-codegen.c +index d025e989a1e..f218504c719 100644 +--- a/gcc/testsuite/gcc.target/aarch64/vect-cse-codegen.c ++++ b/gcc/testsuite/gcc.target/aarch64/vect-cse-codegen.c +@@ -6,8 +6,8 @@ + + /* + **test1: +-** adrp x[0-9]+, .LC[0-9]+ +-** ldr q[0-9]+, \[x[0-9]+, #:lo12:.LC[0-9]+\] ++** adrp x[0-9]+, (.LC[0-9]+|lC[0-9]+@PAGE) ++** ldr q[0-9]+, \[x[0-9]+, #(:lo12:.LC[0-9]+|lC[0-9]+@PAGEOFF)\] + ** add v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d + ** str q[0-9]+, \[x[0-9]+\] + ** fmov x[0-9]+, d[0-9]+ +@@ -27,13 +27,14 @@ test1 (uint64_t a, uint64x2_t b, uint64x2_t* rt) + + /* + **test2: +-** adrp x[0-9]+, .LC[0-1]+ +-** ldr q[0-9]+, \[x[0-9]+, #:lo12:.LC[0-9]+\] ++** adrp x[0-9]+, (.LC[0-1]+|lC[0-1]+@PAGE) ++** ldr q[0-9]+, \[x[0-9]+, #(:lo12:.LC[0-9]+|lC[0-9]+@PAGEOFF)\] + ** add v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d + ** str q[0-9]+, \[x[0-9]+\] + ** fmov x[0-9]+, d[0-9]+ + ** orr x[0-9]+, x[0-9]+, x[0-9]+ + ** ret ++ + */ + + uint64_t +@@ -48,8 +49,8 @@ test2 (uint64_t a, uint64x2_t b, uint64x2_t* rt) + + /* + **test3: +-** adrp x[0-9]+, .LC[0-9]+ +-** ldr q[0-9]+, \[x[0-9]+, #:lo12:.LC[0-9]+\] ++** adrp x[0-9]+, (.LC[0-9]+|lC[0-9]+@PAGE) ++** ldr q[0-9]+, \[x[0-9]+, #(:lo12:.LC[0-9]+|lC[0-9]+@PAGEOFF)\] + ** add v[0-9]+.4s, v[0-9]+.4s, v[0-9]+.4s + ** str q[0-9]+, \[x1\] + ** fmov w[0-9]+, s[0-9]+ +diff --git a/gcc/testsuite/gfortran.dg/coarray/caf.exp b/gcc/testsuite/gfortran.dg/coarray/caf.exp +index 8683a2ab435..e76cb69dcd9 100644 +--- a/gcc/testsuite/gfortran.dg/coarray/caf.exp ++++ b/gcc/testsuite/gfortran.dg/coarray/caf.exp +@@ -28,6 +28,7 @@ + + # Load procedures from common libraries. + load_lib gfortran-dg.exp ++load_lib atomic-dg.exp + + # If a testcase doesn't have special options, use these. + global DEFAULT_FFLAGS +@@ -47,6 +48,7 @@ global gfortran_test_path + global gfortran_aux_module_flags + set gfortran_test_path $srcdir/$subdir + set gfortran_aux_module_flags $DEFAULT_FFLAGS ++ + proc dg-compile-aux-modules { args } { + global gfortran_test_path + global gfortran_aux_module_flags +@@ -69,9 +71,21 @@ proc dg-compile-aux-modules { args } { + } + + # Add -latomic only where supported. Assume built-in support elsewhere. +-set maybe_atomic_lib "" + if [check_effective_target_libatomic_available] { +- set maybe_atomic_lib "-latomic" ++ #set maybe_atomic_lib "-latomic" ++ if ![is_remote host] { ++ if [info exists TOOL_OPTIONS] { ++ set maybe_atomic_lib "[atomic_link_flags [get_multilibs ${TOOL_OPTIONS}]]" ++ } else { ++ set maybe_atomic_lib "[atomic_link_flags [get_multilibs]]" ++ } ++ } else { ++ set maybe_atomic_lib "" ++ } ++ set t [get_multilibs] ++ puts "maybe al $maybe_atomic_lib ml $t" ++} else { ++ set maybe_atomic_lib "" + } + + # Main loop. +diff --git a/gcc/testsuite/gfortran.dg/dg.exp b/gcc/testsuite/gfortran.dg/dg.exp +index bd7ad95ad0d..36fc2972b2e 100644 +--- a/gcc/testsuite/gfortran.dg/dg.exp ++++ b/gcc/testsuite/gfortran.dg/dg.exp +@@ -18,6 +18,7 @@ + + # Load support procs. + load_lib gfortran-dg.exp ++load_lib atomic-dg.exp + + # If a testcase doesn't have special options, use these. + global DEFAULT_FFLAGS +@@ -53,12 +54,30 @@ proc dg-compile-aux-modules { args } { + } + } + ++# coarray tests might need libatomic. Assume that it is either not needed or ++# provided by builtins if it's not available. ++if [check_effective_target_libatomic_available] { ++ if ![is_remote host] { ++ if [info exists TOOL_OPTIONS] { ++ set maybe_atomic_lib "[atomic_link_flags [get_multilibs ${TOOL_OPTIONS}]]" ++ } else { ++ set maybe_atomic_lib "[atomic_link_flags [get_multilibs]]" ++ } ++ } else { ++ set maybe_atomic_lib "" ++ } ++ set t [get_multilibs] ++ puts "dg set al $maybe_atomic_lib ml $t" ++} ++ ++puts "DEFAULT_FFLAGS $DEFAULT_FFLAGS" ++ + # Main loop. + gfortran-dg-runtest [lsort \ +- [glob -nocomplain $srcdir/$subdir/*.\[fF\]{,90,95,03,08} ] ] "" $DEFAULT_FFLAGS ++ [glob -nocomplain $srcdir/$subdir/*.\[fF\]{,90,95,03,08} ] ] $maybe_atomic_lib $DEFAULT_FFLAGS + + gfortran-dg-runtest [lsort \ +- [glob -nocomplain $srcdir/$subdir/g77/*.\[fF\] ] ] "" $DEFAULT_FFLAGS ++ [glob -nocomplain $srcdir/$subdir/g77/*.\[fF\] ] ] $maybe_atomic_lib $DEFAULT_FFLAGS + + + # All done. +diff --git a/gcc/testsuite/gfortran.dg/pr95690.f90 b/gcc/testsuite/gfortran.dg/pr95690.f90 +index 47a5df9e894..1afa9d3c467 100644 +--- a/gcc/testsuite/gfortran.dg/pr95690.f90 ++++ b/gcc/testsuite/gfortran.dg/pr95690.f90 +@@ -2,8 +2,8 @@ + module m + contains + subroutine s +- print *, (erfc) ! { dg-error "not a floating constant" "" { target i?86-*-* x86_64-*-* sparc*-*-* cris-*-* } } +- end ! { dg-error "not a floating constant" "" { target { ! "i?86-*-* x86_64-*-* sparc*-*-* cris-*-*" } } } ++ print *, (erfc) ! { dg-error "not a floating constant" "" { target i?86-*-* x86_64-*-* sparc*-*-* cris-*-* aarch64-apple-darwin* } } ++ end ! { dg-error "not a floating constant" "" { target { ! "i?86-*-* x86_64-*-* sparc*-*-* cris-*-* aarch64-apple-darwin*" } } } + function erfc() + end + end +diff --git a/gcc/testsuite/lib/asan-dg.exp b/gcc/testsuite/lib/asan-dg.exp +index 7e0f85dc9b0..88c6ece8caa 100644 +--- a/gcc/testsuite/lib/asan-dg.exp ++++ b/gcc/testsuite/lib/asan-dg.exp +@@ -78,7 +78,7 @@ proc asan_link_flags_1 { paths lib } { + || [file exists "${gccpath}/libsanitizer/${lib}/.libs/lib${lib}.${shlib_ext}"] } { + append flags " -B${gccpath}/libsanitizer/ " + append flags " -B${gccpath}/libsanitizer/${lib}/ " +- append flags " -L${gccpath}/libsanitizer/${lib}/.libs " ++ append flags " -B${gccpath}/libsanitizer/${lib}/.libs " + append ld_library_path ":${gccpath}/libsanitizer/${lib}/.libs" + } + } else { +diff --git a/gcc/testsuite/lib/atomic-dg.exp b/gcc/testsuite/lib/atomic-dg.exp +index 86dcfa674ea..c9244fb6cac 100644 +--- a/gcc/testsuite/lib/atomic-dg.exp ++++ b/gcc/testsuite/lib/atomic-dg.exp +@@ -33,7 +33,7 @@ proc atomic_link_flags { paths } { + if { [file exists "${gccpath}/libatomic/.libs/libatomic.a"] + || [file exists "${gccpath}/libatomic/.libs/libatomic.${shlib_ext}"] } { + append flags " -B${gccpath}/libatomic/ " +- append flags " -L${gccpath}/libatomic/.libs" ++ append flags " -B${gccpath}/libatomic/.libs" + append ld_library_path ":${gccpath}/libatomic/.libs" + } + } else { +diff --git a/gcc/testsuite/lib/scanasm.exp b/gcc/testsuite/lib/scanasm.exp +index a80630bb2a8..195611f1f72 100644 +--- a/gcc/testsuite/lib/scanasm.exp ++++ b/gcc/testsuite/lib/scanasm.exp +@@ -763,7 +763,7 @@ proc scan-lto-assembler { args } { + # to function bodies in array RESULT. FILENAME has already been uploaded + # locally where necessary and is known to exist. + +-proc parse_function_bodies { filename result } { ++proc parse_ELF_function_bodies { filename result } { + upvar $result up_result + + # Regexp for the start of a function definition (name in \1). +@@ -793,6 +793,44 @@ proc parse_function_bodies { filename result } { + close $fd + } + ++proc parse_MACHO_function_bodies { filename result } { ++ upvar $result up_result ++ ++ # Regexp for the start of a function definition (name in \1). ++ set label {^(_[a-zA-Z_]\S+):$} ++ set start {^LFB[0-9]+} ++ ++ # Regexp for the end of a function definition. ++ set terminator {^LFE[0-9]+} ++ ++ # Regexp for lines that aren't interesting. ++ set fluff {^\s*(?:\.|//|@)} ++ set fluff3 {^L[0-9ACESV]} ++ ++ set fd [open $filename r] ++ set in_function 0 ++ while { [gets $fd line] >= 0 } { ++ if { !$in_function && [regexp $label $line dummy function_name] } { ++ set in_function 1 ++ set function_body "" ++ } elseif { $in_function == 1 } { ++ if { [regexp $start $line] } { ++ set in_function 2 ++ } else { ++ set in_function 0 ++ } ++ } elseif { $in_function == 2 } { ++ if { [regexp $terminator $line] } { ++ set up_result($function_name) $function_body ++ set in_function 0 ++ } elseif { ![regexp $fluff $line] && ![regexp $fluff3 $line] } { ++ append function_body $line "\n" ++ } ++ } ++ } ++ close $fd ++} ++ + # FUNCTIONS is an array that maps function names to function bodies. + # Return true if it contains a definition of function NAME and if + # that definition matches BODY_REGEXP. +@@ -820,6 +858,14 @@ proc check-function-bodies { args } { + error "too many arguments to check-function-bodies" + } + ++ set isELF 1 ++ # some targets have a __USER_LABEL_PREFIX__ ++ set needsULP 0 ++ if { [istarget *-*-darwin*] } { ++ set isELF 0 ++ set needsULP 1 ++ } ++ + if { [llength $args] >= 3 } { + set required_flags [lindex $args 2] + +@@ -876,7 +922,11 @@ proc check-function-bodies { args } { + remote_upload host "$filename" + } + if { [file exists $output_filename] } { +- parse_function_bodies $output_filename functions ++ if { $isELF } { ++ parse_ELF_function_bodies $output_filename functions ++ } else { ++ parse_MACHO_function_bodies $output_filename functions ++ } + set have_bodies 1 + } else { + verbose -log "$testcase: output file does not exist" +@@ -921,6 +971,9 @@ proc check-function-bodies { args } { + if { $xfail_all || [string equal $selector "F"] } { + setup_xfail "*-*-*" + } ++ if { $needsULP } { ++ set function_name "_$function_name" ++ } + set testname "$testcase check-function-bodies $function_name" + if { !$have_bodies } { + unresolved $testname +diff --git a/gcc/testsuite/lib/target-libpath.exp b/gcc/testsuite/lib/target-libpath.exp +index d09cd515d20..280ce390845 100644 +--- a/gcc/testsuite/lib/target-libpath.exp ++++ b/gcc/testsuite/lib/target-libpath.exp +@@ -67,6 +67,7 @@ proc set_ld_library_path_env_vars { } { + global orig_dyld_library_path + global orig_path + global orig_gcc_exec_prefix ++ global ENABLE_DARWIN_AT_RPATH + global env + + # Save the original GCC_EXEC_PREFIX. +@@ -133,6 +134,7 @@ proc set_ld_library_path_env_vars { } { + # + # Doing this is somewhat of a hack as ld_library_path gets repeated in + # SHLIB_PATH and LD_LIBRARY_PATH when unix_load sets these variables. ++ if { ![istarget *-*-darwin*] } { + if { $orig_ld_library_path_saved } { + setenv LD_LIBRARY_PATH "$ld_library_path:$orig_ld_library_path" + } else { +@@ -166,11 +168,23 @@ proc set_ld_library_path_env_vars { } { + } else { + setenv LD_LIBRARY_PATH_64 "$ld_library_path" + } +- if { $orig_dyld_library_path_saved } { +- setenv DYLD_LIBRARY_PATH "$ld_library_path:$orig_dyld_library_path" +- } else { +- setenv DYLD_LIBRARY_PATH "$ld_library_path" + } ++ if { [istarget *-*-darwin*] } { ++ if { [info exists ENABLE_DARWIN_AT_RPATH] || [istarget *-*-darwin1\[5-9\]*] ++ || [istarget *-*-darwin2*] } { ++ # Either we are not using DYLD_LIBRARY_PATH or we're on a version of the ++ # OS for which it is not passed through system exes. ++ if [info exists env(DYLD_LIBRARY_PATH)] { ++ unsetenv DYLD_LIBRARY_PATH ++ } ++ } else { ++ if { $orig_dyld_library_path_saved } { ++ setenv DYLD_LIBRARY_PATH "$ld_library_path:$orig_dyld_library_path" ++ } else { ++ setenv DYLD_LIBRARY_PATH "$ld_library_path" ++ } ++ } ++ } + if { [istarget *-*-cygwin*] || [istarget *-*-mingw*] } { + if { $orig_path_saved } { + setenv PATH "$ld_library_path:$orig_path" +@@ -179,6 +193,7 @@ proc set_ld_library_path_env_vars { } { + } + } + ++ verbose -log "set paths" + verbose -log "LD_LIBRARY_PATH=[getenv LD_LIBRARY_PATH]" + verbose -log "LD_RUN_PATH=[getenv LD_RUN_PATH]" + verbose -log "SHLIB_PATH=[getenv SHLIB_PATH]" +diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp +index 244fe2306f4..75581914d6c 100644 +--- a/gcc/testsuite/lib/target-supports.exp ++++ b/gcc/testsuite/lib/target-supports.exp +@@ -8485,7 +8485,7 @@ proc check_effective_target_section_anchors { } { + return [check_cached_effective_target section_anchors { + expr { [istarget powerpc*-*-*] + || [istarget arm*-*-*] +- || [istarget aarch64*-*-*] }}] ++ || ([istarget aarch64*-*-*] && ![istarget aarch64*-*-darwin*]) }}] + } + + # Return 1 if the target supports atomic operations on "int_128" values. +@@ -11526,6 +11526,15 @@ proc check_effective_target_arm_thumb2_ok_no_arm_v8_1_lob { } { + return 0 + } + ++# Return 1 if this is an ARM target where -mabi=ilp32 can be used. ++ ++proc check_effective_target_arm_mabi_ilp32 { } { ++ return [check_no_compiler_messages_nocache arm_mabi_ilp32 assembly { ++ int main() { return 0; } ++ } "-mabi=ilp32"] ++} ++ ++ + # Returns 1 if the target is using glibc, 0 otherwise. + + proc check_effective_target_glibc { } { +diff --git a/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm b/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm +index 92852c3ecea..e0974539ecf 100644 +--- a/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm ++++ b/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm +@@ -19,6 +19,7 @@ this behavior may be defined or documented (for example, if class + + /* { dg-do run } */ + /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ ++/* { dg-skip-if "API unsupported" { arm64*-*-darwin* aarch64*-*-darwin* } { "-fnext-runtime" } { "" } } */ + /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + /* { dg-additional-options "-DOBJC_OLD_DISPATCH_PROTOTYPES" { target { *-*-darwin* } } } */ + // { dg-additional-options "-Wno-objc-root-class" } +diff --git a/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm b/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm +index f6e3d8d22e0..a23968a89b5 100644 +--- a/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm ++++ b/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm +@@ -6,6 +6,7 @@ + + /* { dg-do run } */ + /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ ++/* { dg-skip-if "API unsupported" { arm64*-*-darwin* aarch64*-*-darwin* } { "-fnext-runtime" } { "" } } */ + /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + /* { dg-additional-options "-DOBJC_OLD_DISPATCH_PROTOTYPES" { target { *-*-darwin* } } } */ + // { dg-additional-options "-Wno-objc-root-class" } +diff --git a/gcc/testsuite/obj-c++.dg/torture/strings/const-cfstring-4.mm b/gcc/testsuite/obj-c++.dg/torture/strings/const-cfstring-4.mm +index 1155db5f83f..e0dd8062373 100644 +--- a/gcc/testsuite/obj-c++.dg/torture/strings/const-cfstring-4.mm ++++ b/gcc/testsuite/obj-c++.dg/torture/strings/const-cfstring-4.mm +@@ -18,4 +18,4 @@ + + /* { dg-final { scan-assembler ".section __DATA, __cfstring" } } */ + /* { dg-final { scan-assembler ".long\t___CFConstantStringClassReference\n\t.long\t1992\n\t.long\t.*\n\t.long\t19\n" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t19\n" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t19\n} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-10.mm b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-10.mm +index e1dad124cd6..eb89710d890 100644 +--- a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-10.mm ++++ b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-10.mm +@@ -33,4 +33,4 @@ @interface NSConstantString : NSSimpleCString + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__NSConstantStringClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._NSConstantString\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._NSConstantString\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-11.mm b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-11.mm +index 30a9228a64e..c1b58dc6cb8 100644 +--- a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-11.mm ++++ b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-11.mm +@@ -33,4 +33,4 @@ @interface XStr : XString { + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__XStrClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._XStr\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._XStr\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-9.mm b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-9.mm +index a1a14295e90..8457f46be53 100644 +--- a/gcc/testsuite/obj-c++.dg/torture/strings/const-str-9.mm ++++ b/gcc/testsuite/obj-c++.dg/torture/strings/const-str-9.mm +@@ -25,4 +25,4 @@ @interface NSConstantString: NSObject { + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__NSConstantStringClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._NSConstantString\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._NSConstantString\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m b/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m +index 6c1c76a87a3..41a48f9c685 100644 +--- a/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m ++++ b/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m +@@ -19,6 +19,7 @@ this behavior may be defined or documented (for example, if class + + /* { dg-do run } */ + /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ ++/* { dg-skip-if "API unsupported" { arm64*-*-darwin* aarch64*-*-darwin* } { "-fnext-runtime" } { "" } } */ + /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + /* { dg-additional-options "-Wno-objc-root-class" } */ + /* { dg-additional-options "-DOBJC_OLD_DISPATCH_PROTOTYPES" { target { *-*-darwin* } } } */ +diff --git a/gcc/testsuite/objc.dg/gnu-api-2-class.m b/gcc/testsuite/objc.dg/gnu-api-2-class.m +index d11dae0e6dc..1386ebc2f99 100644 +--- a/gcc/testsuite/objc.dg/gnu-api-2-class.m ++++ b/gcc/testsuite/objc.dg/gnu-api-2-class.m +@@ -6,6 +6,7 @@ + + /* { dg-do run } */ + /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ ++/* { dg-skip-if "API unsupported" { arm64*-*-darwin* aarch64*-*-darwin* } { "-fnext-runtime" } { "" } } */ + /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + /* { dg-additional-options "-Wno-objc-root-class" } */ + /* { dg-additional-options "-DOBJC_OLD_DISPATCH_PROTOTYPES" { target { *-*-darwin* } } } */ +diff --git a/gcc/testsuite/objc.dg/torture/strings/const-cfstring-4.m b/gcc/testsuite/objc.dg/torture/strings/const-cfstring-4.m +index 1155db5f83f..e0dd8062373 100644 +--- a/gcc/testsuite/objc.dg/torture/strings/const-cfstring-4.m ++++ b/gcc/testsuite/objc.dg/torture/strings/const-cfstring-4.m +@@ -18,4 +18,4 @@ + + /* { dg-final { scan-assembler ".section __DATA, __cfstring" } } */ + /* { dg-final { scan-assembler ".long\t___CFConstantStringClassReference\n\t.long\t1992\n\t.long\t.*\n\t.long\t19\n" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t___CFConstantStringClassReference\n\t.long\t1992\n\t.space 4\n\t.quad\t.*\n\t.quad\t19\n" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t___CFConstantStringClassReference\n\t.(long|word)\t1992\n\t.space 4\n\t.(quad|xword)\t.*\n\t.(quad|xword)\t19\n} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/objc.dg/torture/strings/const-str-10.m b/gcc/testsuite/objc.dg/torture/strings/const-str-10.m +index 6565dc20007..81b0d326c56 100644 +--- a/gcc/testsuite/objc.dg/torture/strings/const-str-10.m ++++ b/gcc/testsuite/objc.dg/torture/strings/const-str-10.m +@@ -34,4 +34,4 @@ @interface NSConstantString : NSSimpleCString + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__NSConstantStringClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._NSConstantString\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._NSConstantString\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/objc.dg/torture/strings/const-str-11.m b/gcc/testsuite/objc.dg/torture/strings/const-str-11.m +index 2bdb1531e1d..b044b0fd8c7 100644 +--- a/gcc/testsuite/objc.dg/torture/strings/const-str-11.m ++++ b/gcc/testsuite/objc.dg/torture/strings/const-str-11.m +@@ -33,4 +33,4 @@ @interface XStr : XString { + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__XStrClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._XStr\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._XStr\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/testsuite/objc.dg/torture/strings/const-str-9.m b/gcc/testsuite/objc.dg/torture/strings/const-str-9.m +index 966ea5e498d..d3d2916ed06 100644 +--- a/gcc/testsuite/objc.dg/torture/strings/const-str-9.m ++++ b/gcc/testsuite/objc.dg/torture/strings/const-str-9.m +@@ -25,4 +25,4 @@ @interface NSConstantString: NSObject { + /* { dg-final { scan-assembler ".section __OBJC, __cstring_object" { target { *-*-darwin* && { ! lp64 } } } } } */ + /* { dg-final { scan-assembler ".section __DATA, __objc_stringobj" { target { *-*-darwin* && { lp64 } } } } } */ + /* { dg-final { scan-assembler ".long\t__NSConstantStringClassReference\n\t.long\t.*\n\t.long\t5\n\t.data" { target { *-*-darwin* && { ! lp64 } } } } } */ +-/* { dg-final { scan-assembler ".quad\t_OBJC_CLASS_._NSConstantString\n\t.quad\t.*\n\t.long\t5\n\t.space" { target { *-*-darwin* && { lp64 } } } } } */ ++/* { dg-final { scan-assembler {.(quad|xword)\t_OBJC_CLASS_._NSConstantString\n\t.(quad|xword)\t.*\n\t.(long|word)\t5\n\t.space} { target { *-*-darwin* && { lp64 } } } } } */ +diff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc +index 078ceab3ca3..0308f558036 100644 +--- a/gcc/tree-nested.cc ++++ b/gcc/tree-nested.cc +@@ -611,6 +611,14 @@ get_trampoline_type (struct nesting_info *info) + if (trampoline_type) + return trampoline_type; + ++ /* When trampolines are created off-stack then the only thing we need in the ++ local frame is a single pointer. */ ++ if (flag_off_stack_trampolines) ++ { ++ trampoline_type = build_pointer_type (void_type_node); ++ return trampoline_type; ++ } ++ + align = TRAMPOLINE_ALIGNMENT; + size = TRAMPOLINE_SIZE; + +@@ -2786,17 +2794,27 @@ convert_tramp_reference_op (tree *tp, int *walk_subtrees, void *data) + + /* Compute the address of the field holding the trampoline. */ + x = get_frame_field (info, target_context, x, &wi->gsi); +- x = build_addr (x); +- x = gsi_gimplify_val (info, x, &wi->gsi); + +- /* Do machine-specific ugliness. Normally this will involve +- computing extra alignment, but it can really be anything. */ +- if (descr) +- builtin = builtin_decl_implicit (BUILT_IN_ADJUST_DESCRIPTOR); ++ /* APB: We don't need to do the adjustment calls when using off-stack ++ trampolines, any such adjustment will be done when the off-stack ++ trampoline is created. */ ++ if (!descr && flag_off_stack_trampolines) ++ x = gsi_gimplify_val (info, x, &wi->gsi); + else +- builtin = builtin_decl_implicit (BUILT_IN_ADJUST_TRAMPOLINE); +- call = gimple_build_call (builtin, 1, x); +- x = init_tmp_var_with_call (info, &wi->gsi, call); ++ { ++ x = build_addr (x); ++ ++ x = gsi_gimplify_val (info, x, &wi->gsi); ++ ++ /* Do machine-specific ugliness. Normally this will involve ++ computing extra alignment, but it can really be anything. */ ++ if (descr) ++ builtin = builtin_decl_implicit (BUILT_IN_ADJUST_DESCRIPTOR); ++ else ++ builtin = builtin_decl_implicit (BUILT_IN_ADJUST_TRAMPOLINE); ++ call = gimple_build_call (builtin, 1, x); ++ x = init_tmp_var_with_call (info, &wi->gsi, call); ++ } + + /* Cast back to the proper function type. */ + x = build1 (NOP_EXPR, TREE_TYPE (t), x); +@@ -3375,6 +3393,7 @@ build_init_call_stmt (struct nesting_info *info, tree decl, tree field, + static void + finalize_nesting_tree_1 (struct nesting_info *root) + { ++ gimple_seq cleanup_list = NULL; + gimple_seq stmt_list = NULL; + gimple *stmt; + tree context = root->context; +@@ -3506,9 +3525,48 @@ finalize_nesting_tree_1 (struct nesting_info *root) + if (!field) + continue; + +- x = builtin_decl_implicit (BUILT_IN_INIT_TRAMPOLINE); +- stmt = build_init_call_stmt (root, i->context, field, x); +- gimple_seq_add_stmt (&stmt_list, stmt); ++ if (flag_off_stack_trampolines) ++ { ++ /* We pass a whole bunch of arguments to the builtin function that ++ creates the off-stack trampoline, these are ++ 1. The nested function chain value (that must be passed to the ++ nested function so it can find the function arguments). ++ 2. A pointer to the nested function implementation, ++ 3. The address in the local stack frame where we should write ++ the address of the trampoline. ++ ++ When this code was originally written I just kind of threw ++ everything at the builtin, figuring I'd work out what was ++ actually needed later, I think, the stack pointer could ++ certainly be dropped, arguments #2 and #4 are based off the ++ stack pointer anyway, so #1 doesn't seem to add much value. */ ++ tree arg1, arg2, arg3; ++ ++ gcc_assert (DECL_STATIC_CHAIN (i->context)); ++ arg1 = build_addr (root->frame_decl); ++ arg2 = build_addr (i->context); ++ ++ x = build3 (COMPONENT_REF, TREE_TYPE (field), ++ root->frame_decl, field, NULL_TREE); ++ arg3 = build_addr (x); ++ ++ x = builtin_decl_implicit (BUILT_IN_NESTED_PTR_CREATED); ++ stmt = gimple_build_call (x, 3, arg1, arg2, arg3); ++ gimple_seq_add_stmt (&stmt_list, stmt); ++ ++ /* This call to delete the nested function trampoline is added to ++ the cleanup list, and called when we exit the current scope. */ ++ x = builtin_decl_implicit (BUILT_IN_NESTED_PTR_DELETED); ++ stmt = gimple_build_call (x, 0); ++ gimple_seq_add_stmt (&cleanup_list, stmt); ++ } ++ else ++ { ++ /* Original code to initialise the on stack trampoline. */ ++ x = builtin_decl_implicit (BUILT_IN_INIT_TRAMPOLINE); ++ stmt = build_init_call_stmt (root, i->context, field, x); ++ gimple_seq_add_stmt (&stmt_list, stmt); ++ } + } + } + +@@ -3533,11 +3591,40 @@ finalize_nesting_tree_1 (struct nesting_info *root) + /* If we created initialization statements, insert them. */ + if (stmt_list) + { +- gbind *bind; +- annotate_all_with_location (stmt_list, DECL_SOURCE_LOCATION (context)); +- bind = gimple_seq_first_stmt_as_a_bind (gimple_body (context)); +- gimple_seq_add_seq (&stmt_list, gimple_bind_body (bind)); +- gimple_bind_set_body (bind, stmt_list); ++ if (flag_off_stack_trampolines) ++ { ++ /* Handle the new, off stack trampolines. */ ++ gbind *bind; ++ annotate_all_with_location (stmt_list, DECL_SOURCE_LOCATION (context)); ++ annotate_all_with_location (cleanup_list, DECL_SOURCE_LOCATION (context)); ++ bind = gimple_seq_first_stmt_as_a_bind (gimple_body (context)); ++ gimple_seq_add_seq (&stmt_list, gimple_bind_body (bind)); ++ ++ gimple_seq xxx_list = NULL; ++ ++ if (cleanup_list != NULL) ++ { ++ /* We Maybe shouldn't be creating this try/finally if -fno-exceptions is ++ in use. If this is the case, then maybe we should, instead, be ++ inserting the cleanup code onto every path out of this function? Not ++ yet figured out how we would do this. */ ++ gtry *t = gimple_build_try (stmt_list, cleanup_list, GIMPLE_TRY_FINALLY); ++ gimple_seq_add_stmt (&xxx_list, t); ++ } ++ else ++ xxx_list = stmt_list; ++ ++ gimple_bind_set_body (bind, xxx_list); ++ } ++ else ++ { ++ /* The traditional, on stack trampolines. */ ++ gbind *bind; ++ annotate_all_with_location (stmt_list, DECL_SOURCE_LOCATION (context)); ++ bind = gimple_seq_first_stmt_as_a_bind (gimple_body (context)); ++ gimple_seq_add_seq (&stmt_list, gimple_bind_body (bind)); ++ gimple_bind_set_body (bind, stmt_list); ++ } + } + + /* If a chain_decl was created, then it needs to be registered with +diff --git a/gcc/tree.cc b/gcc/tree.cc +index 4cf3785270b..5cba2ab8171 100644 +--- a/gcc/tree.cc ++++ b/gcc/tree.cc +@@ -9766,6 +9766,23 @@ build_common_builtin_nodes (void) + "__builtin_nonlocal_goto", + ECF_NORETURN | ECF_NOTHROW); + ++ tree ptr_ptr_type_node = build_pointer_type (ptr_type_node); ++ ++ ftype = build_function_type_list (void_type_node, ++ ptr_type_node, // void *chain ++ ptr_type_node, // void *func ++ ptr_ptr_type_node, // void **dst ++ NULL_TREE); ++ local_define_builtin ("__builtin_nested_func_ptr_created", ftype, ++ BUILT_IN_NESTED_PTR_CREATED, ++ "__builtin_nested_func_ptr_created", ECF_NOTHROW); ++ ++ ftype = build_function_type_list (void_type_node, ++ NULL_TREE); ++ local_define_builtin ("__builtin_nested_func_ptr_deleted", ftype, ++ BUILT_IN_NESTED_PTR_DELETED, ++ "__builtin_nested_func_ptr_deleted", ECF_NOTHROW); ++ + ftype = build_function_type_list (void_type_node, + ptr_type_node, ptr_type_node, NULL_TREE); + local_define_builtin ("__builtin_setjmp_setup", ftype, +diff --git a/libada/configure b/libada/configure +index 162d9731f26..9c8b133d817 100755 +--- a/libada/configure ++++ b/libada/configure +@@ -3212,6 +3212,9 @@ case "${host}" in + # sets the default TLS model and affects inlining. + PICFLAG=-fPIC + ;; ++ loongarch*-*-*) ++ PICFLAG=-fpic ++ ;; + mips-sgi-irix6*) + # PIC is the default. + ;; +diff --git a/libatomic/Makefile.am b/libatomic/Makefile.am +index d88515e4a03..3c921f4a86d 100644 +--- a/libatomic/Makefile.am ++++ b/libatomic/Makefile.am +@@ -65,8 +65,13 @@ libatomic_version_script = + libatomic_version_dep = + endif + libatomic_version_info = -version-info $(libtool_VERSION) ++if ENABLE_DARWIN_AT_RPATH ++libatomic_darwin_rpath = -Wc,-nodefaultrpaths ++libatomic_darwin_rpath += -Wl,-rpath,@loader_path ++endif + +-libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) $(lt_host_flags) ++libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) \ ++ $(lt_host_flags) $(libatomic_darwin_rpath) + libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c \ + fenv.c fence.c flag.c + +diff --git a/libatomic/Makefile.in b/libatomic/Makefile.in +index 80d25653dc7..179f9217ad6 100644 +--- a/libatomic/Makefile.in ++++ b/libatomic/Makefile.in +@@ -403,7 +403,12 @@ noinst_LTLIBRARIES = libatomic_convenience.la + @LIBAT_BUILD_VERSIONED_SHLIB_GNU_TRUE@@LIBAT_BUILD_VERSIONED_SHLIB_TRUE@libatomic_version_dep = $(top_srcdir)/libatomic.map + @LIBAT_BUILD_VERSIONED_SHLIB_SUN_TRUE@@LIBAT_BUILD_VERSIONED_SHLIB_TRUE@libatomic_version_dep = libatomic.map-sun + libatomic_version_info = -version-info $(libtool_VERSION) +-libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) $(lt_host_flags) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libatomic_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) \ ++ $(lt_host_flags) $(libatomic_darwin_rpath) ++ + libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c \ + fenv.c fence.c flag.c + +diff --git a/libatomic/configure b/libatomic/configure +index 92853dd8a45..935d5559aed 100755 +--- a/libatomic/configure ++++ b/libatomic/configure +@@ -658,6 +658,8 @@ OPT_LDFLAGS + SECTION_LDFLAGS + enable_aarch64_lse + libtool_VERSION ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + MAINT + MAINTAINER_MODE_FALSE + MAINTAINER_MODE_TRUE +@@ -803,6 +805,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_maintainer_mode + enable_symvers + enable_werror +@@ -1452,6 +1455,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +@@ -9576,6 +9581,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9593,10 +9639,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11382,7 +11437,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11385 "configure" ++#line 11440 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11488,7 +11543,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11491 "configure" ++#line 11546 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11773,6 +11828,15 @@ fi + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # For libtool versioning info, format is CURRENT:REVISION:AGE + libtool_VERSION=3:0:2 + +@@ -15900,6 +15964,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + 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 5563551aaae..6b9d3085806 100644 +--- a/libatomic/configure.ac ++++ b/libatomic/configure.ac +@@ -156,6 +156,8 @@ AC_SUBST(enable_shared) + AC_SUBST(enable_static) + AM_MAINTAINER_MODE + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + # For libtool versioning info, format is CURRENT:REVISION:AGE + libtool_VERSION=3:0:2 + AC_SUBST(libtool_VERSION) +diff --git a/libatomic/testsuite/Makefile.in b/libatomic/testsuite/Makefile.in +index 333980ec2c1..8bc70562e5b 100644 +--- a/libatomic/testsuite/Makefile.in ++++ b/libatomic/testsuite/Makefile.in +@@ -262,6 +262,7 @@ target_alias = @target_alias@ + target_cpu = @target_cpu@ + target_os = @target_os@ + target_vendor = @target_vendor@ ++tmake_file = @tmake_file@ + toolexecdir = @toolexecdir@ + toolexeclibdir = @toolexeclibdir@ + top_build_prefix = @top_build_prefix@ +diff --git a/libatomic/testsuite/lib/libatomic.exp b/libatomic/testsuite/lib/libatomic.exp +index 38f3e5673e2..300e5096f79 100644 +--- a/libatomic/testsuite/lib/libatomic.exp ++++ b/libatomic/testsuite/lib/libatomic.exp +@@ -152,6 +152,7 @@ proc libatomic_init { args } { + lappend ALWAYS_CFLAGS "additional_flags=-I${srcdir}/.." + + if [istarget *-*-darwin*] { ++ lappend ALWAYS_CFLAGS "additional_flags=-B${blddir}/.libs" + lappend ALWAYS_CFLAGS "additional_flags=-shared-libgcc" + } + +diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in +index 08cdd21fb40..8898251161d 100644 +--- a/libbacktrace/Makefile.in ++++ b/libbacktrace/Makefile.in +@@ -15,7 +15,7 @@ + @SET_MAKE@ + + # Makefile.am -- Backtrace Makefile. +-# Copyright (C) 2012-2021 Free Software Foundation, Inc. ++# Copyright (C) 2012-2022 Free Software Foundation, Inc. + + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are +diff --git a/libbacktrace/backtrace.c b/libbacktrace/backtrace.c +index d28575ec897..cf6491682a7 100644 +--- a/libbacktrace/backtrace.c ++++ b/libbacktrace/backtrace.c +@@ -70,6 +70,13 @@ unwind (struct _Unwind_Context *context, void *vdata) + uintptr_t pc; + int ip_before_insn = 0; + ++#ifdef __APPLE__ ++# undef HAVE_GETIPINFO ++# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 ++# define HAVE_GETIPINFO 1 ++# endif ++#endif ++ + #ifdef HAVE_GETIPINFO + pc = _Unwind_GetIPInfo (context, &ip_before_insn); + #else +diff --git a/libbacktrace/configure b/libbacktrace/configure +index 17f470a4bec..957095aaf1b 100755 +--- a/libbacktrace/configure ++++ b/libbacktrace/configure +@@ -675,6 +675,8 @@ PIC_FLAG + WARN_FLAGS + EXTRA_FLAGS + BACKTRACE_FILE ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + OTOOL64 + OTOOL + LIPO +@@ -799,6 +801,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_largefile + enable_cet + enable_werror +@@ -1447,6 +1450,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --disable-largefile omit support for large files + --enable-cet enable Intel CET in target libraries [default=auto] + --disable-werror disable building with -Werror +@@ -9705,6 +9710,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9722,10 +9768,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11511,7 +11566,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11514 "configure" ++#line 11569 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11617,7 +11672,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11620 "configure" ++#line 11675 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11856,6 +11911,15 @@ CC="$lt_save_CC" + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # Check whether --enable-largefile was given. + if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +@@ -14273,6 +14337,10 @@ if test -z "${HAVE_DWZ_TRUE}" && test -z "${HAVE_DWZ_FALSE}"; then + as_fn_error $? "conditional \"HAVE_DWZ\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${HAVE_ELF_TRUE}" && test -z "${HAVE_ELF_FALSE}"; then + 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 597c9705db8..7f89bf33c6b 100644 +--- a/libbacktrace/configure.ac ++++ b/libbacktrace/configure.ac +@@ -84,6 +84,8 @@ AM_CONDITIONAL(HAVE_DWZ, test "$DWZ" != "") + LT_INIT + AM_PROG_LIBTOOL + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + AC_SYS_LARGEFILE + + backtrace_supported=yes +diff --git a/libbacktrace/simple.c b/libbacktrace/simple.c +index 6a1a1c92a12..811255ab6b5 100644 +--- a/libbacktrace/simple.c ++++ b/libbacktrace/simple.c +@@ -65,6 +65,13 @@ simple_unwind (struct _Unwind_Context *context, void *vdata) + uintptr_t pc; + int ip_before_insn = 0; + ++#ifdef __APPLE__ ++# undef HAVE_GETIPINFO ++# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 ++# define HAVE_GETIPINFO 1 ++# endif ++#endif ++ + #ifdef HAVE_GETIPINFO + pc = _Unwind_GetIPInfo (context, &ip_before_insn); + #else +diff --git a/libcc1/Makefile.am b/libcc1/Makefile.am +index 6e3a34ff7e2..44d282c7676 100644 +--- a/libcc1/Makefile.am ++++ b/libcc1/Makefile.am +@@ -55,6 +55,10 @@ marshall_c_source = marshall-c.hh + marshall_cxx_source = marshall-cp.hh + + libcc1plugin_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1plugin.sym ++if ENABLE_DARWIN_AT_RPATH ++libcc1plugin_la_LDFLAGS += -Wc,-nodefaultrpaths ++libcc1plugin_la_LDFLAGS += -Wl,-rpath,@loader_path ++endif + libcc1plugin_la_SOURCES = libcc1plugin.cc context.cc context.hh \ + $(shared_source) $(marshall_c_source) + libcc1plugin.lo_CPPFLAGS = $(CPPFLAGS_FOR_C) +@@ -65,6 +69,10 @@ libcc1plugin_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(CXXFLAGS) $(libcc1plugin_la_LDFLAGS) $(LTLDFLAGS) -o $@ + + libcp1plugin_la_LDFLAGS = -module -export-symbols $(srcdir)/libcp1plugin.sym ++if ENABLE_DARWIN_AT_RPATH ++libcp1plugin_la_LDFLAGS += -Wc,-nodefaultrpaths ++libcp1plugin_la_LDFLAGS += -Wl,-rpath,@loader_path ++endif + libcp1plugin_la_SOURCES = libcp1plugin.cc context.cc context.hh \ + $(shared_source) $(marshall_cxx_source) + libcp1plugin.lo_CPPFLAGS = $(CPPFLAGS_FOR_CXX) +@@ -76,6 +84,10 @@ libcp1plugin_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + + LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS)) + libcc1_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1.sym ++if ENABLE_DARWIN_AT_RPATH ++libcc1_la_LDFLAGS += -Wc,-nodefaultrpaths ++libcc1_la_LDFLAGS += -Wl,-rpath,@loader_path ++endif + libcc1_la_SOURCES = findcomp.cc libcc1.cc libcp1.cc \ + compiler.cc compiler.hh names.cc names.hh $(shared_source) \ + $(marshall_c_source) $(marshall_cxx_source) +diff --git a/libcc1/Makefile.in b/libcc1/Makefile.in +index f8f590d71e9..440567a47d2 100644 +--- a/libcc1/Makefile.in ++++ b/libcc1/Makefile.in +@@ -90,6 +90,12 @@ build_triplet = @build@ + host_triplet = @host@ + target_triplet = @target@ + @DARWIN_DYNAMIC_LOOKUP_TRUE@am__append_1 = -Wl,-undefined,dynamic_lookup ++@ENABLE_DARWIN_AT_RPATH_TRUE@am__append_2 = -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++@ENABLE_DARWIN_AT_RPATH_TRUE@am__append_3 = -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++@ENABLE_DARWIN_AT_RPATH_TRUE@am__append_4 = -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path + subdir = . + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \ +@@ -405,7 +411,8 @@ shared_source = callbacks.cc callbacks.hh connection.cc connection.hh \ + + marshall_c_source = marshall-c.hh + marshall_cxx_source = marshall-cp.hh +-libcc1plugin_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1plugin.sym ++libcc1plugin_la_LDFLAGS = -module -export-symbols \ ++ $(srcdir)/libcc1plugin.sym $(am__append_2) + libcc1plugin_la_SOURCES = libcc1plugin.cc context.cc context.hh \ + $(shared_source) $(marshall_c_source) + +@@ -416,7 +423,8 @@ libcc1plugin_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(libcc1plugin_la_LDFLAGS) $(LTLDFLAGS) -o $@ + +-libcp1plugin_la_LDFLAGS = -module -export-symbols $(srcdir)/libcp1plugin.sym ++libcp1plugin_la_LDFLAGS = -module -export-symbols \ ++ $(srcdir)/libcp1plugin.sym $(am__append_3) + libcp1plugin_la_SOURCES = libcp1plugin.cc context.cc context.hh \ + $(shared_source) $(marshall_cxx_source) + +@@ -428,7 +436,8 @@ libcp1plugin_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(CXXFLAGS) $(libcp1plugin_la_LDFLAGS) $(LTLDFLAGS) -o $@ + + LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS)) +-libcc1_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1.sym ++libcc1_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1.sym \ ++ $(am__append_4) + libcc1_la_SOURCES = findcomp.cc libcc1.cc libcp1.cc \ + compiler.cc compiler.hh names.cc names.hh $(shared_source) \ + $(marshall_c_source) $(marshall_cxx_source) +diff --git a/libcc1/configure b/libcc1/configure +index 01cfb2806da..42fb85a4047 100755 +--- a/libcc1/configure ++++ b/libcc1/configure +@@ -646,6 +646,8 @@ gcc_version + get_gcc_base_ver + CET_HOST_FLAGS + visibility ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + CXXCPP + am__fastdepCXX_FALSE + am__fastdepCXX_TRUE +@@ -787,6 +789,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_cet + with_gcc_major_version_only + enable_werror_always +@@ -1439,6 +1442,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-cet enable Intel CET in host libraries [default=auto] + --enable-werror-always enable -Werror despite compiler version + --enable-plugin enable plugin support +@@ -8971,6 +8976,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -8988,10 +9034,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -10777,7 +10832,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10780 "configure" ++#line 10835 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -10883,7 +10938,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10886 "configure" ++#line 10941 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12165,6 +12220,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -12182,12 +12278,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -14518,6 +14627,14 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ + ac_compiler_gnu=$ac_cv_c_compiler_gnu + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + visibility= + if test "$GXX" = yes; then +@@ -15369,6 +15486,10 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${DARWIN_DYNAMIC_LOOKUP_TRUE}" && test -z "${DARWIN_DYNAMIC_LOOKUP_FALSE}"; then + as_fn_error $? "conditional \"DARWIN_DYNAMIC_LOOKUP\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 +diff --git a/libcc1/configure.ac b/libcc1/configure.ac +index 36f5a7e09f1..e8d068e0ac4 100644 +--- a/libcc1/configure.ac ++++ b/libcc1/configure.ac +@@ -38,6 +38,7 @@ AM_MAINTAINER_MODE + LT_INIT([disable-static]) + AM_PROG_LIBTOOL + AC_PROG_CXX ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + visibility= + if test "$GXX" = yes; then +diff --git a/libffi/Makefile.am b/libffi/Makefile.am +index c6d6f849c53..d2ae0c04c7b 100644 +--- a/libffi/Makefile.am ++++ b/libffi/Makefile.am +@@ -214,7 +214,12 @@ libffi.map: $(top_srcdir)/libffi.map.in + $(COMPILE) -D$(TARGET) -DGENERATE_LIBFFI_MAP \ + -E -x assembler-with-cpp -o $@ $(top_srcdir)/libffi.map.in + +-libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS) ++if ENABLE_DARWIN_AT_RPATH ++libffi_darwin_rpath = -Wl,-rpath,@loader_path ++endif ++libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) \ ++ $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS) \ ++ $(libffi_darwin_rpath) + libffi_la_DEPENDENCIES = $(libffi_la_LIBADD) $(libffi_version_dep) + + 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 +--- a/libffi/Makefile.in ++++ b/libffi/Makefile.in +@@ -597,7 +597,11 @@ AM_CFLAGS = -Wall -g -fexceptions $(CET_FLAGS) $(am__append_2) + @LIBFFI_BUILD_VERSIONED_SHLIB_GNU_TRUE@@LIBFFI_BUILD_VERSIONED_SHLIB_TRUE@libffi_version_dep = libffi.map + @LIBFFI_BUILD_VERSIONED_SHLIB_SUN_TRUE@@LIBFFI_BUILD_VERSIONED_SHLIB_TRUE@libffi_version_dep = libffi.map-sun + libffi_version_info = -version-info `grep -v '^\#' $(srcdir)/libtool-version` +-libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libffi_darwin_rpath = -Wl,-rpath,@loader_path ++libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) \ ++ $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS) \ ++ $(libffi_darwin_rpath) ++ + libffi_la_DEPENDENCIES = $(libffi_la_LIBADD) $(libffi_version_dep) + 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 575641cca1d..002320ca302 100755 +--- a/libffi/configure ++++ b/libffi/configure +@@ -667,6 +667,8 @@ MAINT + MAINTAINER_MODE_FALSE + MAINTAINER_MODE_TRUE + READELF ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + CXXCPP + CPP + OTOOL64 +@@ -810,6 +812,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_maintainer_mode + enable_pax_emutramp + enable_debug +@@ -1465,6 +1468,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +@@ -9766,6 +9771,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9783,10 +9829,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11572,7 +11627,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11575 "configure" ++#line 11630 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11678,7 +11733,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11681 "configure" ++#line 11736 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12554,6 +12609,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -12571,12 +12667,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -14926,6 +15035,14 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + # Only expand once: + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}readelf", so it can be a program name with args. +@@ -17071,6 +17188,10 @@ if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCCAS\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + 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 +--- a/libffi/configure.ac ++++ b/libffi/configure.ac +@@ -55,6 +55,7 @@ AC_SUBST(CET_FLAGS) + AM_PROG_AS + AM_PROG_CC_C_O + AC_PROG_LIBTOOL ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + AC_CHECK_TOOL(READELF, readelf) + +diff --git a/libgcc/config.host b/libgcc/config.host +index 8c56fcae5d2..48eed32e195 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*-*-*) +@@ -241,7 +241,46 @@ case ${host} in + ;; + esac + tmake_file="$tmake_file t-slibgcc-darwin" +- 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 ++ case ${host} in ++ *-*-darwin2* | *-*-darwin1[89]* | aarch64*-*-darwin*) ++ tmake_file="t-darwin-min-8 $tmake_file" ++ ;; ++ *-*-darwin9* | *-*-darwin1[0-7]*) ++ tmake_file="t-darwin-min-5 $tmake_file" ++ ;; ++ *-*-darwin[4-8]*) ++ tmake_file="t-darwin-min-1 $tmake_file" ++ ;; ++ *) ++ # Fall back to configuring for the oldest system known to work with ++ # all archs and the current sources. ++ tmake_file="t-darwin-min-5 $tmake_file" ++ echo "Warning: libgcc configured to support macOS 10.5" 1>&2 ++ ;; ++ esac ++ 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" +@@ -384,6 +423,17 @@ aarch64*-*-elf | aarch64*-*-rtems*) + tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp t-crtfm" + md_unwind_header=aarch64/aarch64-unwind.h + ;; ++aarch64*-*-darwin*) ++ extra_parts="$extra_parts crtfastmath.o" ++ 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" ++ md_unwind_header=aarch64/aarch64-unwind.h ++ if test x$off_stack_trampolines = xyes; then ++ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" ++ fi ++ ;; + aarch64*-*-freebsd*) + extra_parts="$extra_parts crtfastmath.o" + tmake_file="${tmake_file} ${cpu_type}/t-aarch64" +@@ -408,6 +458,9 @@ aarch64*-*-linux*) + tmake_file="${tmake_file} ${cpu_type}/t-aarch64" + tmake_file="${tmake_file} ${cpu_type}/t-lse t-slibgcc-libgcc" + tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp t-crtfm" ++ if test x$off_stack_trampolines = xyes; then ++ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" ++ fi + ;; + aarch64*-*-vxworks7*) + extra_parts="$extra_parts crtfastmath.o" +@@ -701,12 +754,17 @@ 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 libd10-uwfef.a " + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" + ;; + 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 libd10-uwfef.a " + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" ++ if test x$off_stack_trampolines = xyes; then ++ tmake_file="${tmake_file} i386/t-heap-trampoline" ++ fi + ;; + 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" +@@ -773,6 +831,9 @@ 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 ++ if test x$off_stack_trampolines = xyes; then ++ tmake_file="${tmake_file} i386/t-heap-trampoline" ++ fi + ;; + x86_64-*-kfreebsd*-gnu) + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" +@@ -1169,12 +1230,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 +new file mode 100644 +index 00000000000..c8b83681ed7 +--- /dev/null ++++ b/libgcc/config/aarch64/heap-trampoline.c +@@ -0,0 +1,172 @@ ++/* Copyright The GNU Toolchain Authors. */ ++ ++#include <unistd.h> ++#include <sys/mman.h> ++#include <stdint.h> ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#if __APPLE__ ++/* For pthread_jit_write_protect_np */ ++#include <pthread.h> ++#endif ++ ++void *allocate_trampoline_page (void); ++int get_trampolines_per_page (void); ++struct tramp_ctrl_data *allocate_tramp_ctrl (struct tramp_ctrl_data *parent); ++void *allocate_trampoline_page (void); ++ ++void __builtin_nested_func_ptr_created (void *chain, void *func, void **dst); ++void __builtin_nested_func_ptr_deleted (void); ++ ++#if defined(__gnu_linux__) ++static const uint32_t aarch64_trampoline_insns[] = { ++ 0xd503245f, /* hint 34 */ ++ 0x580000b1, /* ldr x17, .+20 */ ++ 0x580000d2, /* ldr x18, .+24 */ ++ 0xd61f0220, /* br x17 */ ++ 0xd5033f9f, /* dsb sy */ ++ 0xd5033fdf /* isb */ ++}; ++ ++#elif __APPLE__ ++static const uint32_t aarch64_trampoline_insns[] = { ++ 0xd503245f, /* hint 34 */ ++ 0x580000b1, /* ldr x17, .+20 */ ++ 0x580000d0, /* ldr x16, .+24 */ ++ 0xd61f0220, /* br x17 */ ++ 0xd5033f9f, /* dsb sy */ ++ 0xd5033fdf /* isb */ ++}; ++ ++#else ++#error "Unsupported AArch64 platform for heap trampolines" ++#endif ++ ++struct aarch64_trampoline { ++ uint32_t insns[6]; ++ void *func_ptr; ++ void *chain_ptr; ++}; ++ ++struct tramp_ctrl_data ++{ ++ struct tramp_ctrl_data *prev; ++ ++ int free_trampolines; ++ ++ /* This will be pointing to an executable mmap'ed page. */ ++ struct aarch64_trampoline *trampolines; ++}; ++ ++int ++get_trampolines_per_page (void) ++{ ++ return getpagesize() / sizeof(struct aarch64_trampoline); ++} ++ ++static _Thread_local struct tramp_ctrl_data *tramp_ctrl_curr = NULL; ++ ++void * ++allocate_trampoline_page (void) ++{ ++ void *page; ++ ++#if defined(__gnu_linux__) ++ page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, ++ MAP_ANON | MAP_PRIVATE, 0, 0); ++#elif __APPLE__ ++ page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, ++ MAP_ANON | MAP_PRIVATE | MAP_JIT, 0, 0); ++#else ++ page = MAP_FAILED; ++#endif ++ ++ return page; ++} ++ ++struct tramp_ctrl_data * ++allocate_tramp_ctrl (struct tramp_ctrl_data *parent) ++{ ++ struct tramp_ctrl_data *p = malloc (sizeof (struct tramp_ctrl_data)); ++ if (p == NULL) ++ return NULL; ++ ++ p->trampolines = allocate_trampoline_page (); ++ ++ if (p->trampolines == MAP_FAILED) ++ return NULL; ++ ++ p->prev = parent; ++ p->free_trampolines = get_trampolines_per_page(); ++ ++ return p; ++} ++ ++void ++__builtin_nested_func_ptr_created (void *chain, void *func, void **dst) ++{ ++ if (tramp_ctrl_curr == NULL) ++ { ++ tramp_ctrl_curr = allocate_tramp_ctrl (NULL); ++ if (tramp_ctrl_curr == NULL) ++ abort (); ++ } ++ ++ if (tramp_ctrl_curr->free_trampolines == 0) ++ { ++ void *tramp_ctrl = allocate_tramp_ctrl (tramp_ctrl_curr); ++ if (!tramp_ctrl) ++ abort (); ++ ++ tramp_ctrl_curr = tramp_ctrl; ++ } ++ ++ struct aarch64_trampoline *trampoline ++ = &tramp_ctrl_curr->trampolines[get_trampolines_per_page () ++ - tramp_ctrl_curr->free_trampolines]; ++ ++#if __APPLE__ ++ /* Disable write protection for the MAP_JIT regions in this thread (see ++ https://developer.apple.com/documentation/apple-silicon/porting-just-in-time-compilers-to-apple-silicon) */ ++ pthread_jit_write_protect_np (0); ++#endif ++ ++ memcpy (trampoline->insns, aarch64_trampoline_insns, ++ sizeof(aarch64_trampoline_insns)); ++ trampoline->func_ptr = func; ++ trampoline->chain_ptr = chain; ++ ++#if __APPLE__ ++ /* Re-enable write protection. */ ++ pthread_jit_write_protect_np (1); ++#endif ++ ++ tramp_ctrl_curr->free_trampolines -= 1; ++ ++ __builtin___clear_cache ((void *)trampoline->insns, ++ ((void *)trampoline->insns + sizeof(trampoline->insns))); ++ ++ *dst = &trampoline->insns; ++} ++ ++void ++__builtin_nested_func_ptr_deleted (void) ++{ ++ if (tramp_ctrl_curr == NULL) ++ abort (); ++ ++ tramp_ctrl_curr->free_trampolines += 1; ++ ++ if (tramp_ctrl_curr->free_trampolines == get_trampolines_per_page ()) ++ { ++ if (tramp_ctrl_curr->prev == NULL) ++ return; ++ ++ munmap (tramp_ctrl_curr->trampolines, getpagesize()); ++ struct tramp_ctrl_data *prev = tramp_ctrl_curr->prev; ++ free (tramp_ctrl_curr); ++ tramp_ctrl_curr = prev; ++ } ++} +diff --git a/libgcc/config/aarch64/lse.S b/libgcc/config/aarch64/lse.S +index 9c29cf08b59..97b68c42cc1 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 + #endif + + /* Declare the symbol gating the LSE implementations. */ ++#if __ELF__ + .hidden __aarch64_have_lse_atomics ++#else ++ .private_extern __aarch64_have_lse_atomics ++#endif + + /* Turn size and memory model defines into mnemonic fragments. */ + #if SIZE == 1 +@@ -164,6 +168,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + #define BTI_C hint 34 + + /* Start and end a function. */ ++#if __ELF__ + .macro STARTFN name + .text + .balign 16 +@@ -187,6 +192,29 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + cbz w(tmp0), \label + .endm + ++#else ++.macro STARTFN name ++ .text ++ .balign 16 ++ .private_extern _\name ++ .cfi_startproc ++_\name: ++ BTI_C ++.endm ++ ++.macro ENDFN name ++ .cfi_endproc ++.endm ++ ++/* Branch to LABEL if LSE is disabled. */ ++.macro JUMP_IF_NOT_LSE label ++ adrp x(tmp0), ___aarch64_have_lse_atomics@PAGE ++ ldrb w(tmp0), [x(tmp0), ___aarch64_have_lse_atomics@PAGEOFF] ++ cbz w(tmp0), \label ++.endm ++ ++#endif ++ + #ifdef L_cas + + STARTFN NAME(cas) +diff --git a/libgcc/config/aarch64/sfp-machine.h b/libgcc/config/aarch64/sfp-machine.h +index be9b42174c4..5dc1827ee3a 100644 +--- a/libgcc/config/aarch64/sfp-machine.h ++++ b/libgcc/config/aarch64/sfp-machine.h +@@ -122,6 +122,27 @@ void __sfp_handle_exceptions (int); + + + /* Define ALIASNAME as a strong alias for NAME. */ ++#if defined __APPLE__ ++/* Mach-O doesn't support aliasing, so we build a secondary function for ++ the alias - we need to do a bit of a dance to find out what the type of ++ the arguments is and then apply that to the secondary function. ++ If these functions ever return anything but CMPtype we need to revisit ++ this... */ ++typedef float alias_HFtype __attribute__ ((mode (HF))); ++typedef float alias_SFtype __attribute__ ((mode (SF))); ++typedef float alias_DFtype __attribute__ ((mode (DF))); ++typedef float alias_TFtype __attribute__ ((mode (TF))); ++#define ALIAS_SELECTOR \ ++ CMPtype (*) (alias_HFtype, alias_HFtype): (alias_HFtype) 0, \ ++ CMPtype (*) (alias_SFtype, alias_SFtype): (alias_SFtype) 0, \ ++ CMPtype (*) (alias_DFtype, alias_DFtype): (alias_DFtype) 0, \ ++ CMPtype (*) (alias_TFtype, alias_TFtype): (alias_TFtype) 0 ++#define strong_alias(name, aliasname) \ ++ CMPtype aliasname (__typeof (_Generic (name, ALIAS_SELECTOR)) a, \ ++ __typeof (_Generic (name, ALIAS_SELECTOR)) b) \ ++ { return name (a, b); } ++#else + # define strong_alias(name, aliasname) _strong_alias(name, aliasname) + # 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 +new file mode 100644 +index 00000000000..f6ecda7b608 +--- /dev/null ++++ b/libgcc/config/aarch64/t-darwin +@@ -0,0 +1,7 @@ ++# Ensure we have a suitable minimum OS version. ++ ++HOST_LIBGCC2_CFLAGS += -mmacosx-version-min=11.0 ++ ++LIB2_SIDITI_CONV_FUNCS = yes ++ ++BUILD_LIBGCCS1 = +diff --git a/libgcc/config/aarch64/t-heap-trampoline b/libgcc/config/aarch64/t-heap-trampoline +new file mode 100644 +index 00000000000..3f70c2cd0c0 +--- /dev/null ++++ b/libgcc/config/aarch64/t-heap-trampoline +@@ -0,0 +1,20 @@ ++# Copyright The GNU Toolchain Authors. ++ ++# This file is part of GCC. ++# ++# GCC 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 3, or (at your option) ++# any later version. ++# ++# GCC 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 GCC; see the file COPYING3. If not see ++# <http://www.gnu.org/licenses/>. ++ ++LIB2ADD += $(srcdir)/config/aarch64/heap-trampoline.c ++HOST_LIBGCC2_CFLAGS += -mmacosx-version-min=11.0 +diff --git a/libgcc/config/i386/heap-trampoline.c b/libgcc/config/i386/heap-trampoline.c +new file mode 100644 +index 00000000000..96e13bf828e +--- /dev/null ++++ b/libgcc/config/i386/heap-trampoline.c +@@ -0,0 +1,172 @@ ++/* Copyright The GNU Toolchain Authors. */ ++ ++#include <unistd.h> ++#include <sys/mman.h> ++#include <stdint.h> ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#if __APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 ++/* For pthread_jit_write_protect_np */ ++#include <pthread.h> ++#endif ++ ++void *allocate_trampoline_page (void); ++int get_trampolines_per_page (void); ++struct tramp_ctrl_data *allocate_tramp_ctrl (struct tramp_ctrl_data *parent); ++void *allocate_trampoline_page (void); ++ ++void __builtin_nested_func_ptr_created (void *chain, void *func, void **dst); ++void __builtin_nested_func_ptr_deleted (void); ++ ++static const uint8_t trampoline_insns[] = { ++ /* movabs $<chain>,%r11 */ ++ 0x49, 0xbb, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ ++ /* movabs $<func>,%r10 */ ++ 0x49, 0xba, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ ++ /* rex.WB jmpq *%r11 */ ++ 0x41, 0xff, 0xe3 ++}; ++ ++union ix86_trampoline { ++ uint8_t insns[sizeof(trampoline_insns)]; ++ ++ struct __attribute__((packed)) fields { ++ uint8_t insn_0[2]; ++ void *func_ptr; ++ uint8_t insn_1[2]; ++ void *chain_ptr; ++ uint8_t insn_2[3]; ++ } fields; ++}; ++ ++struct tramp_ctrl_data ++{ ++ struct tramp_ctrl_data *prev; ++ ++ int free_trampolines; ++ ++ /* This will be pointing to an executable mmap'ed page. */ ++ union ix86_trampoline *trampolines; ++}; ++ ++int ++get_trampolines_per_page (void) ++{ ++ return getpagesize() / sizeof(union ix86_trampoline); ++} ++ ++static _Thread_local struct tramp_ctrl_data *tramp_ctrl_curr = NULL; ++ ++void * ++allocate_trampoline_page (void) ++{ ++ void *page; ++ ++#if defined(__gnu_linux__) ++ page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, ++ MAP_ANON | MAP_PRIVATE, 0, 0); ++#elif __APPLE__ ++# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 ++ page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, ++ MAP_ANON | MAP_PRIVATE | MAP_JIT, 0, 0); ++# else ++ page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, ++ MAP_ANON | MAP_PRIVATE, 0, 0); ++# endif ++#else ++ page = MAP_FAILED; ++#endif ++ ++ return page; ++} ++ ++struct tramp_ctrl_data * ++allocate_tramp_ctrl (struct tramp_ctrl_data *parent) ++{ ++ struct tramp_ctrl_data *p = malloc (sizeof (struct tramp_ctrl_data)); ++ if (p == NULL) ++ return NULL; ++ ++ p->trampolines = allocate_trampoline_page (); ++ ++ if (p->trampolines == MAP_FAILED) ++ return NULL; ++ ++ p->prev = parent; ++ p->free_trampolines = get_trampolines_per_page(); ++ ++ return p; ++} ++ ++void ++__builtin_nested_func_ptr_created (void *chain, void *func, void **dst) ++{ ++ if (tramp_ctrl_curr == NULL) ++ { ++ tramp_ctrl_curr = allocate_tramp_ctrl (NULL); ++ if (tramp_ctrl_curr == NULL) ++ abort (); ++ } ++ ++ if (tramp_ctrl_curr->free_trampolines == 0) ++ { ++ void *tramp_ctrl = allocate_tramp_ctrl (tramp_ctrl_curr); ++ if (!tramp_ctrl) ++ abort (); ++ ++ tramp_ctrl_curr = tramp_ctrl; ++ } ++ ++ union ix86_trampoline *trampoline ++ = &tramp_ctrl_curr->trampolines[get_trampolines_per_page () ++ - tramp_ctrl_curr->free_trampolines]; ++ ++#if __APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 ++ /* Disable write protection for the MAP_JIT regions in this thread (see ++ https://developer.apple.com/documentation/apple-silicon/porting-just-in-time-compilers-to-apple-silicon) */ ++ pthread_jit_write_protect_np (0); ++#endif ++ ++ memcpy (trampoline->insns, trampoline_insns, ++ sizeof(trampoline_insns)); ++ trampoline->fields.func_ptr = func; ++ trampoline->fields.chain_ptr = chain; ++ ++#if __APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 ++ /* Re-enable write protection. */ ++ pthread_jit_write_protect_np (1); ++#endif ++ ++ tramp_ctrl_curr->free_trampolines -= 1; ++ ++ __builtin___clear_cache ((void *)trampoline->insns, ++ ((void *)trampoline->insns + sizeof(trampoline->insns))); ++ ++ *dst = &trampoline->insns; ++} ++ ++void ++__builtin_nested_func_ptr_deleted (void) ++{ ++ if (tramp_ctrl_curr == NULL) ++ abort (); ++ ++ tramp_ctrl_curr->free_trampolines += 1; ++ ++ if (tramp_ctrl_curr->free_trampolines == get_trampolines_per_page ()) ++ { ++ if (tramp_ctrl_curr->prev == NULL) ++ return; ++ ++ munmap (tramp_ctrl_curr->trampolines, getpagesize()); ++ struct tramp_ctrl_data *prev = tramp_ctrl_curr->prev; ++ free (tramp_ctrl_curr); ++ tramp_ctrl_curr = prev; ++ } ++} +diff --git a/libgcc/config/i386/t-heap-trampoline b/libgcc/config/i386/t-heap-trampoline +new file mode 100644 +index 00000000000..76f438d9529 +--- /dev/null ++++ b/libgcc/config/i386/t-heap-trampoline +@@ -0,0 +1,20 @@ ++# Copyright The GNU Toolchain Authors. ++ ++# This file is part of GCC. ++# ++# GCC 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 3, or (at your option) ++# any later version. ++# ++# GCC 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 GCC; see the file COPYING3. If not see ++# <http://www.gnu.org/licenses/>. ++ ++LIB2ADD += $(srcdir)/config/i386/heap-trampoline.c ++HOST_LIBGCC2_CFLAGS += -mmacosx-version-min=10.8 +diff --git a/libgcc/config/t-darwin b/libgcc/config/t-darwin +index 299d26c2c96..a708583d965 100644 +--- a/libgcc/config/t-darwin ++++ b/libgcc/config/t-darwin +@@ -1,15 +1,15 @@ + # Set this as a minimum (unless overriden by arch t-files) since it's a + # reasonable lowest common denominator that works for all our archs. +-HOST_LIBGCC2_CFLAGS += -mmacosx-version-min=10.4 ++HOST_LIBGCC2_CFLAGS += $(DARWIN_MIN_LIB_VERSION) + + crt3.o: $(srcdir)/config/darwin-crt3.c +- $(crt_compile) -mmacosx-version-min=10.4 -c $< ++ $(crt_compile) $(DARWIN_MIN_CRT_VERSION) -c $< + + crttms.o: $(srcdir)/config/darwin-crt-tm.c +- $(crt_compile) -mmacosx-version-min=10.4 -DSTART -c $< ++ $(crt_compile) $(DARWIN_MIN_CRT_VERSION) -DSTART -c $< + + crttme.o: $(srcdir)/config/darwin-crt-tm.c +- $(crt_compile) -mmacosx-version-min=10.4 -DEND -c $< ++ $(crt_compile) $(DARWIN_MIN_CRT_VERSION) -DEND -c $< + + # Make emutls weak so that we can deal with -static-libgcc, override the + # hidden visibility when this is present in libgcc_eh. +@@ -24,7 +24,8 @@ libemutls_w.a: emutls_s.o + $(AR_CREATE_FOR_TARGET) $@ $< + $(RANLIB_FOR_TARGET) $@ + +-# Patch to __Unwind_Find_Enclosing_Function for Darwin10. ++# This has to be built for 10.6, even if the toolchain will not target that ++# version + d10-uwfef.o: $(srcdir)/config/darwin10-unwind-find-enc-func.c libgcc_tm.h + $(crt_compile) -mmacosx-version-min=10.6 -c $< + +diff --git a/libgcc/config/t-darwin-min-1 b/libgcc/config/t-darwin-min-1 +new file mode 100644 +index 00000000000..8c2cf8acd39 +--- /dev/null ++++ b/libgcc/config/t-darwin-min-1 +@@ -0,0 +1,3 @@ ++# Support building with -mmacosx-version-min back to 10.1. ++DARWIN_MIN_LIB_VERSION = -mmacosx-version-min=10.4 ++DARWIN_MIN_CRT_VERSION = -mmacosx-version-min=10.1 +diff --git a/libgcc/config/t-darwin-min-4 b/libgcc/config/t-darwin-min-4 +new file mode 100644 +index 00000000000..04e980de4d5 +--- /dev/null ++++ b/libgcc/config/t-darwin-min-4 +@@ -0,0 +1,3 @@ ++# Support building with -mmacosx-version-min back to 10.4. ++DARWIN_MIN_LIB_VERSION = -mmacosx-version-min=10.4 ++DARWIN_MIN_CRT_VERSION = -mmacosx-version-min=10.4 +diff --git a/libgcc/config/t-darwin-min-5 b/libgcc/config/t-darwin-min-5 +new file mode 100644 +index 00000000000..138193151e7 +--- /dev/null ++++ b/libgcc/config/t-darwin-min-5 +@@ -0,0 +1,3 @@ ++# Support building with -mmacosx-version-min back to 10.5. ++DARWIN_MIN_LIB_VERSION = -mmacosx-version-min=10.5 ++DARWIN_MIN_CRT_VERSION = -mmacosx-version-min=10.5 +diff --git a/libgcc/config/t-darwin-min-8 b/libgcc/config/t-darwin-min-8 +new file mode 100644 +index 00000000000..9efc9dc0257 +--- /dev/null ++++ b/libgcc/config/t-darwin-min-8 +@@ -0,0 +1,3 @@ ++# Support building with -mmacosx-version-min back to 10.8. ++DARWIN_MIN_LIB_VERSION = -mmacosx-version-min=10.8 ++DARWIN_MIN_CRT_VERSION = -mmacosx-version-min=10.8 +diff --git a/libgcc/config/t-darwin-rpath b/libgcc/config/t-darwin-rpath +new file mode 100644 +index 00000000000..951539de7aa +--- /dev/null ++++ b/libgcc/config/t-darwin-rpath +@@ -0,0 +1,5 @@ ++# Use @rpath and add a search path to exes and dylibs that depend on this. ++SHLIB_RPATH = @rpath ++ ++# Enable the libgcc_s.1.dylib compatibility lib to find the dependent 1.1.dylib. ++SHLIB_LOADER_PATH = -Wl,-rpath,@loader_path +diff --git a/libgcc/config/t-slibgcc-darwin b/libgcc/config/t-slibgcc-darwin +index a8f69666a82..ee449de32e6 100644 +--- a/libgcc/config/t-slibgcc-darwin ++++ b/libgcc/config/t-slibgcc-darwin +@@ -1,4 +1,4 @@ +-# Build a shared libgcc library with the darwin linker. ++# Build a shared libgcc library able to use embedded runpaths. + + SHLIB_SOVERSION = 1.1 + SHLIB_SO_MINVERSION = 1 +@@ -6,7 +6,6 @@ SHLIB_VERSTRING = -compatibility_version $(SHLIB_SO_MINVERSION) \ + -current_version $(SHLIB_SOVERSION) + SHLIB_EXT = .dylib + SHLIB_LC = -lSystem +-SHLIB_INSTALL_DIR = $(slibdir) + + SHLIB_MKMAP = $(srcdir)/mkmap-flat.awk + SHLIB_MKMAP_OPTS = -v leading_underscore=1 +@@ -23,11 +22,20 @@ SHLIB_SONAME = @shlib_base_name@$(SHLIB_EXT) + # subdir. The code under MULTIBUILDTOP combines these into a single FAT + # library, that is what we eventually install. + ++# When enable_darwin_at_rpath is true, use @rpath instead of $(slibdir) for ++# this and dylibs that depend on this. So this def must come first and be ++# overridden in a make fragment that depends on the rpath setting. ++SHLIB_RPATH = $(slibdir) ++ ++# Likewise, we only want to add an @loader_path to the shared libs when ++# we have enable_darwin_at_rpath. ++SHLIB_LOADER_PATH = ++ + SHLIB_LINK = $(CC) $(LIBGCC2_CFLAGS) $(LDFLAGS) -dynamiclib -nodefaultlibs \ +- -install_name $(SHLIB_INSTALL_DIR)/$(SHLIB_INSTALL_NAME) \ ++ -install_name $(SHLIB_RPATH)/$(SHLIB_INSTALL_NAME) \ + -single_module -o $(SHLIB_DIR)/$(SHLIB_SONAME) \ + -Wl,-exported_symbols_list,$(SHLIB_MAP) \ +- $(SHLIB_VERSTRING) \ ++ $(SHLIB_VERSTRING) -nodefaultrpaths \ + @multilib_flags@ @shlib_objs@ $(SHLIB_LC) + + # we do our own thing +@@ -63,9 +71,9 @@ EHS_INSTNAME = libgcc_ehs.$(SHLIB_SOVERSION)$(SHLIB_EXT) + libgcc_ehs$(SHLIB_EXT): $(LIBEHSOBJS) $(extra-parts) + mkdir -p $(MULTIDIR) + $(CC) $(LIBGCC2_CFLAGS) $(LDFLAGS) -dynamiclib -nodefaultlibs \ +- -install_name $(SHLIB_INSTALL_DIR)/$(EHS_INSTNAME) \ ++ -install_name $(SHLIB_RPATH)/$(EHS_INSTNAME) \ + -o $(MULTIDIR)/libgcc_ehs$(SHLIB_EXT) $(SHLIB_VERSTRING) \ +- $(LIBEHSOBJS) $(SHLIB_LC) ++ -nodefaultrpaths $(LIBEHSOBJS) $(SHLIB_LC) + + all: libgcc_ehs$(SHLIB_EXT) + +@@ -121,12 +129,13 @@ libgcc_s.1.dylib: all-multi libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT) \ + cp ../$${mlib}/libgcc/$${mlib}/libgcc_ehs$(SHLIB_EXT) \ + ./libgcc_ehs.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} || exit 1 ; \ + arch=`$(LIPO) -info libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} | sed -e 's/.*:\ //'` ; \ +- $(CC) -arch $${arch} -nodefaultlibs -dynamiclib \ ++ $(CC) -arch $${arch} -nodefaultlibs -dynamiclib -nodefaultrpaths \ ++ $(SHLIB_LOADER_PATH) \ + -o libgcc_s.1$(SHLIB_EXT)_T_$${mlib} \ + -Wl,-reexport_library,libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} \ + -Wl,-reexport_library,libgcc_ehs.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} \ +- -install_name $(SHLIB_INSTALL_DIR)/libgcc_s.1.dylib \ +- -compatibility_version 1 -current_version 1 ; \ ++ -install_name $(SHLIB_RPATH)/libgcc_s.1.dylib \ ++ -compatibility_version 1 -current_version 1.1 ; \ + done + $(LIPO) -output libgcc_s.1$(SHLIB_EXT) -create libgcc_s.1$(SHLIB_EXT)_T* + rm libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T* +@@ -140,13 +149,14 @@ libgcc_s.1.dylib: all-multi libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT) + cp ../$${mlib}/libgcc/$${mlib}/libgcc_s$(SHLIB_EXT) \ + ./libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} || exit 1 ; \ + arch=`$(LIPO) -info libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} | sed -e 's/.*:\ //'` ; \ +- $(CC) -arch $${arch} -nodefaultlibs -dynamiclib \ ++ $(CC) -arch $${arch} -nodefaultlibs -dynamiclib -nodefaultrpaths \ ++ $(SHLIB_LOADER_PATH) \ + -o libgcc_s.1$(SHLIB_EXT)_T_$${mlib} \ + -Wl,-reexport_library,libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T_$${mlib} \ + -lSystem \ + -Wl,-reexported_symbols_list,$(srcdir)/config/darwin-unwind.ver \ +- -install_name $(SHLIB_INSTALL_DIR)/libgcc_s.1.dylib \ +- -compatibility_version 1 -current_version 1 ; \ ++ -install_name $(SHLIB_RPATH)/libgcc_s.1.dylib \ ++ -compatibility_version 1 -current_version 1.1 ; \ + 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/configure b/libgcc/configure +index 1f9b2ac578b..a5c228bc3a1 100755 +--- a/libgcc/configure ++++ b/libgcc/configure +@@ -630,7 +630,6 @@ LIPO + AR + toolexeclibdir + toolexecdir +-enable_gcov + target_subdir + host_subdir + build_subdir +@@ -654,6 +653,8 @@ build_cpu + build + with_aix_soname + enable_vtable_verify ++enable_gcov ++off_stack_trampolines + enable_shared + libgcc_topdir + target_alias +@@ -701,6 +702,8 @@ with_target_subdir + with_cross_host + with_ld + enable_shared ++enable_off_stack_trampolines ++enable_gcov + enable_vtable_verify + with_aix_soname + enable_version_specific_runtime_libs +@@ -708,7 +711,6 @@ with_toolexeclibdir + with_slibdir + enable_maintainer_mode + with_build_libsubdir +-enable_gcov + enable_largefile + enable_decimal_float + with_system_libunwind +@@ -1342,12 +1344,15 @@ Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-shared don't provide a shared libgcc ++ --enable-off-stack-trampolines ++ Specify whether to support generating off-stack trampolines ++ ++ --disable-gcov don't provide libgcov and related host tools + --enable-vtable-verify Enable vtable verification feature + --enable-version-specific-runtime-libs Specify that runtime libraries should be installed in a compiler-specific directory + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +- --disable-gcov don't provide libgcov and related host tools + --disable-largefile omit support for large files + --enable-decimal-float={no,yes,bid,dpd} + enable decimal float extension to C. Selecting 'bid' +@@ -2252,6 +2257,48 @@ fi + + + ++# Check whether --enable-off-stack-trampolines was given. ++if test "${enable_off_stack_trampolines+set}" = set; then : ++ enableval=$enable_off_stack_trampolines; ++case "$target" in ++ x86_64-*-linux* | x86_64-*-darwin1[4-9]* | x86_64-*-darwin2*) ++ off_stack_trampolines=$enableval ++ ;; ++ aarch64*-*-linux* ) ++ off_stack_trampolines=$enableval ++ ;; ++ aarch64*-*darwin* ) ++ off_stack_trampolines=$enableval ++ ;; ++ *) ++ as_fn_error $? "Configure option --enable-off-stack-trampolines is not supported \ ++for this platform" "$LINENO" 5 ++ off_stack_trampolines=no ++ ;; ++esac ++else ++ ++case "$target" in ++ *-*-darwin2*) ++ off_stack_trampolines=yes ++ ;; ++ *) ++ off_stack_trampolines=no ++ ;; ++esac ++fi ++ ++ ++ ++# Check whether --enable-gcov was given. ++if test "${enable_gcov+set}" = set; then : ++ enableval=$enable_gcov; ++else ++ enable_gcov=yes ++fi ++ ++ ++ + # Check whether --enable-vtable-verify was given. + if test "${enable_vtable_verify+set}" = set; then : + enableval=$enable_vtable_verify; case "$enableval" in +diff --git a/libgcc/configure.ac b/libgcc/configure.ac +index 2fc9d5d7c93..7d11bf00142 100644 +--- a/libgcc/configure.ac ++++ b/libgcc/configure.ac +@@ -68,6 +68,40 @@ AC_ARG_ENABLE(shared, + ], [enable_shared=yes]) + AC_SUBST(enable_shared) + ++AC_ARG_ENABLE([off-stack-trampolines], ++ [AS_HELP_STRING([--enable-off-stack-trampolines] ++ [Specify whether to support generating off-stack trampolines])],[ ++case "$target" in ++ x86_64-*-linux* | x86_64-*-darwin1[[4-9]]* | x86_64-*-darwin2*) ++ off_stack_trampolines=$enableval ++ ;; ++ aarch64*-*-linux* ) ++ off_stack_trampolines=$enableval ++ ;; ++ aarch64*-*darwin* ) ++ off_stack_trampolines=$enableval ++ ;; ++ *) ++ AC_MSG_ERROR([Configure option --enable-off-stack-trampolines is not supported \ ++for this platform]) ++ off_stack_trampolines=no ++ ;; ++esac],[ ++case "$target" in ++ *-*-darwin2*) ++ off_stack_trampolines=yes ++ ;; ++ *) ++ off_stack_trampolines=no ++ ;; ++esac]) ++AC_SUBST(off_stack_trampolines) ++ ++AC_ARG_ENABLE(gcov, ++[ --disable-gcov don't provide libgcov and related host tools], ++[], [enable_gcov=yes]) ++AC_SUBST(enable_gcov) ++ + AC_ARG_ENABLE(vtable-verify, + [ --enable-vtable-verify Enable vtable verification feature ], + [case "$enableval" in +diff --git a/libgcc/libgcc-std.ver.in b/libgcc/libgcc-std.ver.in +index 513ddd0bd0d..fc0b4052a3b 100644 +--- a/libgcc/libgcc-std.ver.in ++++ b/libgcc/libgcc-std.ver.in +@@ -1943,4 +1943,7 @@ GCC_4.8.0 { + GCC_7.0.0 { + __PFX__divmoddi4 + __PFX__divmodti4 ++ ++ __builtin_nested_func_ptr_created ++ __builtin_nested_func_ptr_deleted + } +diff --git a/libgcc/libgcc2.h b/libgcc/libgcc2.h +index fc24ac34502..536e517b62f 100644 +--- a/libgcc/libgcc2.h ++++ b/libgcc/libgcc2.h +@@ -29,6 +29,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + #pragma GCC visibility push(default) + #endif + ++extern void __builtin_nested_func_ptr_created (void *, void *, void **); ++extern void __builtin_nested_func_ptr_deleted (void); ++ + extern int __gcc_bcmp (const unsigned char *, const unsigned char *, size_t); + 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 5ce0cd7cd05..2073bf6c5ef 100644 +--- a/libgfortran/Makefile.am ++++ b/libgfortran/Makefile.am +@@ -37,6 +37,11 @@ else + version_arg = + version_dep = + endif ++extra_darwin_ldflags_libgfortran = @extra_ldflags_libgfortran@ ++if ENABLE_DARWIN_AT_RPATH ++extra_darwin_ldflags_libgfortran += -Wc,-nodefaultrpaths ++extra_darwin_ldflags_libgfortran += -Wl,-rpath,@loader_path ++endif + + gfor_c_HEADERS = ISO_Fortran_binding.h + gfor_cdir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include +@@ -50,7 +55,7 @@ libgfortran_la_LINK = $(LINK) $(libgfortran_la_LDFLAGS) + libgfortran_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ + $(LTLDFLAGS) $(LIBQUADLIB) ../libbacktrace/libbacktrace.la \ + $(HWCAP_LDFLAGS) \ +- $(LIBM) $(extra_ldflags_libgfortran) \ ++ $(LIBM) $(extra_darwin_ldflags_libgfortran) \ + $(version_arg) -Wc,-shared-libgcc + libgfortran_la_DEPENDENCIES = $(version_dep) libgfortran.spec $(LIBQUADLIB_DEP) + +diff --git a/libgfortran/Makefile.in b/libgfortran/Makefile.in +index 7ac6bfba657..52dd5f1819e 100644 +--- a/libgfortran/Makefile.in ++++ b/libgfortran/Makefile.in +@@ -91,8 +91,10 @@ POST_UNINSTALL = : + build_triplet = @build@ + host_triplet = @host@ + target_triplet = @target@ +-@LIBGFOR_MINIMAL_TRUE@am__append_1 = -DLIBGFOR_MINIMAL +-@LIBGFOR_MINIMAL_FALSE@am__append_2 = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@am__append_1 = -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++@LIBGFOR_MINIMAL_TRUE@am__append_2 = -DLIBGFOR_MINIMAL ++@LIBGFOR_MINIMAL_FALSE@am__append_3 = \ + @LIBGFOR_MINIMAL_FALSE@io/close.c \ + @LIBGFOR_MINIMAL_FALSE@io/file_pos.c \ + @LIBGFOR_MINIMAL_FALSE@io/format.c \ +@@ -110,7 +112,7 @@ target_triplet = @target@ + @LIBGFOR_MINIMAL_FALSE@io/fbuf.c \ + @LIBGFOR_MINIMAL_FALSE@io/async.c + +-@LIBGFOR_MINIMAL_FALSE@am__append_3 = \ ++@LIBGFOR_MINIMAL_FALSE@am__append_4 = \ + @LIBGFOR_MINIMAL_FALSE@intrinsics/access.c \ + @LIBGFOR_MINIMAL_FALSE@intrinsics/c99_functions.c \ + @LIBGFOR_MINIMAL_FALSE@intrinsics/chdir.c \ +@@ -143,9 +145,9 @@ target_triplet = @target@ + @LIBGFOR_MINIMAL_FALSE@intrinsics/umask.c \ + @LIBGFOR_MINIMAL_FALSE@intrinsics/unlink.c + +-@IEEE_SUPPORT_TRUE@am__append_4 = ieee/ieee_helper.c +-@LIBGFOR_MINIMAL_TRUE@am__append_5 = runtime/minimal.c +-@LIBGFOR_MINIMAL_FALSE@am__append_6 = \ ++@IEEE_SUPPORT_TRUE@am__append_5 = ieee/ieee_helper.c ++@LIBGFOR_MINIMAL_TRUE@am__append_6 = runtime/minimal.c ++@LIBGFOR_MINIMAL_FALSE@am__append_7 = \ + @LIBGFOR_MINIMAL_FALSE@runtime/backtrace.c \ + @LIBGFOR_MINIMAL_FALSE@runtime/convert_char.c \ + @LIBGFOR_MINIMAL_FALSE@runtime/environ.c \ +@@ -157,7 +159,7 @@ target_triplet = @target@ + + + # dummy sources for libtool +-@onestep_TRUE@am__append_7 = libgfortran_c.c libgfortran_f.f90 ++@onestep_TRUE@am__append_8 = libgfortran_c.c libgfortran_f.f90 + subdir = . + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \ +@@ -589,7 +591,7 @@ AMTAR = @AMTAR@ + + # Some targets require additional compiler options for IEEE compatibility. + AM_CFLAGS = @AM_CFLAGS@ -fcx-fortran-rules $(SECTION_FLAGS) \ +- $(IEEE_FLAGS) $(am__append_1) ++ $(IEEE_FLAGS) $(am__append_2) + AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ + AM_FCFLAGS = @AM_FCFLAGS@ $(IEEE_FLAGS) + AR = @AR@ +@@ -748,6 +750,8 @@ gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER) + @LIBGFOR_USE_SYMVER_FALSE@version_dep = + @LIBGFOR_USE_SYMVER_GNU_TRUE@@LIBGFOR_USE_SYMVER_TRUE@version_dep = gfortran.ver + @LIBGFOR_USE_SYMVER_SUN_TRUE@@LIBGFOR_USE_SYMVER_TRUE@version_dep = gfortran.ver-sun gfortran.ver ++extra_darwin_ldflags_libgfortran = @extra_ldflags_libgfortran@ \ ++ $(am__append_1) + gfor_c_HEADERS = ISO_Fortran_binding.h + gfor_cdir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include + LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS)) \ +@@ -759,7 +763,7 @@ libgfortran_la_LINK = $(LINK) $(libgfortran_la_LDFLAGS) + libgfortran_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ + $(LTLDFLAGS) $(LIBQUADLIB) ../libbacktrace/libbacktrace.la \ + $(HWCAP_LDFLAGS) \ +- $(LIBM) $(extra_ldflags_libgfortran) \ ++ $(LIBM) $(extra_darwin_ldflags_libgfortran) \ + $(version_arg) -Wc,-shared-libgcc + + libgfortran_la_DEPENDENCIES = $(version_dep) libgfortran.spec $(LIBQUADLIB_DEP) +@@ -780,7 +784,7 @@ AM_CPPFLAGS = -iquote$(srcdir)/io -I$(srcdir)/$(MULTISRCTOP)../gcc \ + -I$(MULTIBUILDTOP)../libbacktrace \ + -I../libbacktrace + +-gfor_io_src = io/size_from_kind.c $(am__append_2) ++gfor_io_src = io/size_from_kind.c $(am__append_3) + gfor_io_headers = \ + io/io.h \ + io/fbuf.h \ +@@ -802,7 +806,7 @@ gfor_helper_src = intrinsics/associated.c intrinsics/abort.c \ + intrinsics/selected_int_kind.f90 \ + intrinsics/selected_real_kind.f90 intrinsics/trigd.c \ + intrinsics/unpack_generic.c runtime/in_pack_generic.c \ +- runtime/in_unpack_generic.c $(am__append_3) $(am__append_4) ++ runtime/in_unpack_generic.c $(am__append_4) $(am__append_5) + @IEEE_SUPPORT_TRUE@gfor_ieee_helper_src = ieee/ieee_helper.c + @IEEE_SUPPORT_FALSE@gfor_ieee_src = + @IEEE_SUPPORT_TRUE@gfor_ieee_src = \ +@@ -811,8 +815,8 @@ gfor_helper_src = intrinsics/associated.c intrinsics/abort.c \ + @IEEE_SUPPORT_TRUE@ieee/ieee_features.F90 + + gfor_src = runtime/bounds.c runtime/compile_options.c runtime/memory.c \ +- runtime/string.c runtime/select.c $(am__append_5) \ +- $(am__append_6) ++ runtime/string.c runtime/select.c $(am__append_6) \ ++ $(am__append_7) + i_all_c = \ + $(srcdir)/generated/all_l1.c \ + $(srcdir)/generated/all_l2.c \ +@@ -1652,7 +1656,7 @@ intrinsics/random_init.f90 + + BUILT_SOURCES = $(gfor_built_src) $(gfor_built_specific_src) \ + $(gfor_built_specific2_src) $(gfor_misc_specifics) \ +- $(am__append_7) ++ $(am__append_8) + prereq_SRC = $(gfor_src) $(gfor_built_src) $(gfor_io_src) \ + $(gfor_helper_src) $(gfor_ieee_src) $(gfor_io_headers) $(gfor_specific_src) + +diff --git a/libgfortran/configure b/libgfortran/configure +index ae64dca3114..f288af81ff5 100755 +--- a/libgfortran/configure ++++ b/libgfortran/configure +@@ -655,6 +655,8 @@ extra_ldflags_libgfortran + ac_ct_FC + FCFLAGS + FC ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -824,6 +826,7 @@ enable_static + with_pic + enable_fast_install + enable_libtool_lock ++enable_darwin_at_rpath + enable_largefile + enable_libquadmath_support + with_gcc_major_version_only +@@ -1479,6 +1482,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --disable-largefile omit support for large files + --disable-libquadmath-support + disable libquadmath support for Fortran +@@ -10939,6 +10944,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10956,10 +11002,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12766,7 +12821,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12769 "configure" ++#line 12824 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12872,7 +12927,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12875 "configure" ++#line 12930 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13274,6 +13329,14 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + #AC_MSG_NOTICE([====== Finished libtool configuration]) ; sleep 10 + + # We need gfortran to compile parts of the library +@@ -14917,6 +14980,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_FC=no + hardcode_direct_FC=no + hardcode_automatic_FC=yes +@@ -14934,10 +15038,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs_FC=no +@@ -16190,9 +16303,10 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + # extra LD Flags which are required for targets ++extra_ldflags_libgfortran= + case "${host}" in +- *-darwin*) +- # Darwin needs -single_module when linking libgfortran ++ *-*-darwin[4567]*) ++ # Earlier Darwin needs -single_module when linking libgfortran + extra_ldflags_libgfortran=-Wl,-single_module + ;; + esac +@@ -28519,6 +28633,10 @@ if test -z "${HAVE_HWCAP_TRUE}" && test -z "${HAVE_HWCAP_FALSE}"; then + as_fn_error $? "conditional \"HAVE_HWCAP\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${LIBGFOR_BUILD_QUAD_TRUE}" && test -z "${LIBGFOR_BUILD_QUAD_FALSE}"; then + as_fn_error $? "conditional \"LIBGFOR_BUILD_QUAD\" 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 97cc490cb5e..a21f56648a2 100644 +--- a/libgfortran/configure.ac ++++ b/libgfortran/configure.ac +@@ -282,6 +282,7 @@ LT_LIB_M + ACX_LT_HOST_FLAGS + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + #AC_MSG_NOTICE([====== Finished libtool configuration]) ; sleep 10 + + # We need gfortran to compile parts of the library +@@ -290,9 +291,10 @@ FC="$GFORTRAN" + AC_PROG_FC(gfortran) + + # extra LD Flags which are required for targets ++extra_ldflags_libgfortran= + case "${host}" in +- *-darwin*) +- # Darwin needs -single_module when linking libgfortran ++ *-*-darwin[[4567]]*) ++ # Earlier Darwin needs -single_module when linking libgfortran + extra_ldflags_libgfortran=-Wl,-single_module + ;; + esac +diff --git a/libgo/configure b/libgo/configure +index ffe17c9be55..de5c1ac9b3d 100755 +--- a/libgo/configure ++++ b/libgo/configure +@@ -708,6 +708,8 @@ glibgo_toolexecdir + WERROR + WARN_FLAGS + CC_FOR_BUILD ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + CPP +@@ -11544,7 +11546,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11547 "configure" ++#line 11549 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11650,7 +11652,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11653 "configure" ++#line 11655 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13779,6 +13781,14 @@ CC="$lt_save_CC" + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + CC_FOR_BUILD=${CC_FOR_BUILD:-gcc} + +@@ -16321,6 +16331,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${USE_LIBFFI_TRUE}" && test -z "${USE_LIBFFI_FALSE}"; then + 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 7e2b98ba67c..7b0222bb620 100644 +--- a/libgo/configure.ac ++++ b/libgo/configure.ac +@@ -53,6 +53,7 @@ AC_LIBTOOL_DLOPEN + AM_PROG_LIBTOOL + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + CC_FOR_BUILD=${CC_FOR_BUILD:-gcc} + AC_SUBST(CC_FOR_BUILD) +diff --git a/libgomp/Makefile.am b/libgomp/Makefile.am +index f8b2a06d63e..81ba6c634fa 100644 +--- a/libgomp/Makefile.am ++++ b/libgomp/Makefile.am +@@ -53,9 +53,14 @@ else + libgomp_version_script = + libgomp_version_dep = + endif ++ + libgomp_version_info = -version-info $(libtool_VERSION) ++if ENABLE_DARWIN_AT_RPATH ++libgomp_darwin_rpath = -Wc,-nodefaultrpaths ++libgomp_darwin_rpath += -Wl,-rpath,@loader_path ++endif + libgomp_la_LDFLAGS = $(libgomp_version_info) $(libgomp_version_script) \ +- $(lt_host_flags) ++ $(lt_host_flags) $(libgomp_darwin_rpath) + libgomp_la_DEPENDENCIES = $(libgomp_version_dep) + libgomp_la_LINK = $(LINK) $(libgomp_la_LDFLAGS) + +diff --git a/libgomp/Makefile.in b/libgomp/Makefile.in +index 6f0cb716135..5cfb149c2ba 100644 +--- a/libgomp/Makefile.in ++++ b/libgomp/Makefile.in +@@ -546,8 +546,11 @@ nodist_toolexeclib_HEADERS = libgomp.spec + @LIBGOMP_BUILD_VERSIONED_SHLIB_GNU_TRUE@@LIBGOMP_BUILD_VERSIONED_SHLIB_TRUE@libgomp_version_dep = libgomp.ver + @LIBGOMP_BUILD_VERSIONED_SHLIB_SUN_TRUE@@LIBGOMP_BUILD_VERSIONED_SHLIB_TRUE@libgomp_version_dep = libgomp.ver-sun + libgomp_version_info = -version-info $(libtool_VERSION) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libgomp_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path + libgomp_la_LDFLAGS = $(libgomp_version_info) $(libgomp_version_script) \ +- $(lt_host_flags) ++ $(lt_host_flags) $(libgomp_darwin_rpath) + + libgomp_la_DEPENDENCIES = $(libgomp_version_dep) + libgomp_la_LINK = $(LINK) $(libgomp_la_LDFLAGS) +diff --git a/libgomp/configure b/libgomp/configure +index 85fdb4d3f48..71b5987dc9a 100755 +--- a/libgomp/configure ++++ b/libgomp/configure +@@ -692,6 +692,8 @@ FC + MAINT + MAINTAINER_MODE_FALSE + MAINTAINER_MODE_TRUE ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -832,6 +834,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_maintainer_mode + with_cuda_driver + with_cuda_driver_include +@@ -1493,6 +1496,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +@@ -9625,6 +9630,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9642,10 +9688,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11431,7 +11486,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11434 "configure" ++#line 11489 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11537,7 +11592,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11540 "configure" ++#line 11595 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11798,6 +11853,14 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +@@ -13473,6 +13536,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_FC=no + hardcode_direct_FC=no + hardcode_automatic_FC=yes +@@ -13490,10 +13594,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_FC="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_FC="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_FC="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_FC="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs_FC=no +@@ -17213,6 +17326,10 @@ if test -z "${BUILD_INFO_TRUE}" && test -z "${BUILD_INFO_FALSE}"; then + as_fn_error $? "conditional \"BUILD_INFO\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + 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 a9b1f3973f7..654fca1f445 100644 +--- a/libgomp/configure.ac ++++ b/libgomp/configure.ac +@@ -149,6 +149,7 @@ AM_PROG_LIBTOOL + ACX_LT_HOST_FLAGS + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + AM_MAINTAINER_MODE + +diff --git a/libiberty/aclocal.m4 b/libiberty/aclocal.m4 +index 3378316dced..1a00b771fe1 100644 +--- a/libiberty/aclocal.m4 ++++ b/libiberty/aclocal.m4 +@@ -12,10 +12,61 @@ + # PARTICULAR PURPOSE. + + m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) ++# AM_CONDITIONAL -*- Autoconf -*- ++ ++# Copyright (C) 1997-2017 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# AM_CONDITIONAL(NAME, SHELL-CONDITION) ++# ------------------------------------- ++# Define a conditional. ++AC_DEFUN([AM_CONDITIONAL], ++[AC_PREREQ([2.52])dnl ++ m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], ++ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl ++AC_SUBST([$1_TRUE])dnl ++AC_SUBST([$1_FALSE])dnl ++_AM_SUBST_NOTMAKE([$1_TRUE])dnl ++_AM_SUBST_NOTMAKE([$1_FALSE])dnl ++m4_define([_AM_COND_VALUE_$1], [$2])dnl ++if $2; then ++ $1_TRUE= ++ $1_FALSE='#' ++else ++ $1_TRUE='#' ++ $1_FALSE= ++fi ++AC_CONFIG_COMMANDS_PRE( ++[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then ++ AC_MSG_ERROR([[conditional "$1" was never defined. ++Usually this means the macro was only invoked conditionally.]]) ++fi])]) ++ ++# Copyright (C) 2006-2017 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# _AM_SUBST_NOTMAKE(VARIABLE) ++# --------------------------- ++# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. ++# This macro is traced by Automake. ++AC_DEFUN([_AM_SUBST_NOTMAKE]) ++ ++# AM_SUBST_NOTMAKE(VARIABLE) ++# -------------------------- ++# Public sister of _AM_SUBST_NOTMAKE. ++AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) ++ + m4_include([../config/acx.m4]) + m4_include([../config/cet.m4]) + m4_include([../config/enable.m4]) + m4_include([../config/no-executables.m4]) ++m4_include([../config/override.m4]) + m4_include([../config/picflag.m4]) + m4_include([../config/warnings.m4]) + m4_include([acinclude.m4]) +diff --git a/libiberty/configure b/libiberty/configure +index 0a797255c70..a346be40cc2 100755 +--- a/libiberty/configure ++++ b/libiberty/configure +@@ -632,6 +632,8 @@ PICFLAG + INSTALL_DATA + INSTALL_SCRIPT + INSTALL_PROGRAM ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + OUTPUT_OPTION + NO_MINUS_C_MINUS_O + ac_libiberty_warn_cflags +@@ -2459,6 +2461,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + ++ ++ ++ + # This works around the fact that libtool configuration may change LD + # for this particular configuration, but some shells, instead of + # keeping the changes in LD private, export them just because LD is +@@ -5046,6 +5051,15 @@ $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + ac_config_headers="$ac_config_headers config.h:config.in" + + +@@ -5208,6 +5222,9 @@ case "${host}" in + # sets the default TLS model and affects inlining. + PICFLAG=-fPIC + ;; ++ loongarch*-*-*) ++ PICFLAG=-fpic ++ ;; + mips-sgi-irix6*) + # PIC is the default. + ;; +@@ -7837,6 +7854,10 @@ LTLIBOBJS=$ac_ltlibobjs + + + ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/libiberty/configure.ac b/libiberty/configure.ac +index 84a7b378fad..4dad84ea77a 100644 +--- a/libiberty/configure.ac ++++ b/libiberty/configure.ac +@@ -190,6 +190,8 @@ dnl AM_DISABLE_SHARED + dnl When we start using libtool: + dnl AM_PROG_LIBTOOL + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + dnl When we start using automake: + dnl AM_CONFIG_HEADER(config.h:config.in) + AC_CONFIG_HEADER(config.h:config.in) +diff --git a/libitm/Makefile.am b/libitm/Makefile.am +index 3f31ad30556..a25317b07fe 100644 +--- a/libitm/Makefile.am ++++ b/libitm/Makefile.am +@@ -54,7 +54,12 @@ libitm_version_info = -version-info $(libtool_VERSION) + # want or need libstdc++. + libitm_la_DEPENDENCIES = $(libitm_version_dep) + libitm_la_LINK = $(LINK) $(libitm_la_LDFLAGS) +-libitm_la_LDFLAGS = $(libitm_version_info) $(libitm_version_script) ++if ENABLE_DARWIN_AT_RPATH ++libitm_darwin_rpath = -Wc,-nodefaultrpaths ++libitm_darwin_rpath += -Wl,-rpath,@loader_path ++endif ++libitm_la_LDFLAGS = $(libitm_version_info) $(libitm_version_script) \ ++ $(libitm_darwin_rpath) + + 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 +--- a/libitm/Makefile.in ++++ b/libitm/Makefile.in +@@ -481,7 +481,12 @@ libitm_version_info = -version-info $(libtool_VERSION) + # want or need libstdc++. + libitm_la_DEPENDENCIES = $(libitm_version_dep) + libitm_la_LINK = $(LINK) $(libitm_la_LDFLAGS) +-libitm_la_LDFLAGS = $(libitm_version_info) $(libitm_version_script) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libitm_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++libitm_la_LDFLAGS = $(libitm_version_info) $(libitm_version_script) \ ++ $(libitm_darwin_rpath) ++ + libitm_la_SOURCES = aatree.cc alloc.cc alloc_c.cc alloc_cpp.cc \ + 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 296cb683a9f..941e886143e 100644 +--- a/libitm/config/aarch64/sjlj.S ++++ b/libitm/config/aarch64/sjlj.S +@@ -57,10 +57,19 @@ + + .text + .align 2 ++#if __ELF__ + .global _ITM_beginTransaction + .type _ITM_beginTransaction, %function + + _ITM_beginTransaction: ++ ++#elif __MACH__ ++ .global __ITM_beginTransaction ++ ++__ITM_beginTransaction: ++ ++#endif ++ + cfi_startproc + CFI_PAC_KEY + PAC_AND_BTI +@@ -84,8 +93,13 @@ _ITM_beginTransaction: + + /* Invoke GTM_begin_transaction with the struct we just built. */ + mov x1, sp ++#if __ELF__ + bl GTM_begin_transaction +- ++#elif __MACH__ ++ bl _GTM_begin_transaction ++#else ++#error "unexpected object format" ++#endif + /* Return; we don't need to restore any of the call-saved regs. */ + ldp x29, x30, [sp], 11*16 + cfi_adjust_cfa_offset(-11*16) +@@ -95,14 +109,23 @@ _ITM_beginTransaction: + CFI_PAC_TOGGLE + ret + cfi_endproc ++#if __ELF__ + .size _ITM_beginTransaction, . - _ITM_beginTransaction ++#endif + + .align 2 ++#if __ELF__ + .global GTM_longjmp + .hidden GTM_longjmp + .type GTM_longjmp, %function + + GTM_longjmp: ++ ++#elif __MACH__ ++ .private_extern _GTM_longjmp ++ ++_GTM_longjmp: ++#endif + /* The first parameter becomes the return value (x0). + The third parameter is ignored for now. */ + cfi_startproc +@@ -126,7 +149,9 @@ GTM_longjmp: + CFI_PAC_TOGGLE + br x30 + cfi_endproc ++#if __ELF__ + .size GTM_longjmp, . - GTM_longjmp ++#endif + + /* 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 18fc2d3a10a..5beb48a6b99 100755 +--- a/libitm/configure ++++ b/libitm/configure +@@ -660,6 +660,8 @@ libtool_VERSION + MAINT + MAINTAINER_MODE_FALSE + MAINTAINER_MODE_TRUE ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + CXXCPP +@@ -810,6 +812,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_maintainer_mode + enable_linux_futex + enable_tls +@@ -1462,6 +1465,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +@@ -10252,6 +10257,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10269,10 +10315,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12058,7 +12113,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12061 "configure" ++#line 12116 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12164,7 +12219,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12167 "configure" ++#line 12222 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13040,6 +13095,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -13057,12 +13153,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -15414,6 +15523,14 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +@@ -18172,6 +18289,10 @@ if test -z "${BUILD_INFO_TRUE}" && test -z "${BUILD_INFO_FALSE}"; then + as_fn_error $? "conditional \"BUILD_INFO\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + 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 78a682376d9..209a025a90e 100644 +--- a/libitm/configure.ac ++++ b/libitm/configure.ac +@@ -157,6 +157,7 @@ AM_CONDITIONAL(BUILD_INFO, test $gcc_cv_prog_makeinfo_modern = "yes") + AM_PROG_LIBTOOL + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + AM_MAINTAINER_MODE + +diff --git a/libitm/configure.tgt b/libitm/configure.tgt +index 06e90973ef3..acaf4f85712 100644 +--- a/libitm/configure.tgt ++++ b/libitm/configure.tgt +@@ -50,7 +50,7 @@ fi + # Map the target cpu to an ARCH sub-directory. At the same time, + # work out any special compilation flags as necessary. + case "${target_cpu}" in +- aarch64*) ARCH=aarch64 ;; ++ aarch64* | arm64*) ARCH=aarch64 ;; + alpha*) ARCH=alpha ;; + rs6000 | powerpc*) + XCFLAGS="${XCFLAGS} -mhtm" +diff --git a/libitm/testsuite/lib/libitm.exp b/libitm/testsuite/lib/libitm.exp +index 6d8e3e71310..906534022eb 100644 +--- a/libitm/testsuite/lib/libitm.exp ++++ b/libitm/testsuite/lib/libitm.exp +@@ -158,6 +158,7 @@ proc libitm_init { args } { + } + + if [istarget *-*-darwin*] { ++ lappend ALWAYS_CFLAGS "additional_flags=-B${blddir}/.libs" + lappend ALWAYS_CFLAGS "additional_flags=-shared-libgcc" + } + +diff --git a/libitm/testsuite/libitm.c++/c++.exp b/libitm/testsuite/libitm.c++/c++.exp +index f92aa096104..295c5bd4703 100644 +--- a/libitm/testsuite/libitm.c++/c++.exp ++++ b/libitm/testsuite/libitm.c++/c++.exp +@@ -56,8 +56,10 @@ if { $lang_test_file_found } { + # Gather a list of all tests. + set tests [lsort [glob -nocomplain $srcdir/$subdir/*.C]] + ++ set stdcxxadder "" + if { $blddir != "" } { + set ld_library_path "$always_ld_library_path:${blddir}/${lang_library_path}" ++ set stdcxxadder "-B ${blddir}/${lang_library_path}" + } else { + set ld_library_path "$always_ld_library_path" + } +@@ -72,7 +74,7 @@ if { $lang_test_file_found } { + } + + # Main loop. +- dg-runtest $tests "" $libstdcxx_includes ++ dg-runtest $tests $stdcxxadder $libstdcxx_includes + } + + # All done. +diff --git a/libobjc/configure b/libobjc/configure +index 5d1b424a66d..21ac18723c3 100755 +--- a/libobjc/configure ++++ b/libobjc/configure +@@ -636,6 +636,9 @@ OBJC_BOEHM_GC_LIBS + OBJC_BOEHM_GC_INCLUDES + OBJC_BOEHM_GC + OBJC_GCFLAGS ++extra_ldflags_libobjc ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + SET_MAKE + CPP + OTOOL64 +@@ -667,7 +670,6 @@ RANLIB + AR + AS + XCFLAGS +-extra_ldflags_libobjc + lt_host_flags + OBJEXT + EXEEXT +@@ -755,6 +757,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_tls + enable_objc_gc + with_target_bdw_gc +@@ -1392,6 +1395,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-tls Use thread-local storage [default=yes] + --enable-objc-gc enable use of Boehm's garbage collector with the GNU + Objective-C runtime +@@ -3430,17 +3435,6 @@ esac + + + +-case "${host}" in +- *-darwin*) +- # Darwin needs -single_module when linking libobjc +- extra_ldflags_libobjc='$(lt_host_flags) -Wl,-single_module' +- ;; +- *-cygwin*|*-mingw*) +- # Tell libtool to build DLLs on Windows +- extra_ldflags_libobjc='$(lt_host_flags)' +- ;; +-esac +- + + # Add CET specific flags if CET is enabled + +@@ -3466,7 +3460,7 @@ case "$host" in + case "$enable_cet" in + auto) + # Check if target supports multi-byte NOPs +- # and if assembler supports CET insn. ++ # and if compiler and assembler support CET insn. + cet_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fcf-protection" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -8944,6 +8938,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -8961,10 +8996,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -10771,7 +10815,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10784 "configure" ++#line 10818 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -10877,7 +10921,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10890 "configure" ++#line 10924 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11149,6 +11193,38 @@ $as_echo "no" >&6; } + fi + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ ++# Must come after libtool is initialized. ++case "${host}" in ++ *-darwin[4567]*) ++ # Earlier Darwin versions need -single_module when linking libobjc; they ++ # do not support @rpath. ++ extra_ldflags_libobjc='$(lt_host_flags) -Wl,-single_module' ++ ;; ++ *-darwin*) ++ # Otherwise, single_module is the default and multi-module is ignored and ++ # obsolete. ++ extra_ldflags_libobjc='$(lt_host_flags)' ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ extra_ldflags_libobjc="${extra_ldflags_libobjc} -Wc,-nodefaultrpaths" ++ extra_ldflags_libobjc="${extra_ldflags_libobjc} -Wl,-rpath,@loader_path" ++ fi ++ ;; ++ *-cygwin*|*-mingw*) ++ # Tell libtool to build DLLs on Windows ++ extra_ldflags_libobjc='$(lt_host_flags)' ++ ;; ++esac ++ ++ + # ------- + # Headers + # ------- +@@ -11890,6 +11966,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/libobjc/configure.ac b/libobjc/configure.ac +index f8f577cfbef..2a9bf1fed4c 100644 +--- a/libobjc/configure.ac ++++ b/libobjc/configure.ac +@@ -147,17 +147,6 @@ m4_rename_force([real_PRECIOUS],[_AC_ARG_VAR_PRECIOUS]) + + # extra LD Flags which are required for targets + ACX_LT_HOST_FLAGS +-case "${host}" in +- *-darwin*) +- # Darwin needs -single_module when linking libobjc +- extra_ldflags_libobjc='$(lt_host_flags) -Wl,-single_module' +- ;; +- *-cygwin*|*-mingw*) +- # Tell libtool to build DLLs on Windows +- extra_ldflags_libobjc='$(lt_host_flags)' +- ;; +-esac +-AC_SUBST(extra_ldflags_libobjc) + + # Add CET specific flags if CET is enabled + GCC_CET_FLAGS(CET_FLAGS) +@@ -182,6 +171,31 @@ AM_PROG_CC_C_O + + AC_PROG_MAKE_SET + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ ++# Must come after libtool is initialized. ++case "${host}" in ++ *-darwin[[4567]]*) ++ # Earlier Darwin versions need -single_module when linking libobjc; they ++ # do not support @rpath. ++ extra_ldflags_libobjc='$(lt_host_flags) -Wl,-single_module' ++ ;; ++ *-darwin*) ++ # Otherwise, single_module is the default and multi-module is ignored and ++ # obsolete. ++ extra_ldflags_libobjc='$(lt_host_flags)' ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ extra_ldflags_libobjc="${extra_ldflags_libobjc} -Wc,-nodefaultrpaths" ++ extra_ldflags_libobjc="${extra_ldflags_libobjc} -Wl,-rpath,@loader_path" ++ fi ++ ;; ++ *-cygwin*|*-mingw*) ++ # Tell libtool to build DLLs on Windows ++ extra_ldflags_libobjc='$(lt_host_flags)' ++ ;; ++esac ++AC_SUBST(extra_ldflags_libobjc) ++ + # ------- + # Headers + # ------- +diff --git a/liboffloadmic/configure b/liboffloadmic/configure +index dfa8287fd75..84447cbb7eb 100755 +--- a/liboffloadmic/configure ++++ b/liboffloadmic/configure +@@ -639,6 +639,8 @@ link_offloadmic_host + lt_cv_dlopen_libs + toolexeclibdir + toolexecdir ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + CXXCPP + OTOOL64 + OTOOL +@@ -782,6 +784,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + with_gcc_major_version_only + ' + ac_precious_vars='build_alias +@@ -1434,6 +1437,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -7900,23 +7905,25 @@ _LT_EOF + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 + $as_echo "$lt_cv_ld_force_load" >&6; } +- case $host_os in +- rhapsody* | darwin1.[012]) ++ # Allow for Darwin 4-7 (macOS 10.0-10.3) although these are not expect to ++ # build without first building modern cctools / linker. ++ case $host_cpu-$host_os in ++ *-rhapsody* | *-darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; +- darwin1.*) ++ *-darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; +- darwin*) # darwin 5.x on +- # if running on 10.5 or later, the deployment target defaults +- # to the OS version, if on x86, and 10.4, the deployment +- # target defaults to 10.4. Don't you love it? +- case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in +- 10.0,*86*-darwin8*|10.0,*-darwin[91]*) +- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; ++ *-darwin*) ++ # darwin 5.x (macOS 10.1) onwards we only need to adjust when the ++ # deployment target is forced to an earlier version. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host in ++ UNSET,*-darwin[89]*|UNSET,*-darwin[12][0123456789]*) ++ ;; + 10.[012][,.]*) +- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; +- 10.*) +- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; +- esac ++ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ++ ;; ++ *) ++ ;; ++ esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then +@@ -9614,6 +9621,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9631,10 +9679,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11420,7 +11477,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11433 "configure" ++#line 11480 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11526,7 +11583,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11539 "configure" ++#line 11586 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12402,6 +12459,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -12419,12 +12517,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -14265,16 +14376,6 @@ freebsd* | dragonfly*) + esac + ;; + +-gnu*) +- version_type=linux +- need_lib_prefix=no +- need_version=no +- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' +- soname_spec='${libname}${release}${shared_ext}$major' +- shlibpath_var=LD_LIBRARY_PATH +- hardcode_into_libs=yes +- ;; +- + haiku*) + version_type=linux + need_lib_prefix=no +@@ -14396,7 +14497,7 @@ linux*oldld* | linux*aout* | linux*coff*) + # project, but have not yet been accepted: they are GCC-local changes + # for the time being. (See + # https://lists.gnu.org/archive/html/libtool-patches/2018-05/msg00000.html) +-linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi) ++linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu* | uclinuxfdpiceabi) + version_type=linux + need_lib_prefix=no + need_version=no +@@ -14784,6 +14885,15 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + # Only expand once: + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # Forbid libtool to hardcode RPATH, because we want to be able to specify + # library search directory using LD_LIBRARY_PATH + hardcode_into_libs=no +@@ -14999,6 +15109,10 @@ if test -z "${LIBOFFLOADMIC_HOST_TRUE}" && test -z "${LIBOFFLOADMIC_HOST_FALSE}" + as_fn_error $? "conditional \"LIBOFFLOADMIC_HOST\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/liboffloadmic/configure.ac b/liboffloadmic/configure.ac +index f64f182e8ef..b96e7eaf9e3 100644 +--- a/liboffloadmic/configure.ac ++++ b/liboffloadmic/configure.ac +@@ -118,6 +118,8 @@ esac + + AC_LIBTOOL_DLOPEN + AM_PROG_LIBTOOL ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + # Forbid libtool to hardcode RPATH, because we want to be able to specify + # library search directory using LD_LIBRARY_PATH + hardcode_into_libs=no +diff --git a/liboffloadmic/plugin/Makefile.in b/liboffloadmic/plugin/Makefile.in +index 8d5ad0025c2..c53f2d32b3b 100644 +--- a/liboffloadmic/plugin/Makefile.in ++++ b/liboffloadmic/plugin/Makefile.in +@@ -123,10 +123,10 @@ subdir = . + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/../../config/acx.m4 \ + $(top_srcdir)/../../config/depstand.m4 \ +- $(top_srcdir)/../../config/toolexeclibdir.m4 \ + $(top_srcdir)/../../config/lead-dot.m4 \ + $(top_srcdir)/../../config/multi.m4 \ + $(top_srcdir)/../../config/override.m4 \ ++ $(top_srcdir)/../../config/toolexeclibdir.m4 \ + $(top_srcdir)/../../libtool.m4 \ + $(top_srcdir)/../../ltoptions.m4 \ + $(top_srcdir)/../../ltsugar.m4 \ +diff --git a/liboffloadmic/plugin/aclocal.m4 b/liboffloadmic/plugin/aclocal.m4 +index 9fa1d1216c1..1bb91402f66 100644 +--- a/liboffloadmic/plugin/aclocal.m4 ++++ b/liboffloadmic/plugin/aclocal.m4 +@@ -1169,10 +1169,10 @@ AC_SUBST([am__untar]) + + m4_include([../../config/acx.m4]) + m4_include([../../config/depstand.m4]) +-m4_include([../../config/toolexeclibdir.m4]) + m4_include([../../config/lead-dot.m4]) + m4_include([../../config/multi.m4]) + m4_include([../../config/override.m4]) ++m4_include([../../config/toolexeclibdir.m4]) + m4_include([../../libtool.m4]) + m4_include([../../ltoptions.m4]) + m4_include([../../ltsugar.m4]) +diff --git a/liboffloadmic/plugin/configure b/liboffloadmic/plugin/configure +index 0b21d7d4eed..a9416401a65 100755 +--- a/liboffloadmic/plugin/configure ++++ b/liboffloadmic/plugin/configure +@@ -635,6 +635,8 @@ LIBOBJS + get_gcc_base_ver + toolexeclibdir + toolexecdir ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + CXXCPP + CPP + OTOOL64 +@@ -778,6 +780,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + with_gcc_major_version_only + ' + ac_precious_vars='build_alias +@@ -1431,6 +1434,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -7280,23 +7285,25 @@ _LT_EOF + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 + $as_echo "$lt_cv_ld_force_load" >&6; } +- case $host_os in +- rhapsody* | darwin1.[012]) ++ # Allow for Darwin 4-7 (macOS 10.0-10.3) although these are not expect to ++ # build without first building modern cctools / linker. ++ case $host_cpu-$host_os in ++ *-rhapsody* | *-darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; +- darwin1.*) ++ *-darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; +- darwin*) # darwin 5.x on +- # if running on 10.5 or later, the deployment target defaults +- # to the OS version, if on x86, and 10.4, the deployment +- # target defaults to 10.4. Don't you love it? +- case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in +- 10.0,*86*-darwin8*|10.0,*-darwin[91]*) +- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; ++ *-darwin*) ++ # darwin 5.x (macOS 10.1) onwards we only need to adjust when the ++ # deployment target is forced to an earlier version. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host in ++ UNSET,*-darwin[89]*|UNSET,*-darwin[12][0123456789]*) ++ ;; + 10.[012][,.]*) +- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; +- 10.*) +- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; +- esac ++ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ++ ;; ++ *) ++ ;; ++ esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then +@@ -9261,6 +9268,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9278,10 +9326,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11067,7 +11124,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11080 "configure" ++#line 11127 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11173,7 +11230,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11186 "configure" ++#line 11233 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12049,6 +12106,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -12066,12 +12164,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -13912,16 +14023,6 @@ freebsd* | dragonfly*) + esac + ;; + +-gnu*) +- version_type=linux +- need_lib_prefix=no +- need_version=no +- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' +- soname_spec='${libname}${release}${shared_ext}$major' +- shlibpath_var=LD_LIBRARY_PATH +- hardcode_into_libs=yes +- ;; +- + haiku*) + version_type=linux + need_lib_prefix=no +@@ -14043,7 +14144,7 @@ linux*oldld* | linux*aout* | linux*coff*) + # project, but have not yet been accepted: they are GCC-local changes + # for the time being. (See + # https://lists.gnu.org/archive/html/libtool-patches/2018-05/msg00000.html) +-linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi) ++linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu* | uclinuxfdpiceabi) + version_type=linux + need_lib_prefix=no + need_version=no +@@ -14431,6 +14532,15 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + # Only expand once: + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # Forbid libtool to hardcode RPATH, because we want to be able to specify + # library search directory using LD_LIBRARY_PATH + hardcode_into_libs=no +@@ -14634,6 +14744,10 @@ if test -z "${PLUGIN_HOST_TRUE}" && test -z "${PLUGIN_HOST_FALSE}"; then + as_fn_error $? "conditional \"PLUGIN_HOST\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/liboffloadmic/plugin/configure.ac b/liboffloadmic/plugin/configure.ac +index cbcd0130d05..3329b03638d 100644 +--- a/liboffloadmic/plugin/configure.ac ++++ b/liboffloadmic/plugin/configure.ac +@@ -134,6 +134,8 @@ esac + + AC_LIBTOOL_DLOPEN + AM_PROG_LIBTOOL ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + # Forbid libtool to hardcode RPATH, because we want to be able to specify + # library search directory using LD_LIBRARY_PATH + hardcode_into_libs=no +diff --git a/libphobos/configure b/libphobos/configure +index 9da06f087d0..9fbb3c91e93 100755 +--- a/libphobos/configure ++++ b/libphobos/configure +@@ -707,6 +707,8 @@ get_gcc_base_ver + phobos_compiler_shared_flag + phobos_compiler_pic_flag + phobos_lt_pic_flag ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + OTOOL64 +@@ -838,6 +840,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + with_gcc_major_version_only + enable_werror + with_libatomic +@@ -1490,6 +1493,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-werror turns on -Werror [default=no] + --enable-version-specific-runtime-libs + Specify that runtime libraries should be installed +@@ -9944,6 +9949,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9961,10 +10007,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -11750,7 +11805,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11753 "configure" ++#line 11808 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11856,7 +11911,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11859 "configure" ++#line 11914 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13381,6 +13436,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_D=no + hardcode_direct_D=no + hardcode_automatic_D=yes +@@ -13398,10 +13494,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_D="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_D="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_D="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_D="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_D="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_D="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_D="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_D="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_D="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_D="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_D="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_D="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs_D=no +@@ -14002,6 +14107,14 @@ CFLAGS=$lt_save_CFLAGS + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + # libtool variables for Phobos shared and position-independent compiles. + # +@@ -15726,6 +15839,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${DRUNTIME_CPU_AARCH64_TRUE}" && test -z "${DRUNTIME_CPU_AARCH64_FALSE}"; then + 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 31209ba2920..cc372587939 100644 +--- a/libphobos/configure.ac ++++ b/libphobos/configure.ac +@@ -93,6 +93,7 @@ AM_PROG_LIBTOOL + WITH_LOCAL_DRUNTIME([LT_LANG([D])], []) + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + # libtool variables for Phobos shared and position-independent compiles. + # +diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am +index 6ca4012b713..861ec0ebc03 100644 +--- a/libphobos/libdruntime/Makefile.am ++++ b/libphobos/libdruntime/Makefile.am +@@ -128,8 +128,11 @@ ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \ + toolexeclib_LTLIBRARIES = libgdruntime.la + libgdruntime_la_SOURCES = $(ALL_DRUNTIME_SOURCES) + libgdruntime_la_LIBTOOLFLAGS = ++if ENABLE_DARWIN_AT_RPATH ++libgdruntime_darwin_rpath = -Wl,-rpath,@loader_path ++endif + libgdruntime_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../src,-Bgcc \ +- -version-info $(libtool_VERSION) ++ -version-info $(libtool_VERSION) $(libgdruntime_darwin_rpath) + libgdruntime_la_LIBADD = $(LIBATOMIC) $(LIBBACKTRACE) + 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 f7f78d71ff7..9f3361c7702 100644 +--- a/libphobos/libdruntime/Makefile.in ++++ b/libphobos/libdruntime/Makefile.in +@@ -805,8 +805,9 @@ ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \ + toolexeclib_LTLIBRARIES = libgdruntime.la + libgdruntime_la_SOURCES = $(ALL_DRUNTIME_SOURCES) + libgdruntime_la_LIBTOOLFLAGS = ++@ENABLE_DARWIN_AT_RPATH_TRUE@libgdruntime_darwin_rpath = -Wl,-rpath,@loader_path + libgdruntime_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../src,-Bgcc \ +- -version-info $(libtool_VERSION) ++ -version-info $(libtool_VERSION) $(libgdruntime_darwin_rpath) + + libgdruntime_la_LIBADD = $(LIBATOMIC) $(LIBBACKTRACE) + libgdruntime_la_DEPENDENCIES = $(DRTSTUFF) +diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am +index da7a2004ff8..a47d985c5b7 100644 +--- a/libphobos/src/Makefile.am ++++ b/libphobos/src/Makefile.am +@@ -44,8 +44,11 @@ toolexeclib_DATA = libgphobos.spec + toolexeclib_LTLIBRARIES = libgphobos.la + libgphobos_la_SOURCES = $(ALL_PHOBOS_SOURCES) + libgphobos_la_LIBTOOLFLAGS = ++if ENABLE_DARWIN_AT_RPATH ++libgphobos_darwin_rpath = -Wl,-rpath,@loader_path ++endif + libgphobos_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../libdruntime/gcc \ +- -version-info $(libtool_VERSION) ++ -version-info $(libtool_VERSION) $(libgphobos_darwin_rpath) + if ENABLE_LIBDRUNTIME_ONLY + libgphobos_la_LIBADD = ../libdruntime/libgdruntime_convenience.la + else +diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in +index 6f58fee01ac..212ea2469f2 100644 +--- a/libphobos/src/Makefile.in ++++ b/libphobos/src/Makefile.in +@@ -528,8 +528,9 @@ toolexeclib_DATA = libgphobos.spec + toolexeclib_LTLIBRARIES = libgphobos.la + libgphobos_la_SOURCES = $(ALL_PHOBOS_SOURCES) + libgphobos_la_LIBTOOLFLAGS = ++@ENABLE_DARWIN_AT_RPATH_TRUE@libgphobos_darwin_rpath = -Wl,-rpath,@loader_path + libgphobos_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../libdruntime/gcc \ +- -version-info $(libtool_VERSION) ++ -version-info $(libtool_VERSION) $(libgphobos_darwin_rpath) + + @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..4bf4bf6eebc 100644 +--- a/libquadmath/Makefile.am ++++ b/libquadmath/Makefile.am +@@ -36,8 +36,13 @@ endif + + toolexeclib_LTLIBRARIES = libquadmath.la + libquadmath_la_LIBADD = ++ ++if ENABLE_DARWIN_AT_RPATH ++libquadmath_darwin_rpath = -Wc,-nodefaultrpaths ++libquadmath_darwin_rpath += -Wl,-rpath,@loader_path ++endif + libquadmath_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ +- $(version_arg) $(lt_host_flags) -lm ++ $(version_arg) $(lt_host_flags) $(LIBM) $(libquadmath_darwin_rpath) + libquadmath_la_DEPENDENCIES = $(version_dep) $(libquadmath_la_LIBADD) + + nodist_libsubinclude_HEADERS = quadmath.h quadmath_weak.h +diff --git a/libquadmath/Makefile.in b/libquadmath/Makefile.in +index 8c011212258..b59aac7f1ac 100644 +--- a/libquadmath/Makefile.in ++++ b/libquadmath/Makefile.in +@@ -355,6 +355,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ + LD = @LD@ + LDFLAGS = @LDFLAGS@ ++LIBM = @LIBM@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LIBTOOL = @LIBTOOL@ +@@ -463,8 +464,10 @@ AUTOMAKE_OPTIONS = foreign info-in-builddir + @BUILD_LIBQUADMATH_TRUE@@LIBQUAD_USE_SYMVER_SUN_TRUE@@LIBQUAD_USE_SYMVER_TRUE@version_dep = quadmath.map-sun + @BUILD_LIBQUADMATH_TRUE@toolexeclib_LTLIBRARIES = libquadmath.la + @BUILD_LIBQUADMATH_TRUE@libquadmath_la_LIBADD = ++@BUILD_LIBQUADMATH_TRUE@@ENABLE_DARWIN_AT_RPATH_TRUE@libquadmath_darwin_rpath = -Wc,-nodefaultrpaths \ ++@BUILD_LIBQUADMATH_TRUE@@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path + @BUILD_LIBQUADMATH_TRUE@libquadmath_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ +-@BUILD_LIBQUADMATH_TRUE@ $(version_arg) $(lt_host_flags) -lm ++@BUILD_LIBQUADMATH_TRUE@ $(version_arg) $(lt_host_flags) $(LIBM) $(libquadmath_darwin_rpath) + + @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 b3ee64f9c7d..23a99be108f 100755 +--- a/libquadmath/configure ++++ b/libquadmath/configure +@@ -644,11 +644,14 @@ LIBQUAD_USE_SYMVER_GNU_FALSE + LIBQUAD_USE_SYMVER_GNU_TRUE + LIBQUAD_USE_SYMVER_FALSE + LIBQUAD_USE_SYMVER_TRUE ++LIBM + toolexeclibdir + toolexecdir + MAINT + MAINTAINER_MODE_FALSE + MAINTAINER_MODE_TRUE ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -785,6 +788,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_maintainer_mode + with_toolexeclibdir + enable_symvers +@@ -1435,6 +1439,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer +@@ -8979,6 +8985,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -8996,10 +9043,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -10806,7 +10862,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10819 "configure" ++#line 10865 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -10912,7 +10968,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10925 "configure" ++#line 10971 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11173,6 +11229,14 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +@@ -12137,6 +12201,148 @@ esac + + + ++LIBM= ++case $host in ++*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) ++ # These system don't have libm, or don't need it ++ ;; ++*-ncr-sysv4.3*) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _mwvalidcheckl in -lmw" >&5 ++$as_echo_n "checking for _mwvalidcheckl in -lmw... " >&6; } ++if ${ac_cv_lib_mw__mwvalidcheckl+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lmw $LIBS" ++if test x$gcc_no_link = xyes; then ++ as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5 ++fi ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char _mwvalidcheckl (); ++int ++main () ++{ ++return _mwvalidcheckl (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_mw__mwvalidcheckl=yes ++else ++ ac_cv_lib_mw__mwvalidcheckl=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mw__mwvalidcheckl" >&5 ++$as_echo "$ac_cv_lib_mw__mwvalidcheckl" >&6; } ++if test "x$ac_cv_lib_mw__mwvalidcheckl" = xyes; then : ++ LIBM="-lmw" ++fi ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5 ++$as_echo_n "checking for cos in -lm... " >&6; } ++if ${ac_cv_lib_m_cos+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lm $LIBS" ++if test x$gcc_no_link = xyes; then ++ as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5 ++fi ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char cos (); ++int ++main () ++{ ++return cos (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_m_cos=yes ++else ++ ac_cv_lib_m_cos=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5 ++$as_echo "$ac_cv_lib_m_cos" >&6; } ++if test "x$ac_cv_lib_m_cos" = xyes; then : ++ LIBM="$LIBM -lm" ++fi ++ ++ ;; ++*) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5 ++$as_echo_n "checking for cos in -lm... " >&6; } ++if ${ac_cv_lib_m_cos+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lm $LIBS" ++if test x$gcc_no_link = xyes; then ++ as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5 ++fi ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char cos (); ++int ++main () ++{ ++return cos (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_m_cos=yes ++else ++ ac_cv_lib_m_cos=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5 ++$as_echo "$ac_cv_lib_m_cos" >&6; } ++if test "x$ac_cv_lib_m_cos" = xyes; then : ++ LIBM="-lm" ++fi ++ ++ ;; ++esac ++ ++ ++ + for ac_header in fenv.h langinfo.h locale.h wchar.h wctype.h limits.h ctype.h printf.h errno.h + do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +@@ -13031,7 +13237,7 @@ case "$host" in + case "$enable_cet" in + auto) + # Check if target supports multi-byte NOPs +- # and if assembler supports CET insn. ++ # and if compiler and assembler support CET insn. + cet_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fcf-protection" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -13397,6 +13603,10 @@ if test -z "${BUILD_INFO_TRUE}" && test -z "${BUILD_INFO_FALSE}"; then + as_fn_error $? "conditional \"BUILD_INFO\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + 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..94a3f2179e9 100644 +--- a/libquadmath/configure.ac ++++ b/libquadmath/configure.ac +@@ -59,6 +59,7 @@ AM_PROG_LIBTOOL + ACX_LT_HOST_FLAGS + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + AM_MAINTAINER_MODE + +@@ -121,6 +122,8 @@ esac + AC_SUBST(toolexecdir) + AC_SUBST(toolexeclibdir) + ++AC_CHECK_LIBM ++ + AC_CHECK_HEADERS(fenv.h langinfo.h locale.h wchar.h wctype.h limits.h ctype.h printf.h errno.h) + LIBQUAD_CHECK_MATH_H_SIGNGAM + +diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in +index aab88deb6e8..65e7f2e9553 100644 +--- a/libsanitizer/Makefile.in ++++ b/libsanitizer/Makefile.in +@@ -345,7 +345,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am +index 4f802f723d6..223d3e07816 100644 +--- a/libsanitizer/asan/Makefile.am ++++ b/libsanitizer/asan/Makefile.am +@@ -60,7 +60,12 @@ libasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + endif + libasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) + +-libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libasan) ++if ENABLE_DARWIN_AT_RPATH ++libasan_darwin_rpath = -Wc,-nodefaultrpaths ++libasan_darwin_rpath += -Wl,-rpath,@loader_path ++endif ++libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libasan) $(libasan_darwin_rpath) + + libasan_preinit.o: asan_preinit.o + cp $< $@ +diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in +index 2476fbc5a26..e88e5e0b0a7 100644 +--- a/libsanitizer/asan/Makefile.in ++++ b/libsanitizer/asan/Makefile.in +@@ -399,7 +399,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -466,7 +465,12 @@ libasan_la_LIBADD = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \ + $(am__append_3) $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libasan) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libasan_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libasan) $(libasan_darwin_rpath) ++ + + # 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 771b135573a..dfd99e34288 100755 +--- a/libsanitizer/configure ++++ b/libsanitizer/configure +@@ -666,6 +666,8 @@ LSAN_SUPPORTED_FALSE + LSAN_SUPPORTED_TRUE + TSAN_SUPPORTED_FALSE + TSAN_SUPPORTED_TRUE ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + CXXCPP +@@ -817,6 +819,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_werror + with_gcc_major_version_only + enable_cet +@@ -1471,6 +1474,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --disable-werror disable building with -Werror + --enable-cet enable Intel CET in target libraries [default=auto] + +@@ -10553,6 +10558,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10570,10 +10616,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12359,7 +12414,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12362 "configure" ++#line 12417 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12465,7 +12520,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12468 "configure" ++#line 12523 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13341,6 +13396,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -13358,12 +13454,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -15763,6 +15872,15 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # The cast to long int works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +@@ -17152,6 +17270,10 @@ if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCCAS\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${TSAN_SUPPORTED_TRUE}" && test -z "${TSAN_SUPPORTED_FALSE}"; then + 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 7f1ef3979c4..3549b904c62 100644 +--- a/libsanitizer/configure.ac ++++ b/libsanitizer/configure.ac +@@ -85,6 +85,8 @@ esac + AC_SUBST(enable_shared) + AC_SUBST(enable_static) + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + AC_CHECK_SIZEOF([void *]) + + if test "${multilib}" = "yes"; then +diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am +index e12c0a0ce71..4061078c734 100644 +--- a/libsanitizer/hwasan/Makefile.am ++++ b/libsanitizer/hwasan/Makefile.am +@@ -46,7 +46,11 @@ libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + endif + libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) + +-libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan) ++if ENABLE_DARWIN_AT_RPATH ++libhwasan_darwin_rpath = -nodefaultrpaths -Wl,-rpath,@loader_path/ ++endif ++libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libhwasan) $(libhwasan_darwin_rpath) + + # 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/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in +index 67553f3979d..d20f2dc6eef 100644 +--- a/libsanitizer/hwasan/Makefile.in ++++ b/libsanitizer/hwasan/Makefile.in +@@ -387,7 +387,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -442,7 +441,10 @@ libhwasan_la_SOURCES = $(hwasan_files) + libhwasan_la_LIBADD = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libhwasan_darwin_rpath = -nodefaultrpaths -Wl,-rpath,@loader_path/ ++libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libhwasan) $(libhwasan_darwin_rpath) ++ + + # 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/interception/Makefile.in b/libsanitizer/interception/Makefile.in +index bce788aeea7..85dd386de47 100644 +--- a/libsanitizer/interception/Makefile.in ++++ b/libsanitizer/interception/Makefile.in +@@ -317,7 +317,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in +index ece4f11a855..c0243fa4aab 100644 +--- a/libsanitizer/libbacktrace/Makefile.in ++++ b/libsanitizer/libbacktrace/Makefile.in +@@ -367,7 +367,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am +index 6ff28ff5eea..7701b0e18cf 100644 +--- a/libsanitizer/lsan/Makefile.am ++++ b/libsanitizer/lsan/Makefile.am +@@ -41,8 +41,12 @@ if LIBBACKTRACE_SUPPORTED + liblsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + endif + liblsan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-liblsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_liblsan) +- ++if ENABLE_DARWIN_AT_RPATH ++liblsan_darwin_rpath = -Wc,-nodefaultrpaths ++liblsan_darwin_rpath += -Wl,-rpath,@loader_path ++endif ++liblsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_liblsan) $(liblsan_darwin_rpath) + liblsan_preinit.o: lsan_preinit.o + cp $< $@ + +diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in +index 857f244cd86..078edf01fda 100644 +--- a/libsanitizer/lsan/Makefile.in ++++ b/libsanitizer/lsan/Makefile.in +@@ -362,7 +362,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -414,7 +413,12 @@ liblsan_la_LIBADD = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(top_builddir)/interception/libinterception.la \ + $(am__append_1) $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-liblsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_liblsan) ++@ENABLE_DARWIN_AT_RPATH_TRUE@liblsan_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++liblsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_liblsan) $(liblsan_darwin_rpath) ++ + + # 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 +@@ -789,7 +793,6 @@ uninstall-am: uninstall-nodist_toolexeclibHEADERS \ + + .PRECIOUS: Makefile + +- + liblsan_preinit.o: lsan_preinit.o + cp $< $@ + +diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in +index c4b009fed83..e5e1c1d51fe 100644 +--- a/libsanitizer/sanitizer_common/Makefile.in ++++ b/libsanitizer/sanitizer_common/Makefile.in +@@ -354,7 +354,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am +index ae588a67df6..47ee50bee1a 100644 +--- a/libsanitizer/tsan/Makefile.am ++++ b/libsanitizer/tsan/Makefile.am +@@ -58,7 +58,11 @@ libtsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + libtsan_la_DEPENDENCIES +=$(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + endif + libtsan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libtsan) ++if ENABLE_DARWIN_AT_RPATH ++libtsan_darwin_rpath = -nodefaultrpaths -Wl,-rpath,@loader_path/ ++endif ++libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libtsan) $(libtsan_darwin_rpath) + + libtsan_preinit.o: tsan_preinit.o + cp $< $@ +diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in +index 538d2e8eb68..d6efff71e2f 100644 +--- a/libsanitizer/tsan/Makefile.in ++++ b/libsanitizer/tsan/Makefile.in +@@ -391,7 +391,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -466,7 +465,10 @@ libtsan_la_DEPENDENCIES = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(top_builddir)/interception/libinterception.la \ + $(TSAN_TARGET_DEPENDENT_OBJECTS) $(am__append_2) +-libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libtsan) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libtsan_darwin_rpath = -nodefaultrpaths -Wl,-rpath,@loader_path/ ++libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libtsan) $(libtsan_darwin_rpath) ++ + + # 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 +--- a/libsanitizer/ubsan/Makefile.am ++++ b/libsanitizer/ubsan/Makefile.am +@@ -36,7 +36,12 @@ if LIBBACKTRACE_SUPPORTED + libubsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la + endif + libubsan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libubsan) ++if ENABLE_DARWIN_AT_RPATH ++libubsan_darwin_rpath = -Wc,-nodefaultrpaths ++libubsan_darwin_rpath += -Wl,-rpath,@loader_path ++endif ++libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libubsan) $(libubsan_darwin_rpath) + + # 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 497e0338696..7e51480e970 100644 +--- a/libsanitizer/ubsan/Makefile.in ++++ b/libsanitizer/ubsan/Makefile.in +@@ -356,7 +356,6 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ +-runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -401,7 +400,12 @@ libubsan_la_SOURCES = $(ubsan_files) + libubsan_la_LIBADD = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS) +-libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libubsan) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libubsan_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path ++libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ ++ $(link_libubsan) $(libubsan_darwin_rpath) ++ + + # 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 945dc3c8336..d2a92b3aed1 100644 +--- a/libssp/Makefile.am ++++ b/libssp/Makefile.am +@@ -49,8 +49,12 @@ libssp_la_SOURCES = \ + vsnprintf-chk.c vsprintf-chk.c + libssp_la_LIBADD = + libssp_la_DEPENDENCIES = $(version_dep) $(libssp_la_LIBADD) ++if ENABLE_DARWIN_AT_RPATH ++libssp_darwin_rpath = -Wc,-nodefaultrpaths ++libssp_darwin_rpath += -Wl,-rpath,@loader_path ++endif + libssp_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ +- $(version_arg) $(lt_host_flags) ++ $(version_arg) $(lt_host_flags) $(libssp_darwin_rpath) + + libssp_nonshared_la_SOURCES = \ + ssp-local.c +diff --git a/libssp/Makefile.in b/libssp/Makefile.in +index bc8a0dc2b28..1cf86361b96 100644 +--- a/libssp/Makefile.in ++++ b/libssp/Makefile.in +@@ -376,8 +376,11 @@ libssp_la_SOURCES = \ + + libssp_la_LIBADD = + libssp_la_DEPENDENCIES = $(version_dep) $(libssp_la_LIBADD) ++@ENABLE_DARWIN_AT_RPATH_TRUE@libssp_darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path + libssp_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` \ +- $(version_arg) $(lt_host_flags) ++ $(version_arg) $(lt_host_flags) $(libssp_darwin_rpath) + + libssp_nonshared_la_SOURCES = \ + ssp-local.c +diff --git a/libssp/configure b/libssp/configure +index 10ba209bde8..5d62fef54a1 100755 +--- a/libssp/configure ++++ b/libssp/configure +@@ -636,6 +636,8 @@ LIBOBJS + get_gcc_base_ver + toolexeclibdir + toolexecdir ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -781,6 +783,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + with_toolexeclibdir + with_gcc_major_version_only + ' +@@ -1426,6 +1429,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -4338,7 +4343,7 @@ case "$host" in + case "$enable_cet" in + auto) + # Check if target supports multi-byte NOPs +- # and if assembler supports CET insn. ++ # and if compiler and assembler support CET insn. + cet_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fcf-protection" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -9165,6 +9170,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -9182,10 +9228,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -10992,7 +11047,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11005 "configure" ++#line 11050 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11098,7 +11153,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11111 "configure" ++#line 11156 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11376,6 +11431,15 @@ fi + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ ++ + # Calculate toolexeclibdir + # Also toolexecdir, though it's only used in toolexeclibdir + case ${version_specific_libs} in +@@ -11585,6 +11649,10 @@ if test -z "${LIBSSP_USE_SYMVER_SUN_TRUE}" && test -z "${LIBSSP_USE_SYMVER_SUN_F + as_fn_error $? "conditional \"LIBSSP_USE_SYMVER_SUN\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/libssp/configure.ac b/libssp/configure.ac +index f30f81c54f6..90778e2355d 100644 +--- a/libssp/configure.ac ++++ b/libssp/configure.ac +@@ -165,6 +165,8 @@ AC_SUBST(enable_static) + + GCC_WITH_TOOLEXECLIBDIR + ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) ++ + # Calculate toolexeclibdir + # 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 eac60392121..bdaa160dac6 100755 +--- a/libstdc++-v3/configure ++++ b/libstdc++-v3/configure +@@ -786,6 +786,8 @@ GLIBCXX_HOSTED_TRUE + glibcxx_compiler_shared_flag + glibcxx_compiler_pic_flag + glibcxx_lt_pic_flag ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -921,6 +923,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_hosted_libstdcxx + enable_libstdcxx_verbose + enable_libstdcxx_pch +@@ -1608,6 +1611,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --disable-hosted-libstdcxx + only build freestanding C++ runtime support + --disable-libstdcxx-verbose +@@ -10364,6 +10369,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10381,10 +10427,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12191,7 +12246,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12194 "configure" ++#line 12249 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12297,7 +12352,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12300 "configure" ++#line 12355 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13179,6 +13234,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -13196,12 +13292,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -15578,6 +15687,14 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + if test "$enable_vtable_verify" = yes; then + predep_objects_CXX="${predep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_start.o" +@@ -15981,7 +16098,7 @@ $as_echo "$glibcxx_cv_atomic_long_long" >&6; } + # Fake what AC_TRY_COMPILE does. + + cat > conftest.$ac_ext << EOF +-#line 15984 "configure" ++#line 16101 "configure" + int main() + { + typedef bool atomic_type; +@@ -16016,7 +16133,7 @@ $as_echo "$glibcxx_cv_atomic_bool" >&6; } + rm -f conftest* + + cat > conftest.$ac_ext << EOF +-#line 16019 "configure" ++#line 16136 "configure" + int main() + { + typedef short atomic_type; +@@ -16051,7 +16168,7 @@ $as_echo "$glibcxx_cv_atomic_short" >&6; } + rm -f conftest* + + cat > conftest.$ac_ext << EOF +-#line 16054 "configure" ++#line 16171 "configure" + int main() + { + // NB: _Atomic_word not necessarily int. +@@ -16087,7 +16204,7 @@ $as_echo "$glibcxx_cv_atomic_int" >&6; } + rm -f conftest* + + cat > conftest.$ac_ext << EOF +-#line 16090 "configure" ++#line 16207 "configure" + int main() + { + typedef long long atomic_type; +@@ -16243,7 +16360,7 @@ $as_echo "mutex" >&6; } + # unnecessary for this test. + + cat > conftest.$ac_ext << EOF +-#line 16246 "configure" ++#line 16363 "configure" + int main() + { + _Decimal32 d1; +@@ -16285,7 +16402,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + # unnecessary for this test. + + cat > conftest.$ac_ext << EOF +-#line 16288 "configure" ++#line 16405 "configure" + template<typename T1, typename T2> + struct same + { typedef T2 type; }; +@@ -79038,6 +79155,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${GLIBCXX_HOSTED_TRUE}" && test -z "${GLIBCXX_HOSTED_FALSE}"; then + as_fn_error $? "conditional \"GLIBCXX_HOSTED\" 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 e59bcdb2944..f3dda5a4ff9 100644 +--- a/libstdc++-v3/configure.ac ++++ b/libstdc++-v3/configure.ac +@@ -99,6 +99,7 @@ AM_PROG_LIBTOOL + ACX_LT_HOST_FLAGS + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + if test "$enable_vtable_verify" = yes; then + predep_objects_CXX="${predep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_start.o" +diff --git a/libstdc++-v3/configure.host b/libstdc++-v3/configure.host +index ec32980aa0d..da5b1578d91 100644 +--- a/libstdc++-v3/configure.host ++++ b/libstdc++-v3/configure.host +@@ -234,11 +234,6 @@ case "${host_os}" in + darwin8 | darwin8.* ) + # For 8+ compatibility is better if not -flat_namespace. + OPT_LDFLAGS="${OPT_LDFLAGS} -Wl,-single_module" +- case "${host_cpu}" in +- i[34567]86 | x86_64) +- OPTIMIZE_CXXFLAGS="${OPTIMIZE_CXXFLAGS} -fvisibility-inlines-hidden" +- ;; +- esac + os_include_dir="os/bsd/darwin" + ;; + darwin*) +diff --git a/libstdc++-v3/src/Makefile.am b/libstdc++-v3/src/Makefile.am +index 9c3f4aca655..016d68ecd5f 100644 +--- a/libstdc++-v3/src/Makefile.am ++++ b/libstdc++-v3/src/Makefile.am +@@ -133,8 +133,13 @@ libstdc___la_DEPENDENCIES = \ + $(top_builddir)/src/c++17/libc++17convenience.la \ + $(top_builddir)/src/c++20/libc++20convenience.la + ++if ENABLE_DARWIN_AT_RPATH ++libstdc___darwin_rpath = -Wc,-nodefaultrpaths ++libstdc___darwin_rpath += -Wl,-rpath,@loader_path ++endif ++ + libstdc___la_LDFLAGS = \ +- -version-info $(libtool_VERSION) ${version_arg} -lm ++ -version-info $(libtool_VERSION) ${version_arg} -lm $(libstdc___darwin_rpath) + + 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 4a06f6cfec1..0dc6c9650fc 100644 +--- a/libstdc++-v3/src/Makefile.in ++++ b/libstdc++-v3/src/Makefile.in +@@ -546,8 +546,11 @@ libstdc___la_DEPENDENCIES = \ + $(top_builddir)/src/c++17/libc++17convenience.la \ + $(top_builddir)/src/c++20/libc++20convenience.la + ++@ENABLE_DARWIN_AT_RPATH_TRUE@libstdc___darwin_rpath = \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wc,-nodefaultrpaths \ ++@ENABLE_DARWIN_AT_RPATH_TRUE@ -Wl,-rpath,@loader_path + libstdc___la_LDFLAGS = \ +- -version-info $(libtool_VERSION) ${version_arg} -lm ++ -version-info $(libtool_VERSION) ${version_arg} -lm $(libstdc___darwin_rpath) + + 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 17f8e5f3074..5452de03793 100644 +--- a/libtool.m4 ++++ b/libtool.m4 +@@ -1039,6 +1039,45 @@ _LT_EOF + m4_defun([_LT_DARWIN_LINKER_FEATURES], + [ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ AC_ARG_ENABLE([darwin-at-rpath], ++ AS_HELP_STRING([--enable-darwin-at-path], ++ [install libraries with @rpath/library-name, requires rpaths to be added to executables]), ++ [if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[[45678]]*|UNSET,rhapsody*|10.[[01234]][[,.]]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&AS_MESSAGE_LOG_FD ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi], ++ [case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[[45678]]*|UNSET,rhapsody*|10.[[01234]][[,.]]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[[56789]]*|UNSET,darwin2*|10.1[[123456789]][[,.]]*|1[[123456789]].*[[,.]]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&AS_MESSAGE_LOG_FD ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ]) ++ + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes +@@ -1056,13 +1095,26 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES], + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&AS_MESSAGE_LOG_FD ++ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&AS_MESSAGE_LOG_FD ++ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + m4_if([$1], [CXX], + [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + ],[]) +@@ -4203,6 +4255,7 @@ _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) + ])# _LT_COMPILER_PIC + ++_LT_TAGVAR(enable_darwin_at_rpath, $1)=no + + # _LT_LINKER_SHLIBS([TAGNAME]) + # ---------------------------- +@@ -6441,7 +6494,6 @@ fi # test "$_lt_caught_CXX_error" != yes + AC_LANG_POP + ])# _LT_LANG_CXX_CONFIG + +- + # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) + # --------------------------------- + # Figure out "hidden" library dependencies from verbose +diff --git a/libvtv/configure b/libvtv/configure +index d64b4af5c6b..4280e3b20a9 100755 +--- a/libvtv/configure ++++ b/libvtv/configure +@@ -640,6 +640,8 @@ VTV_CYGMIN_FALSE + VTV_CYGMIN_TRUE + XCFLAGS + libtool_VERSION ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + enable_static + enable_shared + lt_host_flags +@@ -797,6 +799,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + enable_cet + with_gcc_major_version_only + ' +@@ -1446,6 +1449,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-cet enable Intel CET in target libraries [default=auto] + + Optional Packages: +@@ -10448,6 +10453,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10465,10 +10511,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12254,7 +12309,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12267 "configure" ++#line 12312 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12360,7 +12415,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12373 "configure" ++#line 12418 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -13236,6 +13291,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes +@@ -13253,12 +13349,25 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + if test "$lt_cv_apple_cc_single_mod" != "yes"; then +- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring${_lt_dsymutil}" ++ else ++ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" ++ fi + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + +@@ -15099,16 +15208,6 @@ freebsd* | dragonfly*) + esac + ;; + +-gnu*) +- version_type=linux +- need_lib_prefix=no +- need_version=no +- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' +- soname_spec='${libname}${release}${shared_ext}$major' +- shlibpath_var=LD_LIBRARY_PATH +- hardcode_into_libs=yes +- ;; +- + haiku*) + version_type=linux + need_lib_prefix=no +@@ -15230,7 +15329,7 @@ linux*oldld* | linux*aout* | linux*coff*) + # project, but have not yet been accepted: they are GCC-local changes + # for the time being. (See + # https://lists.gnu.org/archive/html/libtool-patches/2018-05/msg00000.html) +-linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi) ++linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu* | uclinuxfdpiceabi) + version_type=linux + need_lib_prefix=no + need_version=no +@@ -15642,6 +15741,14 @@ esac + + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + # For libtool versioning info, format is CURRENT:REVISION:AGE + libtool_VERSION=1:0:0 +@@ -15672,7 +15779,7 @@ case "$host" in + case "$enable_cet" in + auto) + # Check if target supports multi-byte NOPs +- # and if assembler supports CET insn. ++ # and if compiler and assembler support CET insn. + cet_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fcf-protection" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -15987,6 +16094,10 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${VTV_CYGMIN_TRUE}" && test -z "${VTV_CYGMIN_FALSE}"; then + 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 +--- a/libvtv/configure.ac ++++ b/libvtv/configure.ac +@@ -153,6 +153,7 @@ AM_PROG_LIBTOOL + ACX_LT_HOST_FLAGS + AC_SUBST(enable_shared) + AC_SUBST(enable_static) ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + # 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 b820accfd65..8faa13c4a8b 100755 +--- a/lto-plugin/configure ++++ b/lto-plugin/configure +@@ -634,6 +634,8 @@ LTLIBOBJS + LIBOBJS + target_noncanonical + lt_host_flags ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + OTOOL64 + OTOOL + LIPO +@@ -785,6 +787,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + ' + ac_precious_vars='build_alias + host_alias +@@ -1430,6 +1433,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -10275,6 +10280,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -10292,10 +10338,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -12081,7 +12136,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12084 "configure" ++#line 12139 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12187,7 +12242,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 12190 "configure" ++#line 12245 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -12424,6 +12479,14 @@ CC="$lt_save_CC" + # Only expand once: + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + + +@@ -12670,6 +12733,10 @@ if test -z "${LTO_PLUGIN_USE_SYMVER_SUN_TRUE}" && test -z "${LTO_PLUGIN_USE_SYMV + as_fn_error $? "conditional \"LTO_PLUGIN_USE_SYMVER_SUN\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + + : "${CONFIG_STATUS=./config.status}" + ac_write_fail=0 +diff --git a/lto-plugin/configure.ac b/lto-plugin/configure.ac +index bc5b618a495..317596288b2 100644 +--- a/lto-plugin/configure.ac ++++ b/lto-plugin/configure.ac +@@ -88,6 +88,7 @@ AM_CONDITIONAL(LTO_PLUGIN_USE_SYMVER_GNU, [test "x$lto_plugin_use_symver" = xgnu + AM_CONDITIONAL(LTO_PLUGIN_USE_SYMVER_SUN, [test "x$lto_plugin_use_symver" = xsun]) + + AM_PROG_LIBTOOL ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + ACX_LT_HOST_FLAGS + AC_SUBST(target_noncanonical) + AC_TYPE_INT64_T +diff --git a/zlib/configure b/zlib/configure +index f489f31bc70..f7adce0db2c 100755 +--- a/zlib/configure ++++ b/zlib/configure +@@ -639,6 +639,8 @@ TARGET_LIBRARY_FALSE + TARGET_LIBRARY_TRUE + toolexeclibdir + toolexecdir ++ENABLE_DARWIN_AT_RPATH_FALSE ++ENABLE_DARWIN_AT_RPATH_TRUE + CPP + OTOOL64 + OTOOL +@@ -776,6 +778,7 @@ with_pic + enable_fast_install + with_gnu_ld + enable_libtool_lock ++enable_darwin_at_rpath + with_toolexeclibdir + enable_host_shared + ' +@@ -1419,6 +1422,8 @@ Optional Features: + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) ++ --enable-darwin-at-path install libraries with @rpath/library-name, requires ++ rpaths to be added to executables + --enable-host-shared build host code as shared libraries + + Optional Packages: +@@ -4169,7 +4174,7 @@ case "$host" in + case "$enable_cet" in + auto) + # Check if target supports multi-byte NOPs +- # and if assembler supports CET insn. ++ # and if compiler and assembler support CET insn. + cet_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fcf-protection" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -8908,6 +8913,47 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + darwin* | rhapsody*) + + ++ ++ # Publish an arg to allow the user to select that Darwin host (and target) ++ # libraries should be given install-names like @rpath/libfoo.dylib. This ++ # requires that the user of the library then adds an 'rpath' to the DSO that ++ # needs access. ++ # NOTE: there are defaults below, for systems that support rpaths. The person ++ # configuring can override the defaults for any system version that supports ++ # them - they are, however, forced off for system versions without support. ++ # Check whether --enable-darwin-at-rpath was given. ++if test "${enable_darwin_at_rpath+set}" = set; then : ++ enableval=$enable_darwin_at_rpath; if test "x$enable_darwin_at_rpath" = "xyes"; then ++ # This is not supported before macOS 10.5 / Darwin9. ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ echo "WARNING: Darwin @rpath library names are incompatible with macOS versions earlier than 10.5 (rpaths disabled)" 1>&5 ++ enable_darwin_at_rpath=no ++ ;; ++ esac ++ fi ++else ++ case ${MACOSX_DEPLOYMENT_TARGET-UNSET},$host_os in ++ # As above, before 10.5 / Darwin9 this does not work. ++ UNSET,darwin[45678]*|UNSET,rhapsody*|10.[01234][,.]*) ++ enable_darwin_at_rpath=no ++ ;; ++ ++ # We cannot build and test reliably on macOS 10.11+ (Darwin15+) without use ++ # of rpaths, since runpaths set via DYLD_LIBRARY_PATH are elided by key ++ # system executables (e.g. /bin/sh). Force rpaths on for these systems. ++ UNSET,darwin1[56789]*|UNSET,darwin2*|10.1[123456789][,.]*|1[123456789].*[,.]* ) ++ echo "@rpath library names are needed on macOS versions later than 10.11 (rpaths enabled)" 1>&5 ++ enable_darwin_at_rpath=yes ++ ;; ++ # NOTE: we are not (yet) doing anything for 10.5 .. 10.10, since they can ++ # work with either DYLD_LIBRARY_PATH or embedded rpaths. ++ ++ esac ++ ++fi ++ ++ + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes +@@ -8925,10 +8971,19 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all +- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" +- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" +- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" +- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ echo "using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name @rpath/\$soname \$verstring ${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ else ++ echo "NOT using Darwin @rpath" 1>&5 ++ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" ++ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" ++ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" ++ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" ++ fi + + else + ld_shlibs=no +@@ -10735,7 +10790,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10748 "configure" ++#line 10793 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -10841,7 +10896,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 10854 "configure" ++#line 10899 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11078,6 +11133,14 @@ CC="$lt_save_CC" + # Only expand once: + + ++ if test x$enable_darwin_at_rpath = xyes; then ++ ENABLE_DARWIN_AT_RPATH_TRUE= ++ ENABLE_DARWIN_AT_RPATH_FALSE='#' ++else ++ ENABLE_DARWIN_AT_RPATH_TRUE='#' ++ ENABLE_DARWIN_AT_RPATH_FALSE= ++fi ++ + + # Find CPP now so that any conditional tests below won't do it and + # thereby make the resulting definitions conditional. +@@ -11708,6 +11771,10 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_DARWIN_AT_RPATH_TRUE}" && test -z "${ENABLE_DARWIN_AT_RPATH_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_DARWIN_AT_RPATH\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${TARGET_LIBRARY_TRUE}" && test -z "${TARGET_LIBRARY_FALSE}"; then + 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..2327d003a05 100644 +--- a/zlib/configure.ac ++++ b/zlib/configure.ac +@@ -64,6 +64,7 @@ GCC_CET_FLAGS(CET_FLAGS) + AC_SUBST(CET_FLAGS) + + AC_PROG_LIBTOOL ++AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes]) + + # Find CPP now so that any conditional tests below won't do it and + # thereby make the resulting definitions conditional. diff --git a/build/pkgs/gcc/spkg-build.in b/build/pkgs/gcc/spkg-build.in index 2c07c4ea988..215201207d6 100644 --- a/build/pkgs/gcc/spkg-build.in +++ b/build/pkgs/gcc/spkg-build.in @@ -1,41 +1,6 @@ # Exit on error set -e -# The ld (linker) on old Darwin systems doesn't understand -# -compatibility_version, it needs -dylib_compatibility_version. -# Similarly for a few more options. We create an ld wrapper to fix -# these command line options. -if { uname -sr | grep 'Darwin [0-9]\.' ;} &>/dev/null; then - mkdir -p bin/ - LD=`which "${LD:-ld}"` - - echo '#!/bin/bash' >>"bin/ld" - echo '# ld wrapper generated by the GCC spkg' >>"bin/ld" - echo >>"bin/ld" - # Hardcode the path to the "real" ld. - echo "LD=$LD" >>"bin/ld" - -cat >>"bin/ld" <<'EOF' - -for arg in "$@"; do - arg=`echo "$arg" | sed \ - -e 's/^-\(compatibility_version\)/-dylib_\1/' \ - -e 's/^-\(current_version\)/-dylib_\1/' \ - -e 's/^-\(install_name\)/-dylib_\1/' \ - ` - argv=("${argv[@]}" "$arg") -done - -exec "$LD" "${argv[@]}" -EOF - - chmod +x "bin/ld" - - # Make GCC use this ld wrapper (found in the $PATH) - export LD=ld - export PATH="$(pwd)/bin:$PATH" -fi - # On OS X 10.9, g++ and the cdefs.h header are currently incompatible # Temporary workaround posted at http://trac.macports.org/ticket/41033 if { uname -sr | grep 'Darwin 13\.' ;} &>/dev/null; then diff --git a/build/pkgs/gfortran/SPKG.rst b/build/pkgs/gfortran/SPKG.rst index 1bea5fae5fe..9559b98456d 100644 --- a/build/pkgs/gfortran/SPKG.rst +++ b/build/pkgs/gfortran/SPKG.rst @@ -4,8 +4,21 @@ gfortran: Fortran compiler from the GNU Compiler Collection Description ----------- -The GNU Compiler Collection, including the C, C++ and Fortran compiler. -This particular package is meant to only make gfortran available. +This package represents the required Fortran compiler. + +Officially we support ``gfortran`` from `GNU Compiler Collection (GCC) +<https://gcc.gnu.org/>`_. It has also been reported that using ``flang`` +(from LLVM) might work. + +You can pass the names of compilers to use to ``./configure`` using +the environment variables :envvar:`CC`, :envvar:`CXX`, and +:envvar:`FC`, for C, C++, and Fortran compilers, respectively. + +For example, if your C compiler is ``clang``, your C++ compiler is +``clang++``, and your Fortran compiler is ``flang``, then you would +need to run:: + + $ ./configure CC=clang CXX=clang++ FC=flang License ------- diff --git a/build/pkgs/gfortran/spkg-build.in b/build/pkgs/gfortran/spkg-build.in index ac74c12ad5c..bf4ce381cfe 100644 --- a/build/pkgs/gfortran/spkg-build.in +++ b/build/pkgs/gfortran/spkg-build.in @@ -8,4 +8,4 @@ case $(uname -m),"$ARCH" in ;; esac -./build-gcc --disable-bootstrap --enable-languages=fortran $EXTRA_GCC_CONFIGURE +./build-gcc --disable-bootstrap --enable-languages=fortran --disable-darwin-at-rpath $EXTRA_GCC_CONFIGURE diff --git a/build/pkgs/giac/patches/pari_2_15.patch b/build/pkgs/giac/patches/pari_2_15.patch new file mode 100644 index 00000000000..d2900a5ffc7 --- /dev/null +++ b/build/pkgs/giac/patches/pari_2_15.patch @@ -0,0 +1,21 @@ +ANYARG patch + +diff --git a/src/pari.cc b/src/pari.cc +index 76ce8e1..50d08ab 100644 +--- a/src/pari.cc ++++ b/src/pari.cc +@@ -40,6 +40,13 @@ using namespace std; + + #ifdef HAVE_LIBPARI + ++// Anyarg disappeared from PARI 2.15.0 ++#ifdef __cplusplus ++# define ANYARG ... ++#else ++# define ANYARG ++#endif ++ + #ifdef HAVE_PTHREAD_H + #include <pthread.h> + #endif + diff --git a/build/pkgs/giac/spkg-configure.m4 b/build/pkgs/giac/spkg-configure.m4 index d6c1d094198..5859e35f12e 100644 --- a/build/pkgs/giac/spkg-configure.m4 +++ b/build/pkgs/giac/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([giac], [ SAGE_SPKG_DEPCHECK([pari], [ dnl giac does not seem to reveal its patchlevel - m4_pushdef([GIAC_MIN_VERSION], [1.5.0]) + m4_pushdef([GIAC_MIN_VERSION], [1.9.0]) m4_pushdef([GIAC_MAX_VERSION], [1.9.999]) AC_CACHE_CHECK([for giac >= ]GIAC_MIN_VERSION[, <= ]GIAC_MAX_VERSION, [ac_cv_path_GIAC], [ AC_PATH_PROGS_FEATURE_CHECK([GIAC], [giac], [ diff --git a/build/pkgs/git/distros/arch.txt b/build/pkgs/git/distros/arch.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/arch.txt @@ -0,0 +1 @@ +git diff --git a/build/pkgs/github_cli/SPKG.rst b/build/pkgs/github_cli/SPKG.rst new file mode 100644 index 00000000000..5ab5b6bfbdc --- /dev/null +++ b/build/pkgs/github_cli/SPKG.rst @@ -0,0 +1,19 @@ +github_cli: Command-line interface for GitHub +============================================= + +Description +----------- + +``gh`` is GitHub on the command line. It brings pull requests, issues, and +other GitHub concepts to the terminal next to where you are already +working with ``git`` and your code. + +License +------- + +MIT + +Upstream Contact +---------------- + +https://github.com/cli/cli diff --git a/build/pkgs/github_cli/distros/alpine.txt b/build/pkgs/github_cli/distros/alpine.txt new file mode 100644 index 00000000000..5b249a1e859 --- /dev/null +++ b/build/pkgs/github_cli/distros/alpine.txt @@ -0,0 +1 @@ +github-cli diff --git a/build/pkgs/github_cli/distros/arch.txt b/build/pkgs/github_cli/distros/arch.txt new file mode 100644 index 00000000000..5b249a1e859 --- /dev/null +++ b/build/pkgs/github_cli/distros/arch.txt @@ -0,0 +1 @@ +github-cli diff --git a/build/pkgs/github_cli/distros/conda.txt b/build/pkgs/github_cli/distros/conda.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/conda.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/debian.txt b/build/pkgs/github_cli/distros/debian.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/debian.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/fedora.txt b/build/pkgs/github_cli/distros/fedora.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/fedora.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/freebsd.txt b/build/pkgs/github_cli/distros/freebsd.txt new file mode 100644 index 00000000000..7362ba57a11 --- /dev/null +++ b/build/pkgs/github_cli/distros/freebsd.txt @@ -0,0 +1 @@ +devel/gh diff --git a/build/pkgs/github_cli/distros/gentoo.txt b/build/pkgs/github_cli/distros/gentoo.txt new file mode 100644 index 00000000000..660aea3ced4 --- /dev/null +++ b/build/pkgs/github_cli/distros/gentoo.txt @@ -0,0 +1 @@ +dev-util/github-cli diff --git a/build/pkgs/github_cli/distros/homebrew.txt b/build/pkgs/github_cli/distros/homebrew.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/homebrew.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/macports.txt b/build/pkgs/github_cli/distros/macports.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/macports.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/nix.txt b/build/pkgs/github_cli/distros/nix.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/nix.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/opensuse.txt b/build/pkgs/github_cli/distros/opensuse.txt new file mode 100644 index 00000000000..7f8d49f4704 --- /dev/null +++ b/build/pkgs/github_cli/distros/opensuse.txt @@ -0,0 +1 @@ +gh diff --git a/build/pkgs/github_cli/distros/repology.txt b/build/pkgs/github_cli/distros/repology.txt new file mode 100644 index 00000000000..5b249a1e859 --- /dev/null +++ b/build/pkgs/github_cli/distros/repology.txt @@ -0,0 +1 @@ +github-cli diff --git a/build/pkgs/github_cli/distros/void.txt b/build/pkgs/github_cli/distros/void.txt new file mode 100644 index 00000000000..5b249a1e859 --- /dev/null +++ b/build/pkgs/github_cli/distros/void.txt @@ -0,0 +1 @@ +github-cli diff --git a/build/pkgs/github_cli/type b/build/pkgs/github_cli/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/github_cli/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/gitpython/SPKG.rst b/build/pkgs/gitpython/SPKG.rst new file mode 100644 index 00000000000..8928ececb8d --- /dev/null +++ b/build/pkgs/gitpython/SPKG.rst @@ -0,0 +1,18 @@ +gitpython: GitPython is a python library used to interact with Git repositories +=============================================================================== + +Description +----------- + +GitPython is a python library used to interact with Git repositories + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/GitPython/ + diff --git a/build/pkgs/gitpython/dependencies b/build/pkgs/gitpython/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/gitpython/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/gitpython/requirements.txt b/build/pkgs/gitpython/requirements.txt new file mode 100644 index 00000000000..64b1adaeeb4 --- /dev/null +++ b/build/pkgs/gitpython/requirements.txt @@ -0,0 +1 @@ +GitPython diff --git a/build/pkgs/gitpython/type b/build/pkgs/gitpython/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/gitpython/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/gmpy2/checksums.ini b/build/pkgs/gmpy2/checksums.ini index d17df675312..70ecbe7244d 100644 --- a/build/pkgs/gmpy2/checksums.ini +++ b/build/pkgs/gmpy2/checksums.ini @@ -1,5 +1,5 @@ tarball=gmpy2-VERSION.tar.gz -sha1=fcb929ab9a44d96bfb47b7ed411cc2f048b484b2 -md5=877d324e676b162053772affda5c0de7 -cksum=2408333571 +sha1=8280f6c68c57dd4a2fce149162c080c1dac86eb0 +md5=bb21846e99800e04d5b330b76c23ba10 +cksum=3835762323 upstream_url=https://pypi.io/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz diff --git a/build/pkgs/gmpy2/package-version.txt b/build/pkgs/gmpy2/package-version.txt index 3e3c2f1e5ed..eca07e4c1a8 100644 --- a/build/pkgs/gmpy2/package-version.txt +++ b/build/pkgs/gmpy2/package-version.txt @@ -1 +1 @@ -2.1.1 +2.1.2 diff --git a/build/pkgs/gsl/checksums.ini b/build/pkgs/gsl/checksums.ini index affe99b550f..8bef7d5f43c 100644 --- a/build/pkgs/gsl/checksums.ini +++ b/build/pkgs/gsl/checksums.ini @@ -1,5 +1,5 @@ tarball=gsl-VERSION.tar.gz -sha1=29179db0d746f422bb0ceca2cbda4de107a2c651 -md5=9e47e81caaebcd92b7aca27a5348df74 -cksum=2494121348 +sha1=549e1105cd1198537be9707257161531e109bd94 +md5=36aee97e67f64dbdab7afae197e3483b +cksum=171022903 upstream_url=https://ftp.gnu.org/gnu/gsl/gsl-VERSION.tar.gz diff --git a/build/pkgs/gsl/package-version.txt b/build/pkgs/gsl/package-version.txt index 1effb003408..860487ca19c 100644 --- a/build/pkgs/gsl/package-version.txt +++ b/build/pkgs/gsl/package-version.txt @@ -1 +1 @@ -2.7 +2.7.1 diff --git a/build/pkgs/gsl/patches/configure-big_sur.patch b/build/pkgs/gsl/patches/configure-big_sur.patch new file mode 100644 index 00000000000..aba05df4d8a --- /dev/null +++ b/build/pkgs/gsl/patches/configure-big_sur.patch @@ -0,0 +1,23 @@ +--- a/configure.orig 2021-10-01 08:15:08.000000000 -0700 ++++ b/configure 2021-10-20 12:44:47.000000000 -0700 +@@ -8733,16 +8733,11 @@ + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; +- darwin*) # darwin 5.x on +- # if running on 10.5 or later, the deployment target defaults +- # to the OS version, if on x86, and 10.4, the deployment +- # target defaults to 10.4. Don't you love it? +- case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in +- 10.0,*86*-darwin8*|10.0,*-darwin[91]*) +- _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; +- 10.[012][,.]*) ++ darwin*) ++ case ${MACOSX_DEPLOYMENT_TARGET},$host in ++ 10.[012],*|,*powerpc*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; +- 10.*) ++ *) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; diff --git a/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst b/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst new file mode 100644 index 00000000000..4e076e4e3cb --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst @@ -0,0 +1,18 @@ +hatch_fancy_pypi_readme: Fancy PyPI READMEs with Hatch +====================================================== + +Description +----------- + +Fancy PyPI READMEs with Hatch + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-fancy-pypi-readme/ + diff --git a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini new file mode 100644 index 00000000000..6728a45be5c --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_fancy_pypi_readme-VERSION.tar.gz +sha1=2cdf215fdd13de69f5de09c7ef0e2ceff4a03666 +md5=588776ea8e3608714d4cbba16dffa92b +cksum=613442646 +upstream_url=https://pypi.io/packages/source/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION.tar.gz diff --git a/build/pkgs/hatch_fancy_pypi_readme/dependencies b/build/pkgs/hatch_fancy_pypi_readme/dependencies new file mode 100644 index 00000000000..8cd44d06682 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) hatchling + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt b/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt new file mode 100644 index 00000000000..6d9a1f85903 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt @@ -0,0 +1 @@ +hatch-fancy-pypi-readme diff --git a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt new file mode 100644 index 00000000000..9d673278df8 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt @@ -0,0 +1 @@ +22.8.0 diff --git a/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in b/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_fancy_pypi_readme/type b/build/pkgs/hatch_fancy_pypi_readme/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/hatch_nodejs_version/SPKG.rst b/build/pkgs/hatch_nodejs_version/SPKG.rst new file mode 100644 index 00000000000..6a1cd5f991c --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/SPKG.rst @@ -0,0 +1,18 @@ +hatch_nodejs_version: Hatch plugin for versioning from a package.json file +========================================================================== + +Description +----------- + +Hatch plugin for versioning from a package.json file + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-nodejs-version/ + diff --git a/build/pkgs/hatch_nodejs_version/checksums.ini b/build/pkgs/hatch_nodejs_version/checksums.ini new file mode 100644 index 00000000000..bb5f28d0e24 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_nodejs_version-VERSION.tar.gz +sha1=ce77992d461d5108c481e985250cfb401b4ee5df +md5=6e5f9d5cfa442572637478cacaa8ea81 +cksum=1271494344 +upstream_url=https://pypi.io/packages/source/h/hatch_nodejs_version/hatch_nodejs_version-VERSION.tar.gz diff --git a/build/pkgs/hatch_nodejs_version/dependencies b/build/pkgs/hatch_nodejs_version/dependencies new file mode 100644 index 00000000000..e9293f104ca --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) hatchling | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_nodejs_version/install-requires.txt b/build/pkgs/hatch_nodejs_version/install-requires.txt new file mode 100644 index 00000000000..5c606fe80f5 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/install-requires.txt @@ -0,0 +1 @@ +hatch-nodejs-version diff --git a/build/pkgs/hatch_nodejs_version/package-version.txt b/build/pkgs/hatch_nodejs_version/package-version.txt new file mode 100644 index 00000000000..9e11b32fcaa --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/package-version.txt @@ -0,0 +1 @@ +0.3.1 diff --git a/build/pkgs/hatch_nodejs_version/spkg-install.in b/build/pkgs/hatch_nodejs_version/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_nodejs_version/type b/build/pkgs/hatch_nodejs_version/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/hatch_vcs/SPKG.rst b/build/pkgs/hatch_vcs/SPKG.rst new file mode 100644 index 00000000000..51f4780749e --- /dev/null +++ b/build/pkgs/hatch_vcs/SPKG.rst @@ -0,0 +1,16 @@ +hatch_vcs: Hatch plugin for versioning with your preferred VCS +============================================================== + +Description +----------- + +Hatch plugin for versioning with your preferred VCS + +License +------- + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-vcs/ + diff --git a/build/pkgs/hatch_vcs/checksums.ini b/build/pkgs/hatch_vcs/checksums.ini new file mode 100644 index 00000000000..47e0c350f03 --- /dev/null +++ b/build/pkgs/hatch_vcs/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_vcs-VERSION.tar.gz +sha1=9d38f55610b156b513d3d2a79f81cbf4fdea3cb2 +md5=e56b6d0c05cfb9b59d493c67f94d6e48 +cksum=680867691 +upstream_url=https://pypi.io/packages/source/h/hatch_vcs/hatch_vcs-VERSION.tar.gz diff --git a/build/pkgs/hatch_vcs/dependencies b/build/pkgs/hatch_vcs/dependencies new file mode 100644 index 00000000000..8cd44d06682 --- /dev/null +++ b/build/pkgs/hatch_vcs/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) hatchling + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_vcs/install-requires.txt b/build/pkgs/hatch_vcs/install-requires.txt new file mode 100644 index 00000000000..04e2069fbb3 --- /dev/null +++ b/build/pkgs/hatch_vcs/install-requires.txt @@ -0,0 +1 @@ +hatch-vcs diff --git a/build/pkgs/hatch_vcs/package-version.txt b/build/pkgs/hatch_vcs/package-version.txt new file mode 100644 index 00000000000..0ea3a944b39 --- /dev/null +++ b/build/pkgs/hatch_vcs/package-version.txt @@ -0,0 +1 @@ +0.2.0 diff --git a/build/pkgs/hatch_vcs/spkg-install.in b/build/pkgs/hatch_vcs/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_vcs/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_vcs/type b/build/pkgs/hatch_vcs/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_vcs/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index ec1317175da..2980620c8d8 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,5 +1,5 @@ tarball=hatchling-VERSION.tar.gz -sha1=63ae7f29657e4d069c716e098a9ac8114d2f29f9 -md5=e05f845d94f400c3085bbaab21adcdbe -cksum=3213522818 +sha1=5d2e7ac6feffa2dfdbf81e7d10661ac1b08b9608 +md5=e06cc65ac646f9b01df5406aa1f97022 +cksum=310056602 upstream_url=https://pypi.io/packages/source/h/hatchling/hatchling-VERSION.tar.gz diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt index 3a3cd8cc8b0..720c7384c61 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.3.1 +1.11.1 diff --git a/build/pkgs/idna/checksums.ini b/build/pkgs/idna/checksums.ini index d0e20f2098a..47b585b69cd 100644 --- a/build/pkgs/idna/checksums.ini +++ b/build/pkgs/idna/checksums.ini @@ -1,5 +1,5 @@ tarball=idna-VERSION.tar.gz -sha1=08c0449533fc94462f78652dea209099754d9ee4 -md5=5856306eac5f25db8249e37a4c6ee3e7 -cksum=2700709091 +sha1=c01a061b5ace87f662049d205d5d15e7f8a3a533 +md5=13ea24e076212b6baae1135a116d1e0e +cksum=1497605198 upstream_url=https://pypi.io/packages/source/i/idna/idna-VERSION.tar.gz diff --git a/build/pkgs/idna/package-version.txt b/build/pkgs/idna/package-version.txt index eb39e5382f4..2f4b60750dc 100644 --- a/build/pkgs/idna/package-version.txt +++ b/build/pkgs/idna/package-version.txt @@ -1 +1 @@ -3.3 +3.4 diff --git a/build/pkgs/igraph/checksums.ini b/build/pkgs/igraph/checksums.ini index 9da572238e5..f7d3336bda8 100644 --- a/build/pkgs/igraph/checksums.ini +++ b/build/pkgs/igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=igraph-VERSION.tar.gz -sha1=74abe82bdebdefc295a4f04b54170880dcb265e8 -md5=4e61e5e86ebe4fe478df415b7407e87e -cksum=2574937983 +sha1=20587332f0f36d6d7eb3cca248e2dab76e1e58ad +md5=af41eb9c614946c4a92a51834e9cab4a +cksum=4011381306 upstream_url=https://github.com/igraph/igraph/releases/download/VERSION/igraph-VERSION.tar.gz diff --git a/build/pkgs/igraph/package-version.txt b/build/pkgs/igraph/package-version.txt index 571215736a6..5eef0f10e8c 100644 --- a/build/pkgs/igraph/package-version.txt +++ b/build/pkgs/igraph/package-version.txt @@ -1 +1 @@ -0.10.1 +0.10.2 diff --git a/build/pkgs/imagesize/checksums.ini b/build/pkgs/imagesize/checksums.ini index 6a49dac52c4..8e0bef1a816 100644 --- a/build/pkgs/imagesize/checksums.ini +++ b/build/pkgs/imagesize/checksums.ini @@ -1,5 +1,5 @@ tarball=imagesize-VERSION.tar.gz -sha1=b88a92cabe93b5a53faacb1cff4e50f8a2d9427a -md5=3a1e124594183778a8f87e4bcdb6dca9 -cksum=2804705518 +sha1=89627e703f80c3ad2a77cc8168d85d119e71dcbe +md5=5a40586a25c07e1a8f16f6267252c321 +cksum=3711184129 upstream_url=https://pypi.io/packages/source/i/imagesize/imagesize-VERSION.tar.gz diff --git a/build/pkgs/imagesize/package-version.txt b/build/pkgs/imagesize/package-version.txt index 26aaba0e866..347f5833ee6 100644 --- a/build/pkgs/imagesize/package-version.txt +++ b/build/pkgs/imagesize/package-version.txt @@ -1 +1 @@ -1.2.0 +1.4.1 diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini index d5a9b1a2668..4d326297be6 100644 --- a/build/pkgs/importlib_metadata/checksums.ini +++ b/build/pkgs/importlib_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_metadata-VERSION.tar.gz -sha1=7d15d8e06299a8f24e076600899aceee75ce8b0b -md5=a605ba6ec315bc1324fd6b7210fe7c12 -cksum=448954927 +sha1=4a49e8c6d8e2eb02e9ea821444b5ba153d8c34a6 +md5=56d34f2e854bb0f318baa9e47aba3439 +cksum=3438247256 upstream_url=https://pypi.io/packages/source/i/importlib_metadata/importlib_metadata-VERSION.tar.gz diff --git a/build/pkgs/importlib_metadata/package-version.txt b/build/pkgs/importlib_metadata/package-version.txt index 326ec6355f3..831446cbd27 100644 --- a/build/pkgs/importlib_metadata/package-version.txt +++ b/build/pkgs/importlib_metadata/package-version.txt @@ -1 +1 @@ -4.8.2 +5.1.0 diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini index f769a9f43b4..9885db7ffb4 100644 --- a/build/pkgs/importlib_resources/checksums.ini +++ b/build/pkgs/importlib_resources/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_resources-VERSION.tar.gz -sha1=d1f2742895a68f3f8d19dd7285df1687877fb15a -md5=5db738106ca7c05340495c36357986a2 -cksum=1338307365 +sha1=a8c7a6a976fffb9841c548230cb633eda3111c4f +md5=8afc48c5f3a7c4ba63cb38163340d78b +cksum=196052500 upstream_url=https://pypi.io/packages/source/i/importlib_resources/importlib_resources-VERSION.tar.gz diff --git a/build/pkgs/importlib_resources/package-version.txt b/build/pkgs/importlib_resources/package-version.txt index ce7f2b425b5..509b0b618ad 100644 --- a/build/pkgs/importlib_resources/package-version.txt +++ b/build/pkgs/importlib_resources/package-version.txt @@ -1 +1 @@ -5.2.2 +5.10.0 diff --git a/build/pkgs/ipykernel/dependencies b/build/pkgs/ipykernel/dependencies index d2677a8d27b..792c3e70634 100644 --- a/build/pkgs/ipykernel/dependencies +++ b/build/pkgs/ipykernel/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) ipython_genutils importlib_metadata matplotlib_inline ipython jupyter_client tornado appnope traitlets | $(PYTHON_TOOLCHAIN) +$(PYTHON) ipython_genutils importlib_metadata matplotlib_inline ipython jupyter_client tornado appnope traitlets executing | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index ab40792a0a7..a539eb830b4 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,5 +1,5 @@ tarball=ipython-VERSION.tar.gz -sha1=98270c68edcfbcd02b3d76d9c1ab6030bbb92171 -md5=dc5d78f3d622b027a0f32f9a545b44ff -cksum=3907293880 +sha1=e0dd247f29befed1159d9bdca987d90c2ee0d34a +md5=8c98f6def0622ea32975cb779247c3d7 +cksum=2860792697 upstream_url=https://pypi.io/packages/source/i/ipython/ipython-VERSION.tar.gz diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index a2f28f43be3..acd405b1d62 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -8.4.0 +8.6.0 diff --git a/build/pkgs/ipywidgets/checksums.ini b/build/pkgs/ipywidgets/checksums.ini index e250f8111d0..63a890dcf81 100644 --- a/build/pkgs/ipywidgets/checksums.ini +++ b/build/pkgs/ipywidgets/checksums.ini @@ -1,5 +1,5 @@ tarball=ipywidgets-VERSION.tar.gz -sha1=db842df5008a1786ab6434875c2d56b974c5109a -md5=61deb9024c3de1848812b97c2560d0cf -cksum=3129459111 +sha1=b2c8adf4fefc012adfb61e03a2e957bddbbb7597 +md5=c976de164b782eac9e5dfc933e8da295 +cksum=305610881 upstream_url=https://pypi.io/packages/source/i/ipywidgets/ipywidgets-VERSION.tar.gz diff --git a/build/pkgs/ipywidgets/dependencies b/build/pkgs/ipywidgets/dependencies index e6cc2ed95d5..64f5151c754 100644 --- a/build/pkgs/ipywidgets/dependencies +++ b/build/pkgs/ipywidgets/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) widgetsnbextension | $(PYTHON_TOOLCHAIN) ipykernel ipython traitlets +$(PYTHON) widgetsnbextension jupyterlab_widgets | $(PYTHON_TOOLCHAIN) ipykernel ipython traitlets ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipywidgets/package-version.txt b/build/pkgs/ipywidgets/package-version.txt index 1985849fb58..8b22a322d0f 100644 --- a/build/pkgs/ipywidgets/package-version.txt +++ b/build/pkgs/ipywidgets/package-version.txt @@ -1 +1 @@ -7.7.0 +8.0.2 diff --git a/build/pkgs/ipywidgets/patches/0001-setup.py-Remove-install-requires-of-jupyterlab_widge.patch b/build/pkgs/ipywidgets/patches/0001-setup.py-Remove-install-requires-of-jupyterlab_widge.patch deleted file mode 100644 index fed30e5445e..00000000000 --- a/build/pkgs/ipywidgets/patches/0001-setup.py-Remove-install-requires-of-jupyterlab_widge.patch +++ /dev/null @@ -1,35 +0,0 @@ -From e64096431a7099f8db46748ef7d1021939f1a624 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Wed, 24 Mar 2021 12:59:13 -0700 -Subject: [PATCH] setup.py: Remove install-requires of jupyterlab_widgets, - nbformat - ---- - setup.py | 4 ---- - 1 file changed, 4 deletions(-) - -diff --git a/setup.py b/setup.py -index e544267a..bff154af 100644 ---- a/setup.py -+++ b/setup.py -@@ -112,9 +112,6 @@ setuptools_args = {} - install_requires = setuptools_args['install_requires'] = [ - 'ipykernel>=4.5.1', - 'traitlets>=4.3.1', -- # Requiring nbformat to specify bugfix version which is not required by -- # notebook. -- 'nbformat>=4.2.0', - # TODO: Dynamically add this dependency - # only if notebook 4.x is installed in this - # interpreter, to allow ipywidgets to be -@@ -125,7 +122,6 @@ install_requires = setuptools_args['install_requires'] = [ - extras_require = setuptools_args['extras_require'] = { - ':python_version<"3.3"' : ['ipython>=4.0.0,<6.0.0'], - ':python_version>="3.3"': ['ipython>=4.0.0'], -- ':python_version>="3.6"': ['jupyterlab_widgets>=1.0.0'], - 'test:python_version=="2.7"': ['mock'], - 'test': ['pytest>=3.6.0', 'pytest-cov'], - } --- -2.28.0 - diff --git a/build/pkgs/jsonschema/checksums.ini b/build/pkgs/jsonschema/checksums.ini index 955e0401faa..5a214ae38fc 100644 --- a/build/pkgs/jsonschema/checksums.ini +++ b/build/pkgs/jsonschema/checksums.ini @@ -1,5 +1,5 @@ tarball=jsonschema-VERSION.tar.gz -sha1=5f90a208235152dc4f0b8ae51ca2860c0771ff51 -md5=c3f7a29c187bf1d038c66a5d5763eab1 -cksum=2329724463 +sha1=ccea159a8a0c453e6fbbcfd7bb681b1bc766500e +md5=527bc4d51d31e8d0b8a0d833b6a50002 +cksum=1885537635 upstream_url=https://pypi.io/packages/source/j/jsonschema/jsonschema-VERSION.tar.gz diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index f487bdbbe91..51698156cf0 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling +$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling hatch_vcs hatch_fancy_pypi_readme ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jsonschema/package-version.txt b/build/pkgs/jsonschema/package-version.txt index 4404a17baed..1b0a87fdfc9 100644 --- a/build/pkgs/jsonschema/package-version.txt +++ b/build/pkgs/jsonschema/package-version.txt @@ -1 +1 @@ -4.5.1 +4.17.1 diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index 6f89053ffab..7e31fb35fc6 100644 --- a/build/pkgs/jupyter_client/checksums.ini +++ b/build/pkgs/jupyter_client/checksums.ini @@ -1,5 +1,5 @@ tarball=jupyter_client-VERSION.tar.gz -sha1=99e1ce34f9022acbd3cc301d501ff83099f559ff -md5=3863b317e78ba701f5b844c2221a101f -cksum=1126052456 +sha1=0a9446eda476e3614d4509db0646ae5a89f6b492 +md5=481db492a8a0d16022c49481438e6285 +cksum=3316985535 upstream_url=https://pypi.io/packages/source/j/jupyter_client/jupyter_client-VERSION.tar.gz diff --git a/build/pkgs/jupyter_client/package-version.txt b/build/pkgs/jupyter_client/package-version.txt index c968a5762bf..4e61aeef901 100644 --- a/build/pkgs/jupyter_client/package-version.txt +++ b/build/pkgs/jupyter_client/package-version.txt @@ -1 +1 @@ -7.3.4 +7.4.4 diff --git a/build/pkgs/jupyter_core/checksums.ini b/build/pkgs/jupyter_core/checksums.ini index 88fbd83a188..5a49b040b98 100644 --- a/build/pkgs/jupyter_core/checksums.ini +++ b/build/pkgs/jupyter_core/checksums.ini @@ -1,5 +1,5 @@ tarball=jupyter_core-VERSION.tar.gz -sha1=f5db3f86f2cf40c9371bfa16a783a79a3502cac4 -md5=812d7410ffb4b671b23a68ef4bf40c12 -cksum=2575874942 +sha1=6e48f90477c11ad41b9404732a2bdcb485f4e630 +md5=84d207d4c48513a2b87ff2ed508beb98 +cksum=2072829465 upstream_url=https://pypi.io/packages/source/j/jupyter_core/jupyter_core-VERSION.tar.gz diff --git a/build/pkgs/jupyter_core/package-version.txt b/build/pkgs/jupyter_core/package-version.txt index 2da4316236a..4f89fb96069 100644 --- a/build/pkgs/jupyter_core/package-version.txt +++ b/build/pkgs/jupyter_core/package-version.txt @@ -1 +1 @@ -4.10.0 +4.11.2 diff --git a/build/pkgs/jupyter_packaging/checksums.ini b/build/pkgs/jupyter_packaging/checksums.ini index eb449d9da6f..65b3bd148f6 100644 --- a/build/pkgs/jupyter_packaging/checksums.ini +++ b/build/pkgs/jupyter_packaging/checksums.ini @@ -1,5 +1,5 @@ tarball=jupyter_packaging-VERSION.tar.gz -sha1=c9b7dd75a70ad6a7c621588db410f07fba7e0fad -md5=d30e6fb387d46398a3ab26765b8fa74f -cksum=669146472 +sha1=092d249360aa56838a188decc4bcd09647fda4d9 +md5=9c6834023bd699bda5365ab7ed18bde2 +cksum=3308833189 upstream_url=https://pypi.io/packages/source/j/jupyter_packaging/jupyter_packaging-VERSION.tar.gz diff --git a/build/pkgs/jupyter_packaging/package-version.txt b/build/pkgs/jupyter_packaging/package-version.txt index 26acbf080be..aa22d3ce39b 100644 --- a/build/pkgs/jupyter_packaging/package-version.txt +++ b/build/pkgs/jupyter_packaging/package-version.txt @@ -1 +1 @@ -0.12.2 +0.12.3 diff --git a/build/pkgs/jupyterlab_widgets/SPKG.rst b/build/pkgs/jupyterlab_widgets/SPKG.rst index f17f3c08eda..758cff7b3db 100644 --- a/build/pkgs/jupyterlab_widgets/SPKG.rst +++ b/build/pkgs/jupyterlab_widgets/SPKG.rst @@ -1,18 +1,18 @@ -jupyterlab_widgets: A JupyterLab extension for Jupyter/IPython widgets -====================================================================== +jupyterlab_widgets: Jupyter interactive widgets for JupyterLab +============================================================== Description ----------- -A JupyterLab extension for Jupyter/IPython widgets. +Jupyter interactive widgets for JupyterLab License ------- -BSD License +BSD-3-Clause Upstream Contact ---------------- -Home page: https://github.com/jupyter-widgets/ipywidgets +https://pypi.org/project/jupyterlab-widgets/ diff --git a/build/pkgs/jupyterlab_widgets/checksums.ini b/build/pkgs/jupyterlab_widgets/checksums.ini new file mode 100644 index 00000000000..5d021049263 --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/checksums.ini @@ -0,0 +1,5 @@ +tarball=jupyterlab_widgets-VERSION-py3-none-any.whl +sha1=584e25e221b38c3ca7139667621a3eaf23260ffc +md5=d7b643a04ef1bb9012c58e6833d277c9 +cksum=202463248 +upstream_url=https://pypi.io/packages/py3/j/jupyterlab_widgets/jupyterlab_widgets-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyterlab_widgets/dependencies b/build/pkgs/jupyterlab_widgets/dependencies index 4d2deddc8bf..0738c2d7777 100644 --- a/build/pkgs/jupyterlab_widgets/dependencies +++ b/build/pkgs/jupyterlab_widgets/dependencies @@ -1,4 +1,4 @@ -ipympl jupyterlab nodejs tornado $(PYTHON) | $(PYTHON_TOOLCHAIN) jupyter_packaging +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jupyterlab_widgets/requirements.txt b/build/pkgs/jupyterlab_widgets/install-requires.txt similarity index 100% rename from build/pkgs/jupyterlab_widgets/requirements.txt rename to build/pkgs/jupyterlab_widgets/install-requires.txt diff --git a/build/pkgs/jupyterlab_widgets/package-version.txt b/build/pkgs/jupyterlab_widgets/package-version.txt new file mode 100644 index 00000000000..75a22a26ac4 --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/package-version.txt @@ -0,0 +1 @@ +3.0.3 diff --git a/build/pkgs/jupyterlab_widgets/type b/build/pkgs/jupyterlab_widgets/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/jupyterlab_widgets/type +++ b/build/pkgs/jupyterlab_widgets/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/kissat/SPKG.rst b/build/pkgs/kissat/SPKG.rst new file mode 100644 index 00000000000..394be2a3daa --- /dev/null +++ b/build/pkgs/kissat/SPKG.rst @@ -0,0 +1,31 @@ +kissat: SAT solver +================== + +Description +----------- + +From the package README:: + + KISSAT is a "keep it simple and clean bare metal SAT solver" written in C. + It is a port of CaDiCaL back to C with improved data structures, better + scheduling of inprocessing and optimized algorithms and implementation. + + Coincidentally 'kissat' also means 'cats' in Finnish. + +From the website:: + + The Kissat SAT solver is a condensed and improved reimplementation of + CaDiCaL in C. + + Kissat won first place in the main track of the SAT Competition 2020 and + first place on unsatisfiable instances. + +License +------- + +MIT license. + +Upstream Contact +---------------- + +Website: http://fmv.jku.at/kissat/ diff --git a/build/pkgs/kissat/checksums.ini b/build/pkgs/kissat/checksums.ini new file mode 100644 index 00000000000..66b187bb599 --- /dev/null +++ b/build/pkgs/kissat/checksums.ini @@ -0,0 +1,5 @@ +tarball=kissat-rel-VERSION.tar.gz +sha1=abfd971d5f560ed76281ed3ed7b75e20cb445618 +md5=6ef4b2efcc60c95a32581bfe59720154 +cksum=1532123399 +upstream_url=https://github.com/arminbiere/kissat/archive/refs/tags/rel-VERSION.tar.gz diff --git a/build/pkgs/kissat/dependencies b/build/pkgs/kissat/dependencies new file mode 100644 index 00000000000..4f00de20375 --- /dev/null +++ b/build/pkgs/kissat/dependencies @@ -0,0 +1,4 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/kissat/distros/fedora.txt b/build/pkgs/kissat/distros/fedora.txt new file mode 100644 index 00000000000..18393dcfd49 --- /dev/null +++ b/build/pkgs/kissat/distros/fedora.txt @@ -0,0 +1 @@ +kissat diff --git a/build/pkgs/kissat/distros/gentoo.txt b/build/pkgs/kissat/distros/gentoo.txt new file mode 100644 index 00000000000..73d01328cca --- /dev/null +++ b/build/pkgs/kissat/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/kissat diff --git a/build/pkgs/kissat/distros/nix.txt b/build/pkgs/kissat/distros/nix.txt new file mode 100644 index 00000000000..18393dcfd49 --- /dev/null +++ b/build/pkgs/kissat/distros/nix.txt @@ -0,0 +1 @@ +kissat diff --git a/build/pkgs/kissat/distros/repology.txt b/build/pkgs/kissat/distros/repology.txt new file mode 100644 index 00000000000..18393dcfd49 --- /dev/null +++ b/build/pkgs/kissat/distros/repology.txt @@ -0,0 +1 @@ +kissat diff --git a/build/pkgs/kissat/package-version.txt b/build/pkgs/kissat/package-version.txt new file mode 100644 index 00000000000..4a36342fcab --- /dev/null +++ b/build/pkgs/kissat/package-version.txt @@ -0,0 +1 @@ +3.0.0 diff --git a/build/pkgs/kissat/spkg-check.in b/build/pkgs/kissat/spkg-check.in new file mode 100644 index 00000000000..05a55f6cb3a --- /dev/null +++ b/build/pkgs/kissat/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make test diff --git a/build/pkgs/kissat/spkg-install.in b/build/pkgs/kissat/spkg-install.in new file mode 100644 index 00000000000..0b8c0a3ccc2 --- /dev/null +++ b/build/pkgs/kissat/spkg-install.in @@ -0,0 +1,5 @@ +cd src +./configure CC="$CC" --test --kitten +cd build +sdh_make +sdh_install kissat kitten "$SAGE_LOCAL"/bin diff --git a/build/pkgs/kissat/type b/build/pkgs/kissat/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/kissat/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/kiwisolver/checksums.ini b/build/pkgs/kiwisolver/checksums.ini index fc15fadea23..3fc15248198 100644 --- a/build/pkgs/kiwisolver/checksums.ini +++ b/build/pkgs/kiwisolver/checksums.ini @@ -1,5 +1,5 @@ tarball=kiwisolver-VERSION.tar.gz -sha1=61811685031328a8a2a77f45593d8238432ce35e -md5=98d746f558685c6b6658cefc39be14df -cksum=1915059157 +sha1=157556602639eb6cc8546463f56feaa9023e3bcd +md5=73a4e57c33ded99dbe9a5cabca5be04b +cksum=3382585353 upstream_url=https://files.pythonhosted.org/packages/source/k/kiwisolver/kiwisolver-VERSION.tar.gz diff --git a/build/pkgs/kiwisolver/package-version.txt b/build/pkgs/kiwisolver/package-version.txt index 1892b926767..428b770e3e2 100644 --- a/build/pkgs/kiwisolver/package-version.txt +++ b/build/pkgs/kiwisolver/package-version.txt @@ -1 +1 @@ -1.3.2 +1.4.3 diff --git a/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch b/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch index 308456304d7..199ab2d0156 100644 --- a/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch +++ b/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch @@ -51,29 +51,3 @@ index c9fa4ace..43a4ab63 100644 ZZ scalar_power(const vec_ZZ &generic_vector, -diff --git a/code/latte/sqlite/IntegrationDB.cpp b/code/latte/sqlite/IntegrationDB.cpp -index ab8df535..c1dde830 100644 ---- a/code/latte/sqlite/IntegrationDB.cpp -+++ b/code/latte/sqlite/IntegrationDB.cpp -@@ -1277,7 +1277,7 @@ void IntegrationDB::insertSpecficPolytopeIntegrationTest(string polymakeFile, i - * @parm filePath: to the latte-style polynomial. - * @return rowid of the inserted row. - */ --int IntegrationDB::insertPolynomial(int dim, int degree, const char*filePath) throw(SqliteDBexception) -+int IntegrationDB::insertPolynomial(int dim, int degree, const char*filePath) - { - if ( doesPolynomialExist(filePath)) - throw SqliteDBexception(string("insertPolynomial::Polynomial ")+filePath+" already exist"); -diff --git a/code/latte/sqlite/IntegrationDB.h b/code/latte/sqlite/IntegrationDB.h -index d690a832..ce8cfac6 100644 ---- a/code/latte/sqlite/IntegrationDB.h -+++ b/code/latte/sqlite/IntegrationDB.h -@@ -67,7 +67,7 @@ class IntegrationDB: public SqliteDB - int insertIntegrationTest(int polynomialID, int polytopeID); - void insertIntegrationTest(int dim, int degree, int vertexCount, int count); - void insertSpecficPolytopeIntegrationTest(string polymakeFile, int degree, int count); -- int insertPolynomial(int dim, int degree, const char*filePath) throw(SqliteDBexception); -+ int insertPolynomial(int dim, int degree, const char*filePath); - - int insertPolytope(int dim, int vertexCount, int simple, int dualRowID, const char* latteFilePath, const char* polymakeFilePath); - diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index b2055242c4d..cbf7302ffb1 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,5 +1,5 @@ tarball=matplotlib-VERSION.tar.gz -sha1=8db4a2bb5c5a56b4ff9a33b59d134404774d85d5 -md5=5a77336c6c5549bed5f9631c734dedc5 -cksum=602312637 +sha1=2b78c671f95d52c65154a0dc68372a97582768e5 +md5=77ca9a5b42152c9e2aeca1556f08f5ce +cksum=3201608 upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index f13fd3f367a..cfcf3edda7e 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler qhull fonttools | $(PYTHON_TOOLCHAIN) kiwisolver certifi setuptools_scm_git_archive +$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler qhull fonttools contourpy | $(PYTHON_TOOLCHAIN) kiwisolver certifi setuptools_scm_git_archive ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 87ce492908a..b72762837ea 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -3.5.2 +3.6.2 diff --git a/build/pkgs/matplotlib_inline/checksums.ini b/build/pkgs/matplotlib_inline/checksums.ini index e0fa917bb26..73d1d152d74 100644 --- a/build/pkgs/matplotlib_inline/checksums.ini +++ b/build/pkgs/matplotlib_inline/checksums.ini @@ -1,5 +1,5 @@ tarball=matplotlib-inline-VERSION.tar.gz -sha1=36cf63817e1e3df521c7aa8c2612f890b4ae7990 -md5=7af8f8ae6ea5217134dd7bd115c78ac3 -cksum=3542531131 +sha1=a09347e3f2eaa6f9453c773132bf4bd9d38e2163 +md5=aded9a57e2f526f76b3a4851d5528d4f +cksum=3152771377 upstream_url=https://pypi.io/packages/source/m/matplotlib_inline/matplotlib-inline-VERSION.tar.gz diff --git a/build/pkgs/matplotlib_inline/package-version.txt b/build/pkgs/matplotlib_inline/package-version.txt index d917d3e26ad..c946ee6160c 100644 --- a/build/pkgs/matplotlib_inline/package-version.txt +++ b/build/pkgs/matplotlib_inline/package-version.txt @@ -1 +1 @@ -0.1.2 +0.1.6 diff --git a/build/pkgs/meson/SPKG.rst b/build/pkgs/meson/SPKG.rst new file mode 100644 index 00000000000..6a586f562b6 --- /dev/null +++ b/build/pkgs/meson/SPKG.rst @@ -0,0 +1,18 @@ +meson: A high performance build system +====================================== + +Description +----------- + +A high performance build system + +License +------- + +Apache License, Version 2.0 + +Upstream Contact +---------------- + +https://pypi.org/project/meson/ + diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini new file mode 100644 index 00000000000..39f0a42c527 --- /dev/null +++ b/build/pkgs/meson/checksums.ini @@ -0,0 +1,5 @@ +tarball=meson-VERSION.tar.gz +sha1=3bce963302f547547c82fda35f84838ebc608e8a +md5=b2f2757b5dd84cc754b9df53ce37a175 +cksum=2257545181 +upstream_url=https://pypi.io/packages/source/m/meson/meson-VERSION.tar.gz diff --git a/build/pkgs/meson/dependencies b/build/pkgs/meson/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/meson/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/meson/distros/alpine.txt b/build/pkgs/meson/distros/alpine.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/alpine.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/arch.txt b/build/pkgs/meson/distros/arch.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/arch.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/debian.txt b/build/pkgs/meson/distros/debian.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/debian.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/fedora.txt b/build/pkgs/meson/distros/fedora.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/fedora.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/freebsd.txt b/build/pkgs/meson/distros/freebsd.txt new file mode 100644 index 00000000000..9dfdf90524a --- /dev/null +++ b/build/pkgs/meson/distros/freebsd.txt @@ -0,0 +1 @@ +devel/meson diff --git a/build/pkgs/meson/distros/gentoo.txt b/build/pkgs/meson/distros/gentoo.txt new file mode 100644 index 00000000000..b5c4183b217 --- /dev/null +++ b/build/pkgs/meson/distros/gentoo.txt @@ -0,0 +1 @@ +dev-util/meson diff --git a/build/pkgs/meson/distros/homebrew.txt b/build/pkgs/meson/distros/homebrew.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/homebrew.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/nix.txt b/build/pkgs/meson/distros/nix.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/nix.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/opensuse.txt b/build/pkgs/meson/distros/opensuse.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/opensuse.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/repology.txt b/build/pkgs/meson/distros/repology.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/repology.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/slackware.txt b/build/pkgs/meson/distros/slackware.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/slackware.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/install-requires.txt b/build/pkgs/meson/install-requires.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/install-requires.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt new file mode 100644 index 00000000000..068337d8307 --- /dev/null +++ b/build/pkgs/meson/package-version.txt @@ -0,0 +1 @@ +0.63.3 diff --git a/build/pkgs/meson/spkg-configure.m4 b/build/pkgs/meson/spkg-configure.m4 new file mode 100644 index 00000000000..3fbcf288065 --- /dev/null +++ b/build/pkgs/meson/spkg-configure.m4 @@ -0,0 +1,15 @@ +SAGE_SPKG_CONFIGURE( + [meson], [ + AC_CACHE_CHECK([for meson >= 0.63.3], [ac_cv_path_MESON], [ + AC_PATH_PROGS_FEATURE_CHECK([MESON], [meson], [ + meson_version=`$ac_path_MESON --version 2>&1` + AS_IF([test -n "$meson_version"], [ + AX_COMPARE_VERSION([$meson_version], [ge], [0.63.3], [ + ac_cv_path_MESON="$ac_path_MESON" + ac_path_MESON_found=: + ]) + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MESON"], [sage_spkg_install_meson=yes]) +]) diff --git a/build/pkgs/meson/spkg-install.in b/build/pkgs/meson/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/meson/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/meson/type b/build/pkgs/meson/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/meson/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/meson_python/SPKG.rst b/build/pkgs/meson_python/SPKG.rst new file mode 100644 index 00000000000..d467894289c --- /dev/null +++ b/build/pkgs/meson_python/SPKG.rst @@ -0,0 +1,16 @@ +meson_python: Meson Python build backend (PEP 517) +================================================== + +Description +----------- + +Meson Python build backend (PEP 517) + +License +------- + +Upstream Contact +---------------- + +https://pypi.org/project/meson-python/ + diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini new file mode 100644 index 00000000000..fa69cec7dca --- /dev/null +++ b/build/pkgs/meson_python/checksums.ini @@ -0,0 +1,5 @@ +tarball=meson_python-VERSION.tar.gz +sha1=09035196e1576073a7e4acac1f010e5e07e55f89 +md5=60856897b63bc91e1f953bf29f410be4 +cksum=3201302061 +upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/dependencies b/build/pkgs/meson_python/dependencies new file mode 100644 index 00000000000..160adbf36c9 --- /dev/null +++ b/build/pkgs/meson_python/dependencies @@ -0,0 +1,10 @@ +$(PYTHON) meson pyproject_metadata tomli ninja_build patchelf | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. + +meson_python actually declares a dependency on ninja, the Python distribution package. +But it only needs the ninja executable. + +buildelf is needed by projects that use meson_python as their build system +for wheel building. diff --git a/build/pkgs/meson_python/dependencies_check b/build/pkgs/meson_python/dependencies_check new file mode 100644 index 00000000000..7aa85eab873 --- /dev/null +++ b/build/pkgs/meson_python/dependencies_check @@ -0,0 +1 @@ +pytest pytest_mock gitpython cython auditwheel_or_delocate diff --git a/build/pkgs/meson_python/install-requires.txt b/build/pkgs/meson_python/install-requires.txt new file mode 100644 index 00000000000..9705cab644e --- /dev/null +++ b/build/pkgs/meson_python/install-requires.txt @@ -0,0 +1 @@ +meson-python diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt new file mode 100644 index 00000000000..d9df1bbc0c7 --- /dev/null +++ b/build/pkgs/meson_python/package-version.txt @@ -0,0 +1 @@ +0.11.0 diff --git a/build/pkgs/meson_python/spkg-check.in b/build/pkgs/meson_python/spkg-check.in new file mode 100644 index 00000000000..8dd1124505a --- /dev/null +++ b/build/pkgs/meson_python/spkg-check.in @@ -0,0 +1,2 @@ +cd src +pytest diff --git a/build/pkgs/meson_python/spkg-install.in b/build/pkgs/meson_python/spkg-install.in new file mode 100644 index 00000000000..b3bbe7b8f3e --- /dev/null +++ b/build/pkgs/meson_python/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install --no-build-isolation --no-deps . diff --git a/build/pkgs/meson_python/type b/build/pkgs/meson_python/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/meson_python/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/mistune/checksums.ini b/build/pkgs/mistune/checksums.ini index fe33dfb5b7d..b8b60db1d7b 100644 --- a/build/pkgs/mistune/checksums.ini +++ b/build/pkgs/mistune/checksums.ini @@ -1,5 +1,5 @@ tarball=mistune-VERSION.tar.gz -sha1=fd210c038fa7b0f2dffad6847b17dc139e7dd9fe -md5=fb6ab174ece938dea09f8b2adad771e4 -cksum=4120783541 +sha1=c15d02c98d04a3e615c3c1932d1b9a3b1759067a +md5=a4437edb22cf6519a7c61730fecb1a3f +cksum=2925260381 upstream_url=https://pypi.io/packages/source/m/mistune/mistune-VERSION.tar.gz diff --git a/build/pkgs/mistune/package-version.txt b/build/pkgs/mistune/package-version.txt index b60d71966ae..2165f8f9b6a 100644 --- a/build/pkgs/mistune/package-version.txt +++ b/build/pkgs/mistune/package-version.txt @@ -1 +1 @@ -0.8.4 +2.0.4 diff --git a/build/pkgs/msolve/SPKG.rst b/build/pkgs/msolve/SPKG.rst new file mode 100644 index 00000000000..00c1c417208 --- /dev/null +++ b/build/pkgs/msolve/SPKG.rst @@ -0,0 +1,21 @@ +msolve: Multivariate polynomial system solver +============================================= + +Description +----------- + +Open source C library implementing computer algebra algorithms for solving +polynomial systems (with rational coefficients or coefficients in a prime field). + +License +------- + +GPL v2+ + +Upstream Contact +---------------- + +https://github.com/algebraic-solving/msolve + +Upstream does not make source tarballs. +We make tarballs from the fork https://github.com/mkoeppe/msolve (branch 0.4.4+sage) diff --git a/build/pkgs/msolve/checksums.ini b/build/pkgs/msolve/checksums.ini new file mode 100644 index 00000000000..0b7558afd2b --- /dev/null +++ b/build/pkgs/msolve/checksums.ini @@ -0,0 +1,5 @@ +tarball=msolve-VERSION.tar.gz +sha1=5b227de8b222bfe8d143e1d7ea77ad71cd209dc8 +md5=2f34bd9ccb089688ae169201281108dc +cksum=941373315 +upstream_url=https://trac.sagemath.org/raw-attachment/ticket/31664/msolve-VERSION.tar.gz diff --git a/build/pkgs/msolve/dependencies b/build/pkgs/msolve/dependencies new file mode 100644 index 00000000000..4f96ff0a6c9 --- /dev/null +++ b/build/pkgs/msolve/dependencies @@ -0,0 +1,4 @@ +$(MP_LIBRARY) flint mpfr + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/msolve/package-version.txt b/build/pkgs/msolve/package-version.txt new file mode 100644 index 00000000000..fb78594e923 --- /dev/null +++ b/build/pkgs/msolve/package-version.txt @@ -0,0 +1 @@ +0.4.4+sage-2022-09-11 diff --git a/build/pkgs/msolve/spkg-install.in b/build/pkgs/msolve/spkg-install.in new file mode 100644 index 00000000000..2aaf0e99043 --- /dev/null +++ b/build/pkgs/msolve/spkg-install.in @@ -0,0 +1,4 @@ +cd src +sdh_configure +sdh_make +sdh_make_install diff --git a/build/pkgs/msolve/type b/build/pkgs/msolve/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/msolve/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/nbclient/checksums.ini b/build/pkgs/nbclient/checksums.ini index de286fc5e1b..459341ff20e 100644 --- a/build/pkgs/nbclient/checksums.ini +++ b/build/pkgs/nbclient/checksums.ini @@ -1,5 +1,5 @@ tarball=nbclient-VERSION.tar.gz -sha1=e20fb7fc1fe8c421f3f7e8f749ee8fdc38c1811a -md5=ad28a33484bbd348ef04f142d32669f5 -cksum=2210542646 +sha1=8f6309bc37fb2c5b49fcdfe835b6ef9762fa583c +md5=6a59800791be74079cf2ade421526289 +cksum=1870577652 upstream_url=https://pypi.io/packages/source/n/nbclient/nbclient-VERSION.tar.gz diff --git a/build/pkgs/nbclient/package-version.txt b/build/pkgs/nbclient/package-version.txt index d2b13eb644d..faef31a4357 100644 --- a/build/pkgs/nbclient/package-version.txt +++ b/build/pkgs/nbclient/package-version.txt @@ -1 +1 @@ -0.6.4 +0.7.0 diff --git a/build/pkgs/nbconvert/SPKG.rst b/build/pkgs/nbconvert/SPKG.rst index 98ee0393814..be08606f92a 100644 --- a/build/pkgs/nbconvert/SPKG.rst +++ b/build/pkgs/nbconvert/SPKG.rst @@ -6,3 +6,14 @@ Description jupyter nbconvert converts notebooks to various other formats via Jinja templates. + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/nbconvert/ + diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index e10e8b224e8..1c3926c6ce3 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,5 +1,5 @@ -tarball=nbconvert-VERSION.tar.gz -sha1=061c9f18039aa8b6c5f83fc2281cbf8f6c4f3198 -md5=486a48c4dc3986e8801058273964d189 -cksum=183380977 -upstream_url=https://pypi.io/packages/source/n/nbconvert/nbconvert-VERSION.tar.gz +tarball=nbconvert-VERSION-py3-none-any.whl +sha1=de3dd5e475d84c2d143c03dfc22bfc490c03092f +md5=942dd716bd6976c58fdbcfec97bfbe20 +cksum=610202196 +upstream_url=https://pypi.io/packages/py3/n/nbconvert/nbconvert-VERSION-py3-none-any.whl diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index f22d756da39..429dc57af3a 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -6.5.0 +7.2.3 diff --git a/build/pkgs/nbformat/checksums.ini b/build/pkgs/nbformat/checksums.ini index 39cbb5ac27b..af04e4f35aa 100644 --- a/build/pkgs/nbformat/checksums.ini +++ b/build/pkgs/nbformat/checksums.ini @@ -1,5 +1,5 @@ tarball=nbformat-VERSION.tar.gz -sha1=77f321311289719867b958f40f58a570f3825c11 -md5=a11ccf44c2d984d1b8325a3463a9ae20 -cksum=3746246082 +sha1=ecad83c07bdc475f6fd88d28485cf8fe31fbba41 +md5=5e11cc3240d4b1410610786309cc6076 +cksum=767940068 upstream_url=https://pypi.io/packages/source/n/nbformat/nbformat-VERSION.tar.gz diff --git a/build/pkgs/nbformat/dependencies b/build/pkgs/nbformat/dependencies index 258e07a9163..6c8921f1382 100644 --- a/build/pkgs/nbformat/dependencies +++ b/build/pkgs/nbformat/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) jsonschema fastjsonschema jupyter_core traitlets | $(PYTHON_TOOLCHAIN) +$(PYTHON) jsonschema fastjsonschema jupyter_core traitlets | $(PYTHON_TOOLCHAIN) hatchling hatch_nodejs_version ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/nbformat/package-version.txt b/build/pkgs/nbformat/package-version.txt index 8a30e8f94a3..42cdd0b540f 100644 --- a/build/pkgs/nbformat/package-version.txt +++ b/build/pkgs/nbformat/package-version.txt @@ -1 +1 @@ -5.4.0 +5.7.0 diff --git a/build/pkgs/nest_asyncio/checksums.ini b/build/pkgs/nest_asyncio/checksums.ini index 3ffec6bed8b..1a11eb5178b 100644 --- a/build/pkgs/nest_asyncio/checksums.ini +++ b/build/pkgs/nest_asyncio/checksums.ini @@ -1,5 +1,5 @@ tarball=nest_asyncio-VERSION.tar.gz -sha1=ca7b9c57e544a7ca1e96d37ead648588cfb9a3eb -md5=0243278ed8804811b00049a545856dcb -cksum=3944772454 +sha1=1e862862afe4c2e057065212eefe7203ccee4927 +md5=7c7108921a64e7abbb6993803343819b +cksum=982689987 upstream_url=https://pypi.io/packages/source/n/nest_asyncio/nest_asyncio-VERSION.tar.gz diff --git a/build/pkgs/nest_asyncio/package-version.txt b/build/pkgs/nest_asyncio/package-version.txt index 9075be49515..eac1e0ada6d 100644 --- a/build/pkgs/nest_asyncio/package-version.txt +++ b/build/pkgs/nest_asyncio/package-version.txt @@ -1 +1 @@ -1.5.5 +1.5.6 diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index fd005a4d478..00b6f1fa335 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,5 +1,5 @@ tarball=networkx-VERSION.tar.gz -sha1=757ea87972243f1dab99687cd94ed51377efa34b -md5=91fdb77298a54c6e7cfb2d5305d23d23 -cksum=2430190254 +sha1=40e981041664856ba473c9079006367ed0d0e71b +md5=22139ab5a47818fa00cbaa91eb126381 +cksum=4201985987 upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index 2701a226a2f..80803faf1b9 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -2.8.4 +2.8.8 diff --git a/build/pkgs/normaliz/checksums.ini b/build/pkgs/normaliz/checksums.ini index c3afbfb2656..f7d47180ceb 100644 --- a/build/pkgs/normaliz/checksums.ini +++ b/build/pkgs/normaliz/checksums.ini @@ -1,5 +1,5 @@ tarball=normaliz-VERSION.tar.gz -sha1=7486d046c5e8e352d6d7c3544a0e6a1164e9b1fd -md5=136edc12b5c027bb1a019e06fb8d9113 -cksum=1640404889 +sha1=6382fcb14b0e602f5bf7d5abd53b421d0e3a0a3d +md5=1c6bdd4da166da1718b08a3b9ee40949 +cksum=2272467212 upstream_url=https://github.com/Normaliz/Normaliz/releases/download/vVERSION/normaliz-VERSION.tar.gz diff --git a/build/pkgs/normaliz/distros/arch.txt b/build/pkgs/normaliz/distros/arch.txt new file mode 100644 index 00000000000..d0991c684ae --- /dev/null +++ b/build/pkgs/normaliz/distros/arch.txt @@ -0,0 +1 @@ +normaliz diff --git a/build/pkgs/normaliz/distros/debian.txt b/build/pkgs/normaliz/distros/debian.txt new file mode 100644 index 00000000000..a3144cefbbb --- /dev/null +++ b/build/pkgs/normaliz/distros/debian.txt @@ -0,0 +1 @@ +libnormaliz-dev diff --git a/build/pkgs/normaliz/distros/fedora.txt b/build/pkgs/normaliz/distros/fedora.txt new file mode 100644 index 00000000000..b48a8dfa158 --- /dev/null +++ b/build/pkgs/normaliz/distros/fedora.txt @@ -0,0 +1 @@ +libnormaliz-devel diff --git a/build/pkgs/normaliz/distros/gentoo.txt b/build/pkgs/normaliz/distros/gentoo.txt new file mode 100644 index 00000000000..4c0fae119ad --- /dev/null +++ b/build/pkgs/normaliz/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/normaliz diff --git a/build/pkgs/normaliz/package-version.txt b/build/pkgs/normaliz/package-version.txt index d20cc2bf020..e0d61b5b062 100644 --- a/build/pkgs/normaliz/package-version.txt +++ b/build/pkgs/normaliz/package-version.txt @@ -1 +1 @@ -3.8.10 +3.9.4 diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index c15b3d8db64..70cc0ddaefd 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,5 +1,5 @@ -tarball=numpy-VERSION.zip -sha1=3ac08064b2ec8db8fe4870c2545c9d154f46bb30 -md5=b44849506fbb54cdef9dbb435b2b1987 -cksum=735479084 -upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.zip +tarball=numpy-VERSION.tar.gz +sha1=6c7f2278b4ddd113b30821e7e4d5f246dc3ee735 +md5=8b2692a511a3795f3af8af2cd7566a15 +cksum=3950457778 +upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.tar.gz diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 2a0ba77cc5e..ca8ec414e78 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.22.4 +1.23.5 diff --git a/build/pkgs/numpy/spkg-install.in b/build/pkgs/numpy/spkg-install.in index f66c6f56426..2b555d8b871 100644 --- a/build/pkgs/numpy/spkg-install.in +++ b/build/pkgs/numpy/spkg-install.in @@ -7,6 +7,14 @@ if [ `uname` = "Darwin" ]; then unset ATLAS unset BLAS unset LAPACK + # https://trac.sagemath.org/ticket/34110#comment:35 + # The fix for "reciprocal" (affected by a clang compiler bug) in + # https://github.com/numpy/numpy/pull/19926 relies on -ftrapping-math + # being used when Apple clang v12+ is used. + # But numpy.distutils.ccompiler only sets this flag when + # $CC contains the string "clang" -- missing the case CC=/usr/bin/gcc. + # So we set it here explicitly if the compiler supports the flag. + export CFLAGS="$(testcflags.sh $CFLAGS -ftrapping-math)" else export {ATLAS,PTATLAS,OPENBLAS,MKL,MKLROOT}=None export LDFLAGS="${LDFLAGS} -shared" @@ -27,15 +35,10 @@ fi export FFLAGS="$FFLAGS -fPIC" export FCFLAGS="$FCFLAGS -fPIC" -# Numpy 1.20.3 enables some intrinsics on machines without support with `-march=native`. -# We disable it until this is fixed. -export CFLAGS="$CFLAGS_NON_NATIVE" - -export NUMPY_FCONFIG="config_fc --noopt --noarch" if [ "$SAGE_FAT_BINARY" = "yes" ]; then export NUMPY_FCONFIG="--cpu-baseline=NONE" else - export NUMPY_FCONFIG + export NUMPY_FCONFIG="" fi # Trac #32423: Fix 32-bit builds on x86_64 diff --git a/build/pkgs/onetbb/SPKG.rst b/build/pkgs/onetbb/SPKG.rst new file mode 100644 index 00000000000..872da274b24 --- /dev/null +++ b/build/pkgs/onetbb/SPKG.rst @@ -0,0 +1,19 @@ +onetbb: oneAPI Threading Building Blocks +======================================== + +Description +----------- + +C++ parallelization library + + +License +------- + +Apache License, Version 2.0 + + +Upstream Contact +---------------- + +https://github.com/oneapi-src/oneTBB diff --git a/build/pkgs/onetbb/checksums.ini b/build/pkgs/onetbb/checksums.ini new file mode 100644 index 00000000000..5ff81545b4b --- /dev/null +++ b/build/pkgs/onetbb/checksums.ini @@ -0,0 +1,5 @@ +tarball=onetbb-VERSION.tar.gz +sha1=e4be99dc35948052296105d2ae7da6d34e01ba2d +md5=68e617448f71df02d8688c84d53778f6 +cksum=1573392748 +upstream_url=https://github.com/oneapi-src/oneTBB/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/onetbb/dependencies b/build/pkgs/onetbb/dependencies new file mode 100644 index 00000000000..ddf67ef8cd6 --- /dev/null +++ b/build/pkgs/onetbb/dependencies @@ -0,0 +1 @@ +| cmake diff --git a/build/pkgs/onetbb/distros/alpine.txt b/build/pkgs/onetbb/distros/alpine.txt new file mode 100644 index 00000000000..d56abf112ef --- /dev/null +++ b/build/pkgs/onetbb/distros/alpine.txt @@ -0,0 +1 @@ +libtbb-dev diff --git a/build/pkgs/onetbb/distros/arch.txt b/build/pkgs/onetbb/distros/arch.txt new file mode 100644 index 00000000000..ab07d9d457a --- /dev/null +++ b/build/pkgs/onetbb/distros/arch.txt @@ -0,0 +1 @@ +intel-oneapi-tbb diff --git a/build/pkgs/onetbb/distros/conda.txt b/build/pkgs/onetbb/distros/conda.txt new file mode 100644 index 00000000000..2e18bff2796 --- /dev/null +++ b/build/pkgs/onetbb/distros/conda.txt @@ -0,0 +1 @@ +tbb diff --git a/build/pkgs/onetbb/distros/debian.txt b/build/pkgs/onetbb/distros/debian.txt new file mode 100644 index 00000000000..d56abf112ef --- /dev/null +++ b/build/pkgs/onetbb/distros/debian.txt @@ -0,0 +1 @@ +libtbb-dev diff --git a/build/pkgs/onetbb/distros/fedora.txt b/build/pkgs/onetbb/distros/fedora.txt new file mode 100644 index 00000000000..21e1846951c --- /dev/null +++ b/build/pkgs/onetbb/distros/fedora.txt @@ -0,0 +1 @@ +tbb-devel diff --git a/build/pkgs/onetbb/distros/freebsd.txt b/build/pkgs/onetbb/distros/freebsd.txt new file mode 100644 index 00000000000..6017c89068b --- /dev/null +++ b/build/pkgs/onetbb/distros/freebsd.txt @@ -0,0 +1 @@ +devel/onetbb diff --git a/build/pkgs/onetbb/distros/gentoo.txt b/build/pkgs/onetbb/distros/gentoo.txt new file mode 100644 index 00000000000..7ad0c5b3774 --- /dev/null +++ b/build/pkgs/onetbb/distros/gentoo.txt @@ -0,0 +1 @@ +dev-cpp/tbb diff --git a/build/pkgs/onetbb/distros/homebrew.txt b/build/pkgs/onetbb/distros/homebrew.txt new file mode 100644 index 00000000000..2e18bff2796 --- /dev/null +++ b/build/pkgs/onetbb/distros/homebrew.txt @@ -0,0 +1 @@ +tbb diff --git a/build/pkgs/onetbb/distros/macports.txt b/build/pkgs/onetbb/distros/macports.txt new file mode 100644 index 00000000000..9ab74f73633 --- /dev/null +++ b/build/pkgs/onetbb/distros/macports.txt @@ -0,0 +1 @@ +onetbb diff --git a/build/pkgs/onetbb/distros/nix.txt b/build/pkgs/onetbb/distros/nix.txt new file mode 100644 index 00000000000..2e18bff2796 --- /dev/null +++ b/build/pkgs/onetbb/distros/nix.txt @@ -0,0 +1 @@ +tbb diff --git a/build/pkgs/onetbb/distros/void.txt b/build/pkgs/onetbb/distros/void.txt new file mode 100644 index 00000000000..21e1846951c --- /dev/null +++ b/build/pkgs/onetbb/distros/void.txt @@ -0,0 +1 @@ +tbb-devel diff --git a/build/pkgs/onetbb/package-version.txt b/build/pkgs/onetbb/package-version.txt new file mode 100644 index 00000000000..a489944aae9 --- /dev/null +++ b/build/pkgs/onetbb/package-version.txt @@ -0,0 +1 @@ +2021.7.0 diff --git a/build/pkgs/onetbb/spkg-configure.m4 b/build/pkgs/onetbb/spkg-configure.m4 new file mode 100644 index 00000000000..afec6b2221f --- /dev/null +++ b/build/pkgs/onetbb/spkg-configure.m4 @@ -0,0 +1,17 @@ +SAGE_SPKG_CONFIGURE([onetbb], [dnl + AC_MSG_CHECKING([whether oneTBB >= 2018 is available]) + rm -rf conftest_srcdir + mkdir conftest_srcdir + cat > conftest_srcdir/CMakeLists.txt <<EOF +cmake_minimum_required (VERSION 3.11.0) +project(dummy) +# from https://github.com/scipopt/papilo/blob/master/CMakeLists.txt +find_package(TBB 2018 COMPONENTS tbb tbbmalloc REQUIRED) +EOF + AS_IF([cmake -S conftest_srcdir -B conftest_srcdir/build >& ]AS_MESSAGE_LOG_FD[ 2>&1], [dnl + AC_MSG_RESULT([yes]) + ], [dnl + AC_MSG_RESULT([no]) + AS_VAR_SET([sage_spkg_install_onetbb], [yes]) + ]) +]) diff --git a/build/pkgs/onetbb/spkg-install.in b/build/pkgs/onetbb/spkg-install.in new file mode 100644 index 00000000000..16b4d568d4c --- /dev/null +++ b/build/pkgs/onetbb/spkg-install.in @@ -0,0 +1,6 @@ +cd src +mkdir build +cd build +sdh_cmake -DTBB_STRICT=off .. +sdh_make +sdh_make_install diff --git a/build/pkgs/onetbb/type b/build/pkgs/onetbb/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/onetbb/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/openblas/checksums.ini b/build/pkgs/openblas/checksums.ini index eb87a1d197d..e6e83e7d891 100644 --- a/build/pkgs/openblas/checksums.ini +++ b/build/pkgs/openblas/checksums.ini @@ -1,5 +1,5 @@ tarball=openblas-VERSION.tar.gz -sha1=45ec54b75f53f5b704250e60bd8e82a49b430619 -md5=abfaa43d995046ca4c56ccf14165c93c -cksum=3182749926 +sha1=b052d196ad694b29302e074b3eb8cc66745f6e2f +md5=ffb6120e2309a2280471716301824805 +cksum=241092070 upstream_url=https://github.com/xianyi/OpenBLAS/archive/vVERSION.tar.gz diff --git a/build/pkgs/openblas/dependencies b/build/pkgs/openblas/dependencies index a475340050b..57ddf74f36e 100644 --- a/build/pkgs/openblas/dependencies +++ b/build/pkgs/openblas/dependencies @@ -1,4 +1,4 @@ -gfortran | $(PYTHON) +gfortran ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/openblas/package-version.txt b/build/pkgs/openblas/package-version.txt index f9a4b5f993c..dfdc368868b 100644 --- a/build/pkgs/openblas/package-version.txt +++ b/build/pkgs/openblas/package-version.txt @@ -1 +1 @@ -0.3.20 +0.3.21 diff --git a/build/pkgs/openssl/spkg-configure.m4 b/build/pkgs/openssl/spkg-configure.m4 index fd2d257d721..51dcb7de779 100644 --- a/build/pkgs/openssl/spkg-configure.m4 +++ b/build/pkgs/openssl/spkg-configure.m4 @@ -56,9 +56,10 @@ On Cygwin, openssl must be installed as a system package. This is an error."]) ]) ], [dnl REQUIRED-CHECK AC_REQUIRE([SAGE_SPKG_CONFIGURE_PYTHON3]) - dnl openssl is a dependency only of python3; so if we use system python3, + AC_REQUIRE([SAGE_SPKG_CONFIGURE_CURL]) + dnl openssl is a dependency only of python3 and curl; so if we use system python3 and curl, dnl we do not require it. (In particular, we do not need a specific version.) - AS_IF([test x$sage_spkg_install_python3 = xno], [ + AS_IF([test x$sage_spkg_install_python3 = xno -a x$sage_spkg_install_curl = xno], [ sage_require_openssl=no ]) ]) diff --git a/build/pkgs/ore_algebra/requirements.txt b/build/pkgs/ore_algebra/requirements.txt index 656286fce86..3b6d6cc56ff 100644 --- a/build/pkgs/ore_algebra/requirements.txt +++ b/build/pkgs/ore_algebra/requirements.txt @@ -1 +1 @@ -git+https://github.com/mkauers/ore_algebra@cfcb386f2cc1d3e044c71dfb149444355b16d775#egg=ore_algebra +git+https://github.com/mkauers/ore_algebra@01c357f590685ff362c008229681ee08269457da#egg=ore_algebra diff --git a/build/pkgs/papilo/SPKG.rst b/build/pkgs/papilo/SPKG.rst new file mode 100644 index 00000000000..440d77d5143 --- /dev/null +++ b/build/pkgs/papilo/SPKG.rst @@ -0,0 +1,22 @@ +papilo: Parallel presolve for integer and linear optimization +============================================================= + +Description +----------- + +parallel presolve routines for (mixed integer) linear programming +problems. The routines are implemented using templates which allows +switching to higher precision or rational arithmetic using the boost +multiprecision package. + + +License +------- + +LGPL 3.0 + + +Upstream Contact +---------------- + +https://github.com/scipopt/papilo/ diff --git a/build/pkgs/papilo/checksums.ini b/build/pkgs/papilo/checksums.ini new file mode 100644 index 00000000000..b2385565547 --- /dev/null +++ b/build/pkgs/papilo/checksums.ini @@ -0,0 +1,5 @@ +tarball=papilo-VERSION.tar.gz +sha1=85d599ac9936aa1ddf687e04273b995522909de5 +md5=c41f5aa615ffc9914f8ca924947aa8cb +cksum=1535425476 +upstream_url=https://github.com/scipopt/papilo/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/papilo/dependencies b/build/pkgs/papilo/dependencies new file mode 100644 index 00000000000..0ec4f49b131 --- /dev/null +++ b/build/pkgs/papilo/dependencies @@ -0,0 +1 @@ +$(MP_LIBRARY) boost_cropped onetbb $(BLAS) gfortran | cmake diff --git a/build/pkgs/papilo/package-version.txt b/build/pkgs/papilo/package-version.txt new file mode 100644 index 00000000000..3e3c2f1e5ed --- /dev/null +++ b/build/pkgs/papilo/package-version.txt @@ -0,0 +1 @@ +2.1.1 diff --git a/build/pkgs/papilo/patches/0001-CMakeLists.txt-Do-not-require-boost-program_options-.patch b/build/pkgs/papilo/patches/0001-CMakeLists.txt-Do-not-require-boost-program_options-.patch new file mode 100644 index 00000000000..5001ca4d67a --- /dev/null +++ b/build/pkgs/papilo/patches/0001-CMakeLists.txt-Do-not-require-boost-program_options-.patch @@ -0,0 +1,26 @@ +From 1fc5aecb4eca500917407b008c8c8eb8637a9c27 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Sat, 19 Nov 2022 19:03:37 -0800 +Subject: [PATCH] CMakeLists.txt: Do not require boost program_options for the + library + +--- + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7256877..db905aa 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -47,7 +47,7 @@ if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) + endif() + +-find_package(Boost ${BOOST_MIN_VERSION} COMPONENTS program_options REQUIRED) ++find_package(Boost ${BOOST_MIN_VERSION} REQUIRED) + + if(GMP) + find_package(GMP) +-- +2.37.3 + diff --git a/build/pkgs/papilo/patches/import_memory_multiprecision.patch b/build/pkgs/papilo/patches/import_memory_multiprecision.patch new file mode 100644 index 00000000000..1f16ced0bed --- /dev/null +++ b/build/pkgs/papilo/patches/import_memory_multiprecision.patch @@ -0,0 +1,20 @@ +commit 855bd67a64c5c044080d790e5c9fb7e298a61ab7 +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Thu Dec 8 18:10:29 2022 -0800 + + src/papilo/misc/MultiPrecision.hpp: Add #include <memory> + +diff --git a/src/papilo/misc/MultiPrecision.hpp b/src/papilo/misc/MultiPrecision.hpp +index 669014c..44d3e63 100644 +--- a/src/papilo/misc/MultiPrecision.hpp ++++ b/src/papilo/misc/MultiPrecision.hpp +@@ -26,6 +26,9 @@ + + #include "papilo/Config.hpp" + ++// work around build failure with boost on Fedora 37 ++#include <memory> ++ + #include <boost/serialization/split_free.hpp> + + #ifdef PAPILO_HAVE_FLOAT128 diff --git a/build/pkgs/papilo/spkg-install.in b/build/pkgs/papilo/spkg-install.in new file mode 100644 index 00000000000..87411afcf46 --- /dev/null +++ b/build/pkgs/papilo/spkg-install.in @@ -0,0 +1,8 @@ +cd src +mkdir build +cd build +sdh_cmake -DPAPILO_NO_BINARIES=1 \ + -DBOOST_ROOT="$SAGE_LOCAL" \ + .. +sdh_make +sdh_make_install diff --git a/build/pkgs/papilo/type b/build/pkgs/papilo/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/papilo/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index b736feed312..d72e5246cf7 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,5 +1,5 @@ tarball=pari-VERSION.tar.gz -sha1=e01647aab7e96a8cb4922cf26a4f224337c6647f -md5=922f740fcdf8630b30d63dc76b58f756 -cksum=297133525 +sha1=498e3cd0b7ded8be3fa1cba1125ca5213ed39453 +md5=562a6e973ca3980dc6c1eb2c4f252e92 +cksum=4081416981 upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index a1a4224dd5e..c06afc0ba5f 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.13.3 +2.15.2.p1 diff --git a/build/pkgs/pari/patches/bug2441.patch b/build/pkgs/pari/patches/bug2441.patch new file mode 100644 index 00000000000..01047180d19 --- /dev/null +++ b/build/pkgs/pari/patches/bug2441.patch @@ -0,0 +1,14 @@ +diff --git a/src/basemath/ellsea.c b/src/basemath/ellsea.c +index a6871fa6a7..05f148eadd 100644 +--- a/src/basemath/ellsea.c ++++ b/src/basemath/ellsea.c +@@ -852,7 +852,8 @@ find_isogenous_from_Atkin(GEN a4, GEN a6, ulong ell, struct meqn *MEQN, GEN g, G + GEN a4t, a6t, h; + a4a6t(&a4t, &a6t, ell, E4t, E6t, T, p); + h = find_kernel(a4, a6, ell, a4t, a6t, pp1, T, p, pp, e); +- if (h) return gerepilecopy(ltop, mkvec3(a4t, a6t, h)); ++ if (h && signe(Fq_elldivpolmod(a4, a6, ell, h, T, pp))==0) ++ return gerepilecopy(ltop, mkvec3(a4t, a6t, h)); + } + } + pari_err_BUG("find_isogenous_from_Atkin, kernel not found"); diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 9721849e7ba..e1a022a26ab 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,6 +1,7 @@ SAGE_SPKG_CONFIGURE([pari], [ dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV - m4_pushdef([SAGE_PARI_MINVER],["134401"]) + m4_pushdef([SAGE_PARI_MINVER],["134912"])dnl this version and higher allowed + m4_pushdef([SAGE_PARI_MAXVER],["999999"])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 @@ -66,17 +67,6 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) sage_spkg_install_pari=yes fi - AC_MSG_CHECKING([whether rnfdisc bug of pari 2.13.1 is fixed]) - bug_check=`echo "K = nfinit(y^4-10*y^2+1); disc = rnfdisc(K,x^2-(y^3/2+y^2-5*y/2+1)); idealnorm(K,disc)" | $GP -qf 2>> config.log` - expected="2304" - if test x"$bug_check" = x"$expected"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) - AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) - AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) - sage_spkg_install_pari=yes - fi fi dnl end GP test if test x$sage_spkg_install_pari = xno; then dnl main PARI test @@ -101,6 +91,7 @@ SAGE_SPKG_CONFIGURE([pari], [ [AC_MSG_RESULT([cross compiling. Assume they match])]) AC_MSG_CHECKING([is GP's version good enough? ]) AX_COMPARE_VERSION([$gp_version], [ge], [$SAGE_PARI_MINVER], [ + AX_COMPARE_VERSION([$gp_version], [lt], [$SAGE_PARI_MAXVER], [ AC_MSG_RESULT([yes]) AC_MSG_CHECKING([getting GP's datadir]) gp_datadir=`echo "default(datadir)" | $GP -qf 2>> config.log` @@ -127,13 +118,17 @@ SAGE_SPKG_CONFIGURE([pari], [ [AC_MSG_RESULT([libpari's datadir does not match GP's datadir. Not good]) sage_spkg_install_pari=yes], [AC_MSG_RESULT([cross compiling. Assume yes])]) - ], [ + ], [dnl compared maxver + AC_MSG_RESULT([no]) + sage_spkg_install_pari=yes], [AC_MSG_RESULT([cross compiling. Assume yes])]) + ], [dnl compared minver AC_MSG_RESULT([no]) sage_spkg_install_pari=yes], [AC_MSG_RESULT([cross compiling. Assume yes])]) AC_LANG_POP() ], [sage_spkg_install_pari=yes]) fi dnl end main PARI test ]) + m4_popdef([SAGE_PARI_MAXVER]) m4_popdef([SAGE_PARI_MINVER]) ], [], [], [ if test x$sage_spkg_install_pari = xyes; then diff --git a/build/pkgs/patchelf/SPKG.rst b/build/pkgs/patchelf/SPKG.rst new file mode 100644 index 00000000000..a2d1a1c4ccf --- /dev/null +++ b/build/pkgs/patchelf/SPKG.rst @@ -0,0 +1,17 @@ +patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables +=================================================================================== + +Description +----------- + +A small utility to modify the dynamic linker and RPATH of ELF executables. + +License +------- + +GPL-3.0-or-later + +Upstream Contact +---------------- + +https://github.com/NixOS/patchelf diff --git a/build/pkgs/patchelf/checksums.ini b/build/pkgs/patchelf/checksums.ini new file mode 100644 index 00000000000..bd52a82dbc5 --- /dev/null +++ b/build/pkgs/patchelf/checksums.ini @@ -0,0 +1,5 @@ +tarball=patchelf-VERSION.tar.bz2 +sha1=5d9c1690c0fbe70c312f43d597e04b6c1eeffc60 +md5=04d243d3626a33201b0d6eef0e2c4317 +cksum=92812155 +upstream_url=https://github.com/NixOS/patchelf/releases/download/VERSION/patchelf-VERSION.tar.bz2 diff --git a/build/pkgs/zn_poly/dependencies b/build/pkgs/patchelf/dependencies similarity index 82% rename from build/pkgs/zn_poly/dependencies rename to build/pkgs/patchelf/dependencies index 42dc0e9c107..c856a61a50b 100644 --- a/build/pkgs/zn_poly/dependencies +++ b/build/pkgs/patchelf/dependencies @@ -1,4 +1,4 @@ -$(MP_LIBRARY) +| bzip2 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/patchelf/package-version.txt b/build/pkgs/patchelf/package-version.txt new file mode 100644 index 00000000000..c317a91891f --- /dev/null +++ b/build/pkgs/patchelf/package-version.txt @@ -0,0 +1 @@ +0.13.1 diff --git a/build/pkgs/patchelf/spkg-install.in b/build/pkgs/patchelf/spkg-install.in new file mode 100644 index 00000000000..b4a009197cd --- /dev/null +++ b/build/pkgs/patchelf/spkg-install.in @@ -0,0 +1,6 @@ +cd src +if [ "$UNAME" = "Linux" ]; then + sdh_configure + sdh_make + sdh_make_install +fi diff --git a/build/pkgs/patchelf/type b/build/pkgs/patchelf/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/patchelf/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pathspec/checksums.ini b/build/pkgs/pathspec/checksums.ini index cb80c60c623..a98c270d6e1 100644 --- a/build/pkgs/pathspec/checksums.ini +++ b/build/pkgs/pathspec/checksums.ini @@ -1,5 +1,5 @@ tarball=pathspec-VERSION.tar.gz -sha1=afe51c21951f457f82a5810016d0b6752ffc487b -md5=9b6b70fa5ffc31e6f5700522880140c0 -cksum=3501694416 +sha1=418ae4112af18af995c0f20a22e5a903b8ce50ae +md5=28c87c3581b10152c4581d10fe33f765 +cksum=2161527634 upstream_url=https://pypi.io/packages/source/p/pathspec/pathspec-VERSION.tar.gz diff --git a/build/pkgs/pathspec/package-version.txt b/build/pkgs/pathspec/package-version.txt index ac39a106c48..5eef0f10e8c 100644 --- a/build/pkgs/pathspec/package-version.txt +++ b/build/pkgs/pathspec/package-version.txt @@ -1 +1 @@ -0.9.0 +0.10.2 diff --git a/build/pkgs/pint/checksums.ini b/build/pkgs/pint/checksums.ini index 9be8323d338..b852d25efe6 100644 --- a/build/pkgs/pint/checksums.ini +++ b/build/pkgs/pint/checksums.ini @@ -1,5 +1,5 @@ tarball=Pint-VERSION.tar.gz -sha1=98496b31efc63adfbf640953351aafc51cb46584 -md5=b14aa24f4a4bcbc2c2fae797b25d0b3e -cksum=3392069525 +sha1=c14ac08ca2d5a68d79ea7cd2252dc7e2a572c56a +md5=522a8e633e487e92ba54ccbec395947a +cksum=2139030399 upstream_url=https://pypi.io/packages/source/p/pint/Pint-VERSION.tar.gz diff --git a/build/pkgs/pint/package-version.txt b/build/pkgs/pint/package-version.txt index 50653ad0a6e..847e9aef6d1 100644 --- a/build/pkgs/pint/package-version.txt +++ b/build/pkgs/pint/package-version.txt @@ -1 +1 @@ -0.17 +0.20.1 diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 35d4f6fde7b..1ae66fbc20a 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=be5b671f4868816c6ad2e09258c22bdb3fc82775 -md5=6ec06d38c3aed5d22bcbbbfbf7114d6a -cksum=3372144553 +sha1=29167fffe19874a74247fe92f4fdba1bb1221c61 +md5=996f58a94fe0b8b82b6795c42bd171ba +cksum=537001443 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 30ed8778377..4c3dad97552 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -22.1.2 +22.3.1 diff --git a/build/pkgs/pkgconfig/dependencies b/build/pkgs/pkgconfig/dependencies index 79554fc438d..6dfe046e55e 100644 --- a/build/pkgs/pkgconfig/dependencies +++ b/build/pkgs/pkgconfig/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) pkgconf +$(PYTHON) | $(PYTHON_TOOLCHAIN) pkgconf poetry_core ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pkgconfig/spkg-install.in b/build/pkgs/pkgconfig/spkg-install.in index 761190e309c..d14edc90bcd 100644 --- a/build/pkgs/pkgconfig/spkg-install.in +++ b/build/pkgs/pkgconfig/spkg-install.in @@ -1,10 +1,5 @@ cd src -# Make sure that modern pip uses the generated setup.py -# that is distributed with the PyPI tarball, -# so we do not have to have poetry. Trac #29803. -rm -f pyproject.toml - sdh_pip_install . if [ $? -ne 0 ]; then diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini index e6a6c0613a8..604aa7f6c99 100644 --- a/build/pkgs/platformdirs/checksums.ini +++ b/build/pkgs/platformdirs/checksums.ini @@ -1,5 +1,5 @@ tarball=platformdirs-VERSION.tar.gz -sha1=16e9a89587b4471041c6d1a2444d200a92292c73 -md5=83d3ce3feb4af1ccfaca24375574f44d -cksum=1234653032 +sha1=082974f7d3ea03adfa147f4ab5be76079c2a116f +md5=f449b7f3767577fa2a57465a4523e92e +cksum=2966639810 upstream_url=https://pypi.io/packages/source/p/platformdirs/platformdirs-VERSION.tar.gz diff --git a/build/pkgs/platformdirs/dependencies b/build/pkgs/platformdirs/dependencies index 0738c2d7777..5b4aec583a4 100644 --- a/build/pkgs/platformdirs/dependencies +++ b/build/pkgs/platformdirs/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) setuptools_scm | $(PYTHON_TOOLCHAIN) hatchling hatch_vcs ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt index 73462a5a134..fe16b348d97 100644 --- a/build/pkgs/platformdirs/package-version.txt +++ b/build/pkgs/platformdirs/package-version.txt @@ -1 +1 @@ -2.5.1 +2.5.4 diff --git a/build/pkgs/primecount/distros/homebrew.txt b/build/pkgs/primecount/distros/homebrew.txt new file mode 100644 index 00000000000..f67843baa2b --- /dev/null +++ b/build/pkgs/primecount/distros/homebrew.txt @@ -0,0 +1 @@ +primecount diff --git a/build/pkgs/primesieve/patches/107.patch b/build/pkgs/primesieve/patches/107.patch deleted file mode 100644 index d8986d29337..00000000000 --- a/build/pkgs/primesieve/patches/107.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 1f8d0470b4e9d50393032b86d21a7a2fcc512355 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Sat, 27 Nov 2021 10:50:09 -0800 -Subject: [PATCH] src/MemoryPool.cpp: Work around missing std::align on GCC 4.x - ---- - src/MemoryPool.cpp | 27 ++++++++++++++++++++++++++- - 1 file changed, 26 insertions(+), 1 deletion(-) - -diff --git a/src/MemoryPool.cpp b/src/MemoryPool.cpp -index 7a4a4006..60f62101 100644 ---- a/src/MemoryPool.cpp -+++ b/src/MemoryPool.cpp -@@ -26,6 +26,31 @@ - - using std::size_t; - -+#if defined(__GNUC__) && __GNUC__ == 4 -+ -+// gcc 4.9 does not implement std::align. -+// Use the implementation from -+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57350#c11 -+ -+using std::uintptr_t; -+ -+static inline void *align(size_t alignment, size_t size, -+ void *&ptr, size_t &space) -+{ -+ uintptr_t pn = reinterpret_cast<uintptr_t>(ptr); -+ uintptr_t aligned = (pn + alignment - 1) & -alignment; -+ size_t padding = aligned - pn; -+ if (space < size + padding) return nullptr; -+ space -= padding; -+ return ptr = reinterpret_cast<void *>(aligned); -+} -+ -+#else -+ -+using std::align; -+ -+#endif -+ - namespace primesieve { - - void MemoryPool::addBucket(SievingPrime*& sievingPrime) -@@ -70,7 +95,7 @@ void MemoryPool::allocateBuckets() - void* ptr = memory; - - // Align pointer address to sizeof(Bucket) -- if (!std::align(sizeof(Bucket), sizeof(Bucket), ptr, bytes)) -+ if (!align(sizeof(Bucket), sizeof(Bucket), ptr, bytes)) - throw primesieve_error("MemoryPool: failed to align memory!"); - - initBuckets(ptr, bytes); diff --git a/build/pkgs/pybind11/checksums.ini b/build/pkgs/pybind11/checksums.ini index 1a6fb10c749..1cc6d36c174 100644 --- a/build/pkgs/pybind11/checksums.ini +++ b/build/pkgs/pybind11/checksums.ini @@ -1,5 +1,5 @@ tarball=pybind11-VERSION.tar.gz -sha1=cee2f3c7879783bb5f021c7db332f8a290fe3763 -md5=c232928479f398693bface2731387691 -cksum=3770559962 +sha1=d0e6e22c2ce36fad4bb60dbac4d6d498ceb464df +md5=0b181dbb44c3cc632e724cef5081cae1 +cksum=4287838207 upstream_url=https://pypi.io/packages/source/p/pybind11/pybind11-VERSION.tar.gz diff --git a/build/pkgs/pybind11/package-version.txt b/build/pkgs/pybind11/package-version.txt index 5d9ade10c60..8bbb6e406a7 100644 --- a/build/pkgs/pybind11/package-version.txt +++ b/build/pkgs/pybind11/package-version.txt @@ -1 +1 @@ -2.9.2 +2.10.1 diff --git a/build/pkgs/pygments/checksums.ini b/build/pkgs/pygments/checksums.ini index f7b5fdbde3a..14fc1c04cdb 100644 --- a/build/pkgs/pygments/checksums.ini +++ b/build/pkgs/pygments/checksums.ini @@ -1,5 +1,5 @@ tarball=Pygments-VERSION.tar.gz -sha1=92e83541b557dad3f692d0d9d02733c2c842c836 -md5=2137c19d9ac0cc556badc89e746c0e62 -cksum=3384039979 +sha1=adaf31bf13a7bcc210568537138e0984ecdea626 +md5=6ccae578d28d18968b30a4711652fd9a +cksum=613387624 upstream_url=https://pypi.io/packages/source/p/pygments/Pygments-VERSION.tar.gz diff --git a/build/pkgs/pygments/package-version.txt b/build/pkgs/pygments/package-version.txt index d8b698973a4..fb2c0766b7c 100644 --- a/build/pkgs/pygments/package-version.txt +++ b/build/pkgs/pygments/package-version.txt @@ -1 +1 @@ -2.12.0 +2.13.0 diff --git a/build/pkgs/pynormaliz/checksums.ini b/build/pkgs/pynormaliz/checksums.ini index a7e0febfe80..d9f614efaf9 100644 --- a/build/pkgs/pynormaliz/checksums.ini +++ b/build/pkgs/pynormaliz/checksums.ini @@ -1,5 +1,5 @@ tarball=PyNormaliz-VERSION.tar.gz -sha1=f24f5c4a1b9b7a084ad1f2a0b95374d1a83e31b2 -md5=51e67733702d6cea3cd81144888d9dc7 -cksum=3201946747 +sha1=de8771b0339c4567665331df221c880bfe2b69a2 +md5=e8a571bdc3a8fcad16fdfabf9a6874d3 +cksum=2734845416 upstream_url=https://pypi.io/packages/source/p/pynormaliz/PyNormaliz-VERSION.tar.gz diff --git a/build/pkgs/pynormaliz/install-requires.txt b/build/pkgs/pynormaliz/install-requires.txt index ecc1ec0712b..b1e222ae76c 100644 --- a/build/pkgs/pynormaliz/install-requires.txt +++ b/build/pkgs/pynormaliz/install-requires.txt @@ -1 +1 @@ -pynormaliz ==2.14 +pynormaliz ==2.12 diff --git a/build/pkgs/pynormaliz/package-version.txt b/build/pkgs/pynormaliz/package-version.txt index 123a39a8e91..5c6fb54899b 100644 --- a/build/pkgs/pynormaliz/package-version.txt +++ b/build/pkgs/pynormaliz/package-version.txt @@ -1 +1 @@ -2.14 +2.17 diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini index da299c46588..4fcc0ec49c2 100644 --- a/build/pkgs/pyproject_metadata/checksums.ini +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=pyproject-metadata-VERSION.tar.gz -sha1=5421824aa29786bde43f510365c4d035a0614ba5 -md5=85fcbd5d777809ca2217a996e06fe2e0 -cksum=1327227039 +sha1=c2b7679b1e56a341aa00c186c0d1a6bbd7bd5c2c +md5=e13b11cb723da96f8397addddca963cc +cksum=2246727402 upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt index 8f0916f768f..ee6cdce3c29 100644 --- a/build/pkgs/pyproject_metadata/package-version.txt +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -1 +1 @@ -0.5.0 +0.6.1 diff --git a/build/pkgs/pyrsistent/checksums.ini b/build/pkgs/pyrsistent/checksums.ini index 7ea885d3db7..7d537e43e37 100644 --- a/build/pkgs/pyrsistent/checksums.ini +++ b/build/pkgs/pyrsistent/checksums.ini @@ -1,5 +1,5 @@ tarball=pyrsistent-VERSION.tar.gz -sha1=58f9efc4800acb6f7a083688e988187cccee2266 -md5=cef3da08455664bf917dcf8cd00d49a4 -cksum=350737156 +sha1=a2c5cc517a33dcfd3918d3eabf4859b8901d3913 +md5=23da81256b8817e123568a858bf78997 +cksum=1165148669 upstream_url=https://pypi.io/packages/source/p/pyrsistent/pyrsistent-VERSION.tar.gz diff --git a/build/pkgs/pyrsistent/package-version.txt b/build/pkgs/pyrsistent/package-version.txt index 249afd517d9..61e6e92d914 100644 --- a/build/pkgs/pyrsistent/package-version.txt +++ b/build/pkgs/pyrsistent/package-version.txt @@ -1 +1 @@ -0.18.1 +0.19.2 diff --git a/build/pkgs/pyscipopt/SPKG.rst b/build/pkgs/pyscipopt/SPKG.rst new file mode 100644 index 00000000000..28d6ee75aaa --- /dev/null +++ b/build/pkgs/pyscipopt/SPKG.rst @@ -0,0 +1,22 @@ +pyscipopt: Python interface and modeling environment for SCIP +============================================================= + +Description +----------- + +Python interface and modeling environment for SCIP + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/PySCIPOpt/ + +Dependencies +------------ + +scipoptsuite diff --git a/build/pkgs/pyscipopt/checksums.ini b/build/pkgs/pyscipopt/checksums.ini new file mode 100644 index 00000000000..153466efcb5 --- /dev/null +++ b/build/pkgs/pyscipopt/checksums.ini @@ -0,0 +1,5 @@ +tarball=PySCIPOpt-VERSION.tar.gz +sha1=0c3644ce6a0624774dceaef10694e090e3863ab5 +md5=2e4ce8087fb9acac8e806655f20c3d70 +cksum=3316817556 +upstream_url=https://pypi.io/packages/source/p/pyscipopt/PySCIPOpt-VERSION.tar.gz diff --git a/build/pkgs/pyscipopt/dependencies b/build/pkgs/pyscipopt/dependencies new file mode 100644 index 00000000000..dd41f46d3a1 --- /dev/null +++ b/build/pkgs/pyscipopt/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) scip | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pyscipopt/distros/conda.txt b/build/pkgs/pyscipopt/distros/conda.txt new file mode 100644 index 00000000000..944406c1dad --- /dev/null +++ b/build/pkgs/pyscipopt/distros/conda.txt @@ -0,0 +1 @@ +pyscipopt diff --git a/build/pkgs/pyscipopt/distros/freebsd.txt b/build/pkgs/pyscipopt/distros/freebsd.txt new file mode 100644 index 00000000000..da1010d3171 --- /dev/null +++ b/build/pkgs/pyscipopt/distros/freebsd.txt @@ -0,0 +1 @@ +math/py-PySCIPOpt diff --git a/build/pkgs/pyscipopt/install-requires.txt b/build/pkgs/pyscipopt/install-requires.txt new file mode 100644 index 00000000000..1bd15469865 --- /dev/null +++ b/build/pkgs/pyscipopt/install-requires.txt @@ -0,0 +1 @@ +PySCIPOpt diff --git a/build/pkgs/pyscipopt/package-version.txt b/build/pkgs/pyscipopt/package-version.txt new file mode 100644 index 00000000000..6aba2b245a8 --- /dev/null +++ b/build/pkgs/pyscipopt/package-version.txt @@ -0,0 +1 @@ +4.2.0 diff --git a/build/pkgs/pyscipopt/spkg-install.in b/build/pkgs/pyscipopt/spkg-install.in new file mode 100644 index 00000000000..e8f779c4db4 --- /dev/null +++ b/build/pkgs/pyscipopt/spkg-install.in @@ -0,0 +1,3 @@ +cd src +export SCIPOPTDIR="$SAGE_LOCAL" +sdh_pip_install . diff --git a/build/pkgs/pyscipopt/type b/build/pkgs/pyscipopt/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pyscipopt/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pytest_mock/SPKG.rst b/build/pkgs/pytest_mock/SPKG.rst new file mode 100644 index 00000000000..59692594971 --- /dev/null +++ b/build/pkgs/pytest_mock/SPKG.rst @@ -0,0 +1,18 @@ +pytest_mock: Thin-wrapper around the mock package for easier use with pytest +============================================================================ + +Description +----------- + +Thin-wrapper around the mock package for easier use with pytest + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/pytest-mock/ + diff --git a/build/pkgs/pytest_mock/dependencies b/build/pkgs/pytest_mock/dependencies new file mode 100644 index 00000000000..37ea60eb442 --- /dev/null +++ b/build/pkgs/pytest_mock/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) pytest packaging attrs pluggy tomli py pyparsing | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pytest_mock/requirements.txt b/build/pkgs/pytest_mock/requirements.txt new file mode 100644 index 00000000000..4ef37316e6a --- /dev/null +++ b/build/pkgs/pytest_mock/requirements.txt @@ -0,0 +1 @@ +pytest-mock diff --git a/build/pkgs/pytest_mock/type b/build/pkgs/pytest_mock/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pytest_mock/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/python3/SPKG.rst b/build/pkgs/python3/SPKG.rst index 10d444bd020..94a163de1f3 100644 --- a/build/pkgs/python3/SPKG.rst +++ b/build/pkgs/python3/SPKG.rst @@ -4,7 +4,21 @@ python3: The Python programming language Description ----------- -The Python programming language +By default, Sage will try to use system's ``python3`` to set up a virtual +environment, a.k.a. `venv <https://docs.python.org/3.10/library/venv.html>`_ +rather than building a Python 3 installation from scratch. + +Sage will accept versions 3.8.x to 3.10.x. + +You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use +``/path/to/python3_binary`` to set up the venv. Note that setting up the venv requires +a number of Python modules to be available within the Python in question. Currently, +as of Sage 9.7, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, +``hashlib``, ``crypt``, ``socket``, ``zlib``, ``distutils.core``, ``ssl`` - +they will be checked for by the ``configure`` script. + +Use the ``configure`` option ``--without-system-python3`` in case you want Python 3 +built from scratch. Upstream Contact diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 08ff0289325..f2727eaf401 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,5 +1,5 @@ tarball=Python-VERSION.tar.xz -sha1=b80b9c13bb6ba5eb8762ca0d2b888c404582a405 -md5=f05727cb3489aa93cd57eb561c16747b -cksum=2773343227 +sha1=89ee31611b73dc0c32c178d15aa208734b462c5a +md5=4efe92adf28875c77d3b9b2e8d3bc44a +cksum=2916176597 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 c84ccce96a7..371cfe355dd 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.10.5 +3.11.1 diff --git a/build/pkgs/python3/patches/cygwin-socket-tcpnodelay-21649.patch b/build/pkgs/python3/patches/cygwin-socket-tcpnodelay-21649.patch deleted file mode 100644 index 3769318fe74..00000000000 --- a/build/pkgs/python3/patches/cygwin-socket-tcpnodelay-21649.patch +++ /dev/null @@ -1,36 +0,0 @@ -From bf2b131976694d5f4fe182c4a48d2d90edf5f303 Mon Sep 17 00:00:00 2001 -From: Zackery Spytz <zspytz@gmail.com> -Date: Mon, 27 Jul 2020 19:22:14 -0600 -Subject: [PATCH] bpo-41374: Include netinet/tcp.h on Cygwin - -On Cygwin, constants like TCP_NODELAY are no longer provided by -sys/socket.h. ---- - .../next/Library/2020-07-27-19-21-05.bpo-41374.cd-kFL.rst | 2 ++ - Modules/socketmodule.h | 4 +--- - 2 files changed, 3 insertions(+), 3 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2020-07-27-19-21-05.bpo-41374.cd-kFL.rst - -diff --git a/Misc/NEWS.d/next/Library/2020-07-27-19-21-05.bpo-41374.cd-kFL.rst b/Misc/NEWS.d/next/Library/2020-07-27-19-21-05.bpo-41374.cd-kFL.rst -new file mode 100644 -index 0000000000000..a5b2e042a3fae ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2020-07-27-19-21-05.bpo-41374.cd-kFL.rst -@@ -0,0 +1,2 @@ -+Ensure that ``socket.TCP_*`` constants are exposed on Cygwin 3.1.6 and -+greater. -diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h -index ba2c9f5c31c3b..f565b8e1396ff 100644 ---- a/Modules/socketmodule.h -+++ b/Modules/socketmodule.h -@@ -8,9 +8,7 @@ - # include <sys/socket.h> - # endif - # include <netinet/in.h> --# if !defined(__CYGWIN__) --# include <netinet/tcp.h> --# endif -+# include <netinet/tcp.h> - - #else /* MS_WINDOWS */ - # include <winsock2.h> diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 65cc548d8f3..19642b59dd9 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,8 +1,8 @@ SAGE_SPKG_CONFIGURE([python3], [ m4_pushdef([MIN_VERSION], [3.8.0]) m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.8.0]) - m4_pushdef([LT_STABLE_VERSION], [3.11.0]) - m4_pushdef([LT_VERSION], [3.11.0]) + m4_pushdef([LT_STABLE_VERSION], [3.12.0]) + m4_pushdef([LT_VERSION], [3.12.0]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python=PYTHON3], [Python 3 executable to use for the Sage venv; default: python3])]) diff --git a/build/pkgs/python_igraph/checksums.ini b/build/pkgs/python_igraph/checksums.ini index 9ddab211f0d..5f8bc59a4de 100644 --- a/build/pkgs/python_igraph/checksums.ini +++ b/build/pkgs/python_igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=python-igraph-VERSION.tar.gz -sha1=75eae8ca6de2daa8c5ca43f99487cf20b5cdf432 -md5=18a54cd2fe507f5602e6c10584f665ee -cksum=594785782 +sha1=6a6bca77737ff501e97f808aa18a9045e86b3e3e +md5=6951cc2e803118b74209ae21d54de38a +cksum=650236223 upstream_url=https://pypi.io/packages/source/i/igraph/igraph-VERSION.tar.gz diff --git a/build/pkgs/python_igraph/package-version.txt b/build/pkgs/python_igraph/package-version.txt index 571215736a6..5eef0f10e8c 100644 --- a/build/pkgs/python_igraph/package-version.txt +++ b/build/pkgs/python_igraph/package-version.txt @@ -1 +1 @@ -0.10.1 +0.10.2 diff --git a/build/pkgs/pythran/checksums.ini b/build/pkgs/pythran/checksums.ini index 5c295d3da45..0789ef48f67 100644 --- a/build/pkgs/pythran/checksums.ini +++ b/build/pkgs/pythran/checksums.ini @@ -1,5 +1,5 @@ tarball=pythran-VERSION.tar.gz -sha1=d2a227e14244a9afaeb4732a0299103b6f7ecabe -md5=e09e90484771937ab499380858bdb18d -cksum=1753405982 +sha1=ed5630b0879be9c59885d83c5a24fcd5dfbca5af +md5=d2961ece35b4b9f44a84ef31df1b21ff +cksum=399652957 upstream_url=https://pypi.io/packages/source/p/pythran/pythran-VERSION.tar.gz diff --git a/build/pkgs/pythran/package-version.txt b/build/pkgs/pythran/package-version.txt index d9df1bbc0c7..ac454c6a1fc 100644 --- a/build/pkgs/pythran/package-version.txt +++ b/build/pkgs/pythran/package-version.txt @@ -1 +1 @@ -0.11.0 +0.12.0 diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini index 0faabac40af..9c3074def07 100644 --- a/build/pkgs/pytz/checksums.ini +++ b/build/pkgs/pytz/checksums.ini @@ -1,5 +1,5 @@ tarball=pytz-VERSION.tar.gz -sha1=fa6729d40cfa607daee0f40cdab165976ab0e0a3 -md5=d7b7060bbac4970afa2050c139c9fcb6 -cksum=3161093227 +sha1=b356ab5a8b326e9857bbce3e7a1799fc56844827 +md5=91747f483e2906cddda91b0df0b01254 +cksum=635792532 upstream_url=https://pypi.io/packages/source/p/pytz/pytz-VERSION.tar.gz diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt index c9c8e05b0f8..ef52d12ba35 100644 --- a/build/pkgs/pytz/package-version.txt +++ b/build/pkgs/pytz/package-version.txt @@ -1 +1 @@ -2021.3 +2022.5 diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index cd83e8c1a2d..d9abd25d464 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,5 +1,5 @@ tarball=pyzmq-VERSION.tar.gz -sha1=b31a94f504ef3104a28e64d88b7c1feafb124982 -md5=7de9c7bb05cc89d21e4105ec1ac7c270 -cksum=936855776 +sha1=1a2e7220d7d1b6167c14ae2cc001dfc5d9a28dde +md5=f10b7c3dee2c03557e2c5d00b73dfc7f +cksum=1163982926 upstream_url=https://pypi.io/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index 11ebcd0e4f9..1b3e74f84e7 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -23.2.0 +24.0.1 diff --git a/build/pkgs/r/bin/java b/build/pkgs/r/bin/java deleted file mode 100755 index d5e39b620f7..00000000000 --- a/build/pkgs/r/bin/java +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -exit 1 diff --git a/build/pkgs/r/bin/javac b/build/pkgs/r/bin/javac deleted file mode 100755 index d5e39b620f7..00000000000 --- a/build/pkgs/r/bin/javac +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -exit 1 diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini deleted file mode 100644 index 69a1a7059dc..00000000000 --- a/build/pkgs/r/checksums.ini +++ /dev/null @@ -1,5 +0,0 @@ -tarball=R-VERSION.tar.gz -sha1=d2383dabc0d6c70f8a0171a0fb1bfdc31ddb5b52 -md5=506c9576ba33e1262ad5b5624db9d96a -cksum=2403187565 -upstream_url=https://cran.r-project.org/src/base/R-3/R-VERSION.tar.gz diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt deleted file mode 100644 index 4a788a01dad..00000000000 --- a/build/pkgs/r/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.6.3 diff --git a/build/pkgs/r/patches/autoconf_verb_dash.patch b/build/pkgs/r/patches/autoconf_verb_dash.patch deleted file mode 100644 index c5217fdd94d..00000000000 --- a/build/pkgs/r/patches/autoconf_verb_dash.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 04d878728603db345c0a0b01718864a12bcfa97d Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:36 +0100 -Subject: [PATCH 1/9] autoconf_verb_dash - -Bug in autoconf and R macros. -Use -### instead of -v to detect linker options. -See Sage ticket #12787 and R ticket -* https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=14865 -The corresponding patch to m4/clibs.m4 is not included, as -autoconf-2.68 gives errors, even on the clean upstream sources. - ---- - configure | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure b/configure -index a314719..dee602f 100755 ---- a/configure -+++ b/configure -@@ -24837,7 +24837,7 @@ _ACEOF - if ac_fn_f77_try_compile "$LINENO"; then : - ac_cv_prog_f77_v= - # Try some options frequently used verbose output --for ac_verb in -v -verbose --verbose -V -\#\#\#; do -+for ac_verb in -\#\#\# -v -verbose --verbose -V ; do - cat > conftest.$ac_ext <<_ACEOF - program main - -@@ -25193,7 +25193,7 @@ _ACEOF - if ac_fn_c_try_compile "$LINENO"; then : - r_cv_prog_c_v= - # Try some options frequently used verbose output --for r_verb in -v -verbose --verbose -V -\#\#\#; do -+for r_verb in -\#\#\# -v -verbose --verbose -V ; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - --- -2.16.1 - diff --git a/build/pkgs/r/patches/configure_bzlibtest.patch b/build/pkgs/r/patches/configure_bzlibtest.patch deleted file mode 100644 index b4d3f61ef9e..00000000000 --- a/build/pkgs/r/patches/configure_bzlibtest.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/configure b/configure -index 976de50..14b6b4c 100755 ---- a/configure -+++ b/configure -@@ -42239,6 +42239,8 @@ else - - #ifdef HAVE_BZLIB_H - #include <bzlib.h> -+#include <string.h> -+#include <stdlib.h> - #endif - int main() { - char *ver = BZ2_bzlibVersion(); diff --git a/build/pkgs/r/patches/cygwin_build_support.patch b/build/pkgs/r/patches/cygwin_build_support.patch deleted file mode 100644 index e6965110602..00000000000 --- a/build/pkgs/r/patches/cygwin_build_support.patch +++ /dev/null @@ -1,123 +0,0 @@ -From b03ea08e8fe3cbda01824225447943c77c244ba8 Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:36 +0100 -Subject: [PATCH 2/9] cygwin_build_support - -Patches required to explicitly support Cygwin when building R. - ---- - configure | 15 ++++++++++++--- - configure.ac | 15 ++++++++++++--- - src/library/tools/R/install.R | 2 +- - 3 files changed, 25 insertions(+), 7 deletions(-) - -diff --git a/configure b/configure -index dee602f..fadb84e 100755 ---- a/configure -+++ b/configure -@@ -27723,6 +27723,15 @@ case "${host_os}" in - shlib_cxxldflags="-shared ${shlib_cxxldflags}" - fi - ;; -+ cygwin*) -+ ## All Windows binaries are PIC -+ cpicflags= -+ cxxpicflags= -+ fpicflags= -+ fcpicflags= -+ SHLIB_EXT=".dll" -+ dylib_undefined_allowed=no -+ ;; - darwin*) - darwin_pic="-fPIC" - dylib_undefined_allowed=no -@@ -27987,7 +27996,7 @@ fi - : ${CPICFLAGS="${cpicflags}"} - if test -z "${CPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine CPICFLAGS." >&5 -@@ -28000,7 +28009,7 @@ fi - : ${FPICFLAGS="${fpicflags}"} - if test -z "${FPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine FPICFLAGS." >&5 -@@ -28013,7 +28022,7 @@ fi - : ${CXXPICFLAGS="${cxxpicflags}"} - if test -n "${CXX}" -a -z "${CXXPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - warn_cxxpicflags="I could not determine CXXPICFLAGS." -diff --git a/configure.ac b/configure.ac -index 330d79a..ab7967b 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1294,6 +1294,15 @@ case "${host_os}" in - shlib_cxxldflags="-shared ${shlib_cxxldflags}" - fi - ;; -+ cygwin*) -+ ## All Windows binaries are PIC -+ cpicflags= -+ cxxpicflags= -+ fpicflags= -+ fcpicflags= -+ SHLIB_EXT=".dll" -+ dylib_undefined_allowed=no -+ ;; - darwin*) - darwin_pic="-fPIC" - dylib_undefined_allowed=no -@@ -1542,7 +1551,7 @@ R_SH_VAR_ADD(MAIN_LDFLAGS, [${main_ldflags}]) - : ${CPICFLAGS="${cpicflags}"} - if test -z "${CPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - AC_MSG_WARN([I could not determine CPICFLAGS.]) -@@ -1554,7 +1563,7 @@ fi - : ${FPICFLAGS="${fpicflags}"} - if test -z "${FPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - AC_MSG_WARN([I could not determine FPICFLAGS.]) -@@ -1566,7 +1575,7 @@ fi - : ${CXXPICFLAGS="${cxxpicflags}"} - if test -n "${CXX}" -a -z "${CXXPICFLAGS}"; then - case "${host_os}" in -- aix*|mingw*) -+ aix*|cygwin*|mingw*) - ;; - *) - warn_cxxpicflags="I could not determine CXXPICFLAGS." -diff --git a/src/library/tools/R/install.R b/src/library/tools/R/install.R -index 6f1e9d7..662556b 100644 ---- a/src/library/tools/R/install.R -+++ b/src/library/tools/R/install.R -@@ -841,7 +841,7 @@ - setwd(owd) - test_archs <- archs - for(arch in archs) { -- if (arch == "R") { -+ if (arch == "R" || arch == "R.exe") { - ## top-level, so one arch without subdirs - has_error <- run_shlib(pkg_name, srcs, instdir, "") - } else { --- -2.16.1 - diff --git a/build/pkgs/r/patches/hardcoded_dirs.patch b/build/pkgs/r/patches/hardcoded_dirs.patch deleted file mode 100644 index e76ce4f607b..00000000000 --- a/build/pkgs/r/patches/hardcoded_dirs.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 4dc58ff28b6cc21dc9edc03b76277affc0da2bd6 Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:37 +0100 -Subject: [PATCH 3/9] hardcoded_dirs - -Make R_HOME_DIR relative to SAGE_LOCAL by setting -R_HOME_DIR to "${SAGE_LOCAL}/lib/R/" when running R. - -Also remove the sed scripts hardcoding R_*_DIR's in the "frontend" R script. -See Sage trac #9668. - -This allows to move Sage install tree. - ---- - src/scripts/Makefile.in | 7 +------ - src/scripts/R.sh.in | 8 ++++++++ - 2 files changed, 9 insertions(+), 6 deletions(-) - -diff --git a/src/scripts/Makefile.in b/src/scripts/Makefile.in -index a7fa60d..ce504a0 100644 ---- a/src/scripts/Makefile.in -+++ b/src/scripts/Makefile.in -@@ -89,12 +89,7 @@ $(top_builddir)/libtool: - - install: installdirs install-cmds - @rm -f $(DESTDIR)$(bindir)/R -- @(d=`$(ECHO) '$(rhome)' | sed 's,/,\\\/,g';`; \ -- d2=`$(ECHO) '$(rsharedir)' | sed 's,/,\\\/,g';`; \ -- d3=`$(ECHO) '$(rincludedir)' | sed 's,/,\\\/,g';`; \ -- d4=`$(ECHO) '$(rdocdir)' | sed 's,/,\\\/,g';`; \ -- sed -e "1,/R_HOME_DIR=/s/\\(R_HOME_DIR=\\).*/\\1$${d}/;" -e "s/\\(R_SHARE_DIR=\\).*/\\1$${d2}/;" -e "s/\\(R_INCLUDE_DIR=\\).*/\\1$${d3}/;" -e "s/\\(R_DOC_DIR=\\).*/\\1$${d4}/;"\ -- < R.fe > "$(DESTDIR)$(Rexecbindir)/R") -+ @cat R.fe > "$(DESTDIR)$(Rexecbindir)/R" - @$(INSTALL_SCRIPT) "$(DESTDIR)$(Rexecbindir)/R" "$(DESTDIR)$(bindir)/R" - @chmod 755 "$(DESTDIR)$(bindir)/R" "$(DESTDIR)$(Rexecbindir)/R" - ## why of all the scripts does this alone chmod just one copy? -diff --git a/src/scripts/R.sh.in b/src/scripts/R.sh.in -index 674d5e0..11bfede 100644 ---- a/src/scripts/R.sh.in -+++ b/src/scripts/R.sh.in -@@ -26,6 +26,14 @@ if test "${R_HOME_DIR}" = "@prefix@/@LIBnn@/R"; then - esac - fi - -+# Make R_HOME_DIR relative to SAGE_LOCAL (if SAGE_LOCAL is set) -+# unless SAGE_BUILDING_R is set (as spkg-install does). -+if test -n "$SAGE_LOCAL"; then -+ if test -z "$SAGE_BUILDING_R"; then -+ R_HOME_DIR="$SAGE_LOCAL/lib/R/" -+ fi -+fi -+ - if test -n "${R_HOME}" && \ - test "${R_HOME}" != "${R_HOME_DIR}"; then - echo "WARNING: ignoring environment value of R_HOME" --- -2.16.1 - diff --git a/build/pkgs/r/patches/libcurl_https_support.patch b/build/pkgs/r/patches/libcurl_https_support.patch deleted file mode 100644 index 01c3c066232..00000000000 --- a/build/pkgs/r/patches/libcurl_https_support.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 996ded767d76952d9aae596219f8fb1b9469ef57 Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:37 +0100 -Subject: [PATCH 4/9] libcurl_https_support - -Don't check for HTTPS support in libcurl; see https://trac.sagemath.org/ticket/20523 - ---- - configure | 43 +------------------------------------------ - m4/R.m4 | 23 ++--------------------- - 2 files changed, 3 insertions(+), 63 deletions(-) - -diff --git a/configure b/configure -index fadb84e..02e210e 100755 ---- a/configure -+++ b/configure -@@ -41069,47 +41069,6 @@ if test "x${r_cv_have_curl722}" = xno; then - have_libcurl=no - fi - --if test "x${have_libcurl}" = "xyes"; then --{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libcurl supports https" >&5 --$as_echo_n "checking if libcurl supports https... " >&6; } --if ${r_cv_have_curl_https+:} false; then : -- $as_echo_n "(cached) " >&6 --else -- if test "$cross_compiling" = yes; then : -- r_cv_have_curl_https=no --else -- cat confdefs.h - <<_ACEOF >conftest.$ac_ext --/* end confdefs.h. */ -- --#include <string.h> --#include <curl/curl.h> --int main() --{ -- curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); -- const char * const *p = data->protocols; -- int found = 0; -- for (; *p; p++) -- if(strcmp(*p, "https") == 0) {found = 1; break;} -- exit(found ? 0 : 1); --} -- --_ACEOF --if ac_fn_c_try_run "$LINENO"; then : -- r_cv_have_curl_https=yes --else -- r_cv_have_curl_https=no --fi --rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ -- conftest.$ac_objext conftest.beam conftest.$ac_ext --fi -- --fi --{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $r_cv_have_curl_https" >&5 --$as_echo "$r_cv_have_curl_https" >&6; } --fi --if test "x${r_cv_have_curl_https}" = xno; then -- have_libcurl=no --fi - if test "x${have_libcurl}" = xyes; then - - $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h -@@ -41119,7 +41078,7 @@ $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h - - - else -- as_fn_error $? "libcurl >= 7.22.0 library and headers are required with support for https" "$LINENO" 5 -+ as_fn_error $? "libcurl >= 7.22.0 library and headers are required" "$LINENO" 5 - fi - - -diff --git a/m4/R.m4 b/m4/R.m4 -index 3fe8b27..fb35a5f 100644 ---- a/m4/R.m4 -+++ b/m4/R.m4 -@@ -4205,33 +4205,14 @@ if test "x${r_cv_have_curl722}" = xno; then - have_libcurl=no - fi - --if test "x${have_libcurl}" = "xyes"; then --AC_CACHE_CHECK([if libcurl supports https], [r_cv_have_curl_https], --[AC_RUN_IFELSE([AC_LANG_SOURCE([[ --#include <string.h> --#include <curl/curl.h> --int main() --{ -- curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); -- const char * const *p = data->protocols; -- int found = 0; -- for (; *p; p++) -- if(strcmp(*p, "https") == 0) {found = 1; break;} -- exit(found ? 0 : 1); --} --]])], [r_cv_have_curl_https=yes], [r_cv_have_curl_https=no], [r_cv_have_curl_https=no])]) --fi --if test "x${r_cv_have_curl_https}" = xno; then -- have_libcurl=no --fi - if test "x${have_libcurl}" = xyes; then -- AC_DEFINE(HAVE_LIBCURL, 1, [Define if your system has libcurl >= 7.22.0 with support for https.]) -+ AC_DEFINE(HAVE_LIBCURL, 1, [Define if your system has libcurl >= 7.22.0.]) - CPPFLAGS="${r_save_CPPFLAGS}" - LIBS="${r_save_LIBS}" - AC_SUBST(CURL_CPPFLAGS) - AC_SUBST(CURL_LIBS) - else -- AC_MSG_ERROR([libcurl >= 7.22.0 library and headers are required with support for https]) -+ AC_MSG_ERROR([libcurl >= 7.22.0 library and headers are required]) - fi - ])# R_LIBCURL - --- -2.16.1 - diff --git a/build/pkgs/r/patches/link_all_shared_libs.patch b/build/pkgs/r/patches/link_all_shared_libs.patch deleted file mode 100644 index 980b6842ad3..00000000000 --- a/build/pkgs/r/patches/link_all_shared_libs.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 9250c9e75137e78091da1cf1c25b3dbe40c7e4bf Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:37 +0100 -Subject: [PATCH 5/9] link_all_shared_libs - ---- - etc/Makeconf.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/etc/Makeconf.in b/etc/Makeconf.in -index e7d9fbd..3eccb2c 100644 ---- a/etc/Makeconf.in -+++ b/etc/Makeconf.in -@@ -150,7 +150,7 @@ ALL_OBJCFLAGS = $(PKG_OBJCFLAGS) $(CPICFLAGS) $(SHLIB_CFLAGS) $(OBJCFLAGS) - ALL_OBJCXXFLAGS = $(PKG_OBJCXXFLAGS) $(CXXPICFLAGS) $(SHLIB_CXXFLAGS) $(OBJCXXFLAGS) - ALL_FFLAGS = $(R_XTRA_FFLAGS) $(PKG_FFLAGS) $(FPICFLAGS) $(SHLIB_FFLAGS) $(FFLAGS) - ## LIBR here as a couple of packages use this without SHLIB_LINK --ALL_LIBS = $(PKG_LIBS) $(SHLIB_LIBADD) $(LIBR)@DYLIB_UNDEFINED_ALLOWED_FALSE@ $(LIBINTL) -+ALL_LIBS = $(PKG_LIBS) $(SHLIB_LIBADD) $(LIBR)@DYLIB_UNDEFINED_ALLOWED_FALSE@ $(LIBINTL) $(LIBS) - - .SUFFIXES: - .SUFFIXES: .c .cc .cpp .d .f .f90 .f95 .m .mm .M .o --- -2.16.1 - diff --git a/build/pkgs/r/patches/m4_macro_bug.patch b/build/pkgs/r/patches/m4_macro_bug.patch deleted file mode 100644 index bf96351c131..00000000000 --- a/build/pkgs/r/patches/m4_macro_bug.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0b4ac9b9ff60a82f57bf3852056548274cbde09d Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:37 +0100 -Subject: [PATCH 7/9] m4_macro_bug - -Fix bug in R_PCRE autoconf macro which leads to 'configure' losing '-lz' -and/or '-lbz2' from LIBS (under certain circumstances, and only relevant -if "system" versions of these libraries are used). (cf. #18229) - ---- - configure | 2 +- - m4/R.m4 | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure b/configure -index 02e210e..682b8cd 100755 ---- a/configure -+++ b/configure -@@ -40799,7 +40799,6 @@ fi - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $r_cv_have_pcre820" >&5 - $as_echo "$r_cv_have_pcre820" >&6; } --fi - if test "x${r_cv_have_pcre820}" != xyes; then - have_pcre=no - LIBS="${r_save_LIBS}" -@@ -40844,6 +40843,7 @@ fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $r_cv_have_pcre832" >&5 - $as_echo "$r_cv_have_pcre832" >&6; } - fi -+fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether PCRE support suffices" >&5 - $as_echo_n "checking whether PCRE support suffices... " >&6; } -diff --git a/m4/R.m4 b/m4/R.m4 -index fb35a5f..67cdd07 100644 ---- a/m4/R.m4 -+++ b/m4/R.m4 -@@ -3190,7 +3190,6 @@ int main() { - #endif - } - ]])], [r_cv_have_pcre820=yes], [r_cv_have_pcre820=no], [r_cv_have_pcre820=no])]) --fi - if test "x${r_cv_have_pcre820}" != xyes; then - have_pcre=no - LIBS="${r_save_LIBS}" -@@ -3213,6 +3212,7 @@ int main() { - } - ]])], [r_cv_have_pcre832=yes], [r_cv_have_pcre832=no], [r_cv_have_pcre832=no])]) - fi -+fi - - AC_MSG_CHECKING([whether PCRE support suffices]) - if test "x${r_cv_have_pcre820}" != xyes; then --- -2.16.1 - diff --git a/build/pkgs/r/patches/rcmd_exec.patch b/build/pkgs/r/patches/rcmd_exec.patch deleted file mode 100644 index f743e4791c5..00000000000 --- a/build/pkgs/r/patches/rcmd_exec.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 746b6908518c9725fece85270c587b5b110c08d3 Mon Sep 17 00:00:00 2001 -From: Emmanuel Charpentier <emm.charpentier@free.fr> -Date: Wed, 14 Mar 2018 19:30:37 +0100 -Subject: [PATCH 9/9] rcmd_exec - -On Cygwin some of the scripts in $R_HOME/bin can fail to be recognized -as executable, because they do no contain a shebang line and, depending -on the ACL settings in the Cygwin mount, may not have an executable flag -either. This results in the scripts not being run properly. It's fine -to just check that they exist. See https://trac.sagemath.org/ticket/20655 - ---- - src/scripts/Rcmd.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/scripts/Rcmd.in b/src/scripts/Rcmd.in -index 76d78d5..4b92483 100644 ---- a/src/scripts/Rcmd.in -+++ b/src/scripts/Rcmd.in -@@ -50,7 +50,7 @@ case "${1}" in - exit 1 - ;; - *) -- if test -x "${R_HOME}/bin/${1}"; then -+ if test -f "${R_HOME}/bin/${1}"; then - cmd="${R_HOME}/bin/${1}" - else - cmd="${1}" --- -2.16.1 - diff --git a/build/pkgs/r/spkg-check.in b/build/pkgs/r/spkg-check.in deleted file mode 100644 index 27cd9419538..00000000000 --- a/build/pkgs/r/spkg-check.in +++ /dev/null @@ -1,2 +0,0 @@ -cd src -$MAKE check diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 index dafa9113ece..b31f6bef2ec 100644 --- a/build/pkgs/r/spkg-configure.m4 +++ b/build/pkgs/r/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([r], [ - m4_pushdef([SAGE_R_MINVER],["3.4.4"]) - SAGE_SPKG_DEPCHECK([openblas iconv readline bzip2 liblzma pcre curl], [ - AS_CASE([$host], + dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements + m4_pushdef([SAGE_R_MINVER],["3.5"]) + AS_CASE([$host], [*-*-cygwin*], [ dnl #29486: rpy2 2.8.x does not build against system R on cygwin. sage_spkg_install_r=yes @@ -16,7 +16,6 @@ SAGE_SPKG_CONFIGURE([r], [ sage_spkg_install_r=no ]) ], [sage_spkg_install_r=yes]) - ]) ]) m4_popdef([SAGE_R_MINVER]) ]) diff --git a/build/pkgs/r/spkg-install.in b/build/pkgs/r/spkg-install.in deleted file mode 100644 index c63ff768f5b..00000000000 --- a/build/pkgs/r/spkg-install.in +++ /dev/null @@ -1,123 +0,0 @@ -# r uses grep output during its build -unset GREP_OPTIONS - -# Make sure CPPFLAGS and LDFLAGS are set to something (even the empty -# string) to prevent R from overriding them. Note: LDFLAGS is set by default. -# The --with-readline configure option only understands yes/no -# and cannot be used to pass this path. -CPPFLAGS="$CPPFLAGS" - -# #29170: Compilation errors caused by a silently failing configure check -# "for type of 'hidden' Fortran character lengths" -# on ubuntu-bionic-minimal, ubuntu-eoan/focal-minimal, debian-buster/bullseye/sid-minimal, -# linuxmint-19.3-minimal, archlinux-latest-minimal -CFLAGS="$CFLAGS_NON_NATIVE -fPIC" -FCFLAGS="$FCFLAGS_NON_NATIVE -fPIC" - -export CFLAGS CPPFLAGS FCFLAGS LDFLAGS - -if [ "$UNAME" = "Darwin" ]; then - # Put fake java on the PATH for OSX - export PATH="$(pwd)/bin:$PATH" -fi - -# Note by Karl-Dieter Crisman, April 12th 2010. X support would be nice -# to have in OSX, but see -# http://CRAN.R-project.org/bin/macosx/RMacOSX-FAQ.html#X11-window-server-_0028optional_0029 -# for how differently this would have to be handled on different OSX -# versions, none trivially. In any case, aqua, which we enable, -# performs the same function on OSX. -# -# Also, see #12172: for now, anyway, we disable X support on OS X. -# -# Note by David Kirkby, Feb 16th 2010. /usr/include/X11/Xwindows.h does -# not exist on Solaris, but R configures OK with X support. Hence I've added -# a more specific test on Solaris, by testing for a library. That library -# exists both on Solaris 10 03/2005 (SPARC) and on OpenSolaris. -if [ "$UNAME" = "Darwin" ]; then - XSUPPORT=no -elif [ -f /usr/include/X11/Xwindows.h ]; then - XSUPPORT=yes -elif [ "$UNAME" = "SunOS" ] && [ -f /usr/X11/lib/libXv.so ] ; then - XSUPPORT=yes -else - XSUPPORT=no -fi - -R_CONFIGURE_BLAS="--with-blas=$(pkg-config --libs blas)" -R_CONFIGURE_LAPACK="--with-lapack=$(pkg-config --libs lapack)" -echo "R_CONFIGURE_BLAS=$R_CONFIGURE_BLAS" -echo "R_CONFIGURE_LAPACK=$R_CONFIGURE_LAPACK" - -if [ "$UNAME" = "Darwin" ]; then - # We don't want to install R as a library framework on OSX - R_CONFIGURE="--enable-R-framework=no $R_CONFIGURE" - # OS X 10.10 and/or Xcode 6.3 and over broke the R installation. See - # http://trac.sagemath.org/ticket/18254. - if [ $MACOSX_VERSION -ge 14 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Configuring R without aqua support." - R_CONFIGURE="--with-aqua=no $R_CONFIGURE" - fi - if [ $MACOSX_VERSION -ge 16 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." - CC=clang - fi -fi - -if [ "$SAGE_FAT_BINARY" = yes ]; then - echo "Disabling ICU, OpenMP for a binary build" - R_CONFIGURE="--without-ICU --disable-openmp $R_CONFIGURE" -elif [ "$UNAME" = "SunOS" ]; then - # Note by David Kirkby, 16th Feb 2010. Even after adding the iconv library - # R would not build properly on Solaris 10, complaining of undefined symbols - # uiter_setUTF8 and ucol_strcollIter - # After an email to r-help@r-project.org, Ei-ji Nakama (rim.nakama@gmail.com) - # emailed me and said the option --without-ICU might help, which it did. I don't see - # this option documented, but for now at least, it does allow R to build. - - echo "Disabling ICU on Solaris, using an undocumented option --without-ICU" - echo "since the ICU library is not included on Solaris." - R_CONFIGURE="--without-ICU $R_CONFIGURE" -fi - -cd src - -if [ "$UNAME" = "Darwin" ]; then - # Fixing install_name(s) - sed -i -e 's:\"-install_name :\"-install_name ${libdir}/R/lib/:' configure - sed -i -e "/SHLIB_EXT/s/\.so/.dylib/" configure -fi - -# Don't override R_HOME_DIR in local/bin/R while building R. -# See patches/R.sh.patch -export SAGE_BUILDING_R=yes - -R_HOME="$SAGE_LOCAL"/lib/R -# Set LDFLAGS as it is done in sage-env for $SAGE_LOCAL/lib -LDFLAGS="-L$R_HOME/lib -Wl,-rpath,$R_HOME/lib $LDFLAGS" -if [ "$UNAME" = "Linux" ]; then - LDFLAGS="-Wl,-rpath-link,$R_HOME/lib $LDFLAGS" -fi -export LDFLAGS - -config() { - sdh_configure --enable-R-shlib --with-recommended-packages \ - --with-readline=yes --with-x=$XSUPPORT \ - "$R_CONFIGURE_BLAS" "$R_CONFIGURE_LAPACK" \ - $R_CONFIGURE -} - -if ! (config); then - echo "Configuring R without X11" - export XSUPPORT=no - config -fi - -# Build R -sdh_make R - -# needed for help system -sdh_make vignettes - -# Install new version -sdh_make_install diff --git a/build/pkgs/r/spkg-src b/build/pkgs/r/spkg-src deleted file mode 100755 index 23ce98c4702..00000000000 --- a/build/pkgs/r/spkg-src +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -## What do we want ? -VERSION=$(cat package-version.txt | sed -re "s/^(.*)\\.p.+$/\\1/") -TARGET_TARBALL=r-$VERSION.tar.gz -SOURCE_TARBALL=R-$VERSION.tar.gz - -set -e - -wget http://cran.r-project.org/src/base/R-3/$SOURCE_TARBALL -mv $SOURCE_TARBALL $SAGE_ROOT/upstream/$TARGET_TARBALL - -sage --package fix-checksum diff --git a/build/pkgs/r/spkg-legacy-uninstall b/build/pkgs/r/spkg-uninstall similarity index 100% rename from build/pkgs/r/spkg-legacy-uninstall rename to build/pkgs/r/spkg-uninstall diff --git a/build/pkgs/r/type b/build/pkgs/r/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/r/type +++ b/build/pkgs/r/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/requests/checksums.ini b/build/pkgs/requests/checksums.ini index 4a2693a8d90..5aabe04f278 100644 --- a/build/pkgs/requests/checksums.ini +++ b/build/pkgs/requests/checksums.ini @@ -1,5 +1,5 @@ tarball=requests-VERSION.tar.gz -sha1=a72bdfba339f5058c051c71954b59bef94c84455 -md5=5c1f6e737e1cb6f86a91d7a1473eda95 -cksum=4021136359 +sha1=53381250a0d114109a9e712dd7ce8e40e63e61e2 +md5=796ea875cdae283529c03b9203d9c454 +cksum=4112189908 upstream_url=https://pypi.io/packages/source/r/requests/requests-VERSION.tar.gz diff --git a/build/pkgs/requests/package-version.txt b/build/pkgs/requests/package-version.txt index 90efbd4e31e..9738a24f699 100644 --- a/build/pkgs/requests/package-version.txt +++ b/build/pkgs/requests/package-version.txt @@ -1 +1 @@ -2.28.0 +2.28.1 diff --git a/build/pkgs/rpy2/checksums.ini b/build/pkgs/rpy2/checksums.ini index 00cd4c4f123..ece539eec53 100644 --- a/build/pkgs/rpy2/checksums.ini +++ b/build/pkgs/rpy2/checksums.ini @@ -1,5 +1,5 @@ tarball=rpy2-VERSION.tar.gz -sha1=6436fc9bfc3118d6551c0e2a06d9ec2eb389c28a -md5=fd789967d3e46744cb1b2c4e55f47839 -cksum=906818309 +sha1=7d236c0c6982333b20b6a126f0c17a5481fea64b +md5=8842b153925a2eca21e2552e964facbb +cksum=1249008138 upstream_url=https://pypi.io/packages/source/r/rpy2/rpy2-VERSION.tar.gz diff --git a/build/pkgs/rpy2/install-requires.txt b/build/pkgs/rpy2/install-requires.txt index 7e5dc5f160a..769d7e61e92 100644 --- a/build/pkgs/rpy2/install-requires.txt +++ b/build/pkgs/rpy2/install-requires.txt @@ -1 +1 @@ -rpy2 >=3.3, <3.4 +rpy2 >=3.3 diff --git a/build/pkgs/rpy2/package-version.txt b/build/pkgs/rpy2/package-version.txt index 9c25013dbb8..4f5e69734c9 100644 --- a/build/pkgs/rpy2/package-version.txt +++ b/build/pkgs/rpy2/package-version.txt @@ -1 +1 @@ -3.3.6 +3.4.5 diff --git a/build/pkgs/rpy2/patches/716.patch b/build/pkgs/rpy2/patches/716.patch deleted file mode 100644 index 8446b281633..00000000000 --- a/build/pkgs/rpy2/patches/716.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 87d0f82e2f4be94893881913018ca9085c0ff8e5 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Fri, 3 Jul 2020 12:47:41 -0700 -Subject: [PATCH] setup.py: Print CFFI configuration messages only on build - ---- - setup.py | 44 ++++++++++++++++++++++++++------------------ - 1 file changed, 26 insertions(+), 18 deletions(-) - -diff --git a/setup.py b/setup.py -index e4337838..7fead893 100755 ---- a/setup.py -+++ b/setup.py -@@ -21,6 +21,7 @@ - from rpy2 import situation - - from setuptools import setup -+from distutils.command.build import build as du_build - - PACKAGE_NAME = 'rpy2' - pack_version = __import__('rpy2').__version__ -@@ -111,7 +112,6 @@ def get_r_c_extension_status(): - - - cffi_mode = situation.get_cffi_mode() --print('cffi mode: %s' % cffi_mode) - c_extension_status = get_r_c_extension_status() - if cffi_mode == situation.CFFI_MODE.ABI: - cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_abi'] -@@ -135,6 +135,30 @@ def get_r_c_extension_status(): - # This should never happen. - raise ValueError('Invalid value for cffi_mode') - -+class build(du_build): -+ -+ def run(self): -+ print('cffi mode: %s' % cffi_mode) -+ -+ du_build.run(self) -+ -+ print('---') -+ print(cffi_mode) -+ if cffi_mode in (situation.CFFI_MODE.ABI, -+ situation.CFFI_MODE.BOTH, -+ situation.CFFI_MODE.ANY): -+ print('ABI mode interface built.') -+ if cffi_mode in (situation.CFFI_MODE.API, -+ situation.CFFI_MODE.BOTH): -+ print('API mode interface built.') -+ if cffi_mode == situation.CFFI_MODE.ANY: -+ if c_extension_status == COMPILATION_STATUS.OK: -+ print('API mode interface built.') -+ else: -+ print('API mode interface not built because: %s' % c_extension_status) -+ print('To change the API/ABI build mode, set or modify the environment ' -+ 'variable RPY2_CFFI_MODE.') -+ - LONG_DESCRIPTION = """ - Python interface to the R language. - -@@ -168,6 +192,7 @@ def get_r_c_extension_status(): - install_requires=requires + ['cffi>=1.10.0'], - setup_requires=['cffi>=1.10.0'], - cffi_modules=cffi_modules, -+ cmdclass = dict(build=build), - package_dir=pack_dir, - packages=([PACKAGE_NAME] + - ['{pack_name}.{x}'.format(pack_name=PACKAGE_NAME, x=x) -@@ -193,20 +218,3 @@ def get_r_c_extension_status(): - package_data={'rpy2': ['rinterface_lib/R_API.h', - 'rinterface_lib/R_API_eventloop.h']} - ) -- -- print('---') -- print(cffi_mode) -- if cffi_mode in (situation.CFFI_MODE.ABI, -- situation.CFFI_MODE.BOTH, -- situation.CFFI_MODE.ANY): -- print('ABI mode interface built and installed.') -- if cffi_mode in (situation.CFFI_MODE.API, -- situation.CFFI_MODE.BOTH): -- print('API mode interface built and installed.') -- if cffi_mode == situation.CFFI_MODE.ANY: -- if c_extension_status == COMPILATION_STATUS.OK: -- print('API mode interface built and installed.') -- else: -- print('API mode interface not build because: %s' % c_extension_status) -- print('To change the API/ABI build mode, set or modify the environment ' -- 'variable RPY2_CFFI_MODE.') diff --git a/build/pkgs/rpy2/patches/setup-no-pytest.patch b/build/pkgs/rpy2/patches/setup-no-pytest.patch deleted file mode 100644 index 91cb5127572..00000000000 --- a/build/pkgs/rpy2/patches/setup-no-pytest.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/setup.py b/setup.py -index a9f96f8..7ba69a1 100755 ---- a/setup.py -+++ b/setup.py -@@ -142,7 +142,7 @@ ipython. - if __name__ == '__main__': - pack_dir = {PACKAGE_NAME: os.path.join(package_prefix, 'rpy2')} - -- requires = ['pytest', 'jinja2', 'pytz', 'tzlocal'] -+ requires = ['jinja2', 'pytz', 'tzlocal'] - - setup( - name=PACKAGE_NAME, diff --git a/build/pkgs/rpy2/spkg-configure.m4 b/build/pkgs/rpy2/spkg-configure.m4 new file mode 100644 index 00000000000..c9831c6b60a --- /dev/null +++ b/build/pkgs/rpy2/spkg-configure.m4 @@ -0,0 +1,14 @@ +SAGE_SPKG_CONFIGURE([rpy2], [ + sage_spkg_install_rpy2=yes + ], [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]) + ]) + ]) +]) diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 35a8993af20..819432d90ff 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.8b0 +sage-conf ~= 10.0b0 diff --git a/build/pkgs/sage_conf/spkg-install b/build/pkgs/sage_conf/spkg-install index e572d89f80d..a180fb36542 100755 --- a/build/pkgs/sage_conf/spkg-install +++ b/build/pkgs/sage_conf/spkg-install @@ -12,6 +12,9 @@ fi cd src if [ "$SAGE_EDITABLE" = yes ]; then sdh_pip_editable_install . + if [ "$SAGE_WHEELS" = yes ]; then + sdh_setup_bdist_wheel && sdh_store_wheel . + fi else sdh_pip_install . fi diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 002c6b79c65..73f499affef 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.8b0 +sage-docbuild ~= 10.0b0 diff --git a/build/pkgs/sage_docbuild/spkg-install b/build/pkgs/sage_docbuild/spkg-install index 1bb66bc4a07..cce58b40e22 100755 --- a/build/pkgs/sage_docbuild/spkg-install +++ b/build/pkgs/sage_docbuild/spkg-install @@ -12,6 +12,9 @@ fi cd src if [ "$SAGE_EDITABLE" = yes ]; then sdh_pip_editable_install . + if [ "$SAGE_WHEELS" = yes ]; then + sdh_setup_bdist_wheel && sdh_store_wheel . + fi else sdh_pip_install . fi diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 5720bc8f2c5..169a4c5d9cc 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.8b0 +sage-setup ~= 10.0b0 diff --git a/build/pkgs/sage_setup/spkg-install b/build/pkgs/sage_setup/spkg-install index 1bb66bc4a07..cce58b40e22 100755 --- a/build/pkgs/sage_setup/spkg-install +++ b/build/pkgs/sage_setup/spkg-install @@ -12,6 +12,9 @@ fi cd src if [ "$SAGE_EDITABLE" = yes ]; then sdh_pip_editable_install . + if [ "$SAGE_WHEELS" = yes ]; then + sdh_setup_bdist_wheel && sdh_store_wheel . + fi else sdh_pip_install . fi diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 70be4d731ee..e2b85444aca 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.8b0 +sage-sws2rst ~= 10.0b0 diff --git a/build/pkgs/sage_sws2rst/spkg-install b/build/pkgs/sage_sws2rst/spkg-install index 12d0d16dff1..47ef9ee6cbf 100755 --- a/build/pkgs/sage_sws2rst/spkg-install +++ b/build/pkgs/sage_sws2rst/spkg-install @@ -3,16 +3,21 @@ # For type=script packages, the build rule in build/make/Makefile sources # sage-env but not sage-dist-helpers. lib="$SAGE_ROOT/build/bin/sage-dist-helpers" -. "$lib" +source "$lib" if [ $? -ne 0 ]; then echo >&2 "Error: failed to source $lib" echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" exit 1 fi -set -e -# We build the wheel directly with "setup.py bdist_wheel", not with "pip wheel", -# because pip does not handle our symlinks correctly. -(cd src && sdh_setup_bdist_wheel && sdh_store_and_pip_install_wheel .) +cd src +if [ "$SAGE_EDITABLE" = yes ]; then + sdh_pip_editable_install . + if [ "$SAGE_WHEELS" = yes ]; then + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + sdh_pip_install . +fi # For type=script packages, spkg-check is not run case "$SAGE_CHECK" in yes) diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index 810713712ed..abf21122c87 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica zn_poly $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 359dff35670..565dfde8395 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.8b0 +sagelib ~= 10.0b0 diff --git a/build/pkgs/sagelib/spkg-install b/build/pkgs/sagelib/spkg-install index 8d91b16b3f0..ad8b2ed43fc 100755 --- a/build/pkgs/sagelib/spkg-install +++ b/build/pkgs/sagelib/spkg-install @@ -1,4 +1,15 @@ #!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi + if [ "$SAGE_EDITABLE" = yes ]; then cd "$SAGE_SRC" else @@ -27,8 +38,6 @@ export SAGE_SRC_ROOT=/doesnotexist export SAGE_DOC_SRC=/doesnotexist export SAGE_BUILD_DIR=/doesnotexist -export PYTHON="$SAGE_LOCAL/bin/python3" - # We also poison all directories below SAGE_LOCAL. export SAGE_PKGCONFIG=/doesnotexist export SAGE_SPKG_SCRIPTS=/doesnotexist @@ -51,15 +60,27 @@ if [ "$SAGE_EDITABLE" = yes ]; then # under the old distribution name "sage" (before #30912, which switched to setuptools # and renamed the distribution to "sagemath-standard"). There is no clean way to uninstall # them, so we just use rm. - (cd "$SITEPACKAGESDIR" && rm -rf sage sage_setup sage-[1-9]*.egg-info sage-[1-9]*.dist-info) - time python3 -m pip install --verbose --no-deps --no-index --no-build-isolation --isolated --editable . || exit 1 + (cd "$SITEPACKAGESDIR" && rm -rf sage sage-[1-9]*.egg-info sage-[1-9]*.dist-info) + time sdh_pip_editable_install . + + if [ "$SAGE_WHEELS" = yes ]; then + # Additionally build a wheel (for use in other venvs) + cd $SAGE_PKGS/sagelib/src && time sdh_setup_bdist_wheel && sdh_store_wheel . + fi else # Make sure that an installed old version of sagelib in which sage is an ordinary package # does not shadow the namespace package sage during the build. (cd "$SITEPACKAGESDIR" && rm -f sage/__init__.py) # Likewise, we should remove the egg-link that may have been installed previously. (cd "$SITEPACKAGESDIR" && rm -f sagemath-standard.egg-link) - time python3 -u setup.py --no-user-cfg build install || exit 1 + + if [ "$SAGE_WHEELS" = yes ]; then + # Use --no-build-isolation to avoid rebuilds because of dependencies: + # Compiling sage/interfaces/sagespawn.pyx because it depends on /private/var/folders/38/wnh4gf1552g_crsjnv2vmmww0000gp/T/pip-build-env-609n5985/overlay/lib/python3.10/site-packages/Cython/Includes/posix/unistd.pxd + time sdh_pip_install --no-build-isolation . + else + time python3 -u setup.py --no-user-cfg build install || exit 1 + fi fi # Trac #33103: The temp.* directories are large after a full build. diff --git a/build/pkgs/sagemath_categories/dependencies b/build/pkgs/sagemath_categories/dependencies deleted file mode 120000 index 55c209e6418..00000000000 --- a/build/pkgs/sagemath_categories/dependencies +++ /dev/null @@ -1 +0,0 @@ -../sagemath_objects/dependencies \ No newline at end of file diff --git a/build/pkgs/sagemath_categories/dependencies b/build/pkgs/sagemath_categories/dependencies new file mode 100644 index 00000000000..d8b6bdbd4a7 --- /dev/null +++ b/build/pkgs/sagemath_categories/dependencies @@ -0,0 +1 @@ +$(PYTHON) sagemath_objects | $(PYTHON_TOOLCHAIN) sagemath_environment sage_setup cython pkgconfig python_build diff --git a/build/pkgs/sagemath_categories/dependencies_check b/build/pkgs/sagemath_categories/dependencies_check new file mode 100644 index 00000000000..7d2fe6c3064 --- /dev/null +++ b/build/pkgs/sagemath_categories/dependencies_check @@ -0,0 +1 @@ +tox sagemath_repl diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 070f8caa2ea..15c1dfbcc42 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.8b0 +sagemath-categories ~= 10.0b0 diff --git a/build/pkgs/sagemath_doc_html/dependencies b/build/pkgs/sagemath_doc_html/dependencies index 039e22f11ef..5dfe1c2d453 100644 --- a/build/pkgs/sagemath_doc_html/dependencies +++ b/build/pkgs/sagemath_doc_html/dependencies @@ -1,4 +1,4 @@ -sagelib sphinx 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 +sagelib sphinx 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 # Building the documentation has many dependencies, because all # documented modules are imported and because we use matplotlib to diff --git a/build/pkgs/sagemath_environment/dependencies b/build/pkgs/sagemath_environment/dependencies deleted file mode 120000 index 55c209e6418..00000000000 --- a/build/pkgs/sagemath_environment/dependencies +++ /dev/null @@ -1 +0,0 @@ -../sagemath_objects/dependencies \ No newline at end of file diff --git a/build/pkgs/sagemath_environment/dependencies b/build/pkgs/sagemath_environment/dependencies new file mode 100644 index 00000000000..605611e7a21 --- /dev/null +++ b/build/pkgs/sagemath_environment/dependencies @@ -0,0 +1 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) python_build diff --git a/build/pkgs/sagemath_environment/dependencies_check b/build/pkgs/sagemath_environment/dependencies_check new file mode 100644 index 00000000000..053148f8486 --- /dev/null +++ b/build/pkgs/sagemath_environment/dependencies_check @@ -0,0 +1 @@ +tox diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index e9976c7146f..73ef51c4e92 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.8b0 +sagemath-environment ~= 10.0b0 diff --git a/build/pkgs/sagemath_objects/dependencies b/build/pkgs/sagemath_objects/dependencies index 217821d206b..807b8b17215 100644 --- a/build/pkgs/sagemath_objects/dependencies +++ b/build/pkgs/sagemath_objects/dependencies @@ -1,4 +1,3 @@ -FORCE $(PYTHON) cysignals gmpy2 ipython | $(PYTHON_TOOLCHAIN) sage_setup cython pkgconfig python_build +FORCE $(PYTHON) cysignals gmpy2 | $(PYTHON_TOOLCHAIN) sagemath_environment sage_setup cython pkgconfig python_build # FORCE: Always run the spkg-install script -# ipython - for the doctester diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 0fb621f26f2..a03b35802fe 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.8b0 +sagemath-objects ~= 10.0b0 diff --git a/build/pkgs/sagemath_objects/spkg-install b/build/pkgs/sagemath_objects/spkg-install index 5c2ab2350c4..6cc85e07e55 100755 --- a/build/pkgs/sagemath_objects/spkg-install +++ b/build/pkgs/sagemath_objects/spkg-install @@ -19,15 +19,11 @@ export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" # (Important because sagemath-objects uses MANIFEST.in for filtering.) # Do not install the wheel. DIST_DIR="$(mktemp -d)" -if ! python3 -m build --outdir "$DIST_DIR"/dist .; then - # This happens on Debian without python3-venv installed - "ensurepip" is missing - echo "Falling back to --no-isolation" - python3 -m build --no-isolation --outdir "$DIST_DIR"/dist . || sdh_die "Failure building sdist and wheel" -fi +python3 -m build --outdir "$DIST_DIR"/dist . || sdh_die "Failure building sdist and wheel" wheel=$(cd "$DIST_DIR" && sdh_store_wheel . && echo $wheel) ls -l "$wheel" if [ "$SAGE_CHECK" != no ]; then - tox -v -e sagepython-norequirements --installpkg "$wheel" + tox -r -v -e sagepython-sagewheels-nopypi-norequirements --installpkg $wheel fi diff --git a/build/pkgs/sagemath_repl/dependencies b/build/pkgs/sagemath_repl/dependencies deleted file mode 120000 index 55c209e6418..00000000000 --- a/build/pkgs/sagemath_repl/dependencies +++ /dev/null @@ -1 +0,0 @@ -../sagemath_objects/dependencies \ No newline at end of file diff --git a/build/pkgs/sagemath_repl/dependencies b/build/pkgs/sagemath_repl/dependencies new file mode 100644 index 00000000000..ebc253dac5b --- /dev/null +++ b/build/pkgs/sagemath_repl/dependencies @@ -0,0 +1 @@ +$(PYTHON) sagemath_objects sagemath_environment ipython ipywidgets | $(PYTHON_TOOLCHAIN) python_build diff --git a/build/pkgs/sagemath_repl/dependencies_check b/build/pkgs/sagemath_repl/dependencies_check new file mode 100644 index 00000000000..053148f8486 --- /dev/null +++ b/build/pkgs/sagemath_repl/dependencies_check @@ -0,0 +1 @@ +tox diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt new file mode 100644 index 00000000000..306890d9db9 --- /dev/null +++ b/build/pkgs/sagemath_repl/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-repl ~= 10.0b0 diff --git a/build/pkgs/scip/SPKG.rst b/build/pkgs/scip/SPKG.rst new file mode 100644 index 00000000000..5a6d3082d27 --- /dev/null +++ b/build/pkgs/scip/SPKG.rst @@ -0,0 +1,29 @@ +scip: Mixed integer programming solver +====================================== + +Description +----------- + +SCIP is currently one of the fastest open source mixed integer +programming (MIP) solvers. It is also a framework for constraint integer +programming and branch-cut-and-price. It allows total control of the +solution process and the access of detailed information down to the guts +of the solver. + +License +------- + +Apache 2.0 + + +Upstream Contact +---------------- + +https://scipopt.org/#scipoptsuite + + +Dependencies +------------ + +scip brings its own patched version of the bliss library. +This will conflict with the optional package bliss. diff --git a/build/pkgs/scip/checksums.ini b/build/pkgs/scip/checksums.ini new file mode 100644 index 00000000000..97b8c8a7084 --- /dev/null +++ b/build/pkgs/scip/checksums.ini @@ -0,0 +1,5 @@ +tarball=scip-VERSION.tar.gz +sha1=2637767428e285b6ddda8c462f1cc31d66833d80 +md5=b657369986ecd9b2944206d11ecce2e4 +cksum=967379932 +upstream_url=https://github.com/scipopt/scip/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/scip/dependencies b/build/pkgs/scip/dependencies new file mode 100644 index 00000000000..62836bef5a5 --- /dev/null +++ b/build/pkgs/scip/dependencies @@ -0,0 +1,4 @@ +$(MP_LIBRARY) readline soplex papilo zlib | cmake + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/scip/distros/conda.txt b/build/pkgs/scip/distros/conda.txt new file mode 100644 index 00000000000..3065c152171 --- /dev/null +++ b/build/pkgs/scip/distros/conda.txt @@ -0,0 +1 @@ +scip diff --git a/build/pkgs/scipoptsuite/distros/repology.txt b/build/pkgs/scip/distros/repology.txt similarity index 100% rename from build/pkgs/scipoptsuite/distros/repology.txt rename to build/pkgs/scip/distros/repology.txt diff --git a/build/pkgs/scip/package-version.txt b/build/pkgs/scip/package-version.txt new file mode 100644 index 00000000000..227b54a0631 --- /dev/null +++ b/build/pkgs/scip/package-version.txt @@ -0,0 +1 @@ +802 diff --git a/build/pkgs/scip/patches/no_rpath.patch b/build/pkgs/scip/patches/no_rpath.patch new file mode 100644 index 00000000000..28b4220b6b3 --- /dev/null +++ b/build/pkgs/scip/patches/no_rpath.patch @@ -0,0 +1,40 @@ +commit eaff18abb55c86e90d44583731550c874dc3c3e0 +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Sun Nov 27 14:12:32 2022 -0800 + + CMakeLists.txt: Remove hardcoded RPATH settings + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 8629ace18b..db2505d74b 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -3,8 +3,7 @@ + # + function(setLibProperties targetname outputname) + set_target_properties(${targetname} PROPERTIES +- OUTPUT_NAME ${outputname} +- MACOSX_RPATH "${CMAKE_INSTALL_PREFIX}/lib") ++ OUTPUT_NAME ${outputname}) + endfunction(setLibProperties) + + set(CMAKE_C_STANDARD 99) +@@ -1092,7 +1091,6 @@ add_dependencies(scip scip_update_githash) + set_target_properties(libscip PROPERTIES + VERSION ${SCIP_VERSION_MAJOR}.${SCIP_VERSION_MINOR}.${SCIP_VERSION_PATCH}.${SCIP_VERSION_SUB} + SOVERSION ${SCIP_VERSION_MAJOR}.${SCIP_VERSION_MINOR} +- INSTALL_RPATH_USE_LINK_PATH TRUE + CXX_VISIBILITY_PRESET hidden + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN 1) +@@ -1102,11 +1100,6 @@ target_include_directories(scip PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> + $<INSTALL_INTERFACE:include>) + +-# set the install rpath to the installed destination +-set_target_properties(scip PROPERTIES +- INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" +- INSTALL_RPATH_USE_LINK_PATH TRUE) +- + # install the header files of scip + install(FILES ${lpiheaders} DESTINATION include/lpi) + install(FILES ${dijkstraheaders} DESTINATION include/dijkstra) diff --git a/build/pkgs/scipoptsuite/spkg-check.in b/build/pkgs/scip/spkg-check.in similarity index 100% rename from build/pkgs/scipoptsuite/spkg-check.in rename to build/pkgs/scip/spkg-check.in diff --git a/build/pkgs/scip/spkg-install.in b/build/pkgs/scip/spkg-install.in new file mode 100644 index 00000000000..69bbdfae266 --- /dev/null +++ b/build/pkgs/scip/spkg-install.in @@ -0,0 +1,17 @@ +cd src +mkdir build +cd build +sdh_cmake -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON \ + -DGMP_DIR="${SAGE_GMP_PREFIX}" \ + -DReadline_ROOT_DIR=$(pkg-config --variable=prefix readline) \ + -DHistory_ROOT_DIR=$(pkg-config --variable=prefix readline) \ + -DIPOPT=off \ + -DPAPILO=on -DPAPILO_DIR="${SAGE_LOCAL}" \ + -DZIMPL=off \ + -DAMPL=off \ + -DSYM=bliss \ + .. +sdh_make +sdh_make_install diff --git a/build/pkgs/scip/type b/build/pkgs/scip/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/scip/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/scip_sdp/SPKG.rst b/build/pkgs/scip_sdp/SPKG.rst new file mode 100644 index 00000000000..05452ed5069 --- /dev/null +++ b/build/pkgs/scip_sdp/SPKG.rst @@ -0,0 +1,32 @@ +scip_sdp: Mixed integer semidefinite programming plugin for SCIP +================================================================ + +Description +----------- + +SCIP-SDP allows to solve MISDPs using a nonlinear branch-and-bound +approach or a linear programming cutting-plane approach. + +- In the first case (the default), the semidefinite programming (SDP) + relaxations are solve using interior-point SDP-solvers. + +- In the second case, cutting planes based on eigenvector are + generated. + +SCIP-SDP is based on the branch-and-cut framework SCIP. In addition to +providing a constraint handler for SDP-constraints and a relaxator to +solve continuous SDP-relaxations using interior-point solvers, +SCIP-SDP adds several heuristics and propagators to SCIP. + +License +------- + +Apache 2.0 + + +Upstream Contact +---------------- + +http://www.opt.tu-darmstadt.de/scipsdp/ + +https://github.com/scipopt/SCIP-SDP diff --git a/build/pkgs/scip_sdp/checksums.ini b/build/pkgs/scip_sdp/checksums.ini new file mode 100644 index 00000000000..b0e38cf751d --- /dev/null +++ b/build/pkgs/scip_sdp/checksums.ini @@ -0,0 +1,5 @@ +tarball=scipsdp-VERSION.tgz +sha1=dcfb090a95f79df8524bcc63d34d7ddc6692924e +md5=4f900c60456b3f08160ca494bec8e9f4 +cksum=2622380399 +upstream_url=http://www.opt.tu-darmstadt.de/scipsdp/downloads/scipsdp-VERSION.tgz diff --git a/build/pkgs/scip_sdp/dependencies b/build/pkgs/scip_sdp/dependencies new file mode 100644 index 00000000000..4b4bd5d4ae9 --- /dev/null +++ b/build/pkgs/scip_sdp/dependencies @@ -0,0 +1 @@ +scip dsdp | cmake diff --git a/build/pkgs/scip_sdp/package-version.txt b/build/pkgs/scip_sdp/package-version.txt new file mode 100644 index 00000000000..ee74734aa22 --- /dev/null +++ b/build/pkgs/scip_sdp/package-version.txt @@ -0,0 +1 @@ +4.1.0 diff --git a/build/pkgs/scip_sdp/patches/findlapack-09e79cf75146ca34c637f919d1f4527a63743300.patch b/build/pkgs/scip_sdp/patches/findlapack-09e79cf75146ca34c637f919d1f4527a63743300.patch new file mode 100644 index 00000000000..a2ec71a5e71 --- /dev/null +++ b/build/pkgs/scip_sdp/patches/findlapack-09e79cf75146ca34c637f919d1f4527a63743300.patch @@ -0,0 +1,55 @@ +From 09e79cf75146ca34c637f919d1f4527a63743300 Mon Sep 17 00:00:00 2001 +From: Marc Pfetsch <pfetsch@mathematik.tu-darmstadt.de> +Date: Fri, 9 Dec 2022 18:52:09 +0100 +Subject: [PATCH] use cmake FindLAPACK + +--- + CMakeLists.txt | 14 +++++--------- + src/CMakeLists.txt | 6 +++--- + 2 files changed, 8 insertions(+), 12 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 15e4edc..d4f14f5 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -79,16 +79,12 @@ add_custom_target(scip_update_githash + COMMAND ${CMAKE_COMMAND} -DDST=${PROJECT_SOURCE_DIR}/scipsdpgithash.c + -P ${CMAKE_BINARY_DIR}/scip_update_githash.cmake) + +- + # find lapack and blas +-find_library(LAPACK_LIBRARY lapack) +-if(LAPACK_LIBRARY) +- message(STATUS "Found lapack library: " ${LAPACK_LIBRARY}) +-endif() +- +-find_library(BLAS_LIBRARY blas) +-if(BLAS_LIBRARY) +- message(STATUS "Found blas library: " ${BLAS_LIBRARY}) ++find_package(LAPACK REQUIRED) ++if(LAPACK_FOUND) ++ message(STATUS "Found lapack library: " ${LAPACK_LIBRARIES}) ++else() ++ message(FATAL_ERROR "Lapack not found") + endif() + + # search the selected symmetry computation program +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 30c66b7..5e5d7d3 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -125,11 +125,11 @@ setLibProperties(libscipsdp "scipsdp") + #put binary in bin directory + set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + if(SHARED) +- target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARY} ${BLAS_LIBRARY} m) ++ target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) + add_executable(scipsdp scipsdp/main.c ${scipsdpsources} ${objscipsdpsources} ${sdpisources} ${sym}) +- target_link_libraries(scipsdp ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARY} ${BLAS_LIBRARY} m) ++ target_link_libraries(scipsdp ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) + else() +- target_link_libraries(libscipsdp PRIVATE ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARY} ${BLAS_LIBRARY} m) ++ target_link_libraries(libscipsdp PRIVATE ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) + add_executable(scipsdp scipsdp/main.c ${scipsdpsources} ${objscipsdpsources} ${sdpisources} ${sym}) + target_link_libraries(scipsdp libscipsdp ${SCIP_LIBRARIES}) + endif() diff --git a/build/pkgs/scip_sdp/patches/macos_link.patch b/build/pkgs/scip_sdp/patches/macos_link.patch new file mode 100644 index 00000000000..16f2da86d22 --- /dev/null +++ b/build/pkgs/scip_sdp/patches/macos_link.patch @@ -0,0 +1,19 @@ +commit 09f4b596870a2774f7104e318d9812cd5c7ace4a +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Wed Dec 7 14:59:49 2022 -0800 + + src/CMakeLists.txt: Link libscipsdp through with libscip + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 5e5d7d3..ac3841e 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -125,7 +125,7 @@ setLibProperties(libscipsdp "scipsdp") + #put binary in bin directory + set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + if(SHARED) +- target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) ++ target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) + add_executable(scipsdp scipsdp/main.c ${scipsdpsources} ${objscipsdpsources} ${sdpisources} ${sym}) + target_link_libraries(scipsdp ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) + else() diff --git a/build/pkgs/scip_sdp/patches/no_rpath.patch b/build/pkgs/scip_sdp/patches/no_rpath.patch new file mode 100644 index 00000000000..a4509df7fc7 --- /dev/null +++ b/build/pkgs/scip_sdp/patches/no_rpath.patch @@ -0,0 +1,35 @@ +commit b12db4b95295fec9b32131039a6ee4a746c6f832 +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Wed Dec 7 15:03:48 2022 -0800 + + src/CMakeLists.txt: Remote hardcoded RPATH settings + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index f293115..64ac3d1 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -5,8 +5,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + # + function(setLibProperties targetname outputname) + set_target_properties(${targetname} PROPERTIES +- OUTPUT_NAME ${outputname} +- MACOSX_RPATH "${CMAKE_INSTALL_PREFIX}/lib") ++ OUTPUT_NAME ${outputname}) + endfunction(setLibProperties) + + set(CMAKE_C_STANDARD 99) +@@ -141,13 +140,7 @@ target_compile_definitions(scipsdp PRIVATE EXTERN=extern) + + set_target_properties(libscipsdp PROPERTIES + VERSION ${SCIPSDP_VERSION_MAJOR}.${SCIPSDP_VERSION_MINOR}.${SCIPSDP_VERSION_PATCH} +- SOVERSION ${SCIPSDP_VERSION_MAJOR}.${SCIPSDP_VERSION_MINOR} +- INSTALL_RPATH_USE_LINK_PATH TRUE) +- +-# set the install rpath to the installed destination +-set_target_properties(scipsdp PROPERTIES +- INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" +- INSTALL_RPATH_USE_LINK_PATH TRUE) ++ SOVERSION ${SCIPSDP_VERSION_MAJOR}.${SCIPSDP_VERSION_MINOR}) + + # install the header files of scip + install(FILES ${sdpiheaders} DESTINATION include/sdpi) diff --git a/build/pkgs/scip_sdp/patches/zz_another_blas_change.patch b/build/pkgs/scip_sdp/patches/zz_another_blas_change.patch new file mode 100644 index 00000000000..132f13eab82 --- /dev/null +++ b/build/pkgs/scip_sdp/patches/zz_another_blas_change.patch @@ -0,0 +1,80 @@ +commit acb468a1805055e9f34dc1057eea186335bc2e13 +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Fri Dec 9 15:53:05 2022 -0800 + + cmake/Modules/Find{DSDP,MOSEK}.cmake: Do not hardcode blas/lapack lib names here + +commit 8c8f7f82a16af70e044526f2911a94429d65b588 +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Fri Dec 9 19:09:51 2022 -0800 + + CMakeLists.txt, src/CMakeLists.txt: Explicitly link through with blas + + +diff --git a/cmake/Modules/FindDSDP.cmake b/cmake/Modules/FindDSDP.cmake +index 8f43ae8..376fbdb 100644 +--- a/cmake/Modules/FindDSDP.cmake ++++ b/cmake/Modules/FindDSDP.cmake +@@ -8,7 +8,7 @@ find_library(DSDP_LIBRARY + HINTS ${DSDP_DIR} $ENV{DSDP_DIR} + PATH_SUFFIXES lib) + +-set(DSDP_LIBRARIES ${DSDP_LIBRARY} -llapack -lblas) ++set(DSDP_LIBRARIES ${DSDP_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(DSDP DEFAULT_MSG DSDP_INCLUDE_DIRS DSDP_LIBRARIES) +diff --git a/cmake/Modules/FindMOSEK.cmake b/cmake/Modules/FindMOSEK.cmake +index a8ee1d6..e3b7ab7 100644 +--- a/cmake/Modules/FindMOSEK.cmake ++++ b/cmake/Modules/FindMOSEK.cmake +@@ -15,10 +15,10 @@ find_library(IOMP5_LIBRARY + PATH_SUFFIXES bin) + + if(IOMPS_LIBRARY) +- set(MOSEK_LIBRARIES ${MOSEK_LIBRARY} ${IOMP5_LIBRARY} -llapack -lblas -pthread) ++ set(MOSEK_LIBRARIES ${MOSEK_LIBRARY} ${IOMP5_LIBRARY} -pthread) + else() + # if libiomps is not available, we skip it +- set(MOSEK_LIBRARIES ${MOSEK_LIBRARY} -llapack -lblas -pthread) ++ set(MOSEK_LIBRARIES ${MOSEK_LIBRARY} -pthread) + endif() + + include(FindPackageHandleStandardArgs) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index d4f14f5..fd2bdf9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -86,6 +86,12 @@ if(LAPACK_FOUND) + else() + message(FATAL_ERROR "Lapack not found") + endif() ++find_package(BLAS REQUIRED) ++if(BLAS_FOUND) ++ message(STATUS "Found blas library: " ${BLAS_LIBRARIES}) ++else() ++ message(FATAL_ERROR "Blas not found") ++endif() + + # search the selected symmetry computation program + message(STATUS "Finding symmetry computation program \"${SYM}\"") +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 5e5d7d3..b3e2be9 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -125,11 +125,11 @@ setLibProperties(libscipsdp "scipsdp") + #put binary in bin directory + set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + if(SHARED) +- target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) ++ target_link_libraries(libscipsdp PRIVATE ${ZIMPL_PIC_LIBRARIES} ${SDPS_PIC_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} m) + add_executable(scipsdp scipsdp/main.c ${scipsdpsources} ${objscipsdpsources} ${sdpisources} ${sym}) +- target_link_libraries(scipsdp ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) ++ target_link_libraries(scipsdp ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SCIP_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} m) + else() +- target_link_libraries(libscipsdp PRIVATE ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} m) ++ target_link_libraries(libscipsdp PRIVATE ${ZIMPL_LIBRARIES} ${SDPS_LIBRARIES} ${SYM_LIBRARIES} ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} m) + add_executable(scipsdp scipsdp/main.c ${scipsdpsources} ${objscipsdpsources} ${sdpisources} ${sym}) + target_link_libraries(scipsdp libscipsdp ${SCIP_LIBRARIES}) + endif() diff --git a/build/pkgs/scip_sdp/spkg-check.in b/build/pkgs/scip_sdp/spkg-check.in new file mode 100644 index 00000000000..8df53fc6a36 --- /dev/null +++ b/build/pkgs/scip_sdp/spkg-check.in @@ -0,0 +1,3 @@ +cd src +cd build +$MAKE test diff --git a/build/pkgs/scip_sdp/spkg-install.in b/build/pkgs/scip_sdp/spkg-install.in new file mode 100644 index 00000000000..3a65a004219 --- /dev/null +++ b/build/pkgs/scip_sdp/spkg-install.in @@ -0,0 +1,15 @@ +cd src +mkdir build +cd build +sdh_cmake -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON \ + -DBLA_VENDOR=OpenBLAS \ + -DBLAS_LIBRARIES="$(pkg-config --libs blas)" \ + -DLAPACK_LIBRARIES="$(pkg-config --libs lapack)" \ + -DSCIP_DIR="${SAGE_LOCAL}" \ + -DSYM=bliss \ + -DSDPS=dsdp \ + .. +sdh_make +sdh_make_install diff --git a/build/pkgs/scip_sdp/type b/build/pkgs/scip_sdp/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/scip_sdp/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/scipoptsuite/SPKG.rst b/build/pkgs/scipoptsuite/SPKG.rst deleted file mode 100644 index 37c7e3deb86..00000000000 --- a/build/pkgs/scipoptsuite/SPKG.rst +++ /dev/null @@ -1,50 +0,0 @@ -scipoptsuite: Mixed integer programming solver -============================================== - -Description ------------ - -SCIP is currently one of the fastest non-commercial mixed integer -programming (MIP) solvers. It is also a framework for constraint integer -programming and branch-cut-and-price. It allows total control of the -solution process and the access of detailed information down to the guts -of the solver. - -License -------- - -ZIB Academic License - -The ZIB Academic License allows the use of software distributed under -this license without charge for research purposes as a member of a -non-commercial and academic institution, e.g., a university. The -software is available with its source code. - -http://scip.zib.de/academic.txt - - -SPKG Maintainers ----------------- - -- Martin Albrecht (original spkg) -- Matthias Koeppe (updates for new spkg style) - - -Upstream Contact ----------------- - -http://scip.zib.de/doc/html/AUTHORS.shtml - -Dependencies ------------- - -cmake - - -Special Update/Build Instructions ---------------------------------- - -We do not have permission to redistribute SCIP or SoPlex. Hence, you -must download it yourself from http://scip.zib.de and put the tarball -``scipoptsuite-VERSION.tgz`` in ``$SAGE_ROOT/upstream``, renaming -it to ``scipoptsuite-VERSION-do-not-distribute.tgz``. diff --git a/build/pkgs/scipoptsuite/checksums.ini b/build/pkgs/scipoptsuite/checksums.ini deleted file mode 100644 index 18295f417c1..00000000000 --- a/build/pkgs/scipoptsuite/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=scipoptsuite-VERSION-do-not-distribute.tgz -sha1=6f7f1b6fb09f7fcd289a260bfdc81b2918086514 -md5=56071392f5cc2a85cd995758cd90af25 -cksum=11618526 diff --git a/build/pkgs/scipoptsuite/package-version.txt b/build/pkgs/scipoptsuite/package-version.txt deleted file mode 100644 index 6b244dcd696..00000000000 --- a/build/pkgs/scipoptsuite/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -5.0.1 diff --git a/build/pkgs/scipoptsuite/patches/0001-Use-libhistory.patch b/build/pkgs/scipoptsuite/patches/0001-Use-libhistory.patch deleted file mode 100644 index 965f603e5db..00000000000 --- a/build/pkgs/scipoptsuite/patches/0001-Use-libhistory.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0b79dea019a9b258014454ac527f04f7d76763fd Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Thu, 5 Apr 2018 12:01:25 -0500 -Subject: [PATCH 1/3] Use libhistory - ---- - scip/cmake/Modules/FindReadline.cmake | 11 ++++++++--- - scip/src/CMakeLists.txt | 3 ++- - 2 files changed, 10 insertions(+), 4 deletions(-) - -diff --git a/scip/cmake/Modules/FindReadline.cmake b/scip/cmake/Modules/FindReadline.cmake -index 051a1e7..9f14b5f 100644 ---- a/scip/cmake/Modules/FindReadline.cmake -+++ b/scip/cmake/Modules/FindReadline.cmake -@@ -31,14 +31,19 @@ find_library(Readline_LIBRARY - HINTS ${Readline_ROOT_DIR}/lib - ) - --if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) -+find_library(History_LIBRARY -+ NAMES history -+ HINTS ${Readline_ROOT_DIR}/lib -+) -+ -+if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND History_LIBRARY AND Ncurses_LIBRARY) - set(READLINE_FOUND TRUE) --else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) -+else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND History_LIBRARY AND Ncurses_LIBRARY) - FIND_LIBRARY(Readline_LIBRARY NAMES readline) - include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) - MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) --endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) -+endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND History_LIBRARY AND Ncurses_LIBRARY) - - mark_as_advanced( - Readline_ROOT_DIR -diff --git a/scip/src/CMakeLists.txt b/scip/src/CMakeLists.txt -index bbe9c72..ef55fe7 100644 ---- a/scip/src/CMakeLists.txt -+++ b/scip/src/CMakeLists.txt -@@ -803,6 +803,7 @@ setLibProperties(libscip "scip") - target_link_libraries(libscip PRIVATE - ${ZLIB_LIBRARIES} - ${Readline_LIBRARY} -+ ${History_LIBRARY} - ${GMP_LIBRARIES} - ${THREAD_LIBRARIES} - ${NLPI_LIBRARIES} -@@ -824,7 +825,7 @@ endif() - - target_compile_definitions(scip PRIVATE EXTERN=extern) - --target_link_libraries(scip ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${GMP_LIBRARIES} -+target_link_libraries(scip ${ZLIB_LIBRARIES} ${Readline_LIBRARY} ${History_LIBRARY} ${GMP_LIBRARIES} - ${ZIMPL_LIBRARIES} ${LPS_LIBRARIES} ${SYM_LIBRARIES} ${THREAD_LIBRARIES} ${NLPI_LIBRARIES}) - - add_dependencies(libscip scip_update_githash) --- -2.11.0 - diff --git a/build/pkgs/scipoptsuite/patches/0001-added-cmake-ipopt-patch.patch b/build/pkgs/scipoptsuite/patches/0001-added-cmake-ipopt-patch.patch deleted file mode 100644 index 96f9091d187..00000000000 --- a/build/pkgs/scipoptsuite/patches/0001-added-cmake-ipopt-patch.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 983b0b8630c41d2ce966877709184564432436cf Mon Sep 17 00:00:00 2001 -From: Moritz Firsching <moritz@math.fu-berlin.de> -Date: Tue, 17 Apr 2018 11:09:35 +0200 -Subject: [PATCH] added cmake ipopt patch - ---- - scip/cmake/Modules/FindIPOPT.cmake | 26 ++++++++++++++++---------- - 1 file changed, 16 insertions(+), 10 deletions(-) - -diff --git a/scip/cmake/Modules/FindIPOPT.cmake b/scip/cmake/Modules/FindIPOPT.cmake -index 4d65120..00f4c34 100644 ---- a/scip/cmake/Modules/FindIPOPT.cmake -+++ b/scip/cmake/Modules/FindIPOPT.cmake -@@ -35,6 +35,7 @@ - # (To distribute this file outside of YCM, substitute the full - # License text for the above reference.) - -+SET(IPOPT_FOUND FALSE) - - if(NOT WIN32) - # On non Windows systems we use PkgConfig to find IPOPT -@@ -62,6 +63,7 @@ if(NOT WIN32) - PATHS ${_PC_IPOPT_LIBRARY_DIRS}) - list(APPEND IPOPT_LIBRARIES ${${_LIBRARY}_PATH}) - endforeach() -+ set(IPOPT_FOUND TRUE) - else() - set(IPOPT_DEFINITIONS "") - endif() -@@ -114,6 +116,7 @@ if(NOT WIN32) - endif() - mark_as_advanced(IPOPT_SEARCH_FOR_${LIB}) - endforeach() -+ set(IPOPT_FOUND TRUE) - endif() - endif() - -@@ -180,6 +183,7 @@ else() - endif() - mark_as_advanced(IPOPT_SEARCH_FOR_${LIB}) - endforeach() -+ set(IPOPT_FOUND TRUE) - endif() - endif() - -@@ -188,16 +192,18 @@ else() - endif() - - # parse the version number --if(EXISTS ${IPOPT_INCLUDE_DIRS} ) -- file(STRINGS ${IPOPT_INCLUDE_DIRS}/IpoptConfig.h CONFIGFILE) -- -- foreach(STR ${CONFIGFILE}) -- if("${STR}" MATCHES "^#define IPOPT_VERSION ") -- string(REGEX REPLACE "#define IPOPT_VERSION " "" IPOPT_VERSION ${STR}) -- string(REGEX REPLACE "\"" "" IPOPT_VERSION ${IPOPT_VERSION}) -- endif() -- endforeach() -- # MESSAGE("found Ipopt ${IPOPT_VERSION}") -+if(IPOPT_FOUND) -+ if(EXISTS ${IPOPT_INCLUDE_DIRS} ) -+ file(STRINGS ${IPOPT_INCLUDE_DIRS}/IpoptConfig.h CONFIGFILE) -+ -+ foreach(STR ${CONFIGFILE}) -+ if("${STR}" MATCHES "^#define IPOPT_VERSION ") -+ string(REGEX REPLACE "#define IPOPT_VERSION " "" IPOPT_VERSION ${STR}) -+ string(REGEX REPLACE "\"" "" IPOPT_VERSION ${IPOPT_VERSION}) -+ endif() -+ endforeach() -+ # MESSAGE("found Ipopt ${IPOPT_VERSION}") -+ endif() - endif() - - mark_as_advanced(IPOPT_INCLUDE_DIRS --- -2.17.0 - diff --git a/build/pkgs/scipoptsuite/patches/0002-ZIMPL-cmake-scripts-Put-ZIMPL-includes-first-to-avoi.patch b/build/pkgs/scipoptsuite/patches/0002-ZIMPL-cmake-scripts-Put-ZIMPL-includes-first-to-avoi.patch deleted file mode 100644 index 5e0350f824d..00000000000 --- a/build/pkgs/scipoptsuite/patches/0002-ZIMPL-cmake-scripts-Put-ZIMPL-includes-first-to-avoi.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 656f9c88d52639af5109ca18c329bce6e0cea4da Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Thu, 5 Apr 2018 17:53:39 -0500 -Subject: [PATCH 2/3] ZIMPL cmake scripts: Put ZIMPL includes first to avoid - clash of idxset.h with install soplex idxset.h - ---- - zimpl/src/CMakeLists.txt | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/zimpl/src/CMakeLists.txt b/zimpl/src/CMakeLists.txt -index ee4dbca..a2d0865 100644 ---- a/zimpl/src/CMakeLists.txt -+++ b/zimpl/src/CMakeLists.txt -@@ -5,7 +5,7 @@ flex_target(MMLSCAN zimpl/mmlscan.l ${CMAKE_CURRENT_BINARY_DIR}/mmlscan.c) - include_directories(${CMAKE_CURRENT_BINARY_DIR}) - - # for zimpl headers --include_directories(zimpl) -+include_directories(BEFORE zimpl) - - set(libsources - zimpl/blkmem.c --- -2.11.0 - diff --git a/build/pkgs/scipoptsuite/patches/0003-use-INSTALL_NAME_DIR-fix-for-libscip.patch b/build/pkgs/scipoptsuite/patches/0003-use-INSTALL_NAME_DIR-fix-for-libscip.patch deleted file mode 100644 index a80dd953d4a..00000000000 --- a/build/pkgs/scipoptsuite/patches/0003-use-INSTALL_NAME_DIR-fix-for-libscip.patch +++ /dev/null @@ -1,24 +0,0 @@ -From a6ff146209ed3dfab985870aa529889625492196 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe <mkoeppe@math.ucdavis.edu> -Date: Mon, 16 Apr 2018 12:17:08 +0200 -Subject: [PATCH 3/3] use INSTALL_NAME_DIR fix for libscip - ---- - scip/src/CMakeLists.txt | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/scip/src/CMakeLists.txt b/scip/src/CMakeLists.txt -index ef55fe7..838ef15 100644 ---- a/scip/src/CMakeLists.txt -+++ b/scip/src/CMakeLists.txt -@@ -834,6 +834,7 @@ add_dependencies(scip scip_update_githash) - set_target_properties(libscip PROPERTIES - VERSION ${SCIP_VERSION_MAJOR}.${SCIP_VERSION_MINOR}.${SCIP_VERSION_PATCH}.${SCIP_VERSION_SUB} - SOVERSION ${SCIP_VERSION_MAJOR}.${SCIP_VERSION_MINOR} -+ INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" - INSTALL_RPATH_USE_LINK_PATH TRUE) - - # set the install rpath to the installed destination --- -2.11.0 - diff --git a/build/pkgs/scipoptsuite/spkg-install.in b/build/pkgs/scipoptsuite/spkg-install.in deleted file mode 100644 index ec4d8daca18..00000000000 --- a/build/pkgs/scipoptsuite/spkg-install.in +++ /dev/null @@ -1,18 +0,0 @@ -echo "Building scipoptsuite with cmake" - -cd src -mkdir build -cd build -cmake .. -DCMAKE_VERBOSE_MAKEFILE=ON -DGMP_DIR="${SAGE_LOCAL}" -DZLIB_ROOT="${SAGE_LOCAL}" -DReadline_ROOT_DIR="${SAGE_LOCAL}" -DBLISS_DIR="${SAGE_LOCAL}" -DIPOPT=FALSE -make -make install - -$SAGE_LOCAL/bin/scip -s /dev/null -c quit - -if [ $? -ne 0 ]; then - echo "SCIP build completed but the scip executable does not work." - exit 1 -fi - - -echo "Finished building scipoptsuite" diff --git a/build/pkgs/scipoptsuite/type b/build/pkgs/scipoptsuite/type deleted file mode 100644 index 9839eb20815..00000000000 --- a/build/pkgs/scipoptsuite/type +++ /dev/null @@ -1 +0,0 @@ -experimental diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 25b470f4624..24c26b4981b 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=6dfee9fe5f021409b4d294b0a7d9da05b810d207 -md5=df5ce79288fc457238aeef18e8f70dfc -cksum=3909760197 +sha1=55fb286ab1a0b66a7439c5cc76e3c80e9de409ec +md5=83b0d9eab2ce79b7fe5888f119adee64 +cksum=1605676871 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/dependencies b/build/pkgs/scipy/dependencies index 5cd74c1c23a..5d42789eb10 100644 --- a/build/pkgs/scipy/dependencies +++ b/build/pkgs/scipy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) gfortran numpy pybind11 pythran | $(PYTHON_TOOLCHAIN) +$(PYTHON) $(BLAS) gfortran numpy pybind11 cython pythran | $(PYTHON_TOOLCHAIN) meson_python ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/scipy/dependencies_check b/build/pkgs/scipy/dependencies_check new file mode 100644 index 00000000000..e079f8a6038 --- /dev/null +++ b/build/pkgs/scipy/dependencies_check @@ -0,0 +1 @@ +pytest diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index a8fdfda1c78..77fee73a8cf 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.8.1 +1.9.3 diff --git a/build/pkgs/scipy/patches/boost_math_tools_config.patch b/build/pkgs/scipy/patches/boost_math_tools_config.patch deleted file mode 100644 index 6c13393c407..00000000000 --- a/build/pkgs/scipy/patches/boost_math_tools_config.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/scipy/_lib/boost/boost/math/tools/config.hpp 2021-11-01 02:28:55 UTC -+++ b/scipy/_lib/boost/boost/math/tools/config.hpp -@@ -28,7 +28,7 @@ - - #include <boost/math/tools/user.hpp> - --#if (defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__EMSCRIPTEN__)\ -+#if (defined(__NetBSD__) || defined(__EMSCRIPTEN__)\ - || (defined(__hppa) && !defined(__OpenBSD__)) || (defined(__NO_LONG_DOUBLE_MATH) && (DBL_MANT_DIG != LDBL_MANT_DIG))) \ - && !defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) - # define BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS diff --git a/build/pkgs/scipy/spkg-check.in b/build/pkgs/scipy/spkg-check.in new file mode 100644 index 00000000000..8ab92278cf2 --- /dev/null +++ b/build/pkgs/scipy/spkg-check.in @@ -0,0 +1 @@ +python3 -c 'import scipy; scipy.test()' diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in index 4f359e7363f..37c4e972358 100644 --- a/build/pkgs/scipy/spkg-install.in +++ b/build/pkgs/scipy/spkg-install.in @@ -1,44 +1,10 @@ -# These flags confuse numpy's distutils. In particular, -# if they are set empty bad things happen. -unset CFLAGS CXXFLAGS SHAREDFLAGS -echo "Note: CFLAGS, CXXFLAGS and SHAREDFLAGS are taken from distutils," -echo " so their current settings are ignored." - -if [ "$UNAME" = "Darwin" ]; then - unset ATLAS - unset BLAS - unset LAPACK - export LDFLAGS="-bundle -undefined dynamic_lookup $LDFLAGS" - export CPPFLAGS="-D__ACCELERATE__ $CPPFLAGS" -else - export {ATLAS,PTATLAS,OPENBLAS,MKL,MKLROOT}=None - export LDFLAGS="-shared $LDFLAGS" -fi - -# Make sure that the fortran objects are compiled with -fPIC -export FFLAGS="$FFLAGS -fPIC" -export FCFLAGS="$FCFLAGS -fPIC" - -if [ "$UNAME" = "CYGWIN" ]; then - # Trac #30643 - export CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE" -fi - -if [ "$UNAME" = "CYGWIN" -a "$SAGE_DEBUG" = "yes" ]; then - # Needed for just one or two modules when compiling in debug mode - # Otherwise the debug symbols create too many sections in the binary - export CPPFLAGS="$CPPFLAGS -Wa,-mbig-obj" -fi - - -# This avoids problems on some systems -- until we officially -# support umfpack (which we will likely do, since cvxopt I think includes it): -UMFPACK="None"; export UMFPACK -# See http://projects.scipy.org/pipermail/scipy-user/2006-July/008661.html -# (Currently SWIG gets invoked by scipy when building the umfpack interface, -# which is bad.) +# https://github.com/scipy/scipy/issues/16536 - meson breaks when CXX="g++ -std=gnu++11" +export CXX=$(echo "$CXX" | sed 's/-std=[a-z0-9+]*//g') cd src/ -# Install: -sdh_pip_install . +# mesonpy enforces the build-system requirements, including the strict version pins of numpy +# even when --no-isolation (--no-build-isolation) is in used. We patch it out. +sed -i.bak '/build-system/,/project/s/^ *"numpy.*/ "numpy",/' pyproject.toml + +sdh_pip_install --no-build-isolation . diff --git a/build/pkgs/setuptools/SPKG.rst b/build/pkgs/setuptools/SPKG.rst index 8d510960f1d..a50e171a98d 100644 --- a/build/pkgs/setuptools/SPKG.rst +++ b/build/pkgs/setuptools/SPKG.rst @@ -4,26 +4,24 @@ setuptools: Build system for Python packages Description ----------- -setuptools is a collection of enhancements to the Python distutils (for -Python 2.6 and up) that allow you to more easily build and distribute -Python packages, especially ones that have dependencies on other -packages. +setuptools is the classical build system for Python packages, +a collection of enhancements to the Python distutils. -Website: http://pypi.python.org/pypi/setuptools/ +This package represents version 63.x of ``setuptools``. +Sage installs this version to provide the build system +for non-PEP 517 packages. In particular, Sage uses it +for building ``numpy``, whose build system ``numpy.distutils`` +is not compatible with newer versions of ``setuptools``, +see https://github.com/numpy/numpy/pull/22154 License ------- -PSF or ZPL. i.e Python Software Foundation License or Zope Public -License - +MIT License Upstream Contact ---------------- -- Phillip J. Eby (distutils-sig@python org) - -Dependencies ------------- +http://pypi.python.org/pypi/setuptools/ -- python +https://github.com/pypa/setuptools diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 8a9faad33ca..d47099a8019 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools-VERSION.tar.gz -sha1=2a5a4ac384ace22dd10e3dac0a1b6af49e03c596 -md5=d72acb93671bde8e4ca0971866f9cdda -cksum=2262493785 +sha1=b14b8e2cf965fdb6870ebfccee50c751c056f757 +md5=02a0e4dc4fa13168904e8769a077aa26 +cksum=2734084418 upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz diff --git a/build/pkgs/setuptools/install-requires.txt b/build/pkgs/setuptools/install-requires.txt index 486c3c348ee..0810ca37277 100644 --- a/build/pkgs/setuptools/install-requires.txt +++ b/build/pkgs/setuptools/install-requires.txt @@ -1,2 +1 @@ -# Set this bound until https://trac.sagemath.org/ticket/34209 adds support for PEP660 editable builds -setuptools >=49.6.0,<64.0.0 +setuptools >=49.6.0 diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 61f1f83a084..fe3c6688881 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -63.2.0 +63.4.3 diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index a5c492c3e2f..d4c9ee1a391 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=a4f02fddae697614e356cadfddb6241cc7737f38 -md5=32918d8ac566360c21411e0b3556c695 -cksum=1450556136 +sha1=b8c9447fe6dfc44107c9cbd2fd82314aad3cb95e +md5=0df4e7fd923e4983cd65786efaa0e0d0 +cksum=3196930290 upstream_url=https://pypi.io/packages/source/s/setuptools_scm/setuptools_scm-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm/dependencies b/build/pkgs/setuptools_scm/dependencies index 1e7026365f0..f3662a5c829 100644 --- a/build/pkgs/setuptools_scm/dependencies +++ b/build/pkgs/setuptools_scm/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip wheel tomli packaging +$(PYTHON) typing_extensions | setuptools pip wheel tomli packaging ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 91e4a9f2622..2be8aeb6b14 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -6.3.2 +7.0.5 diff --git a/build/pkgs/setuptools_scm_git_archive/checksums.ini b/build/pkgs/setuptools_scm_git_archive/checksums.ini index 9a773eebc56..ed010b0f53e 100644 --- a/build/pkgs/setuptools_scm_git_archive/checksums.ini +++ b/build/pkgs/setuptools_scm_git_archive/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools_scm_git_archive-VERSION.tar.gz -sha1=eda18924f3917e94cc54227660e3437d47f53bb9 -md5=1c9351fa5cebd12e76488737a7c78f2e -cksum=188023730 +sha1=5e893fcb35f9633ea1431138c1d90a5f2687b36d +md5=df3933d33c49c5d9aca06715b4c65370 +cksum=2554004806 upstream_url=https://pypi.io/packages/source/s/setuptools_scm_git_archive/setuptools_scm_git_archive-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm_git_archive/distros/conda.txt b/build/pkgs/setuptools_scm_git_archive/distros/conda.txt deleted file mode 100644 index 538474ff946..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/conda.txt +++ /dev/null @@ -1 +0,0 @@ -setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/package-version.txt b/build/pkgs/setuptools_scm_git_archive/package-version.txt index 9459d4ba2a0..c068b2447cc 100644 --- a/build/pkgs/setuptools_scm_git_archive/package-version.txt +++ b/build/pkgs/setuptools_scm_git_archive/package-version.txt @@ -1 +1 @@ -1.1 +1.4 diff --git a/build/pkgs/setuptools_wheel/SPKG.rst b/build/pkgs/setuptools_wheel/SPKG.rst index c78602a296a..b77a6679f8f 100644 --- a/build/pkgs/setuptools_wheel/SPKG.rst +++ b/build/pkgs/setuptools_wheel/SPKG.rst @@ -3,3 +3,6 @@ setuptools_wheel: Build the setuptools package as a wheel After installing setuptools and wheel, we build a wheel of setuptools to complete the set of wheels stored in our wheelhouse. + +This version of setuptools is suitable for PEP 517/518/660 builds, +but it is not suitable for building ``numpy``. diff --git a/build/pkgs/setuptools_wheel/checksums.ini b/build/pkgs/setuptools_wheel/checksums.ini deleted file mode 120000 index 4f64d3ce107..00000000000 --- a/build/pkgs/setuptools_wheel/checksums.ini +++ /dev/null @@ -1 +0,0 @@ -../setuptools/checksums.ini \ No newline at end of file diff --git a/build/pkgs/setuptools_wheel/checksums.ini b/build/pkgs/setuptools_wheel/checksums.ini new file mode 100644 index 00000000000..8db0d8c3016 --- /dev/null +++ b/build/pkgs/setuptools_wheel/checksums.ini @@ -0,0 +1,5 @@ +tarball=setuptools-VERSION.tar.gz +sha1=2875e8f9a12d8a971461b36e2d1bd64a3497e0f2 +md5=1fd8bd04b0bed95ad6c81f03b1c080bc +cksum=1462078737 +upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz diff --git a/build/pkgs/setuptools_wheel/package-version.txt b/build/pkgs/setuptools_wheel/package-version.txt deleted file mode 120000 index 5268dbec8f6..00000000000 --- a/build/pkgs/setuptools_wheel/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -../setuptools/package-version.txt \ No newline at end of file diff --git a/build/pkgs/setuptools_wheel/package-version.txt b/build/pkgs/setuptools_wheel/package-version.txt new file mode 100644 index 00000000000..c1a8c8394ad --- /dev/null +++ b/build/pkgs/setuptools_wheel/package-version.txt @@ -0,0 +1 @@ +65.6.3 diff --git a/build/pkgs/singular/checksums.ini b/build/pkgs/singular/checksums.ini index 2e33a405d36..313463d2fea 100644 --- a/build/pkgs/singular/checksums.ini +++ b/build/pkgs/singular/checksums.ini @@ -1,5 +1,5 @@ tarball=singular-VERSION.tar.gz -sha1=6c2b622d3681e2de3d58d30c654d43d3e32b720c -md5=abb1e37c794472e7760655358ab66054 -cksum=17455733 +sha1=28bb3ee97ef48d04dfa96de182fd93eebe08426c +md5=fc0a4f5720dadba45a52ee94324ce00c +cksum=1573851737 upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/4-3-1/singular-VERSION.tar.gz diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 11300c77e7d..66e2bede53a 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.3.1p1 +4.3.1p3 diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index af2eb854143..d4d145defe3 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -7,7 +7,7 @@ SAGE_SPKG_CONFIGURE([singular], [ dnl Use pkg-config to ensure that Singular is new enough. PKG_CHECK_MODULES([SINGULAR], [Singular >= 4.2.1], [ AC_MSG_CHECKING([that Singular's help is working]) - AS_IF([test x`printf "system(\"--browser\", \"builtin\"); \n help;" | Singular 2>&1 | grep "error\ occurred"` = x], [ + AS_IF([test x`printf "system(\"--browser\", \"builtin\"); \n help;" | Singular 2>&1 | grep "error occurred"` = x], [ AC_MSG_RESULT(yes) dnl We have Singular. Now determine the shared library path on dnl platforms on which sage.libs.singular needs to reload the library with RTLD_GLOBAL. diff --git a/build/pkgs/soplex/SPKG.rst b/build/pkgs/soplex/SPKG.rst new file mode 100644 index 00000000000..8a37579455c --- /dev/null +++ b/build/pkgs/soplex/SPKG.rst @@ -0,0 +1,22 @@ +soplex: Linear optimization solver using the revised simplex method +=================================================================== + +Description +----------- + +SoPlex is an optimization package for solving linear programming +problems (LPs) based on an advanced implementation of the primal and +dual revised simplex algorithm. It provides special support for the +exact solution of LPs with rational input data. + + +License +------- + +Apache License, Version 2.0 + + +Upstream Contact +---------------- + +https://github.com/scipopt/soplex diff --git a/build/pkgs/soplex/checksums.ini b/build/pkgs/soplex/checksums.ini new file mode 100644 index 00000000000..5689a553333 --- /dev/null +++ b/build/pkgs/soplex/checksums.ini @@ -0,0 +1,5 @@ +tarball=soplex-VERSION.tar.gz +sha1=6777fa6e7fd02ea6805901dbf60d873b4c312b62 +md5=2865c3a95ee903307d4bd32b0c9594e7 +cksum=278250056 +upstream_url=https://github.com/scipopt/soplex/archive/refs/tags/release-VERSION.tar.gz diff --git a/build/pkgs/soplex/dependencies b/build/pkgs/soplex/dependencies new file mode 100644 index 00000000000..32c8bdd67ec --- /dev/null +++ b/build/pkgs/soplex/dependencies @@ -0,0 +1 @@ +$(MP_LIBRARY) mpfr boost_cropped zlib papilo | cmake diff --git a/build/pkgs/soplex/distros/conda.txt b/build/pkgs/soplex/distros/conda.txt new file mode 100644 index 00000000000..3fcd744bc92 --- /dev/null +++ b/build/pkgs/soplex/distros/conda.txt @@ -0,0 +1 @@ +soplex diff --git a/build/pkgs/soplex/distros/freebsd.txt b/build/pkgs/soplex/distros/freebsd.txt new file mode 100644 index 00000000000..159cb927b0e --- /dev/null +++ b/build/pkgs/soplex/distros/freebsd.txt @@ -0,0 +1 @@ +math/SoPlex diff --git a/build/pkgs/soplex/distros/repology.txt b/build/pkgs/soplex/distros/repology.txt new file mode 100644 index 00000000000..3fcd744bc92 --- /dev/null +++ b/build/pkgs/soplex/distros/repology.txt @@ -0,0 +1 @@ +soplex diff --git a/build/pkgs/soplex/package-version.txt b/build/pkgs/soplex/package-version.txt new file mode 100644 index 00000000000..85c3d27e59d --- /dev/null +++ b/build/pkgs/soplex/package-version.txt @@ -0,0 +1 @@ +602 diff --git a/build/pkgs/soplex/patches/no_rpath.patch b/build/pkgs/soplex/patches/no_rpath.patch new file mode 100644 index 00000000000..7cad9b059f7 --- /dev/null +++ b/build/pkgs/soplex/patches/no_rpath.patch @@ -0,0 +1,44 @@ +commit 0c2527842fe4eaed8d9e5107d6a6621b3d6a716f +Author: Matthias Koeppe <mkoeppe@math.ucdavis.edu> +Date: Sat Nov 26 17:08:40 2022 -0800 + + CMakeLists.txt, src/CMakeLists.txt: Remove hardcoded RPATH settings + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4f8635fc..6f7a6e1b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -64,9 +64,6 @@ if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) + endif() + +-# set the correct rpath for OS X +-set(CMAKE_MACOSX_RPATH ON) +- + # use C++14 standard + set(CMAKE_CXX_STANDARD 14) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 27d52f14..fd3705ed 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -3,8 +3,7 @@ + # + function(setLibProperties targetname outputname) + set_target_properties(${targetname} PROPERTIES +- OUTPUT_NAME ${outputname} +- MACOSX_RPATH "${CMAKE_INSTALL_PREFIX}/lib") ++ OUTPUT_NAME ${outputname}) + endfunction(setLibProperties) + + include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) +@@ -200,9 +199,6 @@ endif() + add_executable(example EXCLUDE_FROM_ALL example.cpp) + target_link_libraries(example libsoplex) + +-# set the install rpath to the installed destination +-set_target_properties(soplex PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") +- + # install the header files of soplex + install(FILES ${headers} ${PROJECT_BINARY_DIR}/soplex/config.h DESTINATION include/soplex) + install(FILES soplex.h soplex.hpp soplex_interface.h DESTINATION include) diff --git a/build/pkgs/soplex/spkg-check.in b/build/pkgs/soplex/spkg-check.in new file mode 100644 index 00000000000..d7ead768edd --- /dev/null +++ b/build/pkgs/soplex/spkg-check.in @@ -0,0 +1,2 @@ +cd src/build +sdh_make test diff --git a/build/pkgs/soplex/spkg-install.in b/build/pkgs/soplex/spkg-install.in new file mode 100644 index 00000000000..388285d4f7b --- /dev/null +++ b/build/pkgs/soplex/spkg-install.in @@ -0,0 +1,8 @@ +cd src +mkdir build +cd build +sdh_cmake -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON \ + -DPAPILO=on -DPAPILO_DIR="${SAGE_LOCAL}" \ + .. +sdh_make +sdh_make_install diff --git a/build/pkgs/soplex/type b/build/pkgs/soplex/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/soplex/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 624ea28a1b1..99c08b0c2d5 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,5 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=c0aa13911c331244877fc3947c0e7cec10d9e72b -md5=663e2f2ee9219ef4913831950825f68b -cksum=1902891868 +sha1=bef629b31b868d3b031050bd36653696f6b73d8e +md5=327ae7af29b3f08f059b52a4b9ed98cb +cksum=2569899835 upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/install-requires.txt b/build/pkgs/sphinx/install-requires.txt index 9a8ff0bf429..16e1ac533d9 100644 --- a/build/pkgs/sphinx/install-requires.txt +++ b/build/pkgs/sphinx/install-requires.txt @@ -1 +1 @@ -sphinx >=4.3, <4.5 +sphinx >=5.2, <6 diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index fdc6698807a..c0baecbaaa9 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -4.4.0 +5.2.3 diff --git a/build/pkgs/sphinxcontrib_websupport/checksums.ini b/build/pkgs/sphinxcontrib_websupport/checksums.ini index 5fd49b7e2ac..6291f2e8e03 100644 --- a/build/pkgs/sphinxcontrib_websupport/checksums.ini +++ b/build/pkgs/sphinxcontrib_websupport/checksums.ini @@ -1,5 +1,5 @@ tarball=sphinxcontrib-websupport-VERSION.tar.gz -sha1=313f4b764872d36f890e76579985e6aaa732f4ca -md5=4fe4d07afe1556c65182d0437228d7bb -cksum=1830011133 +sha1=39f6170825895ffeb0e06d52e351932b5f0c0147 +md5=eecfd8dc4933bd28c07ffb5e64fa2444 +cksum=431532871 upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-websupport/sphinxcontrib-websupport-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_websupport/dependencies b/build/pkgs/sphinxcontrib_websupport/dependencies index 0738c2d7777..dabbd0f750d 100644 --- a/build/pkgs/sphinxcontrib_websupport/dependencies +++ b/build/pkgs/sphinxcontrib_websupport/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) sphinxcontrib_serializinghtml | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sphinxcontrib_websupport/package-version.txt b/build/pkgs/sphinxcontrib_websupport/package-version.txt index 6085e946503..e8ea05db814 100644 --- a/build/pkgs/sphinxcontrib_websupport/package-version.txt +++ b/build/pkgs/sphinxcontrib_websupport/package-version.txt @@ -1 +1 @@ -1.2.1 +1.2.4 diff --git a/build/pkgs/stack_data/checksums.ini b/build/pkgs/stack_data/checksums.ini index 7058412912e..625aaf0578f 100644 --- a/build/pkgs/stack_data/checksums.ini +++ b/build/pkgs/stack_data/checksums.ini @@ -1,5 +1,5 @@ tarball=stack_data-VERSION.tar.gz -sha1=280dc05517f29dd0d450679304a9bb6a0fa41a25 -md5=d39afb043bdb116b8d568c2a82f32227 -cksum=3446203117 +sha1=58ed9cb32a42e07dbc18356d06f8db96475bc0f2 +md5=05c8c6c58c02280bc87b6851e40d38e6 +cksum=1485401259 upstream_url=https://pypi.io/packages/source/s/stack_data/stack_data-VERSION.tar.gz diff --git a/build/pkgs/stack_data/package-version.txt b/build/pkgs/stack_data/package-version.txt index 0d91a54c7d4..ee6cdce3c29 100644 --- a/build/pkgs/stack_data/package-version.txt +++ b/build/pkgs/stack_data/package-version.txt @@ -1 +1 @@ -0.3.0 +0.6.1 diff --git a/build/pkgs/terminado/checksums.ini b/build/pkgs/terminado/checksums.ini index f33efd4b5da..27333946973 100644 --- a/build/pkgs/terminado/checksums.ini +++ b/build/pkgs/terminado/checksums.ini @@ -1,5 +1,5 @@ tarball=terminado-VERSION.tar.gz -sha1=65f40480c1d8077b78dcffb7e0c909eae86998bf -md5=4871263f6aaed18e09603fa6785b4340 -cksum=2070178009 +sha1=e2e37fe16c03d5bb6ab035c78e99a89bc742384c +md5=cf5f5f7dd1ece772f16013ad355b75e1 +cksum=2626848318 upstream_url=https://pypi.io/packages/source/t/terminado/terminado-VERSION.tar.gz diff --git a/build/pkgs/terminado/dependencies b/build/pkgs/terminado/dependencies index e44a0d91033..54ec1c7c229 100644 --- a/build/pkgs/terminado/dependencies +++ b/build/pkgs/terminado/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) ptyprocess tornado +$(PYTHON) ptyprocess tornado | $(PYTHON_TOOLCHAIN) hatchling ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/terminado/package-version.txt b/build/pkgs/terminado/package-version.txt index 34a83616bb5..c5523bd09b1 100644 --- a/build/pkgs/terminado/package-version.txt +++ b/build/pkgs/terminado/package-version.txt @@ -1 +1 @@ -0.12.1 +0.17.0 diff --git a/build/pkgs/terminado/spkg-install.in b/build/pkgs/terminado/spkg-install.in index cb4ba894442..058b1344dc2 100644 --- a/build/pkgs/terminado/spkg-install.in +++ b/build/pkgs/terminado/spkg-install.in @@ -1,8 +1,3 @@ cd src -# Make sure that modern pip uses the generated setup.py -# that is distributed with the PyPI tarball, -# so we do not have to have flit. Trac #29803. -rm -f pyproject.toml - sdh_pip_install . diff --git a/build/pkgs/texttable/checksums.ini b/build/pkgs/texttable/checksums.ini index 9f7f062fc11..380a205bdd3 100644 --- a/build/pkgs/texttable/checksums.ini +++ b/build/pkgs/texttable/checksums.ini @@ -1,5 +1,5 @@ tarball=texttable-VERSION.tar.gz -sha1=f818f1ea121513b54a417ffa6fe8bb26821913d7 -md5=15faadc07ba44d337cc1675ea6092a02 -cksum=4013109377 +sha1=25e1b92e02c8e919dc0da053efbe8c4874418a8d +md5=83eb15fb541dd857ff051a8d0c979b9c +cksum=3183998721 upstream_url=https://pypi.io/packages/source/t/texttable/texttable-VERSION.tar.gz diff --git a/build/pkgs/texttable/package-version.txt b/build/pkgs/texttable/package-version.txt index 9edc58bb1dd..400084b1bf2 100644 --- a/build/pkgs/texttable/package-version.txt +++ b/build/pkgs/texttable/package-version.txt @@ -1 +1 @@ -1.6.4 +1.6.7 diff --git a/build/pkgs/tinycss2/checksums.ini b/build/pkgs/tinycss2/checksums.ini index 668d72fa9e4..c905e293396 100644 --- a/build/pkgs/tinycss2/checksums.ini +++ b/build/pkgs/tinycss2/checksums.ini @@ -1,5 +1,5 @@ tarball=tinycss2-VERSION.tar.gz -sha1=4a05bce2f350b120791a59c6595564274ca4c6b3 -md5=60272f58f8d5834b2e09ffbc9bd5de53 -cksum=3520456675 +sha1=3871ffec30bde346d1a17f80a423dce488bad4f7 +md5=e8a06102e7f42ca791463f11ce7b814d +cksum=1840765267 upstream_url=https://pypi.io/packages/source/t/tinycss2/tinycss2-VERSION.tar.gz diff --git a/build/pkgs/tinycss2/package-version.txt b/build/pkgs/tinycss2/package-version.txt index 524cb55242b..6085e946503 100644 --- a/build/pkgs/tinycss2/package-version.txt +++ b/build/pkgs/tinycss2/package-version.txt @@ -1 +1 @@ -1.1.1 +1.2.1 diff --git a/build/pkgs/tomlkit/checksums.ini b/build/pkgs/tomlkit/checksums.ini index f2116d86933..545bb2bc02b 100644 --- a/build/pkgs/tomlkit/checksums.ini +++ b/build/pkgs/tomlkit/checksums.ini @@ -1,5 +1,5 @@ tarball=tomlkit-VERSION.tar.gz -sha1=c618d9b0490fb626c053c691def29223dcfbd6cc -md5=c4edce886bc20ef8ecea49984cce2e69 -cksum=2455377393 +sha1=b097f71385b3b693a41a23ecd551959faae73e0d +md5=ac33a015aa5f3f8e8e0667081b388bb7 +cksum=785586052 upstream_url=https://pypi.io/packages/source/t/tomlkit/tomlkit-VERSION.tar.gz diff --git a/build/pkgs/tomlkit/package-version.txt b/build/pkgs/tomlkit/package-version.txt index d9df1bbc0c7..e5cbde33e62 100644 --- a/build/pkgs/tomlkit/package-version.txt +++ b/build/pkgs/tomlkit/package-version.txt @@ -1 +1 @@ -0.11.0 +0.11.6 diff --git a/build/pkgs/topcom/checksums.ini b/build/pkgs/topcom/checksums.ini index 8c0bffb0731..764c422b5ac 100644 --- a/build/pkgs/topcom/checksums.ini +++ b/build/pkgs/topcom/checksums.ini @@ -1,4 +1,5 @@ -tarball=topcom-VERSION.tar.bz2 -sha1=e772365e7115289dfb1f75f28c335ec9d5feb0f1 -md5=117708cc3fdbb3368631c69e3cc95f1c -cksum=340369649 +tarball=TOPCOM-1_1_2.tgz +sha1=65db8c00309f3bf8467ee5ba9da109c196db3461 +md5=dbda1ae7946251c9502444ee9b0a2c62 +cksum=1598684291 +upstream_url=https://users.ox.ac.uk/~coml0531/tmp/TOPCOM-1_1_2.tgz diff --git a/build/pkgs/topcom/package-version.txt b/build/pkgs/topcom/package-version.txt index 793949533eb..45a1b3f4452 100644 --- a/build/pkgs/topcom/package-version.txt +++ b/build/pkgs/topcom/package-version.txt @@ -1 +1 @@ -0.17.7 +1.1.2 diff --git a/build/pkgs/topcom/patches/no_vendor_cddlib.patch b/build/pkgs/topcom/patches/no_vendor_cddlib.patch new file mode 100644 index 00000000000..b9e6c97a4ec --- /dev/null +++ b/build/pkgs/topcom/patches/no_vendor_cddlib.patch @@ -0,0 +1,2189 @@ +diff --git a/INSTALL b/INSTALL +index 9d4fccc..8865734 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -1,284 +1,368 @@ +-Remark concerning consistent 32 resp. 64 bit compilation +-======================================================== ++Installation Instructions ++************************* + +-The easiest way to guarantee consistent compilation is +-to configure with explicit setting of CFLAGS and CXXFLAGS +-to "-m32" or "-m64". This can be achieved by calling ++ Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software ++Foundation, Inc. + +- ./configure [other-configure-options-see-below] CFLAGS="-m64" CXXFLAGS="-m64" ++ Copying and distribution of this file, with or without modification, ++are permitted in any medium without royalty provided the copyright ++notice and this notice are preserved. This file is offered as-is, ++without warranty of any kind. + +-This way, all C/C++-Compilations are carried out with +-the corresponding compiler flag. +- +- +-Remark concerning external packages gmp, cdd, and qsopt_ex +-========================================================== +- +-If gmp, cddlib, or qsopt_ex are missing on your system then configure will +-build the version coming with TOPCOM in the `external' subdirectory +-of the TOPCOM root directory. +- +-If $(TOPCOM_DIRECTORY) is the root directory of the TOPCOM package, then +-the necessary files are installed into +- +- $(TOPCOM_DIRECTORY)/external/include +- $(TOPCOM_DIRECTORY)/external/lib ++Basic Installation ++================== + ++ Briefly, the shell command './configure && make && make install' ++should configure, build, and install this package. The following ++more-detailed instructions are generic; see the 'README' file for ++instructions specific to this package. Some packages provide this ++'INSTALL' file but do not implement all of the features documented ++below. The lack of an optional feature in a given package is not ++necessarily a bug. More recommendations for GNU packages can be found ++in *note Makefile Conventions: (standards)Makefile Conventions. + +-Remark concerning the pacakged cddlib LP solver library +-======================================================= ++ The 'configure' shell script attempts to guess correct values for ++various system-dependent variables used during compilation. It uses ++those values to create a 'Makefile' in each directory of the package. ++It may also create one or more '.h' files containing system-dependent ++definitions. Finally, it creates a shell script 'config.status' that ++you can run in the future to recreate the current configuration, and a ++file 'config.log' containing compiler output (useful mainly for ++debugging 'configure'). ++ ++ It can also use an optional file (typically called 'config.cache' and ++enabled with '--cache-file=config.cache' or simply '-C') that saves the ++results of its tests to speed up reconfiguring. Caching is disabled by ++default to prevent problems with accidental use of stale cache files. + +-The packaaged LP solver in cddlib is not thread-safe out of the box. +-Therefore, the version packaged with TOPCOM has been patched to version +-0.94j-TOPCOM. This patched version will be compiled with C++ and +-has thread_local global variables. This is enough to make cddlib +-threadsafe, thus, parallel enumeration can be used. ++ If you need to do unusual things to compile the package, please try ++to figure out how 'configure' could check whether to do them, and mail ++diffs or instructions to the address given in the 'README' so they can ++be considered for the next release. If you are using the cache, and at ++some point 'config.cache' contains results you don't want to keep, you ++may remove or edit it. ++ ++ The file 'configure.ac' (or 'configure.in') is used to create ++'configure' by a program called 'autoconf'. You need 'configure.ac' if ++you want to change it or regenerate 'configure' using a newer version of ++'autoconf'. ++ ++ The simplest way to compile this package is: ++ ++ 1. 'cd' to the directory containing the package's source code and type ++ './configure' to configure the package for your system. ++ ++ Running 'configure' might take a while. While running, it prints ++ some messages telling which features it is checking for. ++ ++ 2. Type 'make' to compile the package. ++ ++ 3. Optionally, type 'make check' to run any self-tests that come with ++ the package, generally using the just-built uninstalled binaries. ++ ++ 4. Type 'make install' to install the programs and any data files and ++ documentation. When installing into a prefix owned by root, it is ++ recommended that the package be configured and built as a regular ++ user, and only the 'make install' phase executed with root ++ privileges. ++ ++ 5. Optionally, type 'make installcheck' to repeat any self-tests, but ++ this time using the binaries in their final installed location. ++ This target does not install anything. Running this target as a ++ regular user, particularly if the prior 'make install' required ++ root privileges, verifies that the installation completed ++ correctly. ++ ++ 6. You can remove the program binaries and object files from the ++ source code directory by typing 'make clean'. To also remove the ++ files that 'configure' created (so you can compile the package for ++ a different kind of computer), type 'make distclean'. There is ++ also a 'make maintainer-clean' target, but that is intended mainly ++ for the package's developers. If you use it, you may have to get ++ all sorts of other programs in order to regenerate files that came ++ with the distribution. + ++ 7. Often, you can also type 'make uninstall' to remove the installed ++ files again. In practice, not all packages have tested that ++ uninstallation works correctly, even though it is required by the ++ GNU Coding Standards. + +-Remark concerning the pacakged qsopt_ex LP solver library +-========================================================= ++ 8. Some packages, particularly those that use Automake, provide 'make ++ distcheck', which can by used by developers to test that all other ++ targets like 'make install' and 'make uninstall' work correctly. ++ This target is generally not run by end users. + +-Configuring with option ++Compilers and Options ++===================== + +- ./configure --enable-qsoptex ++ Some systems require unusual options for compilation or linking that ++the 'configure' script does not know about. Run './configure --help' ++for details on some of the pertinent environment variables. + +-compiles a binary with QSOpt_ex support. ++ You can give 'configure' initial values for configuration parameters ++by setting variables in the command line or in the environment. Here is ++an example: + +-The packaaged LP solver QSOpt_ex is very fast but not threadsafe. +-Therefore, if you use it (option --qsoptex), then no parallel enumeration +-will be carried out. Still, the internal memory managament +-of qsopt_ex may core-dump at the very end of the run. +-I have no idea why. This all happens after the TOPCOM computations +-have finished. So, the results should be ok anyway. ++ ./configure CC=c99 CFLAGS=-g LIBS=-lposix + ++ *Note Defining Variables::, for more details. + +-Remark concerning the soplex LP solver library (versions >= 6.0.0) +-================================================================== ++Compiling For Multiple Architectures ++==================================== + +-The academic licence of soplex does not allow a common delivery within +-the TOPCOM package. Therefore, if you want to use soplex you must +-install it in a place where it can be found by the compiler and +-the linker. This is slightly tedious but rewarding, since +-soplex is faster than cddlib. ++ You can compile the package for more than one kind of computer at the ++same time, by placing the object files for each architecture in their ++own directory. To do this, you can use GNU 'make'. 'cd' to the ++directory where you want the object files and executables to go and run ++the 'configure' script. 'configure' automatically checks for the source ++code in the directory that 'configure' is in and in '..'. This is known ++as a "VPATH" build. ++ ++ With a non-GNU 'make', it is safer to compile the package for one ++architecture at a time in the source code directory. After you have ++installed the package for one architecture, use 'make distclean' before ++reconfiguring for another architecture. ++ ++ On MacOS X 10.5 and later systems, you can create libraries and ++executables that work on multiple system types--known as "fat" or ++"universal" binaries--by specifying multiple '-arch' options to the ++compiler but only a single '-arch' option to the preprocessor. Like ++this: + +-Using soplex requires libsoplex.a in ${TOPCOM_DIRECTORY}/external/lib and +-all the soplex includes in ${TOPCOM_DIRECTORY}/external/include. +-As 6.0.0 of soplex, the boost includes are required in the same place. +-This changed, e.g., the access to the underlying gmp rationals. +-Therefore, versions of soplex prior to 6.0.0 are not supported out +-of the box. ++ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ ++ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ ++ CPP="gcc -E" CXXCPP="g++ -E" + +-In order to use soplex, I recommend to place symbolic links in +-${TOPCOM_DIRECTORY}/external/lib to the libraries and all the soplex includes +-in ${TOPCOM_DIRECTORY}/external/include. +-(This method supports that non-superusers can install TOPCOM locally +-without the help of a system-administrator.) ++ This is not guaranteed to produce working output in all cases, you ++may have to build one architecture at a time and combine the results ++using the 'lipo' tool if you have problems. + +-Summarized, if you have the includes and the libs of soplex 6.0.0 +-or later below ${SOPLEX_INCLUDES_DIR} and ${SOPLEX_LIBS_DIR}, resp., +-and boost below ${BOOST_INCLUDES_DIR}, then you can start with ++Installation Names ++================== + +- ./configure --enable-soplex ++ By default, 'make install' installs the package's commands under ++'/usr/local/bin', include files under '/usr/local/include', etc. You ++can specify an installation prefix other than '/usr/local' by giving ++'configure' the option '--prefix=PREFIX', where PREFIX must be an ++absolute file name. + +-to activate the soplex support in TOPCOM. Then you can place +-the symbolic links by ++ You can specify separate installation prefixes for ++architecture-specific files and architecture-independent files. If you ++pass the option '--exec-prefix=PREFIX' to 'configure', the package uses ++PREFIX as the prefix for installing programs and libraries. ++Documentation and other data files still use the regular prefix. + +- cd ${TOPCOM_DIRECTORY}/external/ +- cd include +- ln -s ${SOPLEX_INCLUDES_DIR}/soplex* . +- ln -s ${BOOST_INCLUDES_DIR}/boost . +- cd ../lib +- ln -s ${SOPLEX_LIBS_DIR}/libsoplex*.a . +- cd ../.. ++ In addition, if you use an unusual directory layout you can give ++options like '--bindir=DIR' to specify different values for particular ++kinds of files. Run 'configure --help' for a list of the directories ++you can set and what kinds of files go in them. In general, the default ++for these options is expressed in terms of '${prefix}', so that ++specifying just '--prefix' will affect all of the other directory ++specifications that were not explicitly provided. ++ ++ The most portable way to affect installation locations is to pass the ++correct locations to 'configure'; however, many packages provide one or ++both of the following shortcuts of passing variable assignments to the ++'make install' command line to change installation locations without ++having to reconfigure or recompile. ++ ++ The first method involves providing an override variable for each ++affected directory. For example, 'make install ++prefix=/alternate/directory' will choose an alternate location for all ++directory configuration variables that were expressed in terms of ++'${prefix}'. Any directories that were specified during 'configure', ++but not in terms of '${prefix}', must each be overridden at install time ++for the entire installation to be relocated. The approach of makefile ++variable overrides for each directory variable is required by the GNU ++Coding Standards, and ideally causes no recompilation. However, some ++platforms have known limitations with the semantics of shared libraries ++that end up requiring recompilation when using this method, particularly ++noticeable in packages that use GNU Libtool. ++ ++ The second method involves providing the 'DESTDIR' variable. For ++example, 'make install DESTDIR=/alternate/directory' will prepend ++'/alternate/directory' before all installation names. The approach of ++'DESTDIR' overrides is not required by the GNU Coding Standards, and ++does not work on platforms that have drive letters. On the other hand, ++it does better at avoiding recompilation issues, and works well even ++when some directory options were not specified in terms of '${prefix}' ++at 'configure' time. + +-to link the includes and libraries to the right place where TOPCOM can +-find them. Finally, just ++Optional Features ++================= + +- make +- make install ++ If the package supports it, you can cause programs to be installed ++with an extra prefix or suffix on their names by giving 'configure' the ++option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. ++ ++ Some packages pay attention to '--enable-FEATURE' options to ++'configure', where FEATURE indicates an optional part of the package. ++They may also pay attention to '--with-PACKAGE' options, where PACKAGE ++is something like 'gnu-as' or 'x' (for the X Window System). The ++'README' should mention any '--enable-' and '--with-' options that the ++package recognizes. + +-and you are set. ++ For packages that use the X Window System, 'configure' can usually ++find the X include and library files automatically, but if it doesn't, ++you can use the 'configure' options '--x-includes=DIR' and ++'--x-libraries=DIR' to specify their locations. + ++ Some packages offer the ability to configure how verbose the ++execution of 'make' will be. For these packages, running './configure ++--enable-silent-rules' sets the default to minimal output, which can be ++overridden with 'make V=1'; while running './configure ++--disable-silent-rules' sets the default to verbose, which can be ++overridden with 'make V=0'. + +-Basic Installation ++Particular systems + ================== + +- These are generic installation instructions. ++ On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC ++is not installed, it is recommended to use the following options in ++order to use an ANSI C compiler: + +- The `configure' shell script attempts to guess correct values for +-various system-dependent variables used during compilation. It uses +-those values to create a `Makefile' in each directory of the package. +-It may also create one or more `.h' files containing system-dependent +-definitions. Finally, it creates a shell script `config.status' that +-you can run in the future to recreate the current configuration, a file +-`config.cache' that saves the results of its tests to speed up +-reconfiguring, and a file `config.log' containing compiler output +-(useful mainly for debugging `configure'). ++ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +- If you need to do unusual things to compile the package, please try +-to figure out how `configure' could check whether to do them, and mail +-diffs or instructions to the address given in the `README' so they can +-be considered for the next release. If at some point `config.cache' +-contains results you don't want to keep, you may remove or edit it. ++and if that doesn't work, install pre-built binaries of GCC for HP-UX. + +- The file `configure.in' is used to create `configure' by a program +-called `autoconf'. You only need `configure.in' if you want to change +-it or regenerate `configure' using a newer version of `autoconf'. ++ HP-UX 'make' updates targets which have the same time stamps as their ++prerequisites, which makes it generally unusable when shipped generated ++files such as 'configure' are involved. Use GNU 'make' instead. + +-The simplest way to compile this package is: ++ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot ++parse its '<wchar.h>' header file. The option '-nodtk' can be used as a ++workaround. If GNU CC is not installed, it is therefore recommended to ++try + +- 1. `cd' to the directory containing the package's source code and type +- `./configure' to configure the package for your system. If you're +- using `csh' on an old version of System V, you might need to type +- `sh ./configure' instead to prevent `csh' from trying to execute +- `configure' itself. ++ ./configure CC="cc" + +- Running `configure' takes awhile. While running, it prints some +- messages telling which features it is checking for. ++and if that doesn't work, try + +- 2. Type `make' to compile the package. ++ ./configure CC="cc -nodtk" + +- 3. Optionally, type `make check' to run any self-tests that come with +- the package. ++ On Solaris, don't put '/usr/ucb' early in your 'PATH'. This ++directory contains several dysfunctional programs; working variants of ++these programs are available in '/usr/bin'. So, if you need '/usr/ucb' ++in your 'PATH', put it _after_ '/usr/bin'. + +- 4. Type `make install' to install the programs and any data files and +- documentation. ++ On Haiku, software installed for all users goes in '/boot/common', ++not '/usr/local'. It is recommended to use the following options: + +- 5. You can remove the program binaries and object files from the +- source code directory by typing `make clean'. To also remove the +- files that `configure' created (so you can compile the package for +- a different kind of computer), type `make distclean'. There is +- also a `make maintainer-clean' target, but that is intended mainly +- for the package's developers. If you use it, you may have to get +- all sorts of other programs in order to regenerate files that came +- with the distribution. ++ ./configure --prefix=/boot/common + +-Compilers and Options +-===================== +- +- Some systems require unusual options for compilation or linking that +-the `configure' script does not know about. You can give `configure' +-initial values for variables by setting them in the environment. Using +-a Bourne-compatible shell, you can do that on the command line like +-this: +- CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure ++Specifying the System Type ++========================== + +-Or on systems that have the `env' program, you can do it like this: +- env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure ++ There may be some features 'configure' cannot figure out ++automatically, but needs to determine by the type of machine the package ++will run on. Usually, assuming the package is built to be run on the ++_same_ architectures, 'configure' can figure that out, but if it prints ++a message saying it cannot guess the machine type, give it the ++'--build=TYPE' option. TYPE can either be a short name for the system ++type, such as 'sun4', or a canonical name which has the form: + +-Compiling For Multiple Architectures +-==================================== ++ CPU-COMPANY-SYSTEM + +- You can compile the package for more than one kind of computer at the +-same time, by placing the object files for each architecture in their +-own directory. To do this, you must use a version of `make' that +-supports the `VPATH' variable, such as GNU `make'. `cd' to the +-directory where you want the object files and executables to go and run +-the `configure' script. `configure' automatically checks for the +-source code in the directory that `configure' is in and in `..'. ++where SYSTEM can have one of these forms: + +- If you have to use a `make' that does not supports the `VPATH' +-variable, you have to compile the package for one architecture at a time +-in the source code directory. After you have installed the package for +-one architecture, use `make distclean' before reconfiguring for another +-architecture. ++ OS ++ KERNEL-OS + +-Installation Names +-================== ++ See the file 'config.sub' for the possible values of each field. If ++'config.sub' isn't included in this package, then this package doesn't ++need to know the machine type. + +- By default, `make install' will install the package's files in +-`/usr/local/bin', `/usr/local/man', etc. You can specify an +-installation prefix other than `/usr/local' by giving `configure' the +-option `--prefix=PATH'. ++ If you are _building_ compiler tools for cross-compiling, you should ++use the option '--target=TYPE' to select the type of system they will ++produce code for. + +- You can specify separate installation prefixes for +-architecture-specific files and architecture-independent files. If you +-give `configure' the option `--exec-prefix=PATH', the package will use +-PATH as the prefix for installing programs and libraries. +-Documentation and other data files will still use the regular prefix. ++ If you want to _use_ a cross compiler, that generates code for a ++platform different from the build platform, you should specify the ++"host" platform (i.e., that on which the generated programs will ++eventually be run) with '--host=TYPE'. + +- In addition, if you use an unusual directory layout you can give +-options like `--bindir=PATH' to specify different values for particular +-kinds of files. Run `configure --help' for a list of the directories +-you can set and what kinds of files go in them. ++Sharing Defaults ++================ + +- If the package supports it, you can cause programs to be installed +-with an extra prefix or suffix on their names by giving `configure' the +-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. ++ If you want to set default values for 'configure' scripts to share, ++you can create a site shell script called 'config.site' that gives ++default values for variables like 'CC', 'cache_file', and 'prefix'. ++'configure' looks for 'PREFIX/share/config.site' if it exists, then ++'PREFIX/etc/config.site' if it exists. Or, you can set the ++'CONFIG_SITE' environment variable to the location of the site script. ++A warning: not all 'configure' scripts look for a site script. + +-Optional Features +-================= ++Defining Variables ++================== + +- Some packages pay attention to `--enable-FEATURE' options to +-`configure', where FEATURE indicates an optional part of the package. +-They may also pay attention to `--with-PACKAGE' options, where PACKAGE +-is something like `gnu-as' or `x' (for the X Window System). The +-`README' should mention any `--enable-' and `--with-' options that the +-package recognizes. ++ Variables not defined in a site shell script can be set in the ++environment passed to 'configure'. However, some packages may run ++configure again during the build, and the customized values of these ++variables may be lost. In order to avoid this problem, you should set ++them in the 'configure' command line, using 'VAR=value'. For example: + +- For packages that use the X Window System, `configure' can usually +-find the X include and library files automatically, but if it doesn't, +-you can use the `configure' options `--x-includes=DIR' and +-`--x-libraries=DIR' to specify their locations. ++ ./configure CC=/usr/local2/bin/gcc + +-Specifying the System Type +-========================== ++causes the specified 'gcc' to be used as the C compiler (unless it is ++overridden in the site shell script). + +- There may be some features `configure' can not figure out +-automatically, but needs to determine by the type of host the package +-will run on. Usually `configure' can figure that out, but if it prints +-a message saying it can not guess the host type, give it the +-`--host=TYPE' option. TYPE can either be a short name for the system +-type, such as `sun4', or a canonical name with three fields: +- CPU-COMPANY-SYSTEM ++Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an ++Autoconf limitation. Until the limitation is lifted, you can use this ++workaround: + +-See the file `config.sub' for the possible values of each field. If +-`config.sub' isn't included in this package, then this package doesn't +-need to know the host type. ++ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +- If you are building compiler tools for cross-compiling, you can also +-use the `--target=TYPE' option to select the type of system they will +-produce code for and the `--build=TYPE' option to select the type of +-system on which you are compiling the package. ++'configure' Invocation ++====================== + +-Sharing Defaults +-================ ++ 'configure' recognizes the following options to control how it ++operates. + +- If you want to set default values for `configure' scripts to share, +-you can create a site shell script called `config.site' that gives +-default values for variables like `CC', `cache_file', and `prefix'. +-`configure' looks for `PREFIX/share/config.site' if it exists, then +-`PREFIX/etc/config.site' if it exists. Or, you can set the +-`CONFIG_SITE' environment variable to the location of the site script. +-A warning: not all `configure' scripts look for a site script. ++'--help' ++'-h' ++ Print a summary of all of the options to 'configure', and exit. + +-Operation Controls +-================== ++'--help=short' ++'--help=recursive' ++ Print a summary of the options unique to this package's ++ 'configure', and exit. The 'short' variant lists options used only ++ in the top level, while the 'recursive' variant lists options also ++ present in any nested packages. + +- `configure' recognizes the following options to control how it +-operates. ++'--version' ++'-V' ++ Print the version of Autoconf used to generate the 'configure' ++ script, and exit. + +-`--cache-file=FILE' +- Use and save the results of the tests in FILE instead of +- `./config.cache'. Set FILE to `/dev/null' to disable caching, for +- debugging `configure'. ++'--cache-file=FILE' ++ Enable the cache: use and save the results of the tests in FILE, ++ traditionally 'config.cache'. FILE defaults to '/dev/null' to ++ disable caching. + +-`--help' +- Print a summary of the options to `configure', and exit. ++'--config-cache' ++'-C' ++ Alias for '--cache-file=config.cache'. + +-`--quiet' +-`--silent' +-`-q' ++'--quiet' ++'--silent' ++'-q' + Do not print messages saying which checks are being made. To +- suppress all normal output, redirect it to `/dev/null' (any error ++ suppress all normal output, redirect it to '/dev/null' (any error + messages will still be shown). + +-`--srcdir=DIR' ++'--srcdir=DIR' + Look for the package's source code in directory DIR. Usually +- `configure' can determine that directory automatically. ++ 'configure' can determine that directory automatically. + +-`--version' +- Print the version of Autoconf used to generate the `configure' +- script, and exit. ++'--prefix=DIR' ++ Use DIR as the installation prefix. *note Installation Names:: for ++ more details, including other options available for fine-tuning the ++ installation locations. ++ ++'--no-create' ++'-n' ++ Run the configure checks, but stop before creating any output ++ files. + +-`configure' also accepts some other, not widely useful, options. ++'configure' also accepts some other, not widely useful, options. Run ++'configure --help' for more details. +diff --git a/Makefile.am b/Makefile.am +index 17f6960..992689e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,7 +1,5 @@ + SUBDIRS = wrap-gmp-gmpxx lib-src-reg lib-src src-reg src examples share + EXTRA_DIST = external/Makefile +-EXTRA_DIST += external/gmp-6.2.1.tar.bz2 +-EXTRA_DIST += external/cddlib-0.94j-TOPCOMb.tar.gz + EXTRA_DIST += external/qsopt_ex-2.5.10.3.tar.gz + EXTRA_DIST += external/gmpxx-patch + +diff --git a/Makefile.in b/Makefile.in +index b9902f5..93002b8 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -211,6 +211,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -286,6 +287,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -295,9 +297,8 @@ top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + SUBDIRS = wrap-gmp-gmpxx lib-src-reg lib-src src-reg src examples share +-EXTRA_DIST = external/Makefile external/gmp-6.2.1.tar.bz2 \ +- external/cddlib-0.94j-TOPCOMb.tar.gz \ +- external/qsopt_ex-2.5.10.3.tar.gz external/gmpxx-patch ++EXTRA_DIST = external/Makefile external/qsopt_ex-2.5.10.3.tar.gz \ ++ external/gmpxx-patch + all: all-recursive + + .SUFFIXES: +@@ -524,6 +525,10 @@ dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + ++dist-zstd: distdir ++ tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst ++ $(am__post_remove_distdir) ++ + dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 +@@ -566,6 +571,8 @@ distcheck: dist + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ ++ *.tar.zst*) \ ++ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) +@@ -742,7 +749,7 @@ uninstall-am: + am--refresh check check-am clean clean-cscope clean-generic \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ +- distcheck distclean distclean-generic distclean-tags \ ++ dist-zstd distcheck distclean distclean-generic distclean-tags \ + distcleancheck distdir distuninstallcheck dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ +diff --git a/aclocal.m4 b/aclocal.m4 +index 8c6b78f..e4b15fc 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1,6 +1,6 @@ +-# generated automatically by aclocal 1.16.1 -*- Autoconf -*- ++# generated automatically by aclocal 1.16.2 -*- Autoconf -*- + +-# Copyright (C) 1996-2018 Free Software Foundation, Inc. ++# Copyright (C) 1996-2020 Free Software Foundation, Inc. + + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to. + If you have problems, you may need to regenerate the build system entirely. + To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +-# Copyright (C) 2002-2018 Free Software Foundation, Inc. ++# Copyright (C) 2002-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -35,7 +35,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], + [am__api_version='1.16' + dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to + dnl require some minimum version. Point them to the right macro. +-m4_if([$1], [1.16.1], [], ++m4_if([$1], [1.16.2], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl + ]) + +@@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) + # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. + # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. + AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +-[AM_AUTOMAKE_VERSION([1.16.1])dnl ++[AM_AUTOMAKE_VERSION([1.16.2])dnl + m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl + _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + + # AM_AUX_DIR_EXPAND -*- Autoconf -*- + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -110,7 +110,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` + + # AM_CONDITIONAL -*- Autoconf -*- + +-# Copyright (C) 1997-2018 Free Software Foundation, Inc. ++# Copyright (C) 1997-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -141,7 +141,7 @@ AC_CONFIG_COMMANDS_PRE( + Usually this means the macro was only invoked conditionally.]]) + fi])]) + +-# Copyright (C) 1999-2018 Free Software Foundation, Inc. ++# Copyright (C) 1999-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -332,7 +332,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl + + # Generate code to set up dependency tracking. -*- Autoconf -*- + +-# Copyright (C) 1999-2018 Free Software Foundation, Inc. ++# Copyright (C) 1999-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -371,7 +371,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], + done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments +- for automatic dependency tracking. Try re-running configure with the ++ for automatic dependency tracking. If GNU make was not used, consider ++ re-running the configure script with MAKE="gmake" (or whatever is ++ necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi +@@ -398,7 +400,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], + + # Do all the work for Automake. -*- Autoconf -*- + +-# Copyright (C) 1996-2018 Free Software Foundation, Inc. ++# Copyright (C) 1996-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -595,7 +597,7 @@ for _am_header in $config_headers :; do + done + echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -616,7 +618,7 @@ if test x"${install_sh+set}" != xset; then + fi + AC_SUBST([install_sh])]) + +-# Copyright (C) 2003-2018 Free Software Foundation, Inc. ++# Copyright (C) 2003-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -637,7 +639,7 @@ AC_SUBST([am__leading_dot])]) + + # Check to see how 'make' treats includes. -*- Autoconf -*- + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -680,7 +682,7 @@ AC_SUBST([am__quote])]) + + # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +-# Copyright (C) 1997-2018 Free Software Foundation, Inc. ++# Copyright (C) 1997-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -719,7 +721,7 @@ fi + + # Helper functions for option handling. -*- Autoconf -*- + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -748,7 +750,7 @@ AC_DEFUN([_AM_SET_OPTIONS], + AC_DEFUN([_AM_IF_OPTION], + [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +-# Copyright (C) 1999-2018 Free Software Foundation, Inc. ++# Copyright (C) 1999-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -795,7 +797,7 @@ AC_LANG_POP([C])]) + # For backward compatibility. + AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -814,7 +816,7 @@ AC_DEFUN([AM_RUN_LOG], + + # Check to make sure that the build environment is sane. -*- Autoconf -*- + +-# Copyright (C) 1996-2018 Free Software Foundation, Inc. ++# Copyright (C) 1996-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -895,7 +897,7 @@ AC_CONFIG_COMMANDS_PRE( + rm -f conftest.file + ]) + +-# Copyright (C) 2009-2018 Free Software Foundation, Inc. ++# Copyright (C) 2009-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -955,7 +957,7 @@ AC_SUBST([AM_BACKSLASH])dnl + _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl + ]) + +-# Copyright (C) 2001-2018 Free Software Foundation, Inc. ++# Copyright (C) 2001-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -983,7 +985,7 @@ fi + INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +-# Copyright (C) 2006-2018 Free Software Foundation, Inc. ++# Copyright (C) 2006-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -1002,7 +1004,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + + # Check how to create a tarball. -*- Autoconf -*- + +-# Copyright (C) 2004-2018 Free Software Foundation, Inc. ++# Copyright (C) 2004-2020 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +diff --git a/compile b/compile +index 99e5052..23fcba0 100755 +--- a/compile ++++ b/compile +@@ -3,7 +3,7 @@ + + scriptversion=2018-03-07.03; # UTC + +-# Copyright (C) 1999-2018 Free Software Foundation, Inc. ++# Copyright (C) 1999-2020 Free Software Foundation, Inc. + # Written by Tom Tromey <tromey@cygnus.com>. + # + # This program is free software; you can redistribute it and/or modify +@@ -53,7 +53,7 @@ func_file_conv () + MINGW*) + file_conv=mingw + ;; +- CYGWIN*) ++ CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) +@@ -67,7 +67,7 @@ func_file_conv () + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; +- cygwin/*) ++ cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) +diff --git a/configure b/configure +index 7aebaca..6afa3b8 100755 +--- a/configure ++++ b/configure +@@ -627,6 +627,7 @@ USE_SOPLEX_FALSE + USE_SOPLEX_TRUE + USE_QSOPTEX_FALSE + USE_QSOPTEX_TRUE ++CPP + USE_LOCAL_GMP_FALSE + USE_LOCAL_GMP_TRUE + EGREP +@@ -705,6 +706,7 @@ infodir + docdir + oldincludedir + includedir ++runstatedir + localstatedir + sharedstatedir + sysconfdir +@@ -744,7 +746,8 @@ CPPFLAGS + CXX + CXXFLAGS + CCC +-CXXCPP' ++CXXCPP ++CPP' + + + # Initialize some variables set by options. +@@ -783,6 +786,7 @@ datadir='${datarootdir}' + sysconfdir='${prefix}/etc' + sharedstatedir='${prefix}/com' + localstatedir='${prefix}/var' ++runstatedir='${localstatedir}/run' + includedir='${prefix}/include' + oldincludedir='/usr/include' + docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +@@ -1035,6 +1039,15 @@ do + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + ++ -runstatedir | --runstatedir | --runstatedi | --runstated \ ++ | --runstate | --runstat | --runsta | --runst | --runs \ ++ | --run | --ru | --r) ++ ac_prev=runstatedir ;; ++ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ ++ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ ++ | --run=* | --ru=* | --r=*) ++ runstatedir=$ac_optarg ;; ++ + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ +@@ -1172,7 +1185,7 @@ fi + for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ +- libdir localedir mandir ++ libdir localedir mandir runstatedir + do + eval ac_val=\$$ac_var + # Remove trailing slashes. +@@ -1325,6 +1338,7 @@ Fine tuning of the installation directories: + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] ++ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] +@@ -1388,6 +1402,7 @@ Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor ++ CPP C preprocessor + + Use these variables to override the choices made by `configure' or to help + it to find libraries and programs with nonstandard names/locations. +@@ -1742,6 +1757,130 @@ $as_echo "$ac_res" >&6; } + + } # ac_fn_cxx_check_header_compile + ++# ac_fn_c_try_cpp LINENO ++# ---------------------- ++# Try to preprocess conftest.$ac_ext, and return whether this succeeded. ++ac_fn_c_try_cpp () ++{ ++ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ if { { ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ++ ac_status=$? ++ if test -s conftest.err; then ++ grep -v '^ *+' conftest.err >conftest.er1 ++ cat conftest.er1 >&5 ++ mv -f conftest.er1 conftest.err ++ fi ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } > conftest.i && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then : ++ ac_retval=0 ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_retval=1 ++fi ++ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno ++ as_fn_set_status $ac_retval ++ ++} # ac_fn_c_try_cpp ++ ++# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES ++# ------------------------------------------------------- ++# Tests whether HEADER exists, giving a warning if it cannot be compiled using ++# the include files in INCLUDES and setting the cache variable VAR ++# accordingly. ++ac_fn_c_check_header_mongrel () ++{ ++ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ if eval \${$3+:} false; then : ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 ++$as_echo_n "checking for $2... " >&6; } ++if eval \${$3+:} false; then : ++ $as_echo_n "(cached) " >&6 ++fi ++eval ac_res=\$$3 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 ++$as_echo "$ac_res" >&6; } ++else ++ # Is the header compilable? ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 ++$as_echo_n "checking $2 usability... " >&6; } ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++$4 ++#include <$2> ++_ACEOF ++if ac_fn_c_try_compile "$LINENO"; then : ++ ac_header_compiler=yes ++else ++ ac_header_compiler=no ++fi ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 ++$as_echo "$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 ++$as_echo_n "checking $2 presence... " >&6; } ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include <$2> ++_ACEOF ++if ac_fn_c_try_cpp "$LINENO"; then : ++ ac_header_preproc=yes ++else ++ ac_header_preproc=no ++fi ++rm -f conftest.err conftest.i conftest.$ac_ext ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 ++$as_echo "$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( ++ yes:no: ) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 ++$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 ++$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ++ ;; ++ no:yes:* ) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 ++$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 ++$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 ++$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 ++$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 ++$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ++ ;; ++esac ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 ++$as_echo_n "checking for $2... " >&6; } ++if eval \${$3+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ eval "$3=\$ac_header_compiler" ++fi ++eval ac_res=\$$3 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 ++$as_echo "$ac_res" >&6; } ++fi ++ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno ++ ++} # ac_fn_c_check_header_mongrel ++ + # ac_fn_c_try_link LINENO + # ----------------------- + # Try to link conftest.$ac_ext, and return whether this succeeded. +@@ -4801,7 +4940,218 @@ $as_echo "$as_me: gmpxx.h not found on system - building gmp locally ..." >&6;} + $as_echo "$as_me: ... done" >&6;} + fi + +-make -C external cdd ++ ++ac_ext=c ++ac_cpp='$CPP $CPPFLAGS' ++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ++ac_compiler_gnu=$ac_cv_c_compiler_gnu ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 ++$as_echo_n "checking how to run the C preprocessor... " >&6; } ++# On Suns, sometimes $CPP names a directory. ++if test -n "$CPP" && test -d "$CPP"; then ++ CPP= ++fi ++if test -z "$CPP"; then ++ if ${ac_cv_prog_CPP+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ # Double quotes because CPP needs to be expanded ++ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" ++ do ++ ac_preproc_ok=false ++for ac_c_preproc_warn_flag in '' yes ++do ++ # Use a header file that comes with gcc, so configuring glibc ++ # with a fresh cross-compiler works. ++ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since ++ # <limits.h> exists even on freestanding compilers. ++ # On the NeXT, cc -E runs the code through the compiler's parser, ++ # not just through cpp. "Syntax error" is here to catch this case. ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#ifdef __STDC__ ++# include <limits.h> ++#else ++# include <assert.h> ++#endif ++ Syntax error ++_ACEOF ++if ac_fn_c_try_cpp "$LINENO"; then : ++ ++else ++ # Broken: fails on valid input. ++continue ++fi ++rm -f conftest.err conftest.i conftest.$ac_ext ++ ++ # OK, works on sane cases. Now check whether nonexistent headers ++ # can be detected and how. ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include <ac_nonexistent.h> ++_ACEOF ++if ac_fn_c_try_cpp "$LINENO"; then : ++ # Broken: success on invalid input. ++continue ++else ++ # Passes both tests. ++ac_preproc_ok=: ++break ++fi ++rm -f conftest.err conftest.i conftest.$ac_ext ++ ++done ++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. ++rm -f conftest.i conftest.err conftest.$ac_ext ++if $ac_preproc_ok; then : ++ break ++fi ++ ++ done ++ ac_cv_prog_CPP=$CPP ++ ++fi ++ CPP=$ac_cv_prog_CPP ++else ++ ac_cv_prog_CPP=$CPP ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 ++$as_echo "$CPP" >&6; } ++ac_preproc_ok=false ++for ac_c_preproc_warn_flag in '' yes ++do ++ # Use a header file that comes with gcc, so configuring glibc ++ # with a fresh cross-compiler works. ++ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since ++ # <limits.h> exists even on freestanding compilers. ++ # On the NeXT, cc -E runs the code through the compiler's parser, ++ # not just through cpp. "Syntax error" is here to catch this case. ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#ifdef __STDC__ ++# include <limits.h> ++#else ++# include <assert.h> ++#endif ++ Syntax error ++_ACEOF ++if ac_fn_c_try_cpp "$LINENO"; then : ++ ++else ++ # Broken: fails on valid input. ++continue ++fi ++rm -f conftest.err conftest.i conftest.$ac_ext ++ ++ # OK, works on sane cases. Now check whether nonexistent headers ++ # can be detected and how. ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include <ac_nonexistent.h> ++_ACEOF ++if ac_fn_c_try_cpp "$LINENO"; then : ++ # Broken: success on invalid input. ++continue ++else ++ # Passes both tests. ++ac_preproc_ok=: ++break ++fi ++rm -f conftest.err conftest.i conftest.$ac_ext ++ ++done ++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. ++rm -f conftest.i conftest.err conftest.$ac_ext ++if $ac_preproc_ok; then : ++ ++else ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "C preprocessor \"$CPP\" fails sanity check ++See \`config.log' for more details" "$LINENO" 5; } ++fi ++ ++ac_ext=c ++ac_cpp='$CPP $CPPFLAGS' ++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ++ac_compiler_gnu=$ac_cv_c_compiler_gnu ++ ++ ++for ac_header in cddlib/setoper.h ++do : ++ ac_fn_c_check_header_mongrel "$LINENO" "cddlib/setoper.h" "ac_cv_header_cddlib_setoper_h" "$ac_includes_default" ++if test "x$ac_cv_header_cddlib_setoper_h" = xyes; then : ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_CDDLIB_SETOPER_H 1 ++_ACEOF ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dd_free_global_constants" >&5 ++$as_echo_n "checking for library containing dd_free_global_constants... " >&6; } ++if ${ac_cv_search_dd_free_global_constants+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_func_search_save_LIBS=$LIBS ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char dd_free_global_constants (); ++int ++main () ++{ ++return dd_free_global_constants (); ++ ; ++ return 0; ++} ++_ACEOF ++for ac_lib in '' cddgmp; do ++ if test -z "$ac_lib"; then ++ ac_res="none required" ++ else ++ ac_res=-l$ac_lib ++ LIBS="-l$ac_lib $ac_func_search_save_LIBS" ++ fi ++ if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_search_dd_free_global_constants=$ac_res ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext ++ if ${ac_cv_search_dd_free_global_constants+:} false; then : ++ break ++fi ++done ++if ${ac_cv_search_dd_free_global_constants+:} false; then : ++ ++else ++ ac_cv_search_dd_free_global_constants=no ++fi ++rm conftest.$ac_ext ++LIBS=$ac_func_search_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dd_free_global_constants" >&5 ++$as_echo "$ac_cv_search_dd_free_global_constants" >&6; } ++ac_res=$ac_cv_search_dd_free_global_constants ++if test "$ac_res" != no; then : ++ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" ++ ++else ++ as_fn_error $? "Can't use cddgmp library" "$LINENO" 5 ++fi ++ ++ ++else ++ as_fn_error $? "Can't find cdd library headers" "$LINENO" 5 ++fi ++ ++done ++ + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether QSOpt_ex support was enabled" >&5 + $as_echo_n "checking whether QSOpt_ex support was enabled... " >&6; } +@@ -6276,7 +6626,9 @@ $as_echo X/"$am_mf" | + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + as_fn_error $? "Something went wrong bootstrapping makefile fragments +- for automatic dependency tracking. Try re-running configure with the ++ for automatic dependency tracking. If GNU make was not used, consider ++ re-running the configure script with MAKE=\"gmake\" (or whatever is ++ necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). + See \`config.log' for more details" "$LINENO" 5; } +diff --git a/configure.ac b/configure.ac +index 2372711..430f01b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -43,8 +43,10 @@ else + AC_MSG_NOTICE([... done]) + fi + +-dnl Make libcddgmp: +-make -C external cdd ++ ++AC_CHECK_HEADERS([cddlib/setoper.h],[ ++ AC_SEARCH_LIBS([dd_free_global_constants],[cddgmp], [], [AC_MSG_ERROR([Can't use cddgmp library])]) ++ ], [AC_MSG_ERROR([Can't find cdd library headers])]) + + dnl Check for requests for third-party packages: + dnl Check for qsopt_ex: +diff --git a/depcomp b/depcomp +index 65cbf70..6b39162 100755 +--- a/depcomp ++++ b/depcomp +@@ -3,7 +3,7 @@ + + scriptversion=2018-03-07.03; # UTC + +-# Copyright (C) 1999-2018 Free Software Foundation, Inc. ++# Copyright (C) 1999-2020 Free Software Foundation, Inc. + + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by +diff --git a/examples/Makefile.in b/examples/Makefile.in +index 92a0212..30ac680 100644 +--- a/examples/Makefile.in ++++ b/examples/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -129,6 +129,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -204,6 +205,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/install-sh b/install-sh +index 8175c64..20d8b2e 100755 +--- a/install-sh ++++ b/install-sh +@@ -451,7 +451,18 @@ do + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. +- (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && ++ (umask $cp_umask && ++ { test -z "$stripcmd" || { ++ # Create $dsttmp read-write so that cp doesn't create it read-only, ++ # which would cause strip to fail. ++ if test -z "$doit"; then ++ : >"$dsttmp" # No need to fork-exec 'touch'. ++ else ++ $doit touch "$dsttmp" ++ fi ++ } ++ } && ++ $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # +diff --git a/lib-src-reg/LPinterface.hh b/lib-src-reg/LPinterface.hh +index 6522dbe..622a224 100644 +--- a/lib-src-reg/LPinterface.hh ++++ b/lib-src-reg/LPinterface.hh +@@ -21,8 +21,8 @@ + #include "LabelSet.hh" + #include "Rational.h" + +-#include "setoper.h" +-#include "cdd.h" ++#include <cddlib/setoper.h> ++#include <cddlib/cdd.h> + + namespace topcom { + +diff --git a/lib-src-reg/Makefile.in b/lib-src-reg/Makefile.in +index 977f8f8..368b2a5 100644 +--- a/lib-src-reg/Makefile.in ++++ b/lib-src-reg/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -214,6 +214,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -289,6 +290,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in +index 4407b62..a3d3316 100644 +--- a/lib-src/Makefile.in ++++ b/lib-src/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -267,6 +267,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -342,6 +343,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/missing b/missing +index 625aeb1..8d0eaad 100755 +--- a/missing ++++ b/missing +@@ -3,7 +3,7 @@ + + scriptversion=2018-03-07.03; # UTC + +-# Copyright (C) 1996-2018 Free Software Foundation, Inc. ++# Copyright (C) 1996-2020 Free Software Foundation, Inc. + # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. + + # This program is free software; you can redistribute it and/or modify +diff --git a/share/Makefile.in b/share/Makefile.in +index 23488b7..2326a9e 100644 +--- a/share/Makefile.in ++++ b/share/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -129,6 +129,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -204,6 +205,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +diff --git a/src-reg/Makefile.am b/src-reg/Makefile.am +index ed4a016..2ad9026 100644 +--- a/src-reg/Makefile.am ++++ b/src-reg/Makefile.am +@@ -4,7 +4,9 @@ checkregularity_SOURCES = checkregularity.cc + + LDADD = ../lib-src/libTOPCOM.a \ + ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a ++ -lgmpxx -lgmp ++ ++ + if USE_QSOPTEX + LDADD += ../external/lib/libqsopt_ex.a + endif +@@ -12,12 +14,6 @@ endif + if USE_SOPLEX + LDADD += ../external/lib/libsoplex.a + endif +-if USE_LOCAL_GMP +-LDADD += ../external/lib/libgmpxx.a \ +- ../external/lib/libgmp.a +-else +-LIBS += -lgmpxx -lgmp +-endif + + AM_CPPFLAGS += -I../lib-src + AM_CPPFLAGS += -I../lib-src-reg +diff --git a/src-reg/Makefile.in b/src-reg/Makefile.in +index f4b7c08..7ccd2f7 100644 +--- a/src-reg/Makefile.in ++++ b/src-reg/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -89,10 +89,6 @@ POST_UNINSTALL = : + bin_PROGRAMS = checkregularity$(EXEEXT) + @USE_QSOPTEX_TRUE@am__append_1 = ../external/lib/libqsopt_ex.a + @USE_SOPLEX_TRUE@am__append_2 = ../external/lib/libsoplex.a +-@USE_LOCAL_GMP_TRUE@am__append_3 = ../external/lib/libgmpxx.a \ +-@USE_LOCAL_GMP_TRUE@ ../external/lib/libgmp.a +- +-@USE_LOCAL_GMP_FALSE@am__append_4 = -lgmpxx -lgmp + subdir = src-reg + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/configure.ac +@@ -108,8 +104,7 @@ am_checkregularity_OBJECTS = checkregularity.$(OBJEXT) + checkregularity_OBJECTS = $(am_checkregularity_OBJECTS) + checkregularity_LDADD = $(LDADD) + checkregularity_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + AM_V_P = $(am__v_P_@AM_V@) + am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) + am__v_P_0 = false +@@ -184,6 +179,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -205,7 +201,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ + LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ $(am__append_4) ++LIBS = @LIBS@ + LTLIBOBJS = @LTLIBOBJS@ + MAKEINFO = @MAKEINFO@ + MKDIR_P = @MKDIR_P@ +@@ -259,6 +255,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -268,9 +265,8 @@ top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + checkregularity_SOURCES = checkregularity.cc +-LDADD = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++LDADD = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a -lgmpxx \ ++ -lgmp $(am__append_1) $(am__append_2) + all: all-am + + .SUFFIXES: +diff --git a/src/Makefile.am b/src/Makefile.am +index c97ecc5..e20e1c3 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -122,20 +122,15 @@ santos_dim4_triang_SOURCES = santos_dim4_triang.cc + santos_22_triang_SOURCES = santos_22_triang.cc + + LDADD = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a ++ ../lib-src-reg/libCHECKREG.a ++ + if USE_QSOPTEX + LDADD += ../external/lib/libqsopt_ex.a + endif + if USE_SOPLEX + LDADD += ../external/lib/libsoplex.a + endif +-if USE_LOCAL_GMP +-LDADD += ../external/lib/libgmpxx.a \ +- ../external/lib/libgmp.a +-else + LIBS += -lgmpxx -lgmp +-endif + + + AM_CPPFLAGS += -I../lib-src +diff --git a/src/Makefile.in b/src/Makefile.in +index 47a6ac0..bcca997 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -115,10 +115,6 @@ bin_PROGRAMS = B_A$(EXEEXT) B_A_center$(EXEEXT) B_D$(EXEEXT) \ + santos_dim4_triang$(EXEEXT) santos_22_triang$(EXEEXT) + @USE_QSOPTEX_TRUE@am__append_1 = ../external/lib/libqsopt_ex.a + @USE_SOPLEX_TRUE@am__append_2 = ../external/lib/libsoplex.a +-@USE_LOCAL_GMP_TRUE@am__append_3 = ../external/lib/libgmpxx.a \ +-@USE_LOCAL_GMP_TRUE@ ../external/lib/libgmp.a +- +-@USE_LOCAL_GMP_FALSE@am__append_4 = -lgmpxx -lgmp + subdir = src + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/configure.ac +@@ -134,362 +130,302 @@ am_B_A_OBJECTS = B_A.$(OBJEXT) + B_A_OBJECTS = $(am_B_A_OBJECTS) + B_A_LDADD = $(LDADD) + B_A_DEPENDENCIES = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++ $(am__append_1) $(am__append_2) + am_B_A_center_OBJECTS = B_A_center.$(OBJEXT) + B_A_center_OBJECTS = $(am_B_A_center_OBJECTS) + B_A_center_LDADD = $(LDADD) + B_A_center_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_B_D_OBJECTS = B_D.$(OBJEXT) + B_D_OBJECTS = $(am_B_D_OBJECTS) + B_D_LDADD = $(LDADD) + B_D_DEPENDENCIES = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++ $(am__append_1) $(am__append_2) + am_B_D_center_OBJECTS = B_D_center.$(OBJEXT) + B_D_center_OBJECTS = $(am_B_D_center_OBJECTS) + B_D_center_LDADD = $(LDADD) + B_D_center_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_B_S_OBJECTS = B_S.$(OBJEXT) + B_S_OBJECTS = $(am_B_S_OBJECTS) + B_S_LDADD = $(LDADD) + B_S_DEPENDENCIES = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++ $(am__append_1) $(am__append_2) + am_B_S_center_OBJECTS = B_S_center.$(OBJEXT) + B_S_center_OBJECTS = $(am_B_S_center_OBJECTS) + B_S_center_LDADD = $(LDADD) + B_S_center_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_Dnxk_OBJECTS = Dnxk.$(OBJEXT) + Dnxk_OBJECTS = $(am_Dnxk_OBJECTS) + Dnxk_LDADD = $(LDADD) + Dnxk_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_binomial_OBJECTS = binomial.$(OBJEXT) + binomial_OBJECTS = $(am_binomial_OBJECTS) + binomial_LDADD = $(LDADD) + binomial_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_check_OBJECTS = check.$(OBJEXT) + check_OBJECTS = $(am_check_OBJECTS) + check_LDADD = $(LDADD) + check_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2allfinetriangs_OBJECTS = chiro2allfinetriangs.$(OBJEXT) + chiro2allfinetriangs_OBJECTS = $(am_chiro2allfinetriangs_OBJECTS) + chiro2allfinetriangs_LDADD = $(LDADD) + chiro2allfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2alltriangs_OBJECTS = chiro2alltriangs.$(OBJEXT) + chiro2alltriangs_OBJECTS = $(am_chiro2alltriangs_OBJECTS) + chiro2alltriangs_LDADD = $(LDADD) + chiro2alltriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2circuits_OBJECTS = chiro2circuits.$(OBJEXT) + chiro2circuits_OBJECTS = $(am_chiro2circuits_OBJECTS) + chiro2circuits_LDADD = $(LDADD) + chiro2circuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2cocircuits_OBJECTS = chiro2cocircuits.$(OBJEXT) + chiro2cocircuits_OBJECTS = $(am_chiro2cocircuits_OBJECTS) + chiro2cocircuits_LDADD = $(LDADD) + chiro2cocircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2dual_OBJECTS = chiro2dual.$(OBJEXT) + chiro2dual_OBJECTS = $(am_chiro2dual_OBJECTS) + chiro2dual_LDADD = $(LDADD) + chiro2dual_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2finetriang_OBJECTS = chiro2finetriang.$(OBJEXT) + chiro2finetriang_OBJECTS = $(am_chiro2finetriang_OBJECTS) + chiro2finetriang_LDADD = $(LDADD) + chiro2finetriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2finetriangs_OBJECTS = chiro2finetriangs.$(OBJEXT) + chiro2finetriangs_OBJECTS = $(am_chiro2finetriangs_OBJECTS) + chiro2finetriangs_LDADD = $(LDADD) + chiro2finetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2mintriang_OBJECTS = chiro2mintriang.$(OBJEXT) + chiro2mintriang_OBJECTS = $(am_chiro2mintriang_OBJECTS) + chiro2mintriang_LDADD = $(LDADD) + chiro2mintriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2nallfinetriangs_OBJECTS = chiro2nallfinetriangs.$(OBJEXT) + chiro2nallfinetriangs_OBJECTS = $(am_chiro2nallfinetriangs_OBJECTS) + chiro2nallfinetriangs_LDADD = $(LDADD) + chiro2nallfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2nalltriangs_OBJECTS = chiro2nalltriangs.$(OBJEXT) + chiro2nalltriangs_OBJECTS = $(am_chiro2nalltriangs_OBJECTS) + chiro2nalltriangs_LDADD = $(LDADD) + chiro2nalltriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2ncircuits_OBJECTS = chiro2ncircuits.$(OBJEXT) + chiro2ncircuits_OBJECTS = $(am_chiro2ncircuits_OBJECTS) + chiro2ncircuits_LDADD = $(LDADD) + chiro2ncircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2ncocircuits_OBJECTS = chiro2ncocircuits.$(OBJEXT) + chiro2ncocircuits_OBJECTS = $(am_chiro2ncocircuits_OBJECTS) + chiro2ncocircuits_LDADD = $(LDADD) + chiro2ncocircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2nfinetriangs_OBJECTS = chiro2nfinetriangs.$(OBJEXT) + chiro2nfinetriangs_OBJECTS = $(am_chiro2nfinetriangs_OBJECTS) + chiro2nfinetriangs_LDADD = $(LDADD) + chiro2nfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2ntriangs_OBJECTS = chiro2ntriangs.$(OBJEXT) + chiro2ntriangs_OBJECTS = $(am_chiro2ntriangs_OBJECTS) + chiro2ntriangs_LDADD = $(LDADD) + chiro2ntriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2placingtriang_OBJECTS = chiro2placingtriang.$(OBJEXT) + chiro2placingtriang_OBJECTS = $(am_chiro2placingtriang_OBJECTS) + chiro2placingtriang_LDADD = $(LDADD) + chiro2placingtriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_chiro2triangs_OBJECTS = chiro2triangs.$(OBJEXT) + chiro2triangs_OBJECTS = $(am_chiro2triangs_OBJECTS) + chiro2triangs_LDADD = $(LDADD) + chiro2triangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_cocircuits2facets_OBJECTS = cocircuits2facets.$(OBJEXT) + cocircuits2facets_OBJECTS = $(am_cocircuits2facets_OBJECTS) + cocircuits2facets_LDADD = $(LDADD) + cocircuits2facets_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_cross_OBJECTS = cross.$(OBJEXT) + cross_OBJECTS = $(am_cross_OBJECTS) + cross_LDADD = $(LDADD) + cross_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_cube_OBJECTS = cube.$(OBJEXT) + cube_OBJECTS = $(am_cube_OBJECTS) + cube_LDADD = $(LDADD) + cube_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_cyclic_OBJECTS = cyclic.$(OBJEXT) + cyclic_OBJECTS = $(am_cyclic_OBJECTS) + cyclic_LDADD = $(LDADD) + cyclic_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_hypersimplex_OBJECTS = hypersimplex.$(OBJEXT) + hypersimplex_OBJECTS = $(am_hypersimplex_OBJECTS) + hypersimplex_LDADD = $(LDADD) + hypersimplex_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_kDn_OBJECTS = kDn.$(OBJEXT) + kDn_OBJECTS = $(am_kDn_OBJECTS) + kDn_LDADD = $(LDADD) + kDn_DEPENDENCIES = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++ $(am__append_1) $(am__append_2) + am_lattice_OBJECTS = lattice.$(OBJEXT) + lattice_OBJECTS = $(am_lattice_OBJECTS) + lattice_LDADD = $(LDADD) + lattice_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_permutahedron_OBJECTS = permutahedron.$(OBJEXT) + permutahedron_OBJECTS = $(am_permutahedron_OBJECTS) + permutahedron_LDADD = $(LDADD) + permutahedron_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2allfinetriangs_OBJECTS = points2allfinetriangs.$(OBJEXT) + points2allfinetriangs_OBJECTS = $(am_points2allfinetriangs_OBJECTS) + points2allfinetriangs_LDADD = $(LDADD) + points2allfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2alltriangs_OBJECTS = points2alltriangs.$(OBJEXT) + points2alltriangs_OBJECTS = $(am_points2alltriangs_OBJECTS) + points2alltriangs_LDADD = $(LDADD) + points2alltriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2chiro_OBJECTS = points2chiro.$(OBJEXT) + points2chiro_OBJECTS = $(am_points2chiro_OBJECTS) + points2chiro_LDADD = $(LDADD) + points2chiro_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2circuits_OBJECTS = points2circuits.$(OBJEXT) + points2circuits_OBJECTS = $(am_points2circuits_OBJECTS) + points2circuits_LDADD = $(LDADD) + points2circuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2cocircuits_OBJECTS = points2cocircuits.$(OBJEXT) + points2cocircuits_OBJECTS = $(am_points2cocircuits_OBJECTS) + points2cocircuits_LDADD = $(LDADD) + points2cocircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2facets_OBJECTS = points2facets.$(OBJEXT) + points2facets_OBJECTS = $(am_points2facets_OBJECTS) + points2facets_LDADD = $(LDADD) + points2facets_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2finetriang_OBJECTS = points2finetriang.$(OBJEXT) + points2finetriang_OBJECTS = $(am_points2finetriang_OBJECTS) + points2finetriang_LDADD = $(LDADD) + points2finetriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2finetriangs_OBJECTS = points2finetriangs.$(OBJEXT) + points2finetriangs_OBJECTS = $(am_points2finetriangs_OBJECTS) + points2finetriangs_LDADD = $(LDADD) + points2finetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2flips_OBJECTS = points2flips.$(OBJEXT) + points2flips_OBJECTS = $(am_points2flips_OBJECTS) + points2flips_LDADD = $(LDADD) + points2flips_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2gale_OBJECTS = points2gale.$(OBJEXT) + points2gale_OBJECTS = $(am_points2gale_OBJECTS) + points2gale_LDADD = $(LDADD) + points2gale_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2mintriang_OBJECTS = points2mintriang.$(OBJEXT) + points2mintriang_OBJECTS = $(am_points2mintriang_OBJECTS) + points2mintriang_LDADD = $(LDADD) + points2mintriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2nallfinetriangs_OBJECTS = points2nallfinetriangs.$(OBJEXT) + points2nallfinetriangs_OBJECTS = $(am_points2nallfinetriangs_OBJECTS) + points2nallfinetriangs_LDADD = $(LDADD) + points2nallfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2nalltriangs_OBJECTS = points2nalltriangs.$(OBJEXT) + points2nalltriangs_OBJECTS = $(am_points2nalltriangs_OBJECTS) + points2nalltriangs_LDADD = $(LDADD) + points2nalltriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2ncircuits_OBJECTS = points2ncircuits.$(OBJEXT) + points2ncircuits_OBJECTS = $(am_points2ncircuits_OBJECTS) + points2ncircuits_LDADD = $(LDADD) + points2ncircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2ncocircuits_OBJECTS = points2ncocircuits.$(OBJEXT) + points2ncocircuits_OBJECTS = $(am_points2ncocircuits_OBJECTS) + points2ncocircuits_LDADD = $(LDADD) + points2ncocircuits_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2nfinetriangs_OBJECTS = points2nfinetriangs.$(OBJEXT) + points2nfinetriangs_OBJECTS = $(am_points2nfinetriangs_OBJECTS) + points2nfinetriangs_LDADD = $(LDADD) + points2nfinetriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2nflips_OBJECTS = points2nflips.$(OBJEXT) + points2nflips_OBJECTS = $(am_points2nflips_OBJECTS) + points2nflips_LDADD = $(LDADD) + points2nflips_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2ntriangs_OBJECTS = points2ntriangs.$(OBJEXT) + points2ntriangs_OBJECTS = $(am_points2ntriangs_OBJECTS) + points2ntriangs_LDADD = $(LDADD) + points2ntriangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2placingtriang_OBJECTS = points2placingtriang.$(OBJEXT) + points2placingtriang_OBJECTS = $(am_points2placingtriang_OBJECTS) + points2placingtriang_LDADD = $(LDADD) + points2placingtriang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2prettyprint_OBJECTS = points2prettyprint.$(OBJEXT) + points2prettyprint_OBJECTS = $(am_points2prettyprint_OBJECTS) + points2prettyprint_LDADD = $(LDADD) + points2prettyprint_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2symmetries_OBJECTS = points2symmetries.$(OBJEXT) + points2symmetries_OBJECTS = $(am_points2symmetries_OBJECTS) + points2symmetries_LDADD = $(LDADD) + points2symmetries_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2triangs_OBJECTS = points2triangs.$(OBJEXT) + points2triangs_OBJECTS = $(am_points2triangs_OBJECTS) + points2triangs_LDADD = $(LDADD) + points2triangs_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2vertices_OBJECTS = points2vertices.$(OBJEXT) + points2vertices_OBJECTS = $(am_points2vertices_OBJECTS) + points2vertices_LDADD = $(LDADD) + points2vertices_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_points2volume_OBJECTS = points2volume.$(OBJEXT) + points2volume_OBJECTS = $(am_points2volume_OBJECTS) + points2volume_LDADD = $(LDADD) + points2volume_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_santos_22_triang_OBJECTS = santos_22_triang.$(OBJEXT) + santos_22_triang_OBJECTS = $(am_santos_22_triang_OBJECTS) + santos_22_triang_LDADD = $(LDADD) + santos_22_triang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_santos_dim4_triang_OBJECTS = santos_dim4_triang.$(OBJEXT) + santos_dim4_triang_OBJECTS = $(am_santos_dim4_triang_OBJECTS) + santos_dim4_triang_LDADD = $(LDADD) + santos_dim4_triang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + am_santos_triang_OBJECTS = santos_triang.$(OBJEXT) + santos_triang_OBJECTS = $(am_santos_triang_OBJECTS) + santos_triang_LDADD = $(LDADD) + santos_triang_DEPENDENCIES = ../lib-src/libTOPCOM.a \ +- ../lib-src-reg/libCHECKREG.a ../external/lib/libcddgmp.a \ +- $(am__append_1) $(am__append_2) $(am__append_3) ++ ../lib-src-reg/libCHECKREG.a $(am__append_1) $(am__append_2) + AM_V_P = $(am__v_P_@AM_V@) + am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) + am__v_P_0 = false +@@ -657,6 +593,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -678,7 +615,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ + LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ $(am__append_4) ++LIBS = @LIBS@ -lgmpxx -lgmp + LTLIBOBJS = @LTLIBOBJS@ + MAKEINFO = @MAKEINFO@ + MKDIR_P = @MKDIR_P@ +@@ -732,6 +669,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ +@@ -801,8 +739,7 @@ santos_triang_SOURCES = santos_triang.cc + santos_dim4_triang_SOURCES = santos_dim4_triang.cc + santos_22_triang_SOURCES = santos_22_triang.cc + LDADD = ../lib-src/libTOPCOM.a ../lib-src-reg/libCHECKREG.a \ +- ../external/lib/libcddgmp.a $(am__append_1) $(am__append_2) \ +- $(am__append_3) ++ $(am__append_1) $(am__append_2) + all: all-am + + .SUFFIXES: +diff --git a/wrap-gmp-gmpxx/Makefile.in b/wrap-gmp-gmpxx/Makefile.in +index 86f0be0..bd4c95f 100644 +--- a/wrap-gmp-gmpxx/Makefile.in ++++ b/wrap-gmp-gmpxx/Makefile.in +@@ -1,7 +1,7 @@ +-# Makefile.in generated by automake 1.16.1 from Makefile.am. ++# Makefile.in generated by automake 1.16.2 from Makefile.am. + # @configure_input@ + +-# Copyright (C) 1994-2018 Free Software Foundation, Inc. ++# Copyright (C) 1994-2020 Free Software Foundation, Inc. + + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -178,6 +178,7 @@ AWK = @AWK@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ + CFLAGS = @CFLAGS@ ++CPP = @CPP@ + CPPFLAGS = @CPPFLAGS@ + CXX = @CXX@ + CXXCPP = @CXXCPP@ +@@ -253,6 +254,7 @@ pdfdir = @pdfdir@ + prefix = @prefix@ + program_transform_name = @program_transform_name@ + psdir = @psdir@ ++runstatedir = @runstatedir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ + srcdir = @srcdir@ diff --git a/build/pkgs/topcom/spkg-install.in b/build/pkgs/topcom/spkg-install.in index 53eb50b1b98..561925248d7 100644 --- a/build/pkgs/topcom/spkg-install.in +++ b/build/pkgs/topcom/spkg-install.in @@ -1,4 +1,9 @@ cd src + +# Avoid running autotools. Patch no_vendor_cddlib.patch makes changes +# both to input and output. +touch configure Makefile.in + ./configure \ --prefix="$SAGE_LOCAL" \ --libdir="$SAGE_LOCAL/lib" \ diff --git a/build/pkgs/tornado/checksums.ini b/build/pkgs/tornado/checksums.ini index e0e818876c0..9f69d85d5dc 100644 --- a/build/pkgs/tornado/checksums.ini +++ b/build/pkgs/tornado/checksums.ini @@ -1,5 +1,5 @@ tarball=tornado-VERSION.tar.gz -sha1=5fc7bd2ccb94efb8fdc7c9438eca454f2f5bab9b -md5=f324f5e7607798552359d6ab054c4321 -cksum=3079653030 +sha1=2fa6cbd83ebafad83f49e89fbd5bbd20c42bbdc9 +md5=32fbad606b439c3e1bf4e79d4e872741 +cksum=3183867326 upstream_url=https://pypi.io/packages/source/t/tornado/tornado-VERSION.tar.gz diff --git a/build/pkgs/tornado/package-version.txt b/build/pkgs/tornado/package-version.txt index a435f5a56fa..0cda48ac61e 100644 --- a/build/pkgs/tornado/package-version.txt +++ b/build/pkgs/tornado/package-version.txt @@ -1 +1 @@ -6.1 +6.2 diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini index f64c6e12b24..19a159a8e4a 100644 --- a/build/pkgs/tox/checksums.ini +++ b/build/pkgs/tox/checksums.ini @@ -1,5 +1,5 @@ tarball=tox-VERSION.tar.gz -sha1=b755ebd6a4fe57bb64bd9cc460761ac38d2c1bfd -md5=b270c0b956305570fb9a638ab72fecec -cksum=3980705014 +sha1=4a17b94eea345a2fb1a76106fb4d01ac9aca3569 +md5=ed4a11d13cd6a206b516c84750109602 +cksum=3144404727 upstream_url=https://pypi.io/packages/source/t/tox/tox-VERSION.tar.gz diff --git a/build/pkgs/tox/package-version.txt b/build/pkgs/tox/package-version.txt index 693bd59e3e6..8c53120442c 100644 --- a/build/pkgs/tox/package-version.txt +++ b/build/pkgs/tox/package-version.txt @@ -1 +1 @@ -3.24.3 +3.27.0 diff --git a/build/pkgs/tox/spkg-configure.m4 b/build/pkgs/tox/spkg-configure.m4 index 98918e97397..7d8ade4c14b 100644 --- a/build/pkgs/tox/spkg-configure.m4 +++ b/build/pkgs/tox/spkg-configure.m4 @@ -1,17 +1,28 @@ SAGE_SPKG_CONFIGURE([tox], [ dnl Use non-ancient tox with full support for PEP 517. - m4_pushdef([TOX_MIN_VERSION], [3.21.4]) - AC_CACHE_CHECK([for tox >= ]TOX_MIN_VERSION, [ac_cv_path_TOX], [ + m4_pushdef([TOX3_MIN_VERSION], [3.21.4]) + dnl Early 4.0.x versions have bugs regarding complex factor conditions + m4_pushdef([TOX4_MIN_VERSION], [4.0.15]) + AC_CACHE_CHECK([for tox 3 >= ]TOX3_MIN_VERSION[ or tox 4 >= ]TOX4_MIN_VERSION, [ac_cv_path_TOX], [ AC_PATH_PROGS_FEATURE_CHECK([TOX], [tox], [ tox_version=$($ac_path_TOX --version 2> /dev/null | tail -1) AS_IF([test -n "$tox_version"], [ - AX_COMPARE_VERSION([$tox_version], [ge], TOX_MIN_VERSION, [ - ac_cv_path_TOX="$ac_path_TOX" - ac_path_TOX_found=: + AX_COMPARE_VERSION([$tox_version], [lt], [4], [ + AX_COMPARE_VERSION([$tox_version], [ge], TOX3_MIN_VERSION, [ + ac_cv_path_TOX="$ac_path_TOX" + ac_path_TOX_found=: + ]) + ], [ + AX_COMPARE_VERSION([$tox_version], [ge], TOX4_MIN_VERSION, [ + ac_cv_path_TOX="$ac_path_TOX" + ac_path_TOX_found=: + ]) ]) ]) ]) ]) AS_IF([test -z "$ac_cv_path_TOX"], [sage_spkg_install_tox=yes]) + m4_popdef([TOX4_MIN_VERSION]) + m4_popdef([TOX3_MIN_VERSION]) ]) diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index fd4f1d895ed..4d90280ec63 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,5 +1,5 @@ tarball=traitlets-VERSION.tar.gz -sha1=80e1e820c169295ca66428e1414cf33c36c61386 -md5=ea9b920bd09fa8fcc44325c4d6a6800d -cksum=1563220888 +sha1=cb5c3545ddad62ee6700ea0f279b3d168ac58ed9 +md5=d5f87bbea8acf897ac3e435c7b71acdc +cksum=238091586 upstream_url=https://pypi.io/packages/source/t/traitlets/traitlets-VERSION.tar.gz diff --git a/build/pkgs/traitlets/package-version.txt b/build/pkgs/traitlets/package-version.txt index 03f488b076a..d50359de185 100644 --- a/build/pkgs/traitlets/package-version.txt +++ b/build/pkgs/traitlets/package-version.txt @@ -1 +1 @@ -5.3.0 +5.5.0 diff --git a/build/pkgs/typing_extensions/checksums.ini b/build/pkgs/typing_extensions/checksums.ini index 6a9672ec92f..d6cbf2607c0 100644 --- a/build/pkgs/typing_extensions/checksums.ini +++ b/build/pkgs/typing_extensions/checksums.ini @@ -1,5 +1,5 @@ tarball=typing_extensions-VERSION.tar.gz -sha1=efa40572330e9a3c38ba519028f36d7f93647a39 -md5=9b5b33ae64c94479fa0862cf8ae89d58 -cksum=1588180971 +sha1=9dbf798784009efaef80c8198a75b2a9e519eb95 +md5=5cfcb56ea6fc4972c3600c0030f4d136 +cksum=386983249 upstream_url=https://pypi.io/packages/source/t/typing_extensions/typing_extensions-VERSION.tar.gz diff --git a/build/pkgs/typing_extensions/dependencies b/build/pkgs/typing_extensions/dependencies index 0738c2d7777..f8bd1ee040d 100644 --- a/build/pkgs/typing_extensions/dependencies +++ b/build/pkgs/typing_extensions/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) | flit_core ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/typing_extensions/package-version.txt b/build/pkgs/typing_extensions/package-version.txt index 37c18af77ed..fdc6698807a 100644 --- a/build/pkgs/typing_extensions/package-version.txt +++ b/build/pkgs/typing_extensions/package-version.txt @@ -1 +1 @@ -3.10.0.0 +4.4.0 diff --git a/build/pkgs/tzdata/checksums.ini b/build/pkgs/tzdata/checksums.ini index 0f4b1dacf0e..86bf2db8de4 100644 --- a/build/pkgs/tzdata/checksums.ini +++ b/build/pkgs/tzdata/checksums.ini @@ -1,5 +1,5 @@ tarball=tzdata-VERSION.tar.gz -sha1=b63835812a08e453376558df28e05bf30168e4c6 -md5=b458f1ceb9371d9d3051de2fd2c45bb8 -cksum=3348191929 +sha1=e244bf1bde63515d3f8a452d3bbe9f97738e1e70 +md5=2ad4652fecc6ef4f6794726d3f367363 +cksum=2894139369 upstream_url=https://pypi.io/packages/source/t/tzdata/tzdata-VERSION.tar.gz diff --git a/build/pkgs/tzdata/package-version.txt b/build/pkgs/tzdata/package-version.txt index 7eaecee1e5c..abd454cf321 100644 --- a/build/pkgs/tzdata/package-version.txt +++ b/build/pkgs/tzdata/package-version.txt @@ -1 +1 @@ -2022.1 +2022.6 diff --git a/build/pkgs/urllib3/checksums.ini b/build/pkgs/urllib3/checksums.ini index 102eb61180b..bfb7c5afa48 100644 --- a/build/pkgs/urllib3/checksums.ini +++ b/build/pkgs/urllib3/checksums.ini @@ -1,5 +1,5 @@ tarball=urllib3-VERSION.tar.gz -sha1=f7fc28c04042d141272d9aa24ca9506b9e59f870 -md5=d4b58522821a33c5e421191b83e0dbac -cksum=1538549000 +sha1=ad6bd811a3f4c3e04d86c2706c9994c3e2236e53 +md5=ba308b52b9092184cf4905bc59a88fc0 +cksum=2776794349 upstream_url=https://pypi.io/packages/source/u/urllib3/urllib3-VERSION.tar.gz diff --git a/build/pkgs/urllib3/package-version.txt b/build/pkgs/urllib3/package-version.txt index 3cd33cde0f7..b74a856da82 100644 --- a/build/pkgs/urllib3/package-version.txt +++ b/build/pkgs/urllib3/package-version.txt @@ -1 +1 @@ -1.26.9 +1.26.12 diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini index 5e1728927f4..e324ed781ee 100644 --- a/build/pkgs/virtualenv/checksums.ini +++ b/build/pkgs/virtualenv/checksums.ini @@ -1,5 +1,5 @@ tarball=virtualenv-VERSION.tar.gz -sha1=3c00a243652c2bdbf700828b2bca7733941e1453 -md5=1f7ddaff06a58ee4508bed1f03a5ce1c -cksum=2270354493 +sha1=8371dccb9866b40c3fdc5c0aa9c8f034cc0b174b +md5=b2d60f3c431f370b5fed5169b94f4798 +cksum=3124829245 upstream_url=https://pypi.io/packages/source/v/virtualenv/virtualenv-VERSION.tar.gz diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt index 418ef16ce2b..d1df974a56b 100644 --- a/build/pkgs/virtualenv/package-version.txt +++ b/build/pkgs/virtualenv/package-version.txt @@ -1 +1 @@ -20.14.1 +20.16.6 diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini index 5872bf109ab..b01f275c477 100644 --- a/build/pkgs/wheel/checksums.ini +++ b/build/pkgs/wheel/checksums.ini @@ -1,5 +1,5 @@ tarball=wheel-VERSION.tar.gz -sha1=f9f3d980579b88baaacdb6c4a3cc4466b9353030 -md5=f490f1399e5903706cb1d4fbed9ecb28 -cksum=984523375 +sha1=ff9a5efeabf8e73e8b1a8646eaa829154f834726 +md5=83bb4e7bd4d687d398733f341a64ab91 +cksum=518943238 upstream_url=https://pypi.io/packages/source/w/wheel/wheel-VERSION.tar.gz diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt index 9b1bb851239..87f0b954e35 100644 --- a/build/pkgs/wheel/package-version.txt +++ b/build/pkgs/wheel/package-version.txt @@ -1 +1 @@ -0.37.1 +0.38.4 diff --git a/build/pkgs/widgetsnbextension/checksums.ini b/build/pkgs/widgetsnbextension/checksums.ini index 520bd3a6401..2b335f09484 100644 --- a/build/pkgs/widgetsnbextension/checksums.ini +++ b/build/pkgs/widgetsnbextension/checksums.ini @@ -1,5 +1,5 @@ tarball=widgetsnbextension-VERSION.tar.gz -sha1=0dddc23af2ec56d3a666bba070da1518312d1163 -md5=86dd0471c4e376d981d7633224aa0607 -cksum=3308939542 +sha1=21f8dc7732adad09921bf590fa0eb0d4c3dd7f48 +md5=2d44896382b3a587823e08df6d2f3165 +cksum=209268639 upstream_url=https://pypi.io/packages/source/w/widgetsnbextension/widgetsnbextension-VERSION.tar.gz diff --git a/build/pkgs/widgetsnbextension/dependencies b/build/pkgs/widgetsnbextension/dependencies index dd63a9f4ef1..f7ff1dca568 100644 --- a/build/pkgs/widgetsnbextension/dependencies +++ b/build/pkgs/widgetsnbextension/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) jupyter_core +$(PYTHON) jupyter_packaging | $(PYTHON_TOOLCHAIN) jupyter_core ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/widgetsnbextension/package-version.txt b/build/pkgs/widgetsnbextension/package-version.txt index 9575d51bad2..c4e41f94594 100644 --- a/build/pkgs/widgetsnbextension/package-version.txt +++ b/build/pkgs/widgetsnbextension/package-version.txt @@ -1 +1 @@ -3.6.1 +4.0.3 diff --git a/build/pkgs/widgetsnbextension/patches/no-notebook-dep.patch b/build/pkgs/widgetsnbextension/patches/no-notebook-dep.patch deleted file mode 100644 index 976091637f5..00000000000 --- a/build/pkgs/widgetsnbextension/patches/no-notebook-dep.patch +++ /dev/null @@ -1,24 +0,0 @@ -commit 55bc3c93bf4310d4f4d9d02f2e51d1d65b7f6533 (HEAD -> 7.6.3-sage) -Author: Sylvain Corlay <scorlay@bloomberg.net> -Date: Mon Oct 21 01:33:23 2019 +0200 - - Drop notebook dependency from widgetsnbextension - -diff --git a/widgetsnbextension/setup.py b/widgetsnbextension/setup.py -index 866d82eb..88746f95 100644 ---- a/setup.py -+++ b/setup.py -@@ -219,13 +219,5 @@ if 'setuptools' in sys.modules: - from setuptools.command.develop import develop - setup_args['cmdclass']['develop'] = js_prerelease(develop, strict=True) - --setuptools_args = {} --install_requires = setuptools_args['install_requires'] = [ -- 'notebook>=4.4.1', --] -- --if 'setuptools' in sys.modules: -- setup_args.update(setuptools_args) -- - if __name__ == '__main__': - setup(**setup_args) diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini index 3c82ab5cc17..53d559563f8 100644 --- a/build/pkgs/zipp/checksums.ini +++ b/build/pkgs/zipp/checksums.ini @@ -1,5 +1,5 @@ tarball=zipp-VERSION.tar.gz -sha1=52f43da4467b178ac97ca78a2487ecbc94b4a9b2 -md5=8864ff5ed01cd28755cc87f1443dbc67 -cksum=3776620702 +sha1=3f6c57b68f3b9165586ea7cce96fc2540b0078ec +md5=1fbff3bca7294a3a7f09fa3f0652c3da +cksum=1128680850 upstream_url=https://pypi.io/packages/source/z/zipp/zipp-VERSION.tar.gz diff --git a/build/pkgs/zipp/package-version.txt b/build/pkgs/zipp/package-version.txt index 19811903a7f..afad818663d 100644 --- a/build/pkgs/zipp/package-version.txt +++ b/build/pkgs/zipp/package-version.txt @@ -1 +1 @@ -3.8.0 +3.11.0 diff --git a/build/pkgs/zn_poly/SPKG.rst b/build/pkgs/zn_poly/SPKG.rst deleted file mode 100644 index b04ca2b274d..00000000000 --- a/build/pkgs/zn_poly/SPKG.rst +++ /dev/null @@ -1,96 +0,0 @@ -zn_poly: C library for polynomial arithmetic in Z/nZ[x] -======================================================= - -Description ------------ - -zn_poly is a C library for polynomial arithmetic in Z/nZ[x], where n is -any modulus that fits into an unsigned long. - -Website: https://gitlab.com/sagemath/zn_poly - -Note: Original website is at https://web.maths.unsw.edu.au/~davidharvey/code/zn_poly/ but is -no longer maintained. Sage maintains an "official" continuation of the -project at the above link. - -License -------- - -GPL V2 or V3. Some of the code has been copied from other projects - see -the file src/COPYING for details. - - -Upstream Contact ----------------- - -- David Harvey -- \E. M. Bray <erik.m.bray@gmail.com> - -Dependencies ------------- - -- GMP/MPIR -- (some) Python (to create the Makefile) -- GNU patch -- NTL apparently only if we configured zn_poly differently (same for - FLINT) - - -Special Update/Build Instructions ---------------------------------- - -- Make sure the patches still apply. - - Especially changes in ``makemakefile.py`` may also require changes to - ``spkg-install`` (and perhaps also ``spkg-check``). - -- There's also a ``--use-flint`` option to ``configure``; no idea what - it does, - and we currently don't use it either. - -- TODO: -- Use ``make install`` instead of manually "installing" (copying and - symlinking) the [shared] libraries and header files. This requires - further - tweaking of ``makemakefile.py``, since it currently only installs a - static - library and the headers. - -- If everything's fine, i.e., no problems arise, some comments and - especially some code I currently just commented out can certainly be removed. - (-leif, 04/2012) - -- The version number "0.9.p11" is used as a doctest in the function - package_versions in sage/misc/packages.py, so if this package gets - upgraded, that doctest needs to be changed. - -Patches -~~~~~~~ - -- All patches from Sage have been merged into upstream. These include: -- makemakefile.py.patch: - - Improves the Python script creating the Makeefile for better use at - least within Sage; see patch for details. (Last modified at #12433, - which added and changed a lot.) - -- profiler.c.patch, zn_poly.h.patch: - - Fix potential redefinition of ``ulong`` (in combination with other - headers). - -- mpn_mulmid-tune.c.patch, mulmid-tune.c.patch, mul-tune.c.patch: - - Fix "jump into scope of identifier with variably modified type" - errors. (See #8771). - -- mpn_mulmid-test.c.patch: - - Fix a potential problem when the value of ZNP_mpn_smp_kara_thresh is - SIZE_MAX, this is usually irrealistic but can happen at least on - linux on power7 with gcc-4.7.1 (see #14098). - -- fix_fudge_factor_in_nuss-test.c.patch: - - As the name says; fix provided by upstream (David Harvey); see - #13947. diff --git a/build/pkgs/zn_poly/checksums.ini b/build/pkgs/zn_poly/checksums.ini deleted file mode 100644 index 25cdb79c3b3..00000000000 --- a/build/pkgs/zn_poly/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=zn_poly-VERSION.tar.gz -sha1=ac13121e8ba03f12edd22b0a2e0ae3b3a79fc26e -md5=81f09badfe4f941159df70805681d9eb -cksum=2478176718 diff --git a/build/pkgs/zn_poly/distros/arch.txt b/build/pkgs/zn_poly/distros/arch.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/arch.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/conda.txt b/build/pkgs/zn_poly/distros/conda.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/conda.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/debian.txt b/build/pkgs/zn_poly/distros/debian.txt deleted file mode 100644 index f155e4856dd..00000000000 --- a/build/pkgs/zn_poly/distros/debian.txt +++ /dev/null @@ -1 +0,0 @@ -libzn-poly-dev diff --git a/build/pkgs/zn_poly/distros/fedora.txt b/build/pkgs/zn_poly/distros/fedora.txt deleted file mode 100644 index 631dd81dfe1..00000000000 --- a/build/pkgs/zn_poly/distros/fedora.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly zn_poly-devel diff --git a/build/pkgs/zn_poly/distros/freebsd.txt b/build/pkgs/zn_poly/distros/freebsd.txt deleted file mode 100644 index 8231d562435..00000000000 --- a/build/pkgs/zn_poly/distros/freebsd.txt +++ /dev/null @@ -1 +0,0 @@ -math/zn_poly diff --git a/build/pkgs/zn_poly/distros/nix.txt b/build/pkgs/zn_poly/distros/nix.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/nix.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/opensuse.txt b/build/pkgs/zn_poly/distros/opensuse.txt deleted file mode 100644 index 4bbe6043915..00000000000 --- a/build/pkgs/zn_poly/distros/opensuse.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly-devel diff --git a/build/pkgs/zn_poly/distros/repology.txt b/build/pkgs/zn_poly/distros/repology.txt deleted file mode 100644 index 597d1f28efa..00000000000 --- a/build/pkgs/zn_poly/distros/repology.txt +++ /dev/null @@ -1,2 +0,0 @@ -zn-poly -libzn-poly diff --git a/build/pkgs/zn_poly/distros/void.txt b/build/pkgs/zn_poly/distros/void.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/void.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/package-version.txt b/build/pkgs/zn_poly/package-version.txt deleted file mode 100644 index 2003b639c40..00000000000 --- a/build/pkgs/zn_poly/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.9.2 diff --git a/build/pkgs/zn_poly/spkg-check.in b/build/pkgs/zn_poly/spkg-check.in deleted file mode 100644 index c7831aed276..00000000000 --- a/build/pkgs/zn_poly/spkg-check.in +++ /dev/null @@ -1,90 +0,0 @@ -############################################################################### -# -# zn_poly Sage check script -# -############################################################################### - -############################################################################### -# Set up environment variables: -############################################################################### - -CFLAGS="$CFLAGS_O3 -fPIC" -CXXFLAGS="$CXXFLAGS_O3 -fPIC" - -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs with -# '-O3', '-O2' and '-O1' (cf. #12765, #12751, and the bug URL below.) -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ]; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.*) - CFLAGS="$CFLAGS -O0 -finline-functions -fschedule-insns" - CXXFLAGS="$CXXFLAGS -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in (at least)" - echo >&2 " GCC 4.7.0 on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - echo >&2 " (And please report to e.g. sage-devel in case you feel this bug" - echo >&2 " should already be fixed in GCC $gcc_version.)" - esac -fi - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. - -# Actually, these flags have been written to the Makefile during 'configure'. -# Only CC, CPP and CXX settings from the environment currently override the -# ones in the Makefile (which was generated from a patched 'makemakefile.py'). -# -leif 04/2012 - -case "$UNAME" in - SunOS) - if ! (ld --version 2>&1 | grep GNU >/dev/null); then - # Assume it's the Sun linker; change '-soname' to '-h': - # The following is only supported by the Makefile generated by our - # patched 'makemakefile.py': - export SONAME_FLAG=-h - fi;; - *) unset SONAME_FLAG # Leave the Makefile default; for safety. -esac - -unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. - -############################################################################### -# Build the 'test' program (if it's not already built) and run it: -############################################################################### - -cd src/ - -# The methods for testing zn_poly are more complex than those of most other -# packages. A 'make check' does some quick tests. These are run from -# 'spkg-install' to check zn_poly is not obviously disfunctional. -# -# However, a more comprehensive set of tests can be run by first building a -# test program, then running that test program, which we do here. -# -# To quote from the file src/README: -# -# make check -# Runs some quick tests to make sure that everything appears to be working. -# -# make test -# Builds a test program. Running "test/test all" runs the whole zn_poly test -# suite, which tests zn_poly much more thoroughly than "make check". - -echo -echo "Now building zn_poly's extensive test suite (if not already built)..." -# $MAKE test CC="$CC" CXX="$CXX" # See comment above. We don't have to pass these. -$MAKE test # Make sure the test program is built. -if [ $? -ne 0 ] || [ ! -x test/test ]; then - echo >&2 "Error: zn_poly failed to build its 'test' program," - echo >&2 " so zn_poly's extensive test suite cannot be run." - exit 1 -fi - -echo -echo "Now running zn_poly's extensive test suite..." -test/test all # Run the extensive test suite. -if [ $? -ne 0 ]; then - echo >&2 "Error: zn_poly failed to pass its extensive test suite." - exit 1 -fi -echo "zn_poly has passed its extensive test suite." diff --git a/build/pkgs/zn_poly/spkg-configure.m4 b/build/pkgs/zn_poly/spkg-configure.m4 deleted file mode 100644 index c3e47c7621b..00000000000 --- a/build/pkgs/zn_poly/spkg-configure.m4 +++ /dev/null @@ -1,8 +0,0 @@ -SAGE_SPKG_CONFIGURE([zn_poly], [ - SAGE_SPKG_DEPCHECK([gmp], [ - AC_CHECK_HEADER([zn_poly/zn_poly.h], [ - AC_SEARCH_LIBS([zn_mod_init], [zn_poly], [ - ], [sage_spkg_install_zn_poly=yes]) - ], [sage_spkg_install_zn_poly=yes]) - ]) -]) diff --git a/build/pkgs/zn_poly/spkg-install.in b/build/pkgs/zn_poly/spkg-install.in deleted file mode 100644 index 6215a2416e0..00000000000 --- a/build/pkgs/zn_poly/spkg-install.in +++ /dev/null @@ -1,92 +0,0 @@ -############################################################################### -# Set up environment variables: -############################################################################### - -CFLAGS="$CFLAGS_O3 -fPIC" -CXXFLAGS="$CXXFLAGS_O3 -fPIC" - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. - -# (Actually the currently generated Makefile won't use any of the above from the -# environment; instead we have to pass them with special options to 'configure', -# which we do below. Furthermore, CPPFLAGS and CXXFLAGS are only supported by -# our patched 'makemakefile.py', which is called by 'configure'.) - -case "$UNAME" in - SunOS) - if ! (ld --version 2>&1 | grep GNU >/dev/null); then - # Assume it's the Sun linker; change '-soname' to '-h': - # The following is only supported by the Makefile generated by our - # patched 'makemakefile.py': - export SONAME_FLAG=-h - fi;; - *) unset SONAME_FLAG # Leave the Makefile default; for safety. -esac - -unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. - -cd src/ - -############################################################################### -# Configure, tune, build and test zn_poly: -############################################################################### - -echo "Now generating Makefile for zn_poly..." -# Note: The '--cppflags' and '--cxxflags' options are added by our patch to -# 'makemakefile.py', and aren't available with vanilla upstream. -# Moreover, the generated Makefile now takes CC, CXX and CPP from the -# environment, so no need to pass them later explicitly to 'make'. -sage-bootstrap-python makemakefile.py >Makefile \ - --prefix="$SAGE_LOCAL" --cflags="$CFLAGS" --ldflags="$LDFLAGS" \ - --cppflags="$CPPFLAGS" --cxxflags="$CXXFLAGS" \ - --gmp-prefix="$SAGE_LOCAL" || sdh_die "Error generating Makefile." - -echo "Now building zn_poly with its tuning parameters..." -sdh_make - -# Run the brief test suite: -echo -echo "Now building and running zn_poly's quick self-test..." -sdh_make check - -echo -echo "zn_poly's *quick* test suite passed." -if [ "$SAGE_CHECK" != yes ]; then - echo "A more comprehensive test suite can be run if SAGE_CHECK is" - echo "exported to \"yes\", but it takes about 10x as long to run." -fi - -############################################################################### -# Build and manually install the shared library: -############################################################################### - -echo -echo "Now building and installing zn_poly's shared library..." - -case "$UNAME" in -"Darwin") - sdh_make libzn_poly.dylib - sdh_install libzn_poly.dylib "$SAGE_LOCAL/lib" - install_name_tool -id ${SAGE_LOCAL}/lib/libzn_poly.dylib \ - "$SAGE_DESTDIR_LOCAL/lib/libzn_poly.dylib" - ;; -"CYGWIN") - sdh_make libzn_poly.dll.a - sdh_install cygzn_poly.dll "$SAGE_LOCAL/bin" - sdh_install libzn_poly*.dll.a "$SAGE_LOCAL/lib" - ;; -*) - # Linux, SunOS/Solaris, etc.: - sdh_make libzn_poly.so - sdh_install libzn_poly*.so "$SAGE_LOCAL/lib" - ;; -esac - -############################################################################### -# Manually install the header files: -############################################################################### - -echo -echo "Now installing zn_poly's header files..." -sdh_install include/{zn_poly,wide_arith}.h "$SAGE_LOCAL/include/zn_poly" -echo "Finished installing zn_poly." diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index d6094609d88..c9523e8cdc7 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -58,7 +58,7 @@ def list_cls(self, *package_classes, **filters): arb autotools [...] - zn_poly + zlib $ sage -package list --has-file=spkg-configure.m4 :experimental: perl_term_readline_gnu @@ -68,7 +68,7 @@ def list_cls(self, *package_classes, **filters): boost_cropped brial [...] - zn_poly + zlib """ log.debug('Listing packages') pc = PackageClass(*package_classes, **filters) @@ -148,13 +148,22 @@ def update_latest(self, package_name, commit=False): """ Update a package to the latest version. This modifies the Sage sources. """ + pkg = Package(package_name) + dist_name = pkg.distribution_name + if dist_name is None: + log.debug('%s does not have Python distribution info in install-requires.txt' % pkg) + return + if pkg.tarball_pattern.endswith('.whl'): + source = 'wheel' + else: + source = 'pypi' try: - pypi = PyPiVersion(package_name) + pypi = PyPiVersion(dist_name, source=source) except PyPiNotFound: - log.debug('%s is not a pypi package', package_name) + log.debug('%s is not a pypi package', dist_name) return else: - pypi.update(Package(package_name)) + pypi.update(pkg) if commit: self.commit(package_name) @@ -266,12 +275,20 @@ def fix_checksum(self, package_name): def create(self, package_name, version=None, tarball=None, pkg_type=None, upstream_url=None, description=None, license=None, upstream_contact=None, pypi=False, source='normal'): """ - Create a normal package + Create a package + + $ sage --package create foo --version 1.3 --tarball FoO-VERSION.tar.gz --type experimental + + $ sage --package create scikit_spatial --pypi --type optional + + $ sage --package create torch --pypi --source pip --type optional + + $ sage --package create jupyterlab_markup --pypi --source wheel --type optional """ if '-' in package_name: raise ValueError('package names must not contain dashes, use underscore instead') if pypi: - pypi_version = PyPiVersion(package_name) + pypi_version = PyPiVersion(package_name, source=source) if source == 'normal': if not tarball: # Guess the general format of the tarball name. @@ -281,6 +298,14 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre # Use a URL from pypi.io 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) + elif source == 'wheel': + if not tarball: + tarball = pypi_version.tarball.replace(pypi_version.version, 'VERSION') + if not tarball.endswith('-none-any.whl'): + raise ValueError('Only platform-independent wheels can be used for wheel packages, got {0}'.format(tarball)) + if not version: + version = pypi_version.version + upstream_url = 'https://pypi.io/packages/py3/{0:1.1}/{0}/{1}'.format(package_name, tarball) if not description: description = pypi_version.summary if not license: diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 780f3c5a8e3..ca89b4fedac 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -73,13 +73,13 @@ arb autotools [...] - zn_poly + zlib $ sage --package list :standard: | sort arb backports_ssl_match_hostname [...] - zn_poly + zlib """ @@ -312,7 +312,7 @@ def make_parser(): 'package_name', default=None, type=str, help='Package name.') parser_create.add_argument( - '--source', type=str, default='normal', help='Package source (one of normal, script, pip)') + '--source', type=str, default='normal', help='Package source (one of normal, wheel, script, pip)') parser_create.add_argument( '--version', type=str, default=None, help='Package version') parser_create.add_argument( diff --git a/build/sage_bootstrap/creator.py b/build/sage_bootstrap/creator.py index 3779707de1f..f7a6ab203dc 100644 --- a/build/sage_bootstrap/creator.py +++ b/build/sage_bootstrap/creator.py @@ -87,6 +87,8 @@ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): If ``source`` is ``"normal"``, write the files ``spkg-install.in`` and ``install-requires.txt``. + If ``source`` is ``"wheel"``, write the file ``install-requires.txt``. + If ``source`` is ``"pip"``, write the file ``requirements.txt``. """ if pypi_package_name is None: @@ -99,10 +101,13 @@ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): f.write('cd src\nsdh_pip_install .\n') with open(os.path.join(self.path, 'install-requires.txt'), 'w+') as f: f.write('{0}\n'.format(pypi_package_name)) + elif source == 'wheel': + with open(os.path.join(self.path, 'install-requires.txt'), 'w+') as f: + f.write('{0}\n'.format(pypi_package_name)) elif source == 'pip': with open(os.path.join(self.path, 'requirements.txt'), 'w+') as f: f.write('{0}\n'.format(pypi_package_name)) elif source == 'script': pass else: - raise ValueError('package source must be one of normal, script, or pip') + raise ValueError('package source must be one of normal, script, pip, or wheel') diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index ffe790f9c74..15b342223c4 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -46,6 +46,7 @@ def __init__(self, package_name): self._init_checksum() self._init_version() self._init_type() + self._init_install_requires() def __repr__(self): return 'Package {0}'.format(self.name) @@ -229,6 +230,21 @@ def type(self): """ return self.__type + @property + def distribution_name(self): + """ + Return the Python distribution name or ``None`` for non-Python packages + """ + if self.__install_requires is None: + return None + for line in self.__install_requires.split('\n'): + line = line.strip() + if line.startswith('#'): + continue + for part in line.split(): + return part + return None + def __eq__(self, other): return self.tarball == other.tarball @@ -312,3 +328,10 @@ def _init_type(self): 'base', 'standard', 'optional', 'experimental' ] self.__type = package_type + + def _init_install_requires(self): + try: + with open(os.path.join(self.path, 'install-requires.txt')) as f: + self.__install_requires = f.read().strip() + except IOError: + self.__install_requires = None diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py index 24b050045f9..a11f3a95b46 100644 --- a/build/sage_bootstrap/pypi.py +++ b/build/sage_bootstrap/pypi.py @@ -34,11 +34,15 @@ class PyPiError(Exception): class PyPiVersion(object): - def __init__(self, package_name): + def __init__(self, package_name, source='normal'): self.name = package_name self.json = self._get_json() # Replace provided name with the canonical name self.name = self.json['info']['name'] + if source == 'wheel': + self.python_version = 'py3' + else: + self.python_version = 'source' def _get_json(self): response = urllib.urlopen(self.json_url) @@ -65,9 +69,9 @@ def url(self): Return the source url """ for download in self.json['urls']: - if download['python_version'] == 'source': + if download['python_version'] == self.python_version: return download['url'] - raise PyPiError('No source url for %s found', self.name) + raise PyPiError('No %s url for %s found', self.python_version, self.name) @property def tarball(self): @@ -75,9 +79,9 @@ def tarball(self): Return the source tarball name """ for download in self.json['urls']: - if download['python_version'] == 'source': + if download['python_version'] == self.python_version: return download['filename'] - raise PyPiError('No source url for %s found', self.name) + raise PyPiError('No %s url for %s found', self.python_version, self.name) @property def package_url(self): diff --git a/build/sage_bootstrap/uncompress/filter_os_files.py b/build/sage_bootstrap/uncompress/filter_os_files.py index db05e4303ae..f6d99ee3280 100644 --- a/build/sage_bootstrap/uncompress/filter_os_files.py +++ b/build/sage_bootstrap/uncompress/filter_os_files.py @@ -1,19 +1,16 @@ """ Filtering out OS-specific files """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2016 Volker Braun <vbraun.name@gmail.com> # # 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 os -import sys def filter_os_files(filenames): @@ -23,7 +20,6 @@ def filter_os_files(filenames): Currently removes OSX .DS_Store files and AppleDouble format ._ files. """ - files_set = set(filenames) def is_os_file(path): @@ -45,10 +41,4 @@ def is_os_file(path): return False - filenames = filter(lambda f: not is_os_file(f), filenames) - - if sys.version_info[0] == 2: - return filenames - else: - # Make sure to return a list on Python >= 3 - return list(filenames) + return [f for f in filenames if not is_os_file(f)] diff --git a/configure.ac b/configure.ac index 10351b8c416..0edf830c05d 100644 --- a/configure.ac +++ b/configure.ac @@ -132,6 +132,12 @@ AC_ARG_ENABLE([editable], [AC_SUBST([SAGE_EDITABLE], [$enableval])], [AC_SUBST([SAGE_EDITABLE], [yes])]) +AC_ARG_ENABLE([wheels], + [AS_HELP_STRING([--enable-wheels], + [build wheels for the Sage library and update them on "sage -b"; if disabled, use "make wheels" to build wheels])], + [AC_SUBST([SAGE_WHEELS], [$enableval])], + []) + # Check whether we are on a supported platform AC_CANONICAL_BUILD() AC_CANONICAL_HOST() @@ -435,8 +441,9 @@ AC_ARG_ENABLE([experimental-packages], [AS_HELP_STRING([--enable-experimental-packages], [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) AC_ARG_ENABLE([download-from-upstream-url], - [AS_HELP_STRING([--enable-download-from-upstream-url], - [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) + [AS_HELP_STRING([--disable-download-from-upstream-url], + [disallow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])], [], + [AS_VAR_SET([enable_download_from_upstream_url], [yes])]) SAGE_SPKG_OPTIONS="" AS_IF([test "x$enable_experimental_packages" = "xyes"], [ diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sage-docbuild/setup.cfg b/pkgs/sage-docbuild/setup.cfg index abc71ffcd00..337548c6ff3 100644 --- a/pkgs/sage-docbuild/setup.cfg +++ b/pkgs/sage-docbuild/setup.cfg @@ -20,6 +20,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics diff --git a/pkgs/sage-docbuild/tox.ini b/pkgs/sage-docbuild/tox.ini index 826484699be..77da5a78ede 100644 --- a/pkgs/sage-docbuild/tox.ini +++ b/pkgs/sage-docbuild/tox.ini @@ -15,7 +15,7 @@ setenv = # Sage scripts like to use $HOME/.sage HOME={envdir} -whitelist_externals = +allowlist_externals = bash commands = diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sage-setup/setup.cfg b/pkgs/sage-setup/setup.cfg index 05f152ef60b..5d7f440e7c6 100644 --- a/pkgs/sage-setup/setup.cfg +++ b/pkgs/sage-setup/setup.cfg @@ -20,6 +20,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics @@ -31,7 +32,7 @@ packages = sage_setup.autogen.interpreters.specs sage_setup.command -python_requires = >=3.8, <3.11 +python_requires = >=3.8, <3.12 install_requires = pkgconfig diff --git a/pkgs/sage-setup/tox.ini b/pkgs/sage-setup/tox.ini index 84cf5dab3e0..fd935f2e978 100644 --- a/pkgs/sage-setup/tox.ini +++ b/pkgs/sage-setup/tox.ini @@ -19,7 +19,7 @@ setenv = # Sage scripts such as sage-runtests like to use $HOME/.sage HOME={envdir} -whitelist_externals = +allowlist_externals = bash commands = diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sage-sws2rst/tox.ini b/pkgs/sage-sws2rst/tox.ini index b268eb03b59..c30ce18af4c 100644 --- a/pkgs/sage-sws2rst/tox.ini +++ b/pkgs/sage-sws2rst/tox.ini @@ -11,7 +11,7 @@ [testenv] deps = -rrequirements.txt -whitelist_externals = +allowlist_externals = ./check.sh commands = diff --git a/pkgs/sagemath-categories/MANIFEST.in.m4 b/pkgs/sagemath-categories/MANIFEST.in.m4 index 3a9b1d9bff1..98d10a91fef 100644 --- a/pkgs/sagemath-categories/MANIFEST.in.m4 +++ b/pkgs/sagemath-categories/MANIFEST.in.m4 @@ -1,13 +1,37 @@ dnl MANIFEST.in is generated from this file by SAGE_ROOT/bootstrap via m4. +prune sage -dnl Include all from sagemath-objects (via m4 include) -include(`../sagemath_objects/src/MANIFEST.in') +include VERSION.txt -# Extra in sagemath-categories: global-include all__sagemath_categories.py graft sage/categories +# Exclude what is already shipped in sagemath-objects +exclude sage/categories/action.* +exclude sage/categories/algebra_functor.* +exclude sage/categories/basic.* +exclude sage/categories/cartesian_product.* +exclude sage/categories/category*.* +exclude sage/categories/covariant_functorial_construction.* +exclude sage/categories/facade_sets.* +exclude sage/categories/functor.* +exclude sage/categories/homset.* +exclude sage/categories/homsets.* +exclude sage/categories/map.* +exclude sage/categories/morphism.* +exclude sage/categories/isomorphic_objects.* +exclude sage/categories/objects.* +exclude sage/categories/primer.* +exclude sage/categories/pushout.* +exclude sage/categories/quotients.* +exclude sage/categories/realizations.* +exclude sage/categories/sets_cat.* +exclude sage/categories/sets_with_partial_maps.* +exclude sage/categories/subobjects.* +exclude sage/categories/subquotients.* +exclude sage/categories/with_realizations.* +# Exclude to make it a namespace package exclude sage/categories/__init__.py -include sage/misc/prandom.* # dep of sage/rings/ring + include sage/rings/ideal.* include sage/rings/ring.* graft sage/typeset # dep of sage.categories.tensor @@ -17,10 +41,12 @@ graft sage/typeset # dep of sage.categories.tensor global-exclude *.c global-exclude *.cpp -include sage/cpython/debugimpl.c -include sage/misc/inherit_comparison_impl.c global-exclude __pycache__ global-exclude *.py[co] global-exclude *.bak global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-categories/README.rst b/pkgs/sagemath-categories/README.rst index 81311e5a217..d1f90fea966 100644 --- a/pkgs/sagemath-categories/README.rst +++ b/pkgs/sagemath-categories/README.rst @@ -12,15 +12,27 @@ About SageMath https://www.sagemath.org -SageMath fully supports all major Linux distributions, recent versions of macOS, and Windows (using Cygwin or Windows Subsystem for Linux). +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or 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). +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). About this experimental pip-installable source distribution ----------------------------------------------------------- -This pip-installable source distribution `sagemath-categories` is an experimental distribution of a small part of the Sage Library. Use at your own risk. It provides a small subset of the modules of the Sage library ("sagelib", `sagemath-standard`). It is a superset of the `sagemath-objects` (providing Sage objects, the element/parent framework, categories, the coercion system and the related metaclasses), making various additional categories available without introducing dependencies on additional mathematical libraries. +This pip-installable source distribution `sagemath-categories` is an +experimental distribution of a small part of the Sage Library. Use at your own +risk. It provides a small subset of the modules of the Sage library +("sagelib", `sagemath-standard`). It is a superset of the `sagemath-objects` +(providing Sage objects, the element/parent framework, categories, the coercion +system and the related metaclasses), making various additional categories +available without introducing dependencies on additional mathematical +libraries. Dependencies diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sagemath-categories/bin b/pkgs/sagemath-categories/bin deleted file mode 120000 index 1956438021b..00000000000 --- a/pkgs/sagemath-categories/bin +++ /dev/null @@ -1 +0,0 @@ -../sagemath-objects/bin \ No newline at end of file diff --git a/pkgs/sagemath-categories/pyproject.toml.m4 b/pkgs/sagemath-categories/pyproject.toml.m4 deleted file mode 120000 index b65c0df8a46..00000000000 --- a/pkgs/sagemath-categories/pyproject.toml.m4 +++ /dev/null @@ -1 +0,0 @@ -../sagemath-objects/pyproject.toml.m4 \ No newline at end of file diff --git a/pkgs/sagemath-categories/pyproject.toml.m4 b/pkgs/sagemath-categories/pyproject.toml.m4 new file mode 100644 index 00000000000..0afd7849de5 --- /dev/null +++ b/pkgs/sagemath-categories/pyproject.toml.m4 @@ -0,0 +1,14 @@ +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + esyscmd(`sage-get-system-packages install-requires-toml \ + setuptools \ + wheel \ + sage_setup \ + sagemath_environment \ + sagemath_objects \ + cython \ + gmpy2 \ + cysignals \ + ')] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-categories/sage_setup b/pkgs/sagemath-categories/sage_setup deleted file mode 120000 index 88b8133df49..00000000000 --- a/pkgs/sagemath-categories/sage_setup +++ /dev/null @@ -1 +0,0 @@ -../../src/sage_setup \ No newline at end of file diff --git a/pkgs/sagemath-categories/setup.cfg.m4 b/pkgs/sagemath-categories/setup.cfg.m4 index 4ba67f86fdb..62aca5af44f 100644 --- a/pkgs/sagemath-categories/setup.cfg.m4 +++ b/pkgs/sagemath-categories/setup.cfg.m4 @@ -21,28 +21,16 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.8, <3.11 +python_requires = >=3.8, <3.12 install_requires = esyscmd(`sage-get-system-packages install-requires \ - cython \ - pkgconfig \ - ipython \ - gmpy2 \ - cysignals \ + sagemath_objects \ | sed "2,\$s/^/ /;"')dnl -scripts = - bin/sage - bin/sage-env - bin/sage-eval - bin/sage-fixdoctests - bin/sage-ipython - bin/sage-python - bin/sage-run - bin/sage-runtests - bin/sage-venv-config - bin/sage-version.sh +[options.extras_require] +test = sagemath-repl diff --git a/pkgs/sagemath-categories/tox.ini b/pkgs/sagemath-categories/tox.ini index 7ac63bae0ec..a240b091e30 100644 --- a/pkgs/sagemath-categories/tox.ini +++ b/pkgs/sagemath-categories/tox.ini @@ -13,19 +13,33 @@ envlist = [testenv] deps = !norequirements: -rrequirements.txt + # tox 3.x does not handle extras when using --installpkg. https://github.com/tox-dev/tox/issues/1576 + sagemath-repl -setenv = - # Sage scripts such as sage-runtests like to use $HOME/.sage - HOME={envdir} +extras = test passenv = + # Variables set by .homebrew-build-env + CPATH + LIBRARY_PATH + PKG_CONFIG_PATH # Parallel build SAGE_NUM_THREADS SAGE_NUM_THREADS_PARALLEL - # SAGE_VENV only for referring to the basepython - sagepython: SAGE_VENV + # SAGE_VENV only for referring to the basepython or finding the wheels + sagepython, sagewheels: SAGE_VENV + # Location of the wheels + sagewheels: SAGE_SPKG_WHEELS + +setenv = + # Sage scripts such as sage-runtests like to use $HOME/.sage + HOME={envdir} + # We supply pip options by environment variables so that they + # 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 -whitelist_externals = +allowlist_externals = bash commands = @@ -35,10 +49,19 @@ commands = # Test that importing sage.categories.all initializes categories {envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); from sage.categories.all import *; SimplicialComplexes(); FunctionFields()' - bash -c 'cd bin && SAGE_SRC=$(python -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --environment=sage.all__sagemath_categories --optional=sage $SAGE_SRC/sage/structure || echo "(lots of doctest failures are expected)"' + bash -c 'cd {temp_dir} && SAGE_SRC=$(python -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --initial --environment=sage.all__sagemath_categories --optional=sage $SAGE_SRC/sage/structure || echo "(lots of doctest failures are expected)"' [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 +[testenv:sagepython-sagewheels-nopypi] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels] +basepython = {env:SAGE_VENV}/bin/python3 + [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 diff --git a/pkgs/sagemath-environment/README.rst b/pkgs/sagemath-environment/README.rst index f5c52660ca8..ba5905777c0 100644 --- a/pkgs/sagemath-environment/README.rst +++ b/pkgs/sagemath-environment/README.rst @@ -8,16 +8,27 @@ 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-2022 The Sage Development Team https://www.sagemath.org -SageMath fully supports all major Linux distributions, recent versions of macOS, and Windows (using Cygwin or Windows Subsystem for Linux). +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or 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). +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). About this experimental pip-installable source distribution ----------------------------------------------------------- -This pip-installable source distribution `sagemath-environment` is an experimental distribution of a small part of the Sage Library. Use at your own risk. It provides a small, fundamental subset of the modules of the Sage library ("sagelib", `sagemath-standard`), providing the connection to the system and software environment. +This pip-installable source distribution `sagemath-environment` is an +experimental distribution of a small part of the Sage Library. Use at your own +risk. It provides a small, fundamental subset of the modules of the Sage +library ("sagelib", `sagemath-standard`), providing the connection to the +system and software environment. It also includes the `sage` script for +launching the Sage REPL and accessing various developer tools (see `sage +--help`). diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sagemath-environment/setup.cfg.m4 b/pkgs/sagemath-environment/setup.cfg.m4 index c67043cb715..919a5b576cd 100644 --- a/pkgs/sagemath-environment/setup.cfg.m4 +++ b/pkgs/sagemath-environment/setup.cfg.m4 @@ -18,14 +18,15 @@ classifiers = Operating System :: POSIX Operating System :: MacOS :: MacOS X Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.7, <3.11 +python_requires = >=3.8, <3.12 install_requires = esyscmd(`sage-get-system-packages install-requires \ | sed "2,\$s/^/ /;"')dnl diff --git a/pkgs/sagemath-environment/tox.ini b/pkgs/sagemath-environment/tox.ini index 890ffdf0015..46077bd8e27 100644 --- a/pkgs/sagemath-environment/tox.ini +++ b/pkgs/sagemath-environment/tox.ini @@ -16,18 +16,24 @@ isolated_build = True deps = !norequirements: -rrequirements.txt -setenv = - # Sage scripts such as sage-runtests like to use $HOME/.sage - HOME={envdir} - passenv = # Parallel build SAGE_NUM_THREADS SAGE_NUM_THREADS_PARALLEL - # SAGE_VENV only for referring to the basepython - sagepython: SAGE_VENV + # SAGE_VENV only for referring to the basepython or finding the wheels + sagepython, sagewheels: SAGE_VENV + # Location of the wheels + sagewheels: SAGE_SPKG_WHEELS + +setenv = + # Sage scripts such as sage-runtests like to use $HOME/.sage + HOME={envdir} + # We supply pip options by environment variables so that they + # 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 -whitelist_externals = +allowlist_externals = bash commands = @@ -37,5 +43,14 @@ commands = [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 +[testenv:sagepython-sagewheels-nopypi] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels] +basepython = {env:SAGE_VENV}/bin/python3 + [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 diff --git a/pkgs/sagemath-objects/MANIFEST.in b/pkgs/sagemath-objects/MANIFEST.in index d112d9b5c16..eced95ea865 100644 --- a/pkgs/sagemath-objects/MANIFEST.in +++ b/pkgs/sagemath-objects/MANIFEST.in @@ -1,15 +1,10 @@ prune sage graft sage/cpython -graft sage_setup # FIXME: Vendor it until we have made a new sage_setup release -include sage/env.py # FIXME: sage_setup must be changed so it does not depend on it -include sage/version.py # FIXME: likewise -include sage/misc/package_dir.p* # For sage_setup include VERSION.txt global-include all__sagemath_objects.py -graft sage/features graft sage/structure include sage/categories/action.* include sage/categories/algebra_functor.* @@ -17,6 +12,7 @@ include sage/categories/basic.* include sage/categories/cartesian_product.* include sage/categories/category*.* include sage/categories/covariant_functorial_construction.* +include sage/categories/facade_sets.* include sage/categories/functor.* include sage/categories/homset.* include sage/categories/homsets.* @@ -67,6 +63,9 @@ include sage/misc/instancedoc.* # dep of sage/misc/lazy_import include sage/misc/persist.* include sage/misc/sage_unittest.* # dep of sage/misc/persist +include sage/misc/randstate.* # used in sage.doctest +include sage/misc/prandom.* # dep of sage/rings/ring + include sage/ext/stdsage.pxd include sage/sets/pythonclass.* include sage/arith/power.* @@ -82,28 +81,9 @@ graft sage/libs/gmp # sage/misc/latex -- this should really go to another package -## For doctesting -- this duplication will be removed in #28925 -global-include all__sagemath_environment.py -global-include all__sagemath_repl.py -include bin/sage -include bin/sage-env -include bin/sage-env-config -include bin/sage-python -include bin/sage-runtests -graft sage/doctest -include sage/misc/temporary_file.* -include sage/misc/randstate.* -include sage/misc/misc.* # walltime, cputime -# graft sage/features -include sage/misc/package.* -include sage/misc/sagedoc.py -include sage/misc/banner.py -include sage/misc/sage_input.py -include sage/misc/sage_eval.py -include sage/misc/viewer.py - -graft sage/repl -graft sage/server +## FIXME: Needed for doctesting +include sage/misc/misc.* # walltime, cputime used in sage.doctest + global-exclude *.c global-exclude *.cpp @@ -114,3 +94,7 @@ global-exclude __pycache__ global-exclude *.py[co] global-exclude *.bak global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-objects/README.rst b/pkgs/sagemath-objects/README.rst index 8058f633654..9dc9cfd888f 100644 --- a/pkgs/sagemath-objects/README.rst +++ b/pkgs/sagemath-objects/README.rst @@ -12,15 +12,25 @@ About SageMath https://www.sagemath.org -SageMath fully supports all major Linux distributions, recent versions of macOS, and Windows (using Cygwin or Windows Subsystem for Linux). +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or 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). +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). About this experimental pip-installable source distribution ----------------------------------------------------------- -This pip-installable source distribution `sagemath-objects` is an experimental distribution of a small part of the Sage Library. Use at your own risk. It provides a small, fundamental subset of the modules of the Sage library ("sagelib", `sagemath-standard`), making Sage objects, the element/parent framework, categories, the coercion system and the related metaclasses available. +This pip-installable source distribution `sagemath-objects` is an experimental +distribution of a small part of the Sage Library. Use at your own risk. It +provides a small, fundamental subset of the modules of the Sage library +("sagelib", `sagemath-standard`), making Sage objects, the element/parent +framework, categories, the coercion system and the related metaclasses +available. Dependencies diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sagemath-objects/bin/sage b/pkgs/sagemath-objects/bin/sage deleted file mode 120000 index 028392920ce..00000000000 --- a/pkgs/sagemath-objects/bin/sage +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-env b/pkgs/sagemath-objects/bin/sage-env deleted file mode 120000 index e35bf8a4000..00000000000 --- a/pkgs/sagemath-objects/bin/sage-env +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-env \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-eval b/pkgs/sagemath-objects/bin/sage-eval deleted file mode 120000 index ac096cd38f1..00000000000 --- a/pkgs/sagemath-objects/bin/sage-eval +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-eval \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-fixdoctests b/pkgs/sagemath-objects/bin/sage-fixdoctests deleted file mode 120000 index 3d394f77c44..00000000000 --- a/pkgs/sagemath-objects/bin/sage-fixdoctests +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-fixdoctests \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-ipython b/pkgs/sagemath-objects/bin/sage-ipython deleted file mode 120000 index 00b3d2b0c50..00000000000 --- a/pkgs/sagemath-objects/bin/sage-ipython +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-ipython \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-python b/pkgs/sagemath-objects/bin/sage-python deleted file mode 120000 index 26d8bde2c71..00000000000 --- a/pkgs/sagemath-objects/bin/sage-python +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-python \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-run b/pkgs/sagemath-objects/bin/sage-run deleted file mode 120000 index a2bce45f085..00000000000 --- a/pkgs/sagemath-objects/bin/sage-run +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-run \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-runtests b/pkgs/sagemath-objects/bin/sage-runtests deleted file mode 120000 index 8e3dbbaf100..00000000000 --- a/pkgs/sagemath-objects/bin/sage-runtests +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-runtests \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-venv-config b/pkgs/sagemath-objects/bin/sage-venv-config deleted file mode 120000 index d1e0d8ec19b..00000000000 --- a/pkgs/sagemath-objects/bin/sage-venv-config +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-venv-config \ No newline at end of file diff --git a/pkgs/sagemath-objects/bin/sage-version.sh b/pkgs/sagemath-objects/bin/sage-version.sh deleted file mode 120000 index 46cfd0287a5..00000000000 --- a/pkgs/sagemath-objects/bin/sage-version.sh +++ /dev/null @@ -1 +0,0 @@ -../../../src/bin/sage-version.sh \ No newline at end of file diff --git a/pkgs/sagemath-objects/pyproject.toml.m4 b/pkgs/sagemath-objects/pyproject.toml.m4 index 0a0149b9e45..c13665be51d 100644 --- a/pkgs/sagemath-objects/pyproject.toml.m4 +++ b/pkgs/sagemath-objects/pyproject.toml.m4 @@ -5,6 +5,7 @@ requires = [ setuptools \ wheel \ sage_setup \ + sagemath_environment \ cython \ gmpy2 \ cysignals \ diff --git a/pkgs/sagemath-objects/sage_setup b/pkgs/sagemath-objects/sage_setup deleted file mode 120000 index 88b8133df49..00000000000 --- a/pkgs/sagemath-objects/sage_setup +++ /dev/null @@ -1 +0,0 @@ -../../src/sage_setup \ No newline at end of file diff --git a/pkgs/sagemath-objects/setup.cfg.m4 b/pkgs/sagemath-objects/setup.cfg.m4 index 00c65ca9f8a..88246c422f2 100644 --- a/pkgs/sagemath-objects/setup.cfg.m4 +++ b/pkgs/sagemath-objects/setup.cfg.m4 @@ -21,28 +21,30 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.8, <3.11 +python_requires = >=3.8, <3.12 install_requires = esyscmd(`sage-get-system-packages install-requires \ - cython \ - pkgconfig \ - ipython \ gmpy2 \ cysignals \ | sed "2,\$s/^/ /;"')dnl -scripts = - bin/sage - bin/sage-env - bin/sage-eval - bin/sage-fixdoctests - bin/sage-ipython - bin/sage-python - bin/sage-run - bin/sage-runtests - bin/sage-venv-config - bin/sage-version.sh +[options.extras_require] +# Currently we do not use the sage doctester to test sagemath-objects, +# so we do not list sagemath-repl here. +test = + + +[options.package_data] +sage.cpython = + pyx_visit.h + string_impl.h + cython_metaclass.h + python_debug.h + +sage.rings = + integer_fake.h diff --git a/pkgs/sagemath-objects/setup.py b/pkgs/sagemath-objects/setup.py index 907cabef839..ad114fa0de1 100644 --- a/pkgs/sagemath-objects/setup.py +++ b/pkgs/sagemath-objects/setup.py @@ -14,41 +14,28 @@ import sys sys.path.insert(0, os.path.dirname(__file__)) -if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): - sdist = True -else: - sdist = False - -if sdist: - cmdclass = {} -else: - from sage_setup.excepthook import excepthook - sys.excepthook = excepthook - - from sage_setup.setenv import setenv - setenv() - - import sage.env - sage.env.default_required_modules = sage.env.default_optional_modules = () - - from sage_setup.command.sage_build_cython import sage_build_cython - from sage_setup.command.sage_build_ext import sage_build_ext - - cmdclass = dict(build_cython=sage_build_cython, - build_ext=sage_build_ext) - -if sdist: - python_packages = [] - python_modules = [] - cython_modules = [] -else: - from sage_setup.find import find_python_sources - python_packages, python_modules, cython_modules = find_python_sources( - '.', ['sage']) # for now, we do the filtering using MANIFEST - - log.warn('python_packages = {0}'.format(python_packages)) - log.warn('python_modules = {0}'.format(python_modules)) - log.warn('cython_modules = {0}'.format(cython_modules)) +from sage_setup.excepthook import excepthook +sys.excepthook = excepthook + +from sage_setup.setenv import setenv +setenv() + +import sage.env +sage.env.default_required_modules = sage.env.default_optional_modules = () + +from sage_setup.command.sage_build_cython import sage_build_cython +from sage_setup.command.sage_build_ext import sage_build_ext + +cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage']) # for now, we do the filtering using MANIFEST + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) setup( cmdclass = cmdclass, diff --git a/pkgs/sagemath-objects/tox.ini b/pkgs/sagemath-objects/tox.ini index e03cbab1c3c..e34531467e5 100644 --- a/pkgs/sagemath-objects/tox.ini +++ b/pkgs/sagemath-objects/tox.ini @@ -14,18 +14,30 @@ envlist = deps = !norequirements: -rrequirements.txt -setenv = - # Sage scripts such as sage-runtests like to use $HOME/.sage - HOME={envdir} +extras = test passenv = + # Variables set by .homebrew-build-env + CPATH + LIBRARY_PATH + PKG_CONFIG_PATH # Parallel build SAGE_NUM_THREADS SAGE_NUM_THREADS_PARALLEL - # SAGE_VENV only for referring to the basepython - sagepython: SAGE_VENV + # SAGE_VENV only for referring to the basepython or finding the wheels + sagepython, sagewheels: SAGE_VENV + # Location of the wheels + sagewheels: SAGE_SPKG_WHEELS + +setenv = + # Sage scripts such as sage-runtests like to use $HOME/.sage + HOME={envdir} + # We supply pip options by environment variables so that they + # 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 -whitelist_externals = +allowlist_externals = bash commands = @@ -34,10 +46,19 @@ commands = {envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); from sage.all__sagemath_objects import *' - #bash -c 'cd bin && SAGE_SRC=$(python -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --environment=sage.all__sagemath_objects --optional=sage $SAGE_SRC/sage/structure || echo "(lots of doctest failures are expected)"' + #bash -c 'cd {temp_dir} && SAGE_SRC=$(python -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --environment=sage.all__sagemath_objects --initial --optional=sage $SAGE_SRC/sage/structure || echo "(lots of doctest failures are expected)"' [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 +[testenv:sagepython-sagewheels-nopypi] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels] +basepython = {env:SAGE_VENV}/bin/python3 + [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 diff --git a/pkgs/sagemath-repl/MANIFEST.in b/pkgs/sagemath-repl/MANIFEST.in index 9bee69fd999..c54f63b15ee 100644 --- a/pkgs/sagemath-repl/MANIFEST.in +++ b/pkgs/sagemath-repl/MANIFEST.in @@ -11,4 +11,11 @@ include sage/misc/sage_eval.py include VERSION.txt +global-exclude __pycache__ global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-repl/README.rst b/pkgs/sagemath-repl/README.rst index c240fbc1bac..3dde4aae5e5 100644 --- a/pkgs/sagemath-repl/README.rst +++ b/pkgs/sagemath-repl/README.rst @@ -8,16 +8,25 @@ 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-2022 The Sage Development Team https://www.sagemath.org -SageMath fully supports all major Linux distributions, recent versions of macOS, and Windows (using Cygwin or Windows Subsystem for Linux). +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or 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). +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). About this experimental pip-installable source distribution ----------------------------------------------------------- -This pip-installable source distribution `sagemath-repl` is an experimental distribution of a small part of the Sage Library. Use at your own risk. It provides a small, fundamental subset of the modules of the Sage library ("sagelib", `sagemath-standard`), providing the IPython kernel, Sage preparser, and doctester. +This pip-installable source distribution `sagemath-repl` is an experimental +distribution of a small part of the Sage Library. Use at your own risk. It +provides a small, fundamental subset of the modules of the Sage library +("sagelib", `sagemath-standard`), providing the IPython kernel, Sage preparser, +and doctester. diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/pkgs/sagemath-repl/setup.cfg.m4 b/pkgs/sagemath-repl/setup.cfg.m4 index f8757ee66cb..2367fda7b9a 100644 --- a/pkgs/sagemath-repl/setup.cfg.m4 +++ b/pkgs/sagemath-repl/setup.cfg.m4 @@ -18,14 +18,15 @@ classifiers = Operating System :: POSIX Operating System :: MacOS :: MacOS X Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.7, <3.11 +python_requires = >=3.8, <3.12 install_requires = esyscmd(`sage-get-system-packages install-requires \ sagemath_objects \ diff --git a/pkgs/sagemath-repl/tox.ini b/pkgs/sagemath-repl/tox.ini index a7e321104cd..084016ce768 100644 --- a/pkgs/sagemath-repl/tox.ini +++ b/pkgs/sagemath-repl/tox.ini @@ -16,28 +16,47 @@ isolated_build = True deps = !norequirements: -rrequirements.txt -setenv = - # Sage scripts such as sage-runtests like to use $HOME/.sage - HOME={envdir} - passenv = + # Variables set by .homebrew-build-env + CPATH + LIBRARY_PATH + PKG_CONFIG_PATH # Parallel build SAGE_NUM_THREADS SAGE_NUM_THREADS_PARALLEL - # SAGE_VENV only for referring to the basepython - sagepython: SAGE_VENV + # SAGE_VENV only for referring to the basepython or finding the wheels + sagepython, sagewheels: SAGE_VENV + # Location of the wheels + sagewheels: SAGE_SPKG_WHEELS + +setenv = + # Sage scripts such as sage-runtests like to use $HOME/.sage + HOME={envdir} + # We supply pip options by environment variables so that they + # 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 -whitelist_externals = +allowlist_externals = bash commands = # Beware of the treacherous non-src layout. "./sage/" shadows the installed sage package. {envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); import sage.repl.all; import sage.doctest.all' - bash -c 'cd bin && SAGE_SRC=$({envpython} -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --environment=sage.all__sagemath_repl --optional=sage $SAGE_SRC/sage/repl $SAGE_SRC/sage/doctest $SAGE_SRC/sage/misc/sage_input.py $SAGE_SRC/sage/misc/sage_eval.py || echo "(lots of doctest failures are expected)"' + bash -c 'cd bin && SAGE_SRC=$({envpython} -c "from sage.env import SAGE_SRC; print(SAGE_SRC)") && sage-runtests --environment=sage.all__sagemath_repl --initial --optional=sage $SAGE_SRC/sage/repl $SAGE_SRC/sage/doctest $SAGE_SRC/sage/misc/sage_input.py $SAGE_SRC/sage/misc/sage_eval.py || echo "(lots of doctest failures are expected)"' [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 +[testenv:sagepython-sagewheels-nopypi] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 + +[testenv:sagepython-sagewheels] +basepython = {env:SAGE_VENV}/bin/python3 + [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 diff --git a/pkgs/sagemath-standard/setup.py b/pkgs/sagemath-standard/setup.py index b6006188d33..975f89b5905 100755 --- a/pkgs/sagemath-standard/setup.py +++ b/pkgs/sagemath-standard/setup.py @@ -15,6 +15,15 @@ import multiprocessing multiprocessing.set_start_method('fork', force=True) +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + ######################################################### ### Set source directory ######################################################### @@ -30,28 +39,20 @@ ### Configuration ######################################################### -if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): - sdist = True -else: - sdist = False - -if sdist: - cmdclass = {} -else: - from sage_setup.excepthook import excepthook - sys.excepthook = excepthook +from sage_setup.excepthook import excepthook +sys.excepthook = excepthook - from sage_setup.setenv import setenv - setenv() +from sage_setup.setenv import setenv +setenv() - from sage_setup.command.sage_build_cython import sage_build_cython - from sage_setup.command.sage_build_ext import sage_build_ext - from sage_setup.command.sage_install import sage_develop, sage_install_and_clean +from sage_setup.command.sage_build_cython import sage_build_cython +from sage_setup.command.sage_build_ext import sage_build_ext +from sage_setup.command.sage_install import sage_develop, sage_install_and_clean - cmdclass = dict(build_cython=sage_build_cython, - build_ext=sage_build_ext, - develop=sage_develop, - install=sage_install_and_clean) +cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext, + develop=sage_develop, + install=sage_install_and_clean) ######################################################### ### Testing related stuff @@ -66,41 +67,39 @@ ### Discovering Sources ######################################################### -if sdist: - # No need to compute distributions. This avoids a dependency on Cython - # just to make an sdist. - distributions = None - python_packages = [] - python_modules = [] - cython_modules = [] -else: +if any(x in sys.argv + for x in ['build', 'bdist_wheel', 'install']): log.info("Generating auto-generated sources") from sage_setup.autogen import autogen_all autogen_all() - # TODO: This should be quiet by default - print("Discovering Python/Cython source code....") - t = time.time() - from sage.misc.package import is_package_installed_and_updated - distributions = [''] - optional_packages_with_extensions = ['mcqd', 'bliss', 'tdlib', - 'coxeter3', 'fes', 'sirocco', 'meataxe'] - distributions += ['sagemath-{}'.format(pkg) - for pkg in optional_packages_with_extensions - if is_package_installed_and_updated(pkg)] - log.warn('distributions = {0}'.format(distributions)) - from sage_setup.find import find_python_sources - python_packages, python_modules, cython_modules = find_python_sources( - SAGE_SRC, ['sage'], distributions=distributions) - - log.debug('python_packages = {0}'.format(python_packages)) - print("Discovered Python/Cython sources, time: %.2f seconds." % (time.time() - t)) +# TODO: This should be quiet by default +print("Discovering Python/Cython source code....") +t = time.time() +distributions = [''] +from sage.misc.package import is_package_installed_and_updated +optional_packages_with_extensions = ['mcqd', 'bliss', 'tdlib', + 'coxeter3', 'sirocco', 'meataxe'] +distributions += ['sagemath-{}'.format(pkg) + for pkg in optional_packages_with_extensions + if is_package_installed_and_updated(pkg)] +log.warn('distributions = {0}'.format(distributions)) +from sage_setup.find import find_python_sources, find_extra_files +python_packages, python_modules, cython_modules = find_python_sources( + SAGE_SRC, ['sage'], distributions=distributions) + +log.debug('python_packages = {0}'.format(python_packages)) +log.debug('python_modules = {0}'.format(python_modules)) +log.debug('cython_modules = {0}'.format(cython_modules)) + +print("Discovered Python/Cython sources, time: %.2f seconds." % (time.time() - t)) ######################################################### ### Distutils ######################################################### code = setup( - packages = python_packages, - cmdclass = cmdclass, - ext_modules = cython_modules) + packages=python_packages, + cmdclass=cmdclass, + ext_modules=cython_modules, +) diff --git a/pkgs/sagemath-standard/tox.ini b/pkgs/sagemath-standard/tox.ini index 75e72ae32bd..305eddd7586 100644 --- a/pkgs/sagemath-standard/tox.ini +++ b/pkgs/sagemath-standard/tox.ini @@ -84,8 +84,7 @@ passenv = SAGE_NUM_THREADS # SAGE_VENV only for referring to the basepython or finding the wheels sagepython, sagewheels: SAGE_VENV - # Location of the wheels (needs to include a PEP 503 compliant - # Simple Repository index, i.e., a subdirectory "simple") + # Location of the wheels sagewheels: SAGE_SPKG_WHEELS setenv = @@ -108,7 +107,7 @@ sitepackages = sitepackages: True !sitepackages: False -whitelist_externals = +allowlist_externals = bash skip_install = diff --git a/src/.relint.yml b/src/.relint.yml index abadae0de28..2b49c758dc0 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -46,7 +46,9 @@ - name: 'namespace_pkg_all_import: import from .all of a namespace package' hint: | Sage library code should not import from sage.PAC.KAGE.all when sage.PAC.KAGE is an implicit - Hint: namespace package. Type import_statements("SOME_IDENTIFIER") to find a more specific import. + Hint: namespace package. Type import_statements("SOME_IDENTIFIER") to find a more specific import, + Hint: or use 'sage --fiximports' to fix automatically in the source file. + # Keep in sync with SAGE_ROOT/src/sage/misc/replace_dot_all.py pattern: 'from\s+sage(|[.](arith|categories|combinat|ext|graphs(|[.]decompositions)|interfaces|libs|matrix|misc|numerical(|[.]backends)|rings|sets))[.]all\s+import' filePattern: '.*[.](py|pyx|pxi)$' error: false # Make this a warning instead of an error for now diff --git a/src/MANIFEST.in b/src/MANIFEST.in index f63856e03aa..1e7df6e529b 100644 --- a/src/MANIFEST.in +++ b/src/MANIFEST.in @@ -1,11 +1,54 @@ -global-include *.c *.cc *.cpp *.h *.hh *.hpp *.inc *.py *.pyx *.pxd *.pxi *.rst *.txt *.tex +include VERSION.txt -include MANIFEST.in -include pyproject.toml - -prune .tox +global-include *.pxi *.pxd *.h *.hpp prune sage/ext/interpreters # In particular, __init__.py must not be present in the distribution; or sage_setup.autogen.interpreters.rebuild will not generate the code prune sage_setup prune sage_docbuild prune doc + +# +# Most C and C++ files are generated by Cython and should not +# be included in the sdist. +# +global-exclude *.c +global-exclude *.cpp + +# +# List of C and C++ files that are actual source files, +# NOT generated by Cython. The same list appears in SAGE_ROOT/.gitignore +# +include sage/cpython/debugimpl.c +include sage/graphs/base/boost_interface.cpp +include sage/graphs/cliquer/cl.c +include sage/graphs/graph_decompositions/sage_tdlib.cpp +include sage/libs/eclib/wrap.cpp +include sage/libs/linkages/padics/relaxed/flint_helper.c +include sage/misc/inherit_comparison_impl.c +include sage/modular/arithgroup/farey.cpp +include sage/modular/arithgroup/sl2z.cpp +include sage/rings/bernmm/bern_modp.cpp +include sage/rings/bernmm/bern_modp_util.cpp +include sage/rings/bernmm/bern_rat.cpp +include sage/rings/bernmm/bernmm-test.cpp +include sage/rings/padics/transcendantal.c +include sage/rings/polynomial/weil/power_sums.c +include sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp +include sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp +include sage/stats/distributions/dgs_bern.c +include sage/stats/distributions/dgs_gauss_dp.c +include sage/stats/distributions/dgs_gauss_mp.c +include sage/symbolic/ginac/*.cpp +# Also actual C++ source files. +include sage/geometry/triangulation/triangulations.cc +include sage/geometry/triangulation/data.cc +include sage/geometry/triangulation/functions.cc + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/src/VERSION.txt b/src/VERSION.txt index aadd65deb15..39516ebfda0 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.8.beta0 +10.0.beta0 diff --git a/src/bin/sage b/src/bin/sage index e4d97835bb1..03aec9cfbfc 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -479,6 +479,13 @@ usage_advanced() { echo " Sage documentation for \"string\"." echo " --search_src ... -- same as --grep" echo " --search_doc ... -- same as --grepdoc" + echo " --fixdoctests file.py" + echo " -- Run doctests and replace output of failing doctests" + echo " with actual output." + echo " --fiximports <files|dir>" + echo " -- Replace imports from sage.PAC.KAGE.all by specific" + echo " imports when sage.PAC.KAGE is an implicit namespace" + echo " package" fi echo " --sh [...] -- run a shell with Sage environment variables" echo " as they are set in the runtime of Sage" @@ -974,6 +981,11 @@ if [ "$1" = '-startuptime' -o "$1" = '--startuptime' ]; then exec sage-startuptime.py "$@" fi +if [ "$1" = '-fiximports' -o "$1" = '--fiximports' ]; then + shift + exec sage-python -m sage.misc.replace_dot_all "$@" +fi + if [ "$1" = '-tox' -o "$1" = '--tox' ]; then shift if [ -n "$SAGE_SRC" -a -f "$SAGE_SRC/tox.ini" ]; then diff --git a/src/bin/sage-env b/src/bin/sage-env index 7d786f292c3..3a02640bc97 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -592,8 +592,6 @@ if [ "${MAKEFLAGS-__unset__}" != "__unset__" ]; then fi export MAKE -export PIP_FORMAT="columns" - # Set the cysignals crash logs directory if [ -z "$CYSIGNALS_CRASH_LOGS" ]; then export CYSIGNALS_CRASH_LOGS="$DOT_SAGE/crash_logs" diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index 13636b88703..2e8b12e1a1c 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -60,9 +60,6 @@ class NotebookJupyterlab(): traceback.print_exc() print("Jupyterlab is not installed (at least not in this Sage installation).") print("You can install it by running") - print(" sage -i jupyterlab_widgets") - print("which includes support for interacts and the ability to download and") - print("install other Jupyterlab extensions. For a minimal installation, run") print(" sage -i jupyterlab") print("Alternatively, you can follow the instructions at") print(" " + _system_jupyter_url) @@ -89,9 +86,6 @@ class NotebookNbclassic(): traceback.print_exc() print("Jupyterlab is not installed (at least not in this Sage installation).") print("You can install it by running") - print(" sage -i jupyterlab_widgets") - print("which includes support for interacts and the ability to download and") - print("install other Jupyterlab extensions. For a minimal installation, run") print(" sage -i jupyterlab") raise SystemExit(1) self.print_banner() @@ -115,9 +109,6 @@ class NotebookRetrolab(): traceback.print_exc() print("Retrolab is not installed (at least not in this Sage installation).") print("You can install it by running") - print(" sage -i jupyterlab_widgets retrolab") - print("which includes support for interacts and the ability to download and") - print("install other Jupyterlab extensions. For a minimal installation, run") print(" sage -i retrolab") raise SystemExit(1) self.print_banner() diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 802ca805250..127b827342d 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='9.8.beta0' -SAGE_RELEASE_DATE='2022-09-25' -SAGE_VERSION_BANNER='SageMath version 9.8.beta0, Release Date: 2022-09-25' +SAGE_VERSION='10.0.beta0' +SAGE_RELEASE_DATE='2023-02-12' +SAGE_VERSION_BANNER='SageMath version 10.0.beta0, Release Date: 2023-02-12' diff --git a/src/conftest_inputtest.py b/src/conftest_inputtest.py index d3f5d5cdfb2..aeca6276691 100644 --- a/src/conftest_inputtest.py +++ b/src/conftest_inputtest.py @@ -1,6 +1,6 @@ def something(): """ a doctest in a docstring - + EXAMPLES:: sage: something() diff --git a/src/doc/Makefile b/src/doc/Makefile index 62fb1cafd18..2f76dfb9cb4 100644 --- a/src/doc/Makefile +++ b/src/doc/Makefile @@ -14,6 +14,7 @@ all: doc-html clean: rm -rf en/reference/*/sage + rm -rf en/reference/documentation/sage_docbuild rm -rf en/reference/sage rm -f common/*.pyc diff --git a/src/doc/de/thematische_anleitungen/conf.py b/src/doc/de/thematische_anleitungen/conf.py index 114346944d5..7f2753ab0e4 100644 --- a/src/doc/de/thematische_anleitungen/conf.py +++ b/src/doc/de/thematische_anleitungen/conf.py @@ -27,7 +27,7 @@ # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". -html_title = project + " v"+release +html_title = project + " v" + release # Output file base name for HTML help builder. htmlhelp_basename = name @@ -35,7 +35,6 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ - ('index', name+'.tex', project, + ('index', name + '.tex', project, 'The Sage Group', 'manual'), ] - diff --git a/src/doc/de/tutorial/conf.py b/src/doc/de/tutorial/conf.py index 16804d981c9..8d18c44aa6b 100644 --- a/src/doc/de/tutorial/conf.py +++ b/src/doc/de/tutorial/conf.py @@ -38,4 +38,3 @@ ('index', name+'.tex', project, 'The Sage Group', 'manual'), ] - diff --git a/src/doc/de/tutorial/tour_numtheory.rst b/src/doc/de/tutorial/tour_numtheory.rst index a012234c99b..e3149fe949c 100644 --- a/src/doc/de/tutorial/tour_numtheory.rst +++ b/src/doc/de/tutorial/tour_numtheory.rst @@ -157,7 +157,7 @@ implementiert. Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/en/a_tour_of_sage/conf.py b/src/doc/en/a_tour_of_sage/conf.py index 89225513782..14ecb994215 100644 --- a/src/doc/en/a_tour_of_sage/conf.py +++ b/src/doc/en/a_tour_of_sage/conf.py @@ -37,4 +37,3 @@ ('index', 'a_tour_of_sage.tex', 'A Tour Of Sage', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/constructions/conf.py b/src/doc/en/constructions/conf.py index eee2feb033a..37215f418c6 100644 --- a/src/doc/en/constructions/conf.py +++ b/src/doc/en/constructions/conf.py @@ -36,4 +36,3 @@ ('index', 'constructions.tex', 'Constructions', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/developer/advanced_git.rst b/src/doc/en/developer/advanced_git.rst index cb5422cfb3a..eb128986452 100644 --- a/src/doc/en/developer/advanced_git.rst +++ b/src/doc/en/developer/advanced_git.rst @@ -6,6 +6,17 @@ Advanced Git ============ +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + This chapter covers some advanced uses of git that go beyond what is required to work with branches. These features can be used in Sage development, but are not really necessary to contribute to Sage. If diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 4f208b8e357..0371341a3ab 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -44,7 +44,7 @@ In particular, - Use 4 spaces for indentation levels. Do not use tabs as they can result in indentation confusion. Most editors have a feature that - will insert 4 spaces when the tab key is hit. Also, many editors + will insert 4 spaces when the :kbd:`Tab` key is hit. Also, many editors will automatically search/replace leading tabs with 4 spaces. - Whitespace before and after assignment and binary operator of the diff --git a/src/doc/en/developer/conf.py b/src/doc/en/developer/conf.py index 1ee9e105947..bde00a0970d 100644 --- a/src/doc/en/developer/conf.py +++ b/src/doc/en/developer/conf.py @@ -36,4 +36,3 @@ ('index', 'developer.tex', 'Developer\'s Guide', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst index 580c791ce60..462291488b9 100644 --- a/src/doc/en/developer/git_trac.rst +++ b/src/doc/en/developer/git_trac.rst @@ -6,6 +6,17 @@ Optional: Using the Git-Trac Command ==================================== +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + Git is a separate project from trac, and the two do not know how to talk to each other. To simplify the development, we have a special ``git trac`` subcommand for the git suite. Note that this really is @@ -17,6 +28,8 @@ perform every development task with just git and a web browser. Installing the Git-Trac Command =============================== +:: + [user@localhost]$ git clone https://github.com/sagemath/git-trac-command.git Cloning into 'git-trac-command'... [...] @@ -38,7 +51,7 @@ do this by symlinking:: [user@localhost git-trac-command]$ ln -s `pwd`/git-trac ~/bin/ See the `git-trac README <https://github.com/sagemath/git-trac-command>`_ for -more details. At this point you leave ``git-trac-command`` subdirectory, and only go +more details. At this point you leave ``git-trac-command`` subdirectory, and only go there whenever you need to update the ``git-trac`` command. diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index de24e3e3b7e..d851df7b0e7 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -4,6 +4,17 @@ Welcome to the Sage Developer's Guide! ====================================== +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + Everybody who uses Sage is encouraged to contribute something back to Sage at some point. You could: @@ -44,7 +55,7 @@ development! As an easy way to get started, you can run and edit Sage's code and contribute your changes using `Gitpod <https://www.gitpod.io>`_, a free online development environment based on VS Code. - It will launch a pre-made workspace with all dependencies and tools installed + It will launch a pre-made workspace with all dependencies and tools installed so that you can start contributing straight away. Start by `going to Gitpod <https://gitpod.io/#https://github.com/sagemath/sage>`_, and read :ref:`our Gitpod guidelines <section-gitpod>` to learn more. diff --git a/src/doc/en/developer/manual_git.rst b/src/doc/en/developer/manual_git.rst index c08c44ec793..83f595abab7 100644 --- a/src/doc/en/developer/manual_git.rst +++ b/src/doc/en/developer/manual_git.rst @@ -6,6 +6,17 @@ Using Git with the Sage Trac Server =================================== +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + Now we continue our introduction to git from :ref:`chapter-walkthrough`. We discuss how to push your local changes to a remote repository so that your changes can be reviewed for inclusion in Sage. @@ -154,6 +165,13 @@ repository, use these commands:: [user@localhost sage]$ git remote add trac https://github.com/sagemath/sagetrac-mirror.git -t master [user@localhost sage]$ git remote set-url --push trac git@trac.sagemath.org:sage.git +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** After the + move, the Sage trac server git@trac.sagemath.org:sage.git will no longer be + available, but all branches will be available (in read-only mode) on + https://github.com/sagemath/sagetrac-mirror.git. + Instead of ``trac`` you can use any other name you want, of course. To verify that it is set up correctly:: @@ -201,13 +219,17 @@ following commands instead:: trac git@trac.sagemath.org:sage.git (fetch) trac git@trac.sagemath.org:sage.git (push) +* The Patch buildbot will automatically test your ticket. See :trac:`wiki/patchbot` + for more information about its features and limitations. Make sure that you + look at the log, especially if the patch buildbot did not give you + the green blob. + .. _section-git-checkout: Checking Out Tickets -------------------- - Trac tickets that are finished or in the process of being worked on can have a git branch attached to them. This is the "Branch:" field in the ticket description. The branch name is generally of the form @@ -280,6 +302,7 @@ branch. The ``Branch`` field on the trac ticket can appear in red/green. See :ref:`section-trac-fields` to learn what it means. + .. _section-git-pull: Getting Changes diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 33ca56415f8..8f084b38ff6 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -1106,7 +1106,7 @@ set the ``upstream_url`` field in ``checksums.ini`` described above. For Python packages available from PyPI, you can use:: - [user@localhost]$ sage -package create scikit_spatial --pypi --type optional + [user@localhost]$ sage --package create scikit_spatial --pypi --type optional This automatically downloads the most recent version from PyPI and also obtains most of the necessary information by querying PyPI. @@ -1117,7 +1117,11 @@ in the file ``install-requires.txt``. To create a pip package rather than a normal package, you can use:: - [user@localhost]$ sage -package create scikit_spatial --pypi --source pip --type optional + [user@localhost]$ sage --package create scikit_spatial --pypi --source pip --type optional + +To create a wheel package rather than a normal package, you can use:: + + [user@localhost]$ sage --package create scikit_spatial --pypi --source wheel --type optional .. _section-manual-build: diff --git a/src/doc/en/developer/packaging_sage_library.rst b/src/doc/en/developer/packaging_sage_library.rst index 536e1da5913..7e7a5cde56f 100644 --- a/src/doc/en/developer/packaging_sage_library.rst +++ b/src/doc/en/developer/packaging_sage_library.rst @@ -274,6 +274,10 @@ distribution -- which then must be declared as a run-time dependency. the ``sage:`` prompt. In particular, no Sage library code should import from :mod:`sage.rings.all`. + To audit the Sage library for such imports, use ``sage --tox -e relint``. + In most cases, the imports can be fixed automatically using the + tool ``sage --fiximports``. + - Replace module-level imports by method-level imports. Note that this comes with a small runtime overhead, which can become noticeable if the method is called in tight inner loops. @@ -487,26 +491,49 @@ Hierarchy of distribution packages def node(label, pos): return text(label, (3*pos[0],2*pos[1]), background_color='pink', color='black') def edge(start, end, **kwds): - return arrow((3*start[0],2*start[1]+.5),(3*end[0],2*end[1]-.5), arrowsize=2, **kwds) + return arrow((3*start[0],2*start[1]),(3*end[0],2*end[1]-.28), arrowsize=2, **kwds) def extras_require(start, end): return edge(start, end, linestyle='dashed') g = Graphics() - g += (node("sage_conf", (0.5,0)) + extras_require((0.5,0),(0.5,1))) - g += (node("sagemath-objects", (1.5,0)) + edge((1.5,0),(1.5,1))) - g += (node("sagemath-environment", (0.5,1)) - + edge((0.5,1),(0,2)) + edge((0.5,1),(1,2)) + edge((0.5,1),(2,2))) - g += (node("sagemath-categories", (1.5,1)) + edge((1.5,1),(0,2)) + - edge((1.5,1),(1,2)) + edge((1.5,1),(2,2))) - g += (node("sagemath-graphs", (0,2)) + node("sagemath-polyhedra", (1,2)) + node("sagemath-singular", (2,2)) + - edge((0,2),(0,3)) + edge((0,2),(1,3)) + edge((1,2),(1,3)) + edge((2,2),(2,3))) - g += (node("sagemath-tdlib", (0,3)) + node("sagemath-standard-no-symbolics", (1,3)) + node("sagemath-symbolics", (2,3)) + - edge((1,3),(1,4)) + edge((2,3),(1,4))) + g += (extras_require((0.5,0),(0.5,1)) + node("sage_conf", (0.5,0))) + g += (edge((1.5,0),(0.75,2)) + edge((1.5,0),(1.5,1)) + + node("sagemath-objects", (1.5,0))) + g += (edge((0.5,1),(0,2)) + edge((0.5,1),(0.6,2)) + edge((0.5,1),(1.25,2)) + edge((0.5,1),(1.8,2)) + + node("sagemath-environment", (0.5,1))) + g += (edge((1.5,1),(0.2,2)) + edge((1.5,1),(1.41,2)) + edge((1.5,1),(2,2)) + + node("sagemath-categories", (1.5,1))) + g += (edge((0,2),(0,3)) + edge((0,2),(0.75,3)) + edge((0.67,2),(1,3)) + edge((1.33,2),(1.25,3)) + edge((2,2),(2,3)) + + node("sagemath-graphs", (0,2)) + node("sagemath-repl", (0.67,2)) + node("sagemath-polyhedra", (1.33,2)) + node("sagemath-singular", (2,2))) + g += (edge((1,3),(1,4)) + edge((2,3),(1.2,4)) + + node("sagemath-tdlib", (0,3)) + node("sagemath-standard-no-symbolics", (1,3)) + node("sagemath-symbolics", (2,3))) g += node("sagemath-standard", (1,4)) sphinx_plot(g, figsize=(8, 4), axes=False) Solid arrows indicate ``install_requires``, i.e., a declared runtime dependency. Dashed arrows indicate ``extras_require``, i.e., a declared optional runtime dependency. +Not shown in the diagram are build dependencies and optional dependencies for testing. + +- `sage_conf <https://pypi.org/project/sage-conf/>`_ is a configuration + module. It provides the configuration variable settings determined by the + ``configure`` script. + +- `sagemath-environment <https://pypi.org/project/sagemath-environment/>`_ + provides the connection to the system and software environment. It includes + :mod:`sage.env`, :mod:`sage.features`, :mod:`sage.misc.package_dir`, etc. + +- `sagemath-objects <https://pypi.org/project/sagemath-objects/>`_ + provides a small fundamental subset of the modules of the Sage library, + in particular all of :mod:`sage.structure`, a small portion of :mod:`sage.categories`, + and a portion of :mod:`sage.misc`. + +- `sagemath-categories <https://pypi.org/project/sagemath-categories/>`_ + provides a small subset of the modules of the Sage library, building upon sagemath-objects. + It provides all of :mod:`sage.categories` and a small portion of :mod:`sage.rings`. + +- `sagemath-repl <https://pypi.org/project/sagemath-repl/>`_ provides + the IPython kernel and Sage preparser (:mod:`sage.repl`), + the Sage doctester (:mod:`sage.doctest`), and some related modules from :mod:`sage.misc`. Testing distribution packages diff --git a/src/doc/en/developer/reviewer_checklist.rst b/src/doc/en/developer/reviewer_checklist.rst index 271290e3dc8..3ddc6b94f96 100644 --- a/src/doc/en/developer/reviewer_checklist.rst +++ b/src/doc/en/developer/reviewer_checklist.rst @@ -6,6 +6,17 @@ The reviewer's check list ========================= +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + All code that goes into Sage is peer-reviewed. Two reasons for this are: - Because a developer cannot think of everything at once; diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index a640cc2df24..f4892b02742 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -8,7 +8,7 @@ The Sage Manuals Sage's manuals are written in `ReST <http://docutils.sourceforge.net/rst.html>`_ (reStructuredText), and generated with the software `Sphinx -<http://sphinx.pocoo.org>`_: +<https://www.sphinx-doc.org/>`_: .. LIST-TABLE:: :widths: 4 12 @@ -84,7 +84,7 @@ The documentation can contain links toward modules, classes, or methods, e.g.:: For links toward classes, methods, or function, replace **:mod:** by **:class:**, **:meth:** or **:func:** respectively. See `Sphinx' documentation -<http://sphinx.pocoo.org/markup/inline.html>`_. +<https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html>`_. **Short links:** the link ``:func:`~sage.mod1.mod2.mod3.func1``` is equivalent to ``:func:`func1 <sage.mod1.mod2.mod3.func1>```: the function's name will be @@ -195,7 +195,7 @@ Two **help** commands which give plenty of documentation for the ``sage sage --docbuild -H # a more comprehensive one **Output formats:** All output formats supported by Sphinx (e.g. pdf) can be -used in Sage. See `<http://sphinx.pocoo.org/builders.html>`_. +used in Sage. See `<http://www.sphinx-doc.org/builders.html>`_. **Broken links:** in order to build the documentation while reporting the broken links that it contains, use the ``--warn-links`` flag. Note that Sphinx will not diff --git a/src/doc/en/developer/tools.rst b/src/doc/en/developer/tools.rst index becaad7f829..e2ac3ef82dd 100644 --- a/src/doc/en/developer/tools.rst +++ b/src/doc/en/developer/tools.rst @@ -319,14 +319,3 @@ Pyright Pyflakes ======== `Pyflakes <https://github.com/PyCQA/pyflakes>`_ checks for common coding errors. - - -.. _section-tools-lgtm: - -LGTM -==== -The website ``lgtm.com`` offers a detailed diagnostic about the global code quality and its evolution. - -The reports can be found `here <https://lgtm.com/projects/g/sagemath/sage/>`_. - -Our choice of configuration is made in ``.lgtm.yml``. diff --git a/src/doc/en/developer/trac.rst b/src/doc/en/developer/trac.rst index 8b8872b9eef..a584a61284f 100644 --- a/src/doc/en/developer/trac.rst +++ b/src/doc/en/developer/trac.rst @@ -6,6 +6,17 @@ The Sage Trac Server ==================== +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + Sometimes you will only want to work on local changes to Sage, for your own private needs. However, typically it is beneficial to share code and ideas with others; the manner in which the @@ -33,6 +44,13 @@ trac server. Items on the server are called *tickets*, and anyone may search or browse the tickets. For a list of recent changes, just visit the :trac:`Sage trac timeline <timeline>`. +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** + All functions of our Trac server will be taken over by our main repository, + https://github.com/sagemath/sage. + + .. _section-trac-account: Obtaining an Account @@ -288,11 +306,6 @@ After pushing a branch to a ticket, the ticket will show badges linking to results of automated tests that run on the patchbot and other tests that run on GitHub Actions. -* The Patch buildbot will automatically test your ticket. See :trac:`wiki/patchbot` - for more information about its features and limitations. Make sure that you - look at the log, especially if the patch buildbot did not give you - the green blob. - * A `linting workflow <https://github.com/sagemath/sage/blob/develop/.github/workflows/lint.yml>`_ runs on all pushes to a branch on Trac. It checks that the code of @@ -334,6 +347,17 @@ other tests that run on GitHub Actions. <https://github.com/sagemath/sagetrac-mirror/actions/workflows/doc-build.yml>`_ and choose the particular branch to see what went wrong. +* The patch buildbot will automatically test your ticket. See :trac:`wiki/patchbot` + for more information about its features and limitations. Make sure that you + look at the log, especially if the patch buildbot did not give you + the green blob. + +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** + After the move, the patch buildbot will no longer be available; the three + workflows above are considered a full replacement. If you miss any features + of the patch buildbot, please report this in :trac:`33457`. The following are some other relevant issues: diff --git a/src/doc/en/developer/walk_through.rst b/src/doc/en/developer/walk_through.rst index a4de6f5f138..74f0fd514f0 100644 --- a/src/doc/en/developer/walk_through.rst +++ b/src/doc/en/developer/walk_through.rst @@ -6,6 +6,17 @@ Sage Development Process ======================== +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + This section is a concise overview of the Sage development process. In it, we will see how to make changes to the Sage source code and record them in the ``git`` revision control system. @@ -111,6 +122,11 @@ For the experts, note that the repository at `git.sagemath.org <http://git.sagemath.org>`_ is where development actually takes place. +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** + After the move, https://github.com/sagemath/sage.git will be the + primary repository. .. _section-walkthrough-branch: diff --git a/src/doc/en/developer/workflows.rst b/src/doc/en/developer/workflows.rst index 1b89ecee1c0..b4d00265ee2 100644 --- a/src/doc/en/developer/workflows.rst +++ b/src/doc/en/developer/workflows.rst @@ -6,6 +6,17 @@ Distributed Development ======================= +.. WARNING:: + + **Sage development is scheduled to move to GitHub in February 2023.** The exact + date will be announced in `<https://groups.google.com/g/sage-devel>`_. After + the transition, some parts of this guide (especially those related with `the + Sage Trac server <https://trac.sagemath.org>`_) will become obsolete and be + updated according to the new workflow on GitHub. See our `transition guide from Trac to + GitHub + <https://github.com/sagemath/trac-to-github/blob/master/docs/Migration-Trac-to-Github.md>`_ + for the preliminary version of the workflow. + Git is a tool to exchange commits (organized into branches) with other developers. As a distributed revision control system, it does not have the notion of a central server. The Sage trac server is just one of diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 9d5f2cbeb95..40e4be5ddec 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -229,13 +229,13 @@ Can I do X in Sage? """"""""""""""""""" You are encouraged to use Sage's tab autocompletion. Just type a few -characters, hit the tab key, and see if the command you want appears +characters, hit the :kbd:`Tab` key, and see if the command you want appears in the list of tab autocompletion. If you have a command called -``mycmd``, then type ``mycmd.`` and hit the tab key to get a list of +``mycmd``, then type ``mycmd.`` and hit the :kbd:`Tab` key to get a list of functionalities that are supported by that command. To read the -documentation of ``mycmd``, type ``mycmd?`` and press the enter key to +documentation of ``mycmd``, type ``mycmd?`` and press the :kbd:`Enter` key to read the documentation for that command. Similarly, type ``mycmd??`` -and hit the enter key to get the source code of that command. You are +and hit the :kbd:`Enter` key to get the source code of that command. You are also encouraged to search through the source code and documentation of the Sage library. To search through the source code of the Sage library, use the command ``search_src("<search-keyword>")`` where you diff --git a/src/doc/en/installation/conf.py b/src/doc/en/installation/conf.py index 33ed20fa8e9..e50bf3c1c57 100644 --- a/src/doc/en/installation/conf.py +++ b/src/doc/en/installation/conf.py @@ -36,4 +36,3 @@ ('index', 'installation.tex', 'Installation Guide', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/installation/launching.rst b/src/doc/en/installation/launching.rst index c7b4e1b35ec..543a47463f7 100644 --- a/src/doc/en/installation/launching.rst +++ b/src/doc/en/installation/launching.rst @@ -29,6 +29,36 @@ To start a Jupyter Notebook instead of a Sage console, run the command instead of just ``sage``. To quit the Jupyter Notebook press ``<Ctrl> + <c>`` twice in the console where you launched the command. +Environment variables +--------------------- + +Sage uses the following environment variables when it runs: + +- :envvar:`DOT_SAGE` - this is the directory, to which the user has read and + write access, where Sage stores a number of files. + The default location is :file:`$HOME/.sage/`. + +- :envvar:`SAGE_STARTUP_FILE` - a file including commands to be executed every + time Sage starts. + The default value is :file:`$DOT_SAGE/init.sage`. + +- :envvar:`BROWSER` - on most platforms, Sage will detect the command to + run a web browser, but if this doesn't seem to work on your machine, set this + variable to the appropriate command. + +- :envvar:`TMPDIR` - this variable is used by Python, and hence by + Sage; it gives the directory in which temporary files should be + stored. This includes files used by the notebook. Some browsers have + security settings which restrict the locations of files that they + will access, and users may need to set this variable to handle this + situation. + +- See + https://docs.python.org/3/using/cmdline.html#environment-variables + for more variables used by Python (not an exhaustive list). With + Python 3.11 or later, a brief summary can also be obtained by + running `python3 --help-env`. + Using a Jupyter Notebook remotely --------------------------------- @@ -165,7 +195,7 @@ A command along the lines of .. CODE-BLOCK:: bash - ln -s $(sage -sh -c 'ls -d $SAGE_VENV/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/sagemath-dev + ln -s $(sage -sh -c 'ls -d $SAGE_VENV/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/kernels/sagemath-dev can then be used to create a symlink to the SageMath kernel description in a location where your own ``jupyter`` can find it. @@ -174,7 +204,7 @@ If you have installed SageMath from source, the alternative command .. CODE-BLOCK:: bash - ln -s $(sage -sh -c 'ls -d $SAGE_ROOT/venv/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/sagemath-dev + ln -s $(sage -sh -c 'ls -d $SAGE_ROOT/venv/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/kernels/sagemath-dev creates a symlink that will stay current even if you switch to a different Python version later. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index ca362d68d3e..bd4b406baf1 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -38,12 +38,6 @@ will not need to read them. Prerequisites ------------- -General requirements -~~~~~~~~~~~~~~~~~~~~ - -This section details the technical prerequisites needed on all platforms. See -also the `System-specific requirements`_ below. - Disk space and memory ^^^^^^^^^^^^^^^^^^^^^ @@ -51,169 +45,76 @@ Your computer comes with at least 6 GB of free disk space. It is recommended to have at least 2 GB of RAM, but you might get away with less (be sure to have some swap space in this case). -Command-line tools -^^^^^^^^^^^^^^^^^^ - -In addition to standard :wikipedia:`POSIX <POSIX>` utilities -and the :wikipedia:`bash <Bash_(Unix_shell)>` shell, -the following standard command-line development tools must be installed on your -computer: - -- A **C/C++ compiler**: GCC versions 8.x to 12.x are supported. - Clang (LLVM) is also supported. - See also `Using alternative compilers`_. -- **make**: GNU make, version 3.80 or later. Version 3.82 or later is recommended. -- **m4**: GNU m4 1.4.2 or later (non-GNU or older versions might also work). -- **perl**: version 5.8.0 or later. -- **ar** and **ranlib**: can be obtained as part of GNU binutils. -- **tar**: GNU tar version 1.17 or later, or BSD tar. -- **python**: Python 3.4 or later, or Python 2.7. - (This range of versions is a minimal requirement for internal purposes of the SageMath - build system, which is referred to as ``sage-bootstrap-python``.) - -Other versions of these may work, but they are untested. - - -Fortran and compiler suites -########################### - -Sage installation also needs a Fortran compiler. It is determined -automatically whether Sage's GCC package, or just its part containing -Fortran compiler ``gfortran`` needs to be installed. This can be -overwritten by running ``./configure`` with option -``--without-system-gcc``. - -Officially we support -gfortran from `GNU Compiler Collection (GCC) <https://gcc.gnu.org/>`_. -If C and C++ compilers also come from there (i.e., gcc and g++), their versions -should match. -Alternatively, one may use C and C++ compilers from -`Clang: a C language family frontend for LLVM <https://clang.llvm.org/>`_, -and thus matching versions of -clang, clang++ , along with a recent gfortran. (Flang (or other LLVM-based -Fortran compilers) are not officially supported, however it is possible to -to build Sage using flang, with some extra efforts needed to set various flags; -this is work in progress at the moment (May 2019)). - -Therefore, if you plan on using your own GCC compilers, then make sure that -their versions match. - -To force using specific compilers, set environment variables ``CC``, -``CXX``, and ``FC`` (for C, C++, and Fortran compilers, respectively) -to the desired values, and run ``./configure``. For example, -``./configure CC=clang CXX=clang++ FC=gfortran`` will configure Sage -to be built with Clang C/C++ compilers and Fortran compiler -``gfortran``. - -Alternatively, Sage includes a GCC package, so that C, C++ and Fortran -compilers will be built when the build system detects that it is needed, -e.g., non-GCC compilers, or -versions of the GCC compilers known to miscompile some components of Sage, -or simply a missing Fortran compiler. -In any case, you always need at least a C/C++ compiler to build the GCC -package and its prerequisites before the compilers it provides can be used. - -Note that you can always override this behavior through the configure -options ``--without-system-gcc`` and ``--with-system-gcc``, see -:ref:`section_compilers`. - -There are some known problems with old assemblers, in particular when -building the ``ecm`` and ``fflas_ffpack`` packages. You should ensure -that your assembler understands all instructions for your -processor. On Linux, this means you need a recent version of -``binutils``; on macOS you need a recent version of Xcode. - -Python for venv -^^^^^^^^^^^^^^^ +Software prerequisites and recommended packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sage depends on `a large number of software packages +<../reference/spkg/index.html>`_. Sage provides its own software +distribution providing most of these packages, so you do not have to +worry about having to download and install these packages yourself. + +If you extracted Sage from a source tarball, the subdirectory +:file:`upstream` contains the source distributions for all standard +packages on which Sage depends. If cloned from a git repository, the +upstream tarballs will be downloaded, verified, and cached as part of +the Sage installation process. + +However, there are minimal prerequisites for building Sage that +already must be installed on your system: -By default, Sage will try to use system's ``python3`` to set up a virtual -environment, a.k.a. `venv <https://docs.python.org/3.10/library/venv.html>`_ -rather than building a Python 3 installation from scratch. -Use the ``configure`` option ``--without-system-python3`` in case you want Python 3 -built from scratch. - -Sage will accept versions 3.8.x to 3.10.x. - -You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use -``/path/to/python3_binary`` to set up the venv. Note that setting up venv requires -a number of Python modules to be available within the Python in question. Currently, -for Sage 9.6, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, -``hashlib``, ``crypt``, ``socket``, ``zlib``, ``distutils.core``, ``ssl`` - -they will be checked for by the ``configure`` script. - -Other notes -^^^^^^^^^^^ - -After extracting the Sage source tarball, the subdirectory :file:`upstream` -contains the source distributions for everything on which Sage depends. - -If cloned from a git repository, the upstream tarballs will be downloaded, -verified, and cached as part of the Sage installation process. -We emphasize that all of this software is included with Sage, so you do not -have to worry about trying to download and install any one of these packages -(such as Python, for example) yourself. - -When the Sage installation program is run, -it will check that you have each of the above-listed prerequisites, -and inform you of any that are missing, or have unsuitable versions. - -System-specific requirements -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On macOS, there are various developer tools needed which may require -some registration on Apple's developer site; see -:ref:`section_macprereqs`. - -On Redhat-derived systems not all perl components are installed by -default and you might have to install the ``perl-ExtUtils-MakeMaker`` -package. - -On Linux systems (e.g., Ubuntu, Redhat, etc), ``ar`` and ``ranlib`` are in the -`binutils <https://www.gnu.org/software/binutils/>`_ package. -The other programs are usually located in packages with their respective names. -Assuming you have sufficient privileges, you can install the ``binutils`` and -other necessary/standard components. The lists provided below are longer than -the minimal prerequisites, which are basically ``binutils``, ``gcc``/``clang``, ``make``, -``tar``, but there is no real need to build compilers and other standard tools -and libraries on a modern Linux system, in order to be able to build Sage. +- `Fundamental system packages required for installing from source + <../reference/spkg/_prereq.html>`_ + +- `C/C++ compilers <../reference/spkg/gcc.html>`_ + +If you have sufficient privileges (for example, on Linux you can +use ``sudo`` to become the ``root`` user), then you can install these packages +using the commands for your platform indicated in the pages linked above. If you do not have the privileges to do this, ask your system administrator to -do this, or build the components from source code. -The method of installing additional software varies from distribution to -distribution, but on a `Debian <https://www.debian.org/>`_ based system (e.g. -`Ubuntu <https://www.ubuntu.com/>`_ or `Mint <https://www.linuxmint.com/>`_), -you would use -:wikipedia:`apt-get <Advanced_Packaging_Tool>`. +do this for you. + +In addition to these minimal prerequisites, we strongly recommend to use system +installations of the following: -Installing prerequisites -~~~~~~~~~~~~~~~~~~~~~~~~ +- `Fortran compiler <../reference/spkg/gfortran.html>`_ -To check if you have the above prerequisites installed, for example ``perl``, -type:: +- `Python <../reference/spkg/python3.html>`_ - $ command -v perl +Sage developers will also need the `system packages required for +bootstrapping <../reference/spkg/_bootstrap.html>`_; they cannot be +installed by Sage. -or:: +When the ``./configure`` script runs, it will check for the presence of many +packages (including the above) and inform you of any that are +missing or have unsuitable versions. **Please read the messages that +``./configure`` prints:** It will inform you which additional system packages +you can install to avoid having to build them from source. This can save a lot of +time. - $ which perl +The following sections provide the commands to install a large +recommended set of packages on various systems, which will minimize +the time it takes to build Sage. This is intended as a convenient +shortcut, but of course you can choose to take a more fine-grained +approach. -on the command line. If it gives an error (or returns nothing), then -either ``perl`` is not installed, or it is installed but not in your -:wikipedia:`PATH <PATH_%28variable%29>`. .. _sec-installation-from-sources-linux-recommended-installation: -Debian/Ubuntu prerequisite installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Debian/Ubuntu package installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Debian ("buster" or newer) or Ubuntu ("bionic" or newer): +On Debian ("buster" or newer) or Ubuntu ("bionic" or newer), we recommend that you +install: .. literalinclude:: debian.txt -If you wish to do Sage development, additionally install the following: +If you wish to do Sage development, we recommend that you additionally +install the following: .. literalinclude:: debian-develop.txt -For all users, we recommend the following: +For all users, we recommend that you install the following system packages, +which provide additional functionality and cannot be installed by Sage: .. literalinclude:: debian-recommended.txt @@ -223,16 +124,20 @@ install: .. literalinclude:: debian-optional.txt -Fedora/Redhat/CentOS prerequisite installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Fedora/Redhat/CentOS package installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On Fedora/Redhat/CentOS, we recommend that you install: .. literalinclude:: fedora.txt -If you wish to do Sage development, additionally install the following: +If you wish to do Sage development, we recommend that you additionally +install the following: .. literalinclude:: fedora-develop.txt -For all users, we recommend the following: +For all users, we recommend that you install the following system packages, +which provide additional functionality and cannot be installed by Sage: .. literalinclude:: fedora-recommended.txt @@ -242,16 +147,20 @@ install: .. literalinclude:: fedora-optional.txt -Arch Linux prerequisite installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Arch Linux package installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On ArchLinux, we recommend that you install: .. literalinclude:: arch.txt -If you wish to do Sage development, additionally install the following: +If you wish to do Sage development, we recommend that you additionally +install the following: .. literalinclude:: arch-develop.txt -For all users, we recommend the following: +For all users, we recommend that you install the following system packages, +which provide additional functionality and cannot be installed by Sage: .. literalinclude:: arch-recommended.txt @@ -261,16 +170,20 @@ install: .. literalinclude:: arch-optional.txt -OpenSUSE prerequisite installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +OpenSUSE package installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On OpenSUSE, we recommend that you install: .. literalinclude:: opensuse.txt -If you wish to do Sage development, additionally install the following: +If you wish to do Sage development, we recommend that you additionally +install the following: .. literalinclude:: opensuse-develop.txt -For all users, we recommend the following: +For all users, we recommend that you install the following system packages, +which provide additional functionality and cannot be installed by Sage: .. literalinclude:: opensuse-recommended.txt @@ -282,8 +195,8 @@ install: .. _section_macprereqs: -macOS prerequisite installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +macOS prerequisites +^^^^^^^^^^^^^^^^^^^ On macOS systems, you need a recent version of `Command Line Tools <https://developer.apple.com/downloads/index.action?=command%20line%20tools>`_. @@ -312,25 +225,11 @@ a registration. to Command Line Tools. +macOS package installation +^^^^^^^^^^^^^^^^^^^^^^^^^^ -macOS recommended installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Although Sage can in theory build its own version of gfortran, this -can take a while, and the process fails on some recent versions of -OS X. So instead you can install your own copy. One advantage of this -is that you can install it once, and it will get used every time you -build Sage, rather than building gfortran every time. - -One way to do that is with the `Homebrew package manager -<https://brew.sh>`_. Install Homebrew as their web page describes, and -then the command :: - - $ brew install gcc - -will install Homebrew's gcc package, which includes gfortran. Sage -will also use other Homebrew packages, if they are present. You can -install the following: +If you use the `Homebrew package manager +<https://brew.sh>`_, you can install the following: .. literalinclude:: homebrew.txt @@ -344,11 +243,13 @@ Sage, run :: command like this to your shell profile if you want the settings to persist between shell sessions. -If you wish to do Sage development, additionally install the following: +If you wish to do Sage development, we recommend that you additionally +install the following: .. literalinclude:: homebrew-develop.txt -For all users, we recommend the following: +For all users, we recommend that you install the following system packages, +which provide additional functionality and cannot be installed by Sage: .. literalinclude:: homebrew-recommended.txt @@ -547,56 +448,12 @@ If you don't want conda to be used by sage, deactivate conda (for the current sh Then SageMath will be built either using the compilers provided by the operating system, or its own compilers. -Specific notes for ``make`` and ``tar`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On macOS, the system-wide BSD ``tar`` supplied will build Sage, so there is no -need to install the GNU ``tar``. - -.. _section_compilers: - -Using alternative compilers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sage developers tend to use fairly recent versions of GCC. -Nonetheless, the Sage build process on Linux -should succeed with any reasonable C/C++ compiler; -(we do not recommend GCC older than version 5.1). -This is because Sage will build GCC first (if needed) and then use that newly -built GCC to compile Sage. - -If you don't want this and want to try building Sage with a different set of -compilers, -you need to pass Sage's ``./configure`` compiler names, via environment -variables ``CC``, ``CXX``, and ``FC``, for C, C++, and Fortran compilers, -respectively, e.g. if you C compiler is ``clang``, your C++ compiler is ``clang++``, -and your Fortran compiler is ``flang`` then you would need to run:: - - $ CC=clang CXX=clang++ FC=flang ./configure - -before running ``make``. It is recommended that you inspect the output of ``./configure`` -in order to check that Sage will not try to build GCC. Namely, there should be lines like:: - - gcc-7.2.0 will not be installed (configure check) - ... - gfortran-7.2.0 will not be installed (configure check) - -indicating that Sage will not attempt to build ``gcc/g++/gfortran``. - -If you are interested in working on support for commercial compilers from -`HP <http://docs.hp.com/en/5966-9844/ch01s03.html>`_, -`IBM <http://www-01.ibm.com/software/awdtools/xlcpp/>`_, -`Intel <http://software.intel.com/en-us/articles/intel-compilers/>`_, -`Sun/Oracle <http://www.oracle.com/technetwork/server-storage/solarisstudio/overview/index.html>`_, -etc, -please email the sage-devel mailing list at https://groups.google.com/group/sage-devel. - Additional software ------------------- Recommended programs -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ The following programs are recommended. They are not strictly required at build time or at run time, @@ -649,7 +506,7 @@ On Debian/Ubuntu, the following system packages are recommended. - ``libavdevice-dev`` (to produce animations) Tcl/Tk -~~~~~~ +^^^^^^ If you want to use `Tcl/Tk <https://www.tcl.tk/>`_ libraries in Sage, you need to install the Tcl/Tk and its development headers before building @@ -689,7 +546,7 @@ Step-by-step installation procedure ----------------------------------- General procedure -~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^ #. Follow the procedure in the file `README.md <https://github.com/sagemath/sage/#readme>`_ in ``SAGE_ROOT``. @@ -702,7 +559,7 @@ General procedure serious consequences if you are logged in as root. Typing ``make`` performs the usual steps for each Sage's dependency, - but installs all the resulting files into the local build tree. + but installs all the resulting files into the installation prefix. Depending on the age and the architecture of your system, it can take from a few tens of minutes to several hours to build Sage from source. On really slow hardware, it can even take a few days to build Sage. @@ -867,9 +724,10 @@ General procedure Now typing ``sage`` within your terminal emulator should start Sage. #. Optional: - Install optional Sage packages and databases. - Type ``sage --optional`` to see a list of them (this requires an Internet - connection), or visit https://www.sagemath.org/packages/optional/. + Install optional Sage packages and databases. See `the list of optional packages + in the reference manual <../reference/spkg/index.html#optional-packages>`_ for + detailed information, or type ``sage --optional`` (this requires an Internet connection). + Then type ``sage -i <package-name>`` to automatically download and install a given package. @@ -1228,20 +1086,6 @@ Some standard environment variables which are used by Sage: - :envvar:`OPENBLAS_CONFIGURE` - adds additional configuration flags for the OpenBLAS package that gets added to the make command. (see :trac:`23272`) -Sage uses the following environment variables when it runs: - -- :envvar:`DOT_SAGE` - this is the directory, to which the user has read and - write access, where Sage stores a number of files. - The default location is :file:`$HOME/.sage/`. - -- :envvar:`SAGE_STARTUP_FILE` - a file including commands to be executed every - time Sage starts. - The default value is :file:`$DOT_SAGE/init.sage`. - -- :envvar:`BROWSER` - on most platforms, Sage will detect the command to - run a web browser, but if this doesn't seem to work on your machine, set this - variable to the appropriate command. - Variables dealing with doctesting: - :envvar:`SAGE_TIMEOUT` - used for Sage's doctesting: the number of seconds @@ -1331,4 +1175,4 @@ a single copy of Sage in a multi-user computer network. $ sudo chown -R root SAGE_LOCAL -**This page was last updated in May 2022 (Sage 9.7).** +**This page was last updated in September 2022 (Sage 9.8).** diff --git a/src/doc/en/prep/Advanced-2DPlotting.rst b/src/doc/en/prep/Advanced-2DPlotting.rst index 337457afef4..633e33d68b3 100644 --- a/src/doc/en/prep/Advanced-2DPlotting.rst +++ b/src/doc/en/prep/Advanced-2DPlotting.rst @@ -39,9 +39,10 @@ following sections: - :ref:`Saving` -This tutorial assumes that one is familiar with the basics of Sage, such -as evaluating a cell by clicking the "evaluate" link, or by pressing -Shift\-Enter (hold down Shift while pressing the Enter key). +This tutorial assumes that one is familiar with the basics of Sage, +such as evaluating a cell by clicking the "evaluate" link, or by +pressing :kbd:`Shift` + :kbd:`Enter` (hold down :kbd:`Shift` while +pressing the :kbd:`Enter` key). .. fixme - if log plots are in by the time this makes it in, put them in!!! diff --git a/src/doc/en/prep/Calculus.rst b/src/doc/en/prep/Calculus.rst index 8c9ea73777b..af6902b9e04 100644 --- a/src/doc/en/prep/Calculus.rst +++ b/src/doc/en/prep/Calculus.rst @@ -29,10 +29,10 @@ the United States; the final section is a checkpoint of sorts. The tutorial assumes that one is familiar with the basics of Sage, such as outlined in the previous tutorials. -For a refresher, make sure the syntax below for defining a function and -getting a value makes sense; then evaluate the cell by clicking the -"evaluate" link, or by pressing Shift\-Enter (hold down Shift while -pressing the Enter key). +For a refresher, make sure the syntax below for defining a function +and getting a value makes sense; then evaluate the cell by clicking +the "evaluate" link, or by pressing :kbd:`Shift` + :kbd:`Enter` (hold +down :kbd:`Shift` while pressing the :kbd:`Enter` key). :: diff --git a/src/doc/en/prep/Intro-Tutorial.rst b/src/doc/en/prep/Intro-Tutorial.rst index 2febd01ac19..7ab0a4ad97d 100644 --- a/src/doc/en/prep/Intro-Tutorial.rst +++ b/src/doc/en/prep/Intro-Tutorial.rst @@ -75,8 +75,9 @@ To do math in a Jupyter cell, one must do two things. .. image:: media/RunCellIcon.png :align: center - Or one can use the keyboard shortcut of holding down the Shift key - while you press the Enter key. We call this "Shift\-Enter". + Or one can use the keyboard shortcut of holding down the :kbd:`Shift` key + while you press the :kbd:`Enter` key. + We call this :kbd:`Shift` + :kbd:`Enter`. Sage prints out its response just below the cell (that's the ``4`` below, so Sage confirms that :math:`2+2=4`). Note also that Sage has @@ -365,7 +366,7 @@ Here's an example. - Still, it seems reasonable that the command might start with ``pl``. -- Then one can type ``pl`` in an input cell, and then press the tab key +- Then one can type ``pl`` in an input cell, and then press the :kbd:`Tab` key to see all the commands that start with the letters ``pl``. Try tabbing after the ``pl`` in the following cell to see all the @@ -379,7 +380,7 @@ commands that start with the letters ``pl``. You should see that sage: pl To pick one, just click on it; to stop viewing them, press the -Escape/esc key. +:kbd:`Escape` key. You can also use this to see what you can do to an expression or mathematical object. @@ -399,7 +400,7 @@ defined. sage: f(x)=x^2 -Now put your cursor after the period and press your tab key. +Now put your cursor after the period and press your :kbd:`Tab` key. .. skip @@ -407,7 +408,7 @@ Now put your cursor after the period and press your tab key. sage: f. -Again, Escape should remove the list. +Again, :kbd:`Escape` should remove the list. One of the things in that list above was ``integrate``. Let's try it. @@ -437,7 +438,7 @@ that can illustrate how to use the function. - Press tab *or* evaluate to see the documentation. To see how this help works, move your cursor after the question mark -below and press tab. +below and press :kbd:`Tab`. .. skip @@ -449,8 +450,8 @@ The examples illustrate that the syntax requires ``f.integrate(x)`` and not just ``f.integrate()``. (After all, the latter could be ambiguous if several variables had already been defined). -To stop viewing the documentation after pressing tab, you can press the -Escape key, just like with the completion of options. +To stop viewing the documentation after pressing :kbd:`Tab`, you can press the +:kbd:`Escape` key, just like with the completion of options. If you would like the documentation to be visible longer\-term, you can *evaluate* a command with the question mark (like below) to access the diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index c6da1f7c112..6e0ab69765a 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -29,7 +29,8 @@ and evaluation in Sage. We provide a (very) brief refresher. value makes sense. #. Then evaluate the cell by clicking the "evaluate" link, or by - pressing Shift\-Enter (hold down Shift while pressing the Enter key). + pressing :kbd:`Shift` + :kbd:`Enter` (hold down :kbd:`Shift` + while pressing the :kbd:`Enter` key). :: diff --git a/src/doc/en/reference/algebras/cubic_hecke_algebra.rst b/src/doc/en/reference/algebras/cubic_hecke_algebra.rst index 5ba74eb00f0..94ca9bb27cc 100644 --- a/src/doc/en/reference/algebras/cubic_hecke_algebra.rst +++ b/src/doc/en/reference/algebras/cubic_hecke_algebra.rst @@ -2,7 +2,7 @@ Cubic Hecke Algebras ==================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/hecke_algebras/cubic_hecke_algebra sage/algebras/hecke_algebras/cubic_hecke_base_ring diff --git a/src/doc/en/reference/algebras/fusion_rings.rst b/src/doc/en/reference/algebras/fusion_rings.rst new file mode 100644 index 00000000000..06d2473c558 --- /dev/null +++ b/src/doc/en/reference/algebras/fusion_rings.rst @@ -0,0 +1,20 @@ +Fusion Rings +============ + +.. toctree:: + :maxdepth: 2 + + sage/algebras/fusion_rings/fusion_ring + sage/algebras/fusion_rings/f_matrix + +F-Matrix Backend +---------------- + +.. toctree:: + :maxdepth: 2 + + sage/algebras/fusion_rings/fast_parallel_fmats_methods + sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn + sage/algebras/fusion_rings/poly_tup_engine + sage/algebras/fusion_rings/shm_managers + diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 237343faf32..1aae4090861 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -12,7 +12,7 @@ Free associative algebras and quotients --------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/free_algebra sage/algebras/free_algebra_element @@ -29,7 +29,7 @@ Finite dimensional algebras --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element @@ -40,7 +40,7 @@ Named associative algebras -------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/affine_nil_temperley_lieb sage/algebras/askey_wilson @@ -48,6 +48,7 @@ Named associative algebras sage/algebras/clifford_algebra sage/algebras/cluster_algebra sage/combinat/descent_algebra + fusion_rings sage/algebras/hall_algebra sage/combinat/posets/incidence_algebras sage/algebras/group_algebra @@ -73,7 +74,7 @@ Hecke algebras -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/hecke_algebras/ariki_koike_algebra sage/algebras/iwahori_hecke_algebra @@ -85,7 +86,7 @@ Graded algebras --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/finite_gca sage/algebras/commutative_dga @@ -94,7 +95,7 @@ Various associative algebras ---------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/associated_graded sage/algebras/cellular_basis @@ -106,7 +107,7 @@ Non-associative algebras ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 lie_algebras lie_conformal_algebras @@ -115,6 +116,6 @@ Non-associative algebras sage/combinat/free_prelie_algebra sage/algebras/shuffle_algebra sage/algebras/free_zinbiel_algebra - + .. include:: ../footer.txt diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst index bc132fe88bd..23152ac0449 100644 --- a/src/doc/en/reference/algebras/lie_algebras.rst +++ b/src/doc/en/reference/algebras/lie_algebras.rst @@ -2,7 +2,7 @@ Lie Algebras ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/lie_algebras/abelian sage/algebras/lie_algebras/affine_lie_algebra diff --git a/src/doc/en/reference/algebras/media/fusiontree.png b/src/doc/en/reference/algebras/media/fusiontree.png new file mode 100644 index 00000000000..082e87ab99c Binary files /dev/null and b/src/doc/en/reference/algebras/media/fusiontree.png differ diff --git a/src/doc/en/reference/algebras/media/fusiontree.tex b/src/doc/en/reference/algebras/media/fusiontree.tex new file mode 100644 index 00000000000..34f2447ab47 --- /dev/null +++ b/src/doc/en/reference/algebras/media/fusiontree.tex @@ -0,0 +1,29 @@ +\documentclass[crop,tikz]{standalone}% 'crop' is the default for v1.0, before +% it was 'preview' +%\usetikzlibrary{...}% tikz package already loaded by 'tikz' option +\begin{document} +\begin{tikzpicture}[xscale=0.8,yscale=1.2] + \draw (0,4) -- (4,0); + \draw (2,4) -- (1,3); + \draw (4,4) -- (5,3); + \draw (6,4) -- (3,1); + \draw (4,0) -- (8,4); + \draw (4,0) -- (4,-1); + \path[fill=white] (0,4) circle (.3); + \node at (0,4) {$a$}; + \path[fill=white] (2,4) circle (.3); + \node at (2,4) {$a$}; + \path[fill=white] (4,4) circle (.3); + \node at (4,4) {$a$}; + \path[fill=white] (6,4) circle (.3); + \node at (6,4) {$a$}; + \path[fill=white] (8,4) circle (.3); + \node at (8,4) {$a$}; + \path[fill=white] (1.5,2) circle (.3); + \node at (1.5,2) {$x$}; + \node at (4.5,2) {$y$}; + \node at (3.2,.4) {$z$}; + \path[fill=white] (4,-1) circle (.3); + \node at (4,-1) {$b$}; +\end{tikzpicture} +\end{document} diff --git a/src/doc/en/reference/algebras/quantum_groups.rst b/src/doc/en/reference/algebras/quantum_groups.rst index d89afc4b906..83a8e53cae1 100644 --- a/src/doc/en/reference/algebras/quantum_groups.rst +++ b/src/doc/en/reference/algebras/quantum_groups.rst @@ -2,7 +2,7 @@ Quantum Groups ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/quantum_groups/ace_quantum_onsager sage/algebras/quantum_groups/fock_space diff --git a/src/doc/en/reference/arithgroup/index.rst b/src/doc/en/reference/arithgroup/index.rst index c16f34d88fd..bb7f650320d 100644 --- a/src/doc/en/reference/arithgroup/index.rst +++ b/src/doc/en/reference/arithgroup/index.rst @@ -5,7 +5,7 @@ This chapter describes the basic functionality for finite index subgroups of the modular group `\SL_2(\ZZ)`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/arithgroup/arithgroup_generic sage/modular/arithgroup/arithgroup_perm diff --git a/src/doc/en/reference/arithmetic_curves/index.rst b/src/doc/en/reference/arithmetic_curves/index.rst index 73f6f602490..6ab10dcf1c0 100644 --- a/src/doc/en/reference/arithmetic_curves/index.rst +++ b/src/doc/en/reference/arithmetic_curves/index.rst @@ -2,7 +2,7 @@ Elliptic curves ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/constructor sage/schemes/elliptic_curves/jacobian @@ -15,13 +15,15 @@ Elliptic curves Maps between them .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/hom sage/schemes/elliptic_curves/weierstrass_morphism sage/schemes/elliptic_curves/ell_curve_isogeny sage/schemes/elliptic_curves/hom_velusqrt sage/schemes/elliptic_curves/hom_composite + sage/schemes/elliptic_curves/hom_scalar + sage/schemes/elliptic_curves/hom_frobenius sage/schemes/elliptic_curves/isogeny_small_degree @@ -29,7 +31,7 @@ Elliptic curves over number fields ---------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_rational_field sage/schemes/elliptic_curves/ec_database @@ -51,7 +53,7 @@ Elliptic curves over number fields The following relate to elliptic curves over local nonarchimedean fields. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_local_data sage/schemes/elliptic_curves/kodaira_symbol @@ -60,7 +62,7 @@ The following relate to elliptic curves over local nonarchimedean fields. Analytic properties over `\CC`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_wp sage/schemes/elliptic_curves/period_lattice @@ -69,7 +71,7 @@ Analytic properties over `\CC`. Modularity and `L`-series over `\QQ`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/modular_parametrization sage/schemes/elliptic_curves/ell_modular_symbols @@ -82,7 +84,7 @@ To be sorted ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/descent_two_isogeny sage/schemes/elliptic_curves/ell_egros @@ -98,7 +100,7 @@ Hyperelliptic curves ==================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/hyperelliptic_curves/constructor sage/schemes/hyperelliptic_curves/hyperelliptic_generic diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index f45e9026068..a40cca76e0f 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -5,7 +5,7 @@ Introduction ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/all @@ -13,7 +13,7 @@ The Sage Category Framework --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/primer sage/categories/category @@ -25,7 +25,7 @@ Maps and Morphisms ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/map sage/categories/homset @@ -36,7 +36,7 @@ Individual Categories --------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/action sage/categories/additive_groups @@ -206,7 +206,7 @@ Technical Categories ~~~~~~~~~~~~~~~~~~~~ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/facade_sets @@ -214,7 +214,7 @@ Functorial constructions ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/covariant_functorial_construction @@ -239,7 +239,7 @@ Examples of parents using categories ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/examples/algebras_with_basis sage/categories/examples/commutative_additive_monoids @@ -276,7 +276,7 @@ Internals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/category_types sage/categories/category_singleton diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index abba3458380..637d1a2e65f 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -95,7 +95,7 @@ is from a special code family, the derived codes inherit structural properties like decoding radius or minimum distance: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/subfield_subcode sage/coding/punctured_code @@ -127,7 +127,7 @@ Automorphism Groups of Linear Codes ----------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/codecan/codecan sage/coding/codecan/autgroup_can_label @@ -136,7 +136,7 @@ Bounds for Parameters of Linear Codes ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/code_bounds sage/coding/delsarte_bounds @@ -145,7 +145,7 @@ Databases for Coding Theory --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/databases sage/coding/two_weight_db diff --git a/src/doc/en/reference/coercion/index.rst b/src/doc/en/reference/coercion/index.rst index d8d7c2ade79..d6d117cd263 100644 --- a/src/doc/en/reference/coercion/index.rst +++ b/src/doc/en/reference/coercion/index.rst @@ -671,7 +671,7 @@ Modules ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/structure/coerce sage/structure/coerce_actions diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 7a97a40e30d..3bfa10ed3f7 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -109,6 +109,7 @@ Comprehensive Module List sage/combinat/designs/steiner_quadruple_systems sage/combinat/designs/subhypergraph_search sage/combinat/designs/twographs + sage/combinat/diagram sage/combinat/diagram_algebras sage/combinat/dlx sage/combinat/dyck_word @@ -144,6 +145,7 @@ Comprehensive Module List sage/combinat/k_regular_sequence sage/combinat/k_tableau sage/combinat/kazhdan_lusztig + sage/combinat/key_polynomial sage/combinat/knutson_tao_puzzles sage/combinat/matrices/all sage/combinat/matrices/dancing_links @@ -283,7 +285,6 @@ Comprehensive Module List sage/combinat/root_system/weight_lattice_realizations sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters - sage/combinat/root_system/fusion_ring sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk @@ -339,11 +340,8 @@ Comprehensive Module List sage/combinat/species/permutation_species sage/combinat/species/product_species sage/combinat/species/recursive_species - sage/combinat/species/series - sage/combinat/species/series_order sage/combinat/species/set_species sage/combinat/species/species - sage/combinat/species/stream sage/combinat/species/structure sage/combinat/species/subset_species sage/combinat/species/sum_species @@ -356,12 +354,14 @@ Comprehensive Module List sage/combinat/superpartition sage/combinat/symmetric_group_algebra sage/combinat/symmetric_group_representations + sage/combinat/t_sequences sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple sage/combinat/tamari_lattices sage/combinat/tiling sage/combinat/tools + sage/combinat/triangles_FHM sage/combinat/tuple sage/combinat/tutorial sage/combinat/vector_partition diff --git a/src/doc/en/reference/constants/index.rst b/src/doc/en/reference/constants/index.rst index 96ae5e256ae..b64ddc46a98 100644 --- a/src/doc/en/reference/constants/index.rst +++ b/src/doc/en/reference/constants/index.rst @@ -2,7 +2,7 @@ Constants ========= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/symbolic/constants diff --git a/src/doc/en/reference/cpython/index.rst b/src/doc/en/reference/cpython/index.rst index dce1253a535..5cb77b05605 100644 --- a/src/doc/en/reference/cpython/index.rst +++ b/src/doc/en/reference/cpython/index.rst @@ -5,7 +5,7 @@ SageMath has various modules to provide access to low-level Python internals. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/cpython/atexit sage/cpython/string diff --git a/src/doc/en/reference/cryptography/index.rst b/src/doc/en/reference/cryptography/index.rst index b2d628ab796..3c7997247be 100644 --- a/src/doc/en/reference/cryptography/index.rst +++ b/src/doc/en/reference/cryptography/index.rst @@ -2,7 +2,7 @@ Cryptography ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/crypto/cryptosystem diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index af3aad831d4..b95ab97f839 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -17,7 +17,7 @@ Plane conics ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/plane_conics/constructor sage/schemes/plane_conics/con_field @@ -30,7 +30,7 @@ Plane quartics ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/plane_quartics/quartic_constructor sage/schemes/plane_quartics/quartic_generic @@ -39,7 +39,7 @@ Riemann surfaces ================ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/riemann_surfaces/riemann_surface diff --git a/src/doc/en/reference/data_structures/index.rst b/src/doc/en/reference/data_structures/index.rst index 3cecb0fcfda..08c03313ad3 100644 --- a/src/doc/en/reference/data_structures/index.rst +++ b/src/doc/en/reference/data_structures/index.rst @@ -2,7 +2,7 @@ Data Structures =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/misc/binary_tree sage/data_structures/bitset diff --git a/src/doc/en/reference/diophantine_approximation/index.rst b/src/doc/en/reference/diophantine_approximation/index.rst index a1c49919f81..06a90c647aa 100644 --- a/src/doc/en/reference/diophantine_approximation/index.rst +++ b/src/doc/en/reference/diophantine_approximation/index.rst @@ -6,7 +6,7 @@ The diophantine approximation deals with the approximation of real numbers See the article :wikipedia:`Diophantine_approximation` for more information. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/continued_fraction diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 0289ea570d9..7c0fb57bb1e 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -46,6 +46,7 @@ Lattice polyhedra sage/geometry/polyhedron/palp_database sage/geometry/polyhedron/ppl_lattice_polygon sage/geometry/polyhedron/ppl_lattice_polytope + sage/geometry/polyhedron/generating_function Combinatorial Polyhedra ~~~~~~~~~~~~~~~~~~~~~~~ @@ -112,6 +113,7 @@ Backends for Polyhedra sage/geometry/polyhedron/backend_cdd sage/geometry/polyhedron/backend_cdd_rdf sage/geometry/polyhedron/backend_field + sage/geometry/polyhedron/backend_number_field sage/geometry/polyhedron/backend_normaliz sage/geometry/polyhedron/backend_polymake sage/geometry/polyhedron/backend_ppl diff --git a/src/doc/en/reference/doctest/index.rst b/src/doc/en/reference/doctest/index.rst index 88ec7a54e90..e224d33e4ae 100644 --- a/src/doc/en/reference/doctest/index.rst +++ b/src/doc/en/reference/doctest/index.rst @@ -2,7 +2,7 @@ Sage's Doctesting Framework =========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/doctest/control sage/doctest/sources diff --git a/src/doc/en/reference/documentation/index.rst b/src/doc/en/reference/documentation/index.rst index 13d17594db3..4a96c47ed8c 100644 --- a/src/doc/en/reference/documentation/index.rst +++ b/src/doc/en/reference/documentation/index.rst @@ -1,6 +1,9 @@ Documentation System ==================== +Sage Doc Builders +----------------- + .. toctree:: :maxdepth: 1 @@ -10,3 +13,13 @@ Documentation System sage_docbuild/sphinxbuild sage_docbuild/conf sage_docbuild/utils + +Sphinx Extensions +----------------- + +.. toctree:: + :maxdepth: 1 + + sage_docbuild/ext/inventory_builder + sage_docbuild/ext/sage_autodoc + sage_docbuild/ext/multidocs diff --git a/src/doc/en/reference/dynamics/cellular_automata.rst b/src/doc/en/reference/dynamics/cellular_automata.rst index 6197e127e23..846c5e91677 100644 --- a/src/doc/en/reference/dynamics/cellular_automata.rst +++ b/src/doc/en/reference/dynamics/cellular_automata.rst @@ -2,7 +2,7 @@ Cellular Automata ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 ../sage/dynamics/cellular_automata/catalog diff --git a/src/doc/en/reference/dynamics/complex_dynamics.rst b/src/doc/en/reference/dynamics/complex_dynamics.rst index 06496b4d8a0..4ca4066dffd 100644 --- a/src/doc/en/reference/dynamics/complex_dynamics.rst +++ b/src/doc/en/reference/dynamics/complex_dynamics.rst @@ -2,6 +2,6 @@ Plotting of Mandelbrot and Julia Sets ======================================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 ../sage/dynamics/complex_dynamics/mandel_julia diff --git a/src/doc/en/reference/dynamics/index.rst b/src/doc/en/reference/dynamics/index.rst index badd1b5cf3a..83cb7782f3a 100644 --- a/src/doc/en/reference/dynamics/index.rst +++ b/src/doc/en/reference/dynamics/index.rst @@ -4,7 +4,7 @@ Discrete dynamics ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 cellular_automata complex_dynamics @@ -21,7 +21,7 @@ Arithmetic Dynamical Systems ---------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/dynamics/arithmetic_dynamics/generic_ds sage/dynamics/arithmetic_dynamics/affine_ds diff --git a/src/doc/en/reference/euclidean_spaces/index.rst b/src/doc/en/reference/euclidean_spaces/index.rst index 948458201c0..2f5ce8384d7 100644 --- a/src/doc/en/reference/euclidean_spaces/index.rst +++ b/src/doc/en/reference/euclidean_spaces/index.rst @@ -4,8 +4,7 @@ Euclidean Spaces and Vector Calculus ==================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/examples/euclidean - sage/manifolds/operators diff --git a/src/doc/en/reference/finance/index.rst b/src/doc/en/reference/finance/index.rst index bba570a13ae..2c0f62cbf3c 100644 --- a/src/doc/en/reference/finance/index.rst +++ b/src/doc/en/reference/finance/index.rst @@ -2,7 +2,7 @@ Quantitative Finance ====================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/finance/stock sage/finance/option diff --git a/src/doc/en/reference/finite_rings/index.rst b/src/doc/en/reference/finite_rings/index.rst index dddbd66a54f..02e7f69ed8b 100644 --- a/src/doc/en/reference/finite_rings/index.rst +++ b/src/doc/en/reference/finite_rings/index.rst @@ -5,7 +5,7 @@ Finite Rings ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/integer_mod_ring sage/rings/finite_rings/integer_mod @@ -14,7 +14,7 @@ Finite Fields ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_constructor sage/rings/finite_rings/finite_field_base @@ -26,7 +26,7 @@ Prime Fields ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_prime_modn sage/rings/finite_rings/hom_prime_finite_field @@ -35,7 +35,7 @@ Finite Fields Using Pari ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_pari_ffelt sage/rings/finite_rings/element_pari_ffelt @@ -44,7 +44,7 @@ Finite Fields Using Givaro -------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_givaro sage/rings/finite_rings/element_givaro @@ -54,7 +54,7 @@ Finite Fields of Characteristic 2 Using NTL ------------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_ntl_gf2e sage/rings/finite_rings/element_ntl_gf2e @@ -63,7 +63,7 @@ Miscellaneous ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/residue_field sage/rings/algebraic_closure_finite_field diff --git a/src/doc/en/reference/functions/index.rst b/src/doc/en/reference/functions/index.rst index 54769b6a95e..3916eb0c260 100644 --- a/src/doc/en/reference/functions/index.rst +++ b/src/doc/en/reference/functions/index.rst @@ -8,7 +8,7 @@ Built-in Functions ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/functions/log sage/functions/trig diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index 1bee7a0d46d..eb7dab7193c 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -2,7 +2,7 @@ Game Theory =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/game_theory/cooperative_game sage/game_theory/matching_game diff --git a/src/doc/en/reference/games/index.rst b/src/doc/en/reference/games/index.rst index 8511337bb5d..12a7647655b 100644 --- a/src/doc/en/reference/games/index.rst +++ b/src/doc/en/reference/games/index.rst @@ -6,7 +6,7 @@ Rubik's cube solver (see `Rubik's Cube Group <../groups/sage/groups/perm_gps/cubegroup.html>`_). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/games/sudoku sage/games/quantumino diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index dd0de5ba2f6..e3df953c29a 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -2,7 +2,7 @@ Groups ====== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/groups_catalog sage/groups/group @@ -19,11 +19,13 @@ Groups sage/groups/cubic_braid sage/groups/indexed_free_group sage/groups/raag + sage/groups/cactus_group sage/groups/group_exp sage/groups/group_semidirect_product sage/groups/misc_gps/misc_groups sage/groups/semimonomial_transformations/semimonomial_transformation_group sage/groups/semimonomial_transformations/semimonomial_transformation + sage/groups/kernel_subgroup sage/groups/class_function sage/groups/conjugacy_classes @@ -31,7 +33,7 @@ Abelian Groups -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/abelian_gps/abelian_group sage/groups/abelian_gps/abelian_group_gap @@ -51,7 +53,7 @@ Permutation Groups ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/perm_gps/permutation_groups_catalog sage/groups/perm_gps/constructor @@ -66,7 +68,7 @@ Matrix and Affine Groups ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/matrix_gps/catalog sage/groups/matrix_gps/matrix_group @@ -90,7 +92,7 @@ Lie Groups ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/lie_gps/nilpotent_lie_group @@ -98,7 +100,7 @@ Partition Refinement -------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/perm_gps/partn_ref/canonical_augmentation sage/groups/perm_gps/partn_ref/data_structures @@ -110,7 +112,7 @@ Internals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/matrix_gps/named_group diff --git a/src/doc/en/reference/hecke/index.rst b/src/doc/en/reference/hecke/index.rst index 4726b4e1cc9..24f18d9c662 100644 --- a/src/doc/en/reference/hecke/index.rst +++ b/src/doc/en/reference/hecke/index.rst @@ -8,7 +8,7 @@ Symbols <../modsym/index.html>`_ and `Modular Forms <../modfrm/index.html>`_. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/hecke/module sage/modular/hecke/ambient_module diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index 32424286980..8188233a52b 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -5,7 +5,7 @@ Sage includes some tools for algebraic topology, and in particular computing homology groups. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/homology/chain_complex sage/homology/chains diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 9008c78e157..c91bbc02de9 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -119,17 +119,14 @@ Number Fields, Function Fields, and Valuations Number Theory ------------- -* :doc:`Diophantine approximation <diophantine_approximation/index>` +* :doc:`Diophantine Approximation <diophantine_approximation/index>` * :doc:`Quadratic Forms <quadratic_forms/index>` * :doc:`\\(L\\)-Functions <lfunctions/index>` * :doc:`Arithmetic Subgroups of \\({\\rm SL}_2(\\ZZ)\\) <arithgroup/index>` * :doc:`General Hecke Algebras and Hecke Modules <hecke/index>` -* :doc:`Modular Symbols <modsym/index>` * :doc:`Modular Forms <modfrm/index>` -* :doc:`Quasimodular Forms <quasimodfrm/index>` -* :doc:`Modular Forms for Hecke Triangle Groups <modfrm_hecketriangle/index>` +* :doc:`Modular Symbols <modsym/index>` * :doc:`Modular Abelian Varieties <modabvar/index>` -* :doc:`Miscellaneous Modular-Form-Related Modules <modmisc/index>` Algebraic and Arithmetic Geometry --------------------------------- @@ -143,23 +140,23 @@ Miscellaneous * :doc:`Databases <databases/index>` * :doc:`Games <games/index>` -Programming -=========== +Infrastructure +============== -Facilities ----------- +Programming Facilities +---------------------- * :doc:`Data Structures <data_structures/index>` * :doc:`Utilities <misc/index>` * :doc:`Test Framework <doctest/index>` * :doc:`Parallel Computing <parallel/index>` +* :doc:`Python Technicalities <cpython/index>` -Interfaces ----------- +Subsystem Interfaces +-------------------- * :doc:`Interpreter Interfaces <interfaces/index>` * :doc:`C/C++ Library Interfaces <libs/index>` -* :doc:`Python Technicalities <cpython/index>` Documentation System -------------------- diff --git a/src/doc/en/reference/interfaces/index.rst b/src/doc/en/reference/interfaces/index.rst index d007f6778f3..67d69886285 100644 --- a/src/doc/en/reference/interfaces/index.rst +++ b/src/doc/en/reference/interfaces/index.rst @@ -58,11 +58,12 @@ exact actual program available (especially useful for tab completion and testing to make sure nothing funny is going on). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/interfaces/interface sage/interfaces/expect sage/interfaces/sagespawn + sage/interfaces/abc sage/interfaces/axiom sage/interfaces/ecm sage/interfaces/four_ti_2 diff --git a/src/doc/en/reference/knots/index.rst b/src/doc/en/reference/knots/index.rst index 8fc794b4705..ed42964e5a5 100644 --- a/src/doc/en/reference/knots/index.rst +++ b/src/doc/en/reference/knots/index.rst @@ -2,7 +2,7 @@ Knot Theory =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/knots/knot sage/knots/link diff --git a/src/doc/en/reference/lfunctions/index.rst b/src/doc/en/reference/lfunctions/index.rst index 03238db6534..4803abee5c5 100644 --- a/src/doc/en/reference/lfunctions/index.rst +++ b/src/doc/en/reference/lfunctions/index.rst @@ -1,11 +1,11 @@ -L-Functions -=========== +`L`-Functions +============= Sage includes several standard open source packages for computing -with :math:`L`-functions. +with `L`-functions. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/lfunctions/lcalc sage/lfunctions/sympow diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 43da10f063c..fd0295e60c6 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -21,14 +21,14 @@ to be aware of the modules described in this chapter. ECL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/ecl eclib ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/eclib/interface sage/libs/eclib/mwrank @@ -40,7 +40,7 @@ eclib FLINT ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/flint/flint sage/libs/flint/fmpz_poly @@ -49,35 +49,35 @@ FLINT Giac ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/giac GMP-ECM ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/libecm GSL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/gsl/array lcalc ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/lcalc/lcalc_Lfunction libSingular ----------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/singular/function sage/libs/singular/function_factory @@ -90,7 +90,7 @@ libSingular GAP --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/gap/context_managers sage/libs/gap/gap_functions @@ -104,35 +104,35 @@ GAP LinBox ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/linbox/linbox_flint_interface lrcalc ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/lrcalc/lrcalc mpmath ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/mpmath/utils NTL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/ntl/all PARI ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/pari sage/libs/pari/convert_sage @@ -141,12 +141,12 @@ PARI Symmetrica ---------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/symmetrica/symmetrica .. Cannot be imported independently of mpmath: sage/libs/mpmath/ext_main sage/libs/mpmath/ext_impl sage/libs/mpmath/ext_libmp -.. Modules depending on optional packages: sage/libs/coxeter3/coxeter sage/libs/coxeter3/coxeter_group sage/libs/fes sage/libs/homfly sage/libs/braiding +.. Modules depending on optional packages: sage/libs/coxeter3/coxeter sage/libs/coxeter3/coxeter_group sage/libs/homfly sage/libs/braiding .. include:: ../footer.txt diff --git a/src/doc/en/reference/logic/index.rst b/src/doc/en/reference/logic/index.rst index 8628a5e5bbd..bb914195f9b 100644 --- a/src/doc/en/reference/logic/index.rst +++ b/src/doc/en/reference/logic/index.rst @@ -2,7 +2,7 @@ Symbolic Logic ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/logic/propcalc sage/logic/boolformula diff --git a/src/doc/en/reference/manifolds/chart.rst b/src/doc/en/reference/manifolds/chart.rst index d9f38e2fee1..8cd42210dd7 100644 --- a/src/doc/en/reference/manifolds/chart.rst +++ b/src/doc/en/reference/manifolds/chart.rst @@ -2,7 +2,7 @@ Coordinate Charts ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/chart diff --git a/src/doc/en/reference/manifolds/continuous_map.rst b/src/doc/en/reference/manifolds/continuous_map.rst index bd17639a2f4..59c6180099f 100644 --- a/src/doc/en/reference/manifolds/continuous_map.rst +++ b/src/doc/en/reference/manifolds/continuous_map.rst @@ -2,7 +2,7 @@ Continuous Maps =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/manifold_homset diff --git a/src/doc/en/reference/manifolds/degenerate_metric.rst b/src/doc/en/reference/manifolds/degenerate_metric.rst index c4f1ca4f415..2af47655f3a 100644 --- a/src/doc/en/reference/manifolds/degenerate_metric.rst +++ b/src/doc/en/reference/manifolds/degenerate_metric.rst @@ -2,7 +2,7 @@ Degenerate Metric Manifolds =========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/degenerate diff --git a/src/doc/en/reference/manifolds/diff_form.rst b/src/doc/en/reference/manifolds/diff_form.rst index 4ce40bde2ab..e042770f5bd 100644 --- a/src/doc/en/reference/manifolds/diff_form.rst +++ b/src/doc/en/reference/manifolds/diff_form.rst @@ -2,7 +2,7 @@ Differential Forms ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/diff_form_module diff --git a/src/doc/en/reference/manifolds/diff_manifold.rst b/src/doc/en/reference/manifolds/diff_manifold.rst index 31e86015d97..2edb2aee20f 100644 --- a/src/doc/en/reference/manifolds/diff_manifold.rst +++ b/src/doc/en/reference/manifolds/diff_manifold.rst @@ -2,7 +2,7 @@ Differentiable Manifolds ======================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/manifold diff --git a/src/doc/en/reference/manifolds/diff_map.rst b/src/doc/en/reference/manifolds/diff_map.rst index 385248c0506..11177f6004d 100644 --- a/src/doc/en/reference/manifolds/diff_map.rst +++ b/src/doc/en/reference/manifolds/diff_map.rst @@ -2,12 +2,12 @@ Differentiable Maps and Curves ============================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/manifold_homset sage/manifolds/differentiable/diff_map sage/manifolds/differentiable/curve - + sage/manifolds/differentiable/integrated_curve diff --git a/src/doc/en/reference/manifolds/diff_scalarfield.rst b/src/doc/en/reference/manifolds/diff_scalarfield.rst index 9eb217c7547..f1d86cbd221 100644 --- a/src/doc/en/reference/manifolds/diff_scalarfield.rst +++ b/src/doc/en/reference/manifolds/diff_scalarfield.rst @@ -2,7 +2,7 @@ Scalar Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/scalarfield_algebra diff --git a/src/doc/en/reference/manifolds/diff_vector_bundle.rst b/src/doc/en/reference/manifolds/diff_vector_bundle.rst index fdfdf39df61..d237f8ffca6 100644 --- a/src/doc/en/reference/manifolds/diff_vector_bundle.rst +++ b/src/doc/en/reference/manifolds/diff_vector_bundle.rst @@ -2,7 +2,7 @@ Differentiable Vector Bundles ============================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/vector_bundle diff --git a/src/doc/en/reference/manifolds/euclidean_space.rst b/src/doc/en/reference/manifolds/euclidean_space.rst index 6ee1c2c820f..45c592d39a8 100644 --- a/src/doc/en/reference/manifolds/euclidean_space.rst +++ b/src/doc/en/reference/manifolds/euclidean_space.rst @@ -4,7 +4,7 @@ Euclidean Spaces and Vector Calculus ==================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/examples/euclidean diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst index 1384b507bf3..055ccebca68 100644 --- a/src/doc/en/reference/manifolds/manifold.rst +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -2,7 +2,7 @@ Topological Manifolds ===================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/manifold diff --git a/src/doc/en/reference/manifolds/mixed_form.rst b/src/doc/en/reference/manifolds/mixed_form.rst index 0afbec4f527..51c98b61f15 100644 --- a/src/doc/en/reference/manifolds/mixed_form.rst +++ b/src/doc/en/reference/manifolds/mixed_form.rst @@ -2,7 +2,7 @@ Mixed Differential Forms ======================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/mixed_form_algebra diff --git a/src/doc/en/reference/manifolds/multivector.rst b/src/doc/en/reference/manifolds/multivector.rst index fe2c90815cb..1199ea1fdad 100644 --- a/src/doc/en/reference/manifolds/multivector.rst +++ b/src/doc/en/reference/manifolds/multivector.rst @@ -2,7 +2,7 @@ Alternating Multivector Fields ============================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/multivector_module diff --git a/src/doc/en/reference/manifolds/scalarfield.rst b/src/doc/en/reference/manifolds/scalarfield.rst index 3ba7a2e1e4e..62100d7feb3 100644 --- a/src/doc/en/reference/manifolds/scalarfield.rst +++ b/src/doc/en/reference/manifolds/scalarfield.rst @@ -2,7 +2,7 @@ Scalar Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/scalarfield_algebra diff --git a/src/doc/en/reference/manifolds/tangent_space.rst b/src/doc/en/reference/manifolds/tangent_space.rst index 6c9e17899b5..8e55de2dbf6 100644 --- a/src/doc/en/reference/manifolds/tangent_space.rst +++ b/src/doc/en/reference/manifolds/tangent_space.rst @@ -2,7 +2,7 @@ Tangent Spaces ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/tangent_space diff --git a/src/doc/en/reference/manifolds/tensorfield.rst b/src/doc/en/reference/manifolds/tensorfield.rst index 6710757d5df..cf70207f185 100644 --- a/src/doc/en/reference/manifolds/tensorfield.rst +++ b/src/doc/en/reference/manifolds/tensorfield.rst @@ -2,7 +2,7 @@ Tensor Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/tensorfield_module diff --git a/src/doc/en/reference/manifolds/vector_bundle.rst b/src/doc/en/reference/manifolds/vector_bundle.rst index 85905cbec94..f52b203609e 100644 --- a/src/doc/en/reference/manifolds/vector_bundle.rst +++ b/src/doc/en/reference/manifolds/vector_bundle.rst @@ -2,7 +2,7 @@ Topological Vector Bundles ========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/vector_bundle diff --git a/src/doc/en/reference/manifolds/vectorfield.rst b/src/doc/en/reference/manifolds/vectorfield.rst index df9817539df..d489df1669a 100644 --- a/src/doc/en/reference/manifolds/vectorfield.rst +++ b/src/doc/en/reference/manifolds/vectorfield.rst @@ -2,7 +2,7 @@ Vector Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/vectorfield_module diff --git a/src/doc/en/reference/matrices/index.rst b/src/doc/en/reference/matrices/index.rst index 6dc2a83a821..25aed6c2cb3 100644 --- a/src/doc/en/reference/matrices/index.rst +++ b/src/doc/en/reference/matrices/index.rst @@ -43,7 +43,7 @@ Finally, this module contains some data-structures for matrix-like objects like operation tables (e.g. the multiplication table of a group). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/matrix/matrix_space diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index a78a7f2e729..8a26fcb9a00 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -156,7 +156,6 @@ Features sage/features/csdp sage/features/databases sage/features/dvipng - sage/features/fes sage/features/ffmpeg sage/features/four_ti_2 sage/features/gap diff --git a/src/doc/en/reference/modabvar/index.rst b/src/doc/en/reference/modabvar/index.rst index 768ede8e1f4..efad7a2c386 100644 --- a/src/doc/en/reference/modabvar/index.rst +++ b/src/doc/en/reference/modabvar/index.rst @@ -2,7 +2,7 @@ Modular Abelian Varieties ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/abvar/constructor sage/modular/abvar/abvar diff --git a/src/doc/en/reference/modfrm/index.rst b/src/doc/en/reference/modfrm/index.rst index c4b3cfa9c09..cdda21d5b5c 100644 --- a/src/doc/en/reference/modfrm/index.rst +++ b/src/doc/en/reference/modfrm/index.rst @@ -1,11 +1,11 @@ Modular Forms ============= -Module List ------------ +Modular Forms for Arithmetic Groups +----------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modform/constructor sage/modular/modform/space @@ -28,12 +28,68 @@ Module List sage/modular/modform/j_invariant sage/modular/modform/theta -Design Notes ------------- + sage/modular/modform/notes + +Modular Forms for Hecke Triangle Groups +--------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 - sage/modular/modform/notes + sage/modular/modform_hecketriangle/readme + + sage/modular/modform_hecketriangle/abstract_ring + sage/modular/modform_hecketriangle/abstract_space + + sage/modular/modform_hecketriangle/element + sage/modular/modform_hecketriangle/graded_ring_element + + sage/modular/modform_hecketriangle/constructor + sage/modular/modform_hecketriangle/functors + + sage/modular/modform_hecketriangle/hecke_triangle_groups + sage/modular/modform_hecketriangle/hecke_triangle_group_element + sage/modular/modform_hecketriangle/analytic_type + + sage/modular/modform_hecketriangle/graded_ring + sage/modular/modform_hecketriangle/space + sage/modular/modform_hecketriangle/subspace + + sage/modular/modform_hecketriangle/series_constructor + +Quasimodular Forms +--------------------------------------- + +.. toctree:: + :maxdepth: 1 + + sage/modular/quasimodform/ring + sage/modular/quasimodform/element + +Miscellaneous Modules (to be sorted) +------------------------------------ + +.. toctree:: + :maxdepth: 1 + + sage/modular/dirichlet + sage/modular/cusps + sage/modular/dims + sage/modular/buzzard + + sage/modular/local_comp/local_comp + sage/modular/local_comp/smoothchar + sage/modular/local_comp/type_space + sage/modular/local_comp/liftings + + sage/modular/etaproducts + sage/modular/overconvergent/weightspace + sage/modular/overconvergent/genus0 + sage/modular/overconvergent/hecke_series + sage/modular/ssmod/ssmod + sage/modular/quatalg/brandt + sage/modular/cusps_nf + sage/modular/hypergeometric_motive + sage/modular/multiple_zeta .. include:: ../footer.txt diff --git a/src/doc/en/reference/modfrm_hecketriangle/conf.py b/src/doc/en/reference/modfrm_hecketriangle/conf.py deleted file mode 120000 index 2bdf7e68470..00000000000 --- a/src/doc/en/reference/modfrm_hecketriangle/conf.py +++ /dev/null @@ -1 +0,0 @@ -../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/modfrm_hecketriangle/index.rst b/src/doc/en/reference/modfrm_hecketriangle/index.rst deleted file mode 100644 index 32368bb5aaa..00000000000 --- a/src/doc/en/reference/modfrm_hecketriangle/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -Modular Forms for Hecke Triangle Groups -======================================= - - -.. toctree:: - :maxdepth: 2 - - sage/modular/modform_hecketriangle/readme - - sage/modular/modform_hecketriangle/abstract_ring - sage/modular/modform_hecketriangle/abstract_space - - sage/modular/modform_hecketriangle/element - sage/modular/modform_hecketriangle/graded_ring_element - - sage/modular/modform_hecketriangle/constructor - sage/modular/modform_hecketriangle/functors - - sage/modular/modform_hecketriangle/hecke_triangle_groups - sage/modular/modform_hecketriangle/hecke_triangle_group_element - sage/modular/modform_hecketriangle/analytic_type - - sage/modular/modform_hecketriangle/graded_ring - sage/modular/modform_hecketriangle/space - sage/modular/modform_hecketriangle/subspace - - sage/modular/modform_hecketriangle/series_constructor - - -.. include:: ../footer.txt diff --git a/src/doc/en/reference/modmisc/conf.py b/src/doc/en/reference/modmisc/conf.py deleted file mode 120000 index 2bdf7e68470..00000000000 --- a/src/doc/en/reference/modmisc/conf.py +++ /dev/null @@ -1 +0,0 @@ -../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/modmisc/index.rst b/src/doc/en/reference/modmisc/index.rst deleted file mode 100644 index a465c78c3ad..00000000000 --- a/src/doc/en/reference/modmisc/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -Miscellaneous Modular-Form-Related Modules -========================================== - -.. toctree:: - :maxdepth: 2 - - sage/modular/dirichlet - sage/modular/cusps - sage/modular/dims - sage/modular/buzzard - - sage/modular/local_comp/local_comp - sage/modular/local_comp/smoothchar - sage/modular/local_comp/type_space - sage/modular/local_comp/liftings - - sage/modular/etaproducts - sage/modular/overconvergent/weightspace - sage/modular/overconvergent/genus0 - sage/modular/overconvergent/hecke_series - sage/modular/ssmod/ssmod - sage/modular/quatalg/brandt - - sage/modular/cusps_nf - - sage/modular/hypergeometric_motive - - sage/modular/multiple_zeta - -.. include:: ../footer.txt diff --git a/src/doc/en/reference/modsym/index.rst b/src/doc/en/reference/modsym/index.rst index d23bf9b0224..e5b54be0644 100644 --- a/src/doc/en/reference/modsym/index.rst +++ b/src/doc/en/reference/modsym/index.rst @@ -2,7 +2,7 @@ Modular Symbols =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modsym/modsym @@ -36,7 +36,7 @@ Overconvergent modular symbols ---------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/pollack_stevens/space sage/modular/pollack_stevens/distributions diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 643a669a6b0..c6109643c35 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -4,7 +4,7 @@ Modules Sage provides modules of various kinds over various base rings. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/tutorial_free_modules @@ -13,7 +13,7 @@ Free modules, submodules, and quotients --------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/module sage/modules/free_module @@ -27,7 +27,7 @@ Modules with basis ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/with_basis/__init__ sage/modules/with_basis/cell_module @@ -41,7 +41,7 @@ Finitely generated modules over a PID ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/fg_pid/fgp_module sage/modules/fg_pid/fgp_element @@ -51,7 +51,7 @@ Finitely presented graded modules --------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/fp_graded/free_module sage/modules/fp_graded/free_element @@ -69,7 +69,7 @@ Special modules --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/free_module_integer sage/modules/free_quadratic_module @@ -82,7 +82,7 @@ Morphisms --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/vector_space_homspace sage/modules/vector_space_morphism @@ -96,7 +96,7 @@ Vectors ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/vector_integer_dense sage/modules/vector_mod2_dense @@ -114,7 +114,7 @@ Misc ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/diamond_cutting sage/modules/tensor_operations diff --git a/src/doc/en/reference/monoids/index.rst b/src/doc/en/reference/monoids/index.rst index 0e7279db6d3..fa394698b28 100644 --- a/src/doc/en/reference/monoids/index.rst +++ b/src/doc/en/reference/monoids/index.rst @@ -6,7 +6,7 @@ finite number of indeterminates, as well as free partially commutative monoids (trace monoids). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/monoids/monoid sage/monoids/free_monoid diff --git a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst index 9848988d126..1915006f832 100644 --- a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst +++ b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst @@ -1,11 +1,11 @@ Noncommutative Polynomials ========================== -Univariate Ore polynomial rings -------------------------------- +Univariate Ore Polynomials +-------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/ore_polynomial_ring sage/rings/polynomial/ore_polynomial_element @@ -13,13 +13,6 @@ Univariate Ore polynomial rings sage/rings/polynomial/skew_polynomial_element sage/rings/polynomial/skew_polynomial_finite_order sage/rings/polynomial/skew_polynomial_finite_field - -Fraction field of Ore polynomial rings --------------------------------------- - -.. toctree:: - :maxdepth: 2 - sage/rings/polynomial/ore_function_field sage/rings/polynomial/ore_function_element diff --git a/src/doc/en/reference/padics/index.rst b/src/doc/en/reference/padics/index.rst index 68b68bd3922..6efcee751a6 100644 --- a/src/doc/en/reference/padics/index.rst +++ b/src/doc/en/reference/padics/index.rst @@ -2,7 +2,7 @@ ======================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/padics/tutorial diff --git a/src/doc/en/reference/parallel/index.rst b/src/doc/en/reference/parallel/index.rst index 6900e061a94..13b16a23a53 100644 --- a/src/doc/en/reference/parallel/index.rst +++ b/src/doc/en/reference/parallel/index.rst @@ -2,7 +2,7 @@ Parallel Computing ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/parallel/decorate sage/parallel/reference diff --git a/src/doc/en/reference/plot3d/index.rst b/src/doc/en/reference/plot3d/index.rst index 2cbabe1b75c..cf8ab828276 100644 --- a/src/doc/en/reference/plot3d/index.rst +++ b/src/doc/en/reference/plot3d/index.rst @@ -2,7 +2,7 @@ =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/introduction @@ -10,7 +10,7 @@ Function and Data Plots ----------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/plot3d sage/plot/plot3d/parametric_plot3d @@ -24,7 +24,7 @@ Basic Shapes and Primitives --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/base sage/plot/plot3d/shapes @@ -37,7 +37,7 @@ Infrastructure -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/texture sage/plot/plot3d/index_face_set @@ -48,7 +48,7 @@ Backends -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/tachyon threejs diff --git a/src/doc/en/reference/plotting/index.rst b/src/doc/en/reference/plotting/index.rst index 662c5678481..1c1c4a03b17 100644 --- a/src/doc/en/reference/plotting/index.rst +++ b/src/doc/en/reference/plotting/index.rst @@ -5,7 +5,7 @@ General ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot sage/plot/text @@ -16,7 +16,7 @@ Function and Data Plots ----------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/complex_plot sage/plot/contour_plot @@ -32,7 +32,7 @@ Plots of Other Mathematical Objects ----------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/graphs/graph_plot sage/plot/matrix_plot @@ -41,7 +41,7 @@ Basic Shapes ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/arc sage/plot/arrow @@ -60,7 +60,7 @@ Infrastructure and Low-Level Functions -------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/graphics sage/plot/multigraphics diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index bbdf14685ca..8a8ef70563c 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -5,7 +5,7 @@ Polynomial Rings ---------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/polynomial_ring_constructor @@ -13,7 +13,7 @@ Univariate Polynomials ---------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 polynomial_rings_univar sage/rings/polynomial/convolution @@ -23,7 +23,7 @@ Multivariate Polynomials ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 polynomial_rings_multivar invariant_theory diff --git a/src/doc/en/reference/polynomial_rings/invariant_theory.rst b/src/doc/en/reference/polynomial_rings/invariant_theory.rst index e57e1226eed..48cca8f9cd0 100644 --- a/src/doc/en/reference/polynomial_rings/invariant_theory.rst +++ b/src/doc/en/reference/polynomial_rings/invariant_theory.rst @@ -3,7 +3,7 @@ Classical Invariant Theory ========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/invariants/invariant_theory sage/rings/invariants/reconstruction diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst index 6147929ccf2..9a2ebf494ae 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst @@ -20,7 +20,7 @@ are implemented using the PolyBoRi library (cf. :mod:`sage.rings.polynomial.pbor .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/term_order diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst index 1a27c451f23..baa14664c08 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst @@ -3,7 +3,7 @@ Educational Versions of Groebner Basis Related Algorithms ========================================================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/toy_buchberger sage/rings/polynomial/toy_variety diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst index bd55ed8b014..414c04bb611 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst @@ -12,7 +12,7 @@ than pure Python classes and thus can only inherit from a single base class, whereas others have multiple bases. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/polynomial_ring sage/rings/polynomial/polynomial_ring_homomorphism diff --git a/src/doc/en/reference/power_series/index.rst b/src/doc/en/reference/power_series/index.rst index dba377477f1..6ab1c2004e3 100644 --- a/src/doc/en/reference/power_series/index.rst +++ b/src/doc/en/reference/power_series/index.rst @@ -2,7 +2,7 @@ Power Series Rings and Laurent Series Rings =========================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/power_series_ring sage/rings/power_series_ring_element diff --git a/src/doc/en/reference/probability/index.rst b/src/doc/en/reference/probability/index.rst index 633219bddbf..da9318b3398 100644 --- a/src/doc/en/reference/probability/index.rst +++ b/src/doc/en/reference/probability/index.rst @@ -2,7 +2,7 @@ Probability =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/probability/probability_distribution sage/probability/random_variable diff --git a/src/doc/en/reference/quadratic_forms/index.rst b/src/doc/en/reference/quadratic_forms/index.rst index 65fb2563115..7169e7ac503 100644 --- a/src/doc/en/reference/quadratic_forms/index.rst +++ b/src/doc/en/reference/quadratic_forms/index.rst @@ -2,7 +2,7 @@ Quadratic Forms =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/quadratic_forms/quadratic_form sage/quadratic_forms/binary_qf diff --git a/src/doc/en/reference/quasimodfrm/conf.py b/src/doc/en/reference/quasimodfrm/conf.py deleted file mode 120000 index 2bdf7e68470..00000000000 --- a/src/doc/en/reference/quasimodfrm/conf.py +++ /dev/null @@ -1 +0,0 @@ -../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/quasimodfrm/index.rst b/src/doc/en/reference/quasimodfrm/index.rst deleted file mode 100644 index 4fb81ca671d..00000000000 --- a/src/doc/en/reference/quasimodfrm/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -Quasimodular Forms -================== - -Module List ------------ - -.. toctree:: - :maxdepth: 2 - - sage/modular/quasimodform/ring - sage/modular/quasimodform/element - -.. include:: ../footer.txt diff --git a/src/doc/en/reference/quat_algebras/index.rst b/src/doc/en/reference/quat_algebras/index.rst index 110d732342b..1cdef1b7cea 100644 --- a/src/doc/en/reference/quat_algebras/index.rst +++ b/src/doc/en/reference/quat_algebras/index.rst @@ -2,7 +2,7 @@ Quaternion Algebras =================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/quatalg/quaternion_algebra sage/algebras/quatalg/quaternion_algebra_element diff --git a/src/doc/en/reference/quivers/index.rst b/src/doc/en/reference/quivers/index.rst index b1e3aa48f87..98bf561c4dc 100644 --- a/src/doc/en/reference/quivers/index.rst +++ b/src/doc/en/reference/quivers/index.rst @@ -2,7 +2,7 @@ Quivers ======= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/quivers/algebra sage/quivers/algebra_elements diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index cc87e5490cb..5a7eb0a163c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -450,6 +450,10 @@ REFERENCES: .. [BCDM2019] \T. Beyne, Y. L. Chen, C. Dobraunig, B. Mennink. *Elephant v1* (2019) https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/elephant-spec.pdf +.. [BCL2022] Paolo Bellingeri, Hugo Chemin, and Victoria Lebed. + *Cactus groups, twin groups, and right-angled Artin groups*. + Preprint, :arxiv:`2209.08813` (2022). + .. [BeBo2009] Olivier Bernardi and Nicolas Bonichon, *Intervals in Catalan lattices and realizers of triangulations*, JCTA 116 (2009) @@ -483,6 +487,10 @@ REFERENCES: "PHOTON-BeetleAuthenticated Encryption and Hash Family" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/PHOTON-Beetle-spec.pdf +.. [BH1965] \L. D. Baumert, M. Hall Jr. + *A new construction for Hadamard matrices*. + Bulletin of the American Mathematical Society 71(1):169-170, 1965. + .. [BH2012] \A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, @@ -616,6 +624,11 @@ REFERENCES: and variants*. :arxiv:`1810.00789` +.. [BDKR2013] \D. Best, D.Z. Dokovic, H. Kharaghani and H. Ramp. + *Turyn-Type Sequences: Classification, Enumeration, and Construction*, + Journal of Combinatorial Designs 21(1) (2013): 24-35. + :doi:`10.1002/jcd.21318` + .. [BDLV2006] \S. Brlek, S. Dulucq, A. Ladouceur, L. Vuillon, *Combinatorial properties of smooth infinite words*, Theoret. Comput. Sci. 352 (2006) 306--317. @@ -1765,6 +1778,11 @@ REFERENCES: graphs from orthogonal groups* `O^+(6,2)` *and* `O^-(6,2)`. :arxiv:`1609.07133` +.. [CRSKKY1989] \G. Cohen, D. Rubie, J. Seberry, C. Koukouvinos, S. Kounias, M. Yamada, + *A survey of base sequences, disjoint complementary sequences and OD(4t;t,t,t,t)*. + JCMCC. The Journal of Combinatorial Mathematics and Combinatorial Computing, + **5** (1989), 69-104. + .. [CRST2006] \M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. *The strong perfect graph theorem*. Annals of Mathematics, vol 164, number 1, pages 51--230, 2006. @@ -1784,11 +1802,11 @@ REFERENCES: and groups*, 3rd. ed., Grundlehren der Mathematischen Wissenschaften, vol. 290, Springer-Verlag, New York, 1999. -.. [CS1988] Conway, J. H., and N. J. A. Sloane. “Low-Dimensional Lattices. IV. - The Mass Formula.” Proceedings of the Royal Society of London. +.. [CS1988] Conway, J. H., and N. J. A. Sloane. *Low-Dimensional Lattices. IV. + The Mass Formula*, Proceedings of the Royal Society of London. Series A, Mathematical and Physical Sciences, vol. 419, no. 1857, 1988, pp. 259-286. -.. [CS2003] \John E. Cremona and Michael Stoll. On The Reduction Theory of Binary Forms. +.. [CS2003] \John E. Cremona and Michael Stoll. *On The Reduction Theory of Binary Forms*. Journal für die reine und angewandte Mathematik, 565 (2003), 79-99. .. [CS2006] \J. E. Cremona, and S. Siksek, Computing a Lower Bound for the @@ -1833,9 +1851,22 @@ REFERENCES: *Signature-based algorithms for Gröbner bases over Tate algebras*, :arxiv:`2002.04491` (2020) +.. [CW1972] \J. Cooper and J. Wallis. + *A construction for Hadamard arrays*, + Bulletin of the Australian Mathematical Society 7(2) (1972): 269-277. + :doi:`10.1017/S0004972700045019` + .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. +.. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation + with weakly integral anyons. Quantum Inf. Process. 14 (2015), + no. 8, 2687-2727. + +.. [CW2015] Cui, S. X. and Wang, Z. (2015). Universal quantum computation with + metaplectic anyons. Journal of Mathematical Physics, 56(3), 032202. + :doi:`10.1063/1.4914941` + .. _ref-D: **D** @@ -1848,6 +1879,11 @@ REFERENCES: *Introduction to Lattices and Order*, Cambridge University Press, 1997. +.. [DJS2003] \M. Davis, T. Januszkiewicz, and R. Scott. + *Fundamental groups of blow-ups*. Selecta Math., + Adv. Math. ***177** no. 1 (2002) pp. 115-179. + :arxiv:`math/0203127`. + .. [DB1996] K. Duggal, A. Bejancu, *Lightlike Submanifolds of Semi-Riemannian Manifolds and Applications*, Mathematics and Its Applications, 1996. @@ -2005,6 +2041,15 @@ REFERENCES: and some constructions of de Luca and Rauzy*, Theoret. Comput. Sci. 255 (2001) 539--553. +.. [Djo1992] \D. Đoković. + *Construction of some new Hadamard matrices*, + Bulletin of the Australian Mathematical Society 45(2) (1992): 327-332. + :doi:`10.1017/S0004972700030185` + +.. [Djo1994] \D. Đoković. + *Five New Orders for Hadamard Matrices of Skew Type*, + Australasian Journal of Combinatorics 10 (1994): 259-264. + .. [DK2013] John R. Doyle and David Krumm, *Computing algebraic numbers of bounded height*, :arxiv:`1111.4963v4` (2013). @@ -2160,6 +2205,11 @@ REFERENCES: .. [Early2017] Nick Early. *Canonical bases for permutohedral plates*. Preprint (2017). :arxiv:`1712.08520v3`. +.. [EB1966] \J. Elliot and A. Butson. + *Relative difference sets*, + Illinois Journal of Mathematics 10(3) (1966): 517-531. + :doi:`10.1215/ijm/1256055004` + .. [Eb1989] \W. Eberly, "Computations for algebras and group representations". Ph.D. Thesis, University of Toronto, 1989. http://www.cpsc.ucalgary.ca/~eberly/Research/Papers/phdthesis.pdf @@ -2771,6 +2821,10 @@ REFERENCES: Can. J. Math. 22 (1970) 597-614. :doi:`10.4153/CJM-1970-067-9` +.. [GS70s] \J.M. Goethals and J. J. Seidel, + *A skew Hadamard matrix of order 36*, + J. Aust. Math. Soc. 11(1970), 343-344 + .. [GS1975] \J.M. Goethals, and J. J. Seidel, *The regular two-graph on 276 vertices*, Discrete Mathematics 12, no. 2 (1975): 143-158. @@ -2827,8 +2881,13 @@ REFERENCES: .. [Ha2005] Gerhard Haring. [Online] Available: http://osdir.com/ml/python.db.pysqlite.user/2005-11/msg00047.html +.. [Ha83] \M. Hall, *Combinatorial Theory*, + 2nd edition, Wiley, 1983 + .. [Hac2016] \M. Hachimori. http://infoshako.sk.tsukuba.ac.jp/~hachi/math/library/dunce_hat_eng.html +.. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` + .. [Haf2004] Paul R. Hafner. *On the Graphs of Hoffman-Singleton and Higman-Sims*. The Electronic Journal of Combinatorics 11 (2004), #R77. http://www.combinatorics.org/Volume_11/PDF/v11i1r77.pdf @@ -3017,6 +3076,9 @@ REFERENCES: *The algebra of binary search trees*, :arxiv:`math/0401089v2`. +.. [Hora] \K. J. Horadam, *Hadamard Matrices and Their Applications*, + Princeton University Press, 2006. + .. [HP2003] \W. C. Huffman, V. Pless, Fundamentals of Error-Correcting Codes, Cambridge Univ. Press, 2003. @@ -3636,6 +3698,9 @@ REFERENCES: cohomologie*, in *Élie Cartan et les mathématiques d'aujourd'hui*, Astérisque hors série (1985), p. 257 +.. [KoSt08] \C. Koukouvinos, S. Stylianou, *On skew-Hadamard matrices*, + Discrete Math. **308** (2008) 2723-2731 + .. [KP2002] Volker Kaibel and Marc E. Pfetsch, "Computing the Face Lattice of a Polytope from its Vertex-Facet Incidences", Computational Geometry: Theory and Applications, Volume @@ -3679,6 +3744,10 @@ REFERENCES: and its use for certified homotopy continuation of systems of plane algebraic curves, :arxiv:`1505.03432` +.. [Krumm2016] Daid Krumm, *Computing Points of Bounded Height in Projective Space over a Number Field*, + MATHEMATICS OF COMPUTATION, Volume 85, Number 297, January 2016, Pages 423–447. + http://dx.doi.org/10.1090/mcom/2984 + .. [KR2001] \J. Kahane and A. Ryba. *The hexad game*, Electronic Journal of Combinatorics, **8** (2001). http://www.combinatorics.org/Volume_8/Abstracts/v8i2r11.html @@ -3743,6 +3812,11 @@ REFERENCES: .. [KT2013] \K. Tsukazaki, Explicit Isogenies of Elliptic Curves, PhD thesis, University of Warwick, 2013. +.. [KTR2005] \H. Kharaghani and B. Tayfeh-Rezaie. + *A Hadamard matrix of order 428*, + Journal of Combinatorial Designs 13(6) (2005): 435-440. + :doi:`10.1002/jcd.20043` + .. [KTT2006] \A. Kuniba, T. Takagi, and A. Takenouchi, *Bethe ansatz and inverse scattering transform in a periodic box-ball system*, Nuclear Phys. B **747**, no. 3 (2006), 354--397. @@ -4209,6 +4283,7 @@ REFERENCES: .. [MagmaHGM] *Hypergeometric motives* in Magma, http://magma.maths.usyd.edu.au/~watkins/papers/HGM-chapter.pdf + .. [Mar1980] Jacques Martinet, Petits discriminants des corps de nombres, Journ. Arithm. 1980, Cambridge Univ. Press, 1982, 151--193. @@ -4352,6 +4427,10 @@ REFERENCES: leurs modules*. Effective methods in algebraic geometry (Castiglioncello, 1990), 313--334, Progr. Math., 94, Birkhauser Boston, Boston, MA, 1991. +.. [MG1992] \J. Misra and D. Gries. *A constructive proof of Vizing's theorem*. + Information Processing Letters, (3) 41 (1992), 131-133. + :doi:`10.1016/0020-0190(92)90041-S`. + .. [Mil1958] \J. W. Milnor, *The Steenrod algebra and its dual*, Ann. of Math. (2) 67 (1958), 150-171. @@ -4376,6 +4455,9 @@ REFERENCES: .. [MirMor2009] \R. Miranda, D.R. Morrison, "Embeddings of Integral Quadratic Forms" http://www.math.ucsb.edu/~drm/manuscripts/eiqf.pdf . +.. [Mit2008] \A. Mitra. *On the construction of M-sequences via primitive polynomials with a fast identification method*, + International Journal of Electronics and Communication Engineering 2(9) (2008): 1991-1996. + .. [MKO1998] Hans Munthe--Kaas and Brynjulf Owren. *Computations in a free Lie algebra*. (1998). `Downloadable from Munthe-Kaas's website @@ -4395,6 +4477,11 @@ REFERENCES: *Symmetric cyclotomic Hecke algebras* J. Algebra. **205** (1998) pp. 275-293. +.. [MM2008] Manel Maia and Miguel Méndez. + On the arithmetic product of combinatorial species. + Discrete Mathematics (2008), Volume 308, Issue 23, pp. 5407-5427, + :arxiv:`math/0503436v2`. + .. [MM2015] \J. Matherne and \G. Muller, *Computing upper cluster algebras*, Int. Math. Res. Not. IMRN, 2015, 3121-3149. @@ -4402,6 +4489,11 @@ REFERENCES: of graphs*, Linear Algebra and its Applications 103 (1988), 119–131. +.. [Most2019] Jacob Mostovoy. + *The pure cactus group is residually nilpotent*. + Archiv der Math., **113** (2019). pp. 229-235. + :arxiv:`1804.09165`. + .. [MNO1994] Alexander Molev, Maxim Nazarov, and Grigori Olshanski. *Yangians and classical Lie algebras*. (1994) :arxiv:`hep-th/9409025` @@ -5096,6 +5188,9 @@ REFERENCES: .. [RSS] :wikipedia:`Residual_sum_of_squares`, accessed 13th October 2009. +.. [RS1995] Victor Reiner, Mark Shimozono, *Plactification*, + J. Algebraic Combin. **4** (1995), pp. 331-351. + .. [RS2012] G. Rudolph and M. Schmidt, "Differential Geometry and Mathematical Physics. Part I. Manifolds, Lie Groups and Hamiltonian Systems", Springer, 2012. @@ -5190,6 +5285,11 @@ REFERENCES: .. [Sam2012] \P. Samanta: *Antipodal Graphs* :doi:`10.13140/RG.2.2.28238.46409` +.. [Saw1985] \K. Sawade. + *A Hadamard matrix of order 268*, + Graphs and Combinatorics 1(1) (1985): 185-187. + :doi:`10.1007/BF02582942` + .. [Sch1961] Craige Schensted. *Longest increasing and decreasing subsequences*, Canadian Journal of Mathematics, Vol 13 (1961), pp. 179--191. @@ -5236,6 +5336,10 @@ REFERENCES: *Wide-open encryption design offers flexible implementations*; in Cryptologia, (1985), pp. 75-91. +.. [Seb2017] \J. Seberry, + *Orthogonal designs: Hadamard matrices, quadratic forms and algebras*. + Springer 2017. :doi:`10.1007/978-3-319-59032-5` + .. [SE1962] \N. E. Steenrod and D. B. A. Epstein, Cohomology operations, Ann. of Math. Stud. 50 (Princeton University Press, 1962). @@ -5297,6 +5401,12 @@ REFERENCES: automorphic functions*. Publications of the Mathematical Society of Japan and Princeton University Press, 1971. +.. [Sho1999] Victor Shoup: *Efficient computation of minimal polynomials in + algebraic extensions of finite fields*. + In ISSAC '99, pp. 53–58. ACM, 1999. + :doi:`10.1145/309831.309859`, + https://shoup.net/papers/mpol.pdf + .. [Shr2004] \S. Shreve, *Stochastic Calculus for Finance II: Continuous-Time Models*. New York: Springer, 2004 @@ -5336,6 +5446,8 @@ REFERENCES: systems: Algorithmic, game-theoretic, and logical foundations.* Cambridge University Press, 2008. +.. [SloaHada] \N.J.A. Sloane's Library of Hadamard Matrices, at https://neilsloane.com/hadamard/ + .. [SMMK2013] \T. Suzaki, K. Minematsu, S. Morioka, and E. Kobayashi, *TWINE: A lightweight block cipher for multiple platforms*; in SAC, (2012), pp. 338-354. @@ -5512,6 +5624,11 @@ REFERENCES: matrices, and characteristic polynomials without division* :doi:`10.1023/A:1021878507303` +.. [Spe1975] \E. Spence. + *Hadamard matrices from relative difference sets*, + Journal of Combinatorial Theory, Series A 19(3) (1975): 287-300. + :doi:`10.1016/0097-3165(75)90054-0` + .. [ST1981] \J. J. Seidel and D. E. Taylor, *Two-graphs, a second survey*. Algebraic methods in graph theory, Vol. I, II (Szeged, 1978), @@ -5803,6 +5920,14 @@ REFERENCES: .. [TOPCOM] \J. Rambau, TOPCOM <http://www.rambau.wm.uni-bayreuth.de/TOPCOM/>. +.. [TTWL2009] Trebst, Troyer, Wang and Ludwig, A short introduction to + Fibonacci anyon models, :arxiv:`0902.3275`. + +.. [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. + :doi:`10.1016/0097-3165(74)90056-9` + .. [TW1980] \A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) @@ -5988,6 +6113,11 @@ REFERENCES: pages 150--168, 1932, `available on JSTOR <http://www.jstor.org/stable/2371086>`_ +.. [White2015] Noah White. + *The monodromy of real Bethe vectors for the Gaudin model*. + J. Combin. Algebra, **2** no. 3 (2018). pp. 259-300. + :arxiv:`1511.04740`. + .. [Wich1997] Tim Wichmann. Der FGLM Algorithmus - verallgemeinert und implementiert in Singular Diploma Thesis (University of Kaiserslautern), 1997. @@ -6053,9 +6183,12 @@ REFERENCES: *Numerical modular symbols for elliptic curves*. Math. Comp. 87 (2018), no. 313, 2393–2423. +.. [Wall71] \J. Wallis, *A skew-Hadamard matrix of order 92*, + Bull. Aust. Math. Soc. **5** (1971), 203-204 + .. [WW1972] \J. Wallis and A.L. Whiteman, Some classes of Hadamard matrices with constant diagonal, - Bull. Austral. Math. Soc. 7(1972), 233-249 + Bull. Austral. Math. Soc. **7** (1972), 233-249 .. [WW1991] Michelle Wachs and Dennis White, *p, q-Stirling numbers and set partition statistics*, Journal of Combinatorial @@ -6099,6 +6232,9 @@ REFERENCES: .. [Yu2007] \K. Yu, *p-adic logarithmic forms and group varieties. III.* Forum Math., 19(2):187–280, 2007. +.. [Yu2022] Runze Yu. *linearity of generalized cactus groups*. + Preprint, :arxiv:`2202.00860` (2022). + .. [Yun1976] Yun, David YY. On square-free decomposition algorithms. In Proceedings of the third ACM symposium on Symbolic and algebraic computation, pp. 26-35. ACM, 1976. @@ -6161,6 +6297,10 @@ REFERENCES: Non-Cayley Graphs of Order 8p*. The Electronic Journal of Combinatorics, 19(1), P53, 2012. :doi:`10.37236/2087` +.. [Zie1959] \N. Zierler. *Linear Recurring Sequences*. + Journal of the Society for Industrial and Applied Mathematics 7(1) + (1959): 31-48. :doi:`10.1137/0107003` + .. [Zie1998] \G. M. Ziegler. *Shelling polyhedral 3-balls and 4-polytopes*. Discrete Comput. Geom. 19 (1998), 159-174. :doi:`10.1007/PL00009339` diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 21a2f285f1f..d12d8866dda 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -3,7 +3,7 @@ The Sage Command Line The Sage Read-Eval-Print-Loop (REPL) is based on IPython. In this document, you'll find how the IPython integration works. You should -also be familiar with the documentation for IPython. +also be familiar with the documentation for IPython. For more details about using the Sage command line, see the Sage tutorial. @@ -12,7 +12,7 @@ Running Sage ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 options startup @@ -26,8 +26,8 @@ Sage commands are "preparsed" to valid Python syntax. This allows for example to support the ``R.<x> = QQ[]`` syntax. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/preparse @@ -39,8 +39,8 @@ in a Sage session. Attaching is similar, except that the attached file is reloaded whenever it is changed. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/load sage/repl/attach @@ -53,18 +53,18 @@ printed. This again builds on how IPython formats output. Technically, this works using a modified displayhook in Python. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/display/formatter sage/repl/display/pretty_print sage/repl/display/fancy_repr sage/repl/display/util - + Display Backend Infrastructure ------------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/repl/rich_output/display_manager sage/repl/rich_output/preferences @@ -75,7 +75,7 @@ Display Backend Infrastructure sage/repl/rich_output/output_graphics3d sage/repl/rich_output/output_video sage/repl/rich_output/output_catalog - + sage/repl/rich_output/backend_base sage/repl/rich_output/test_backend sage/repl/rich_output/backend_doctest @@ -85,7 +85,7 @@ Miscellaneous ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/repl/interpreter sage/repl/ipython_extension diff --git a/src/doc/en/reference/resolutions/index.rst b/src/doc/en/reference/resolutions/index.rst index 82f73cb94c8..861023c8aec 100644 --- a/src/doc/en/reference/resolutions/index.rst +++ b/src/doc/en/reference/resolutions/index.rst @@ -5,7 +5,7 @@ Free and graded resolutions are tools for commutative algebra and algebraic geometry. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/homology/free_resolution sage/homology/graded_resolution diff --git a/src/doc/en/reference/riemannian_geometry/index.rst b/src/doc/en/reference/riemannian_geometry/index.rst index caa6ea15685..5f16761f845 100644 --- a/src/doc/en/reference/riemannian_geometry/index.rst +++ b/src/doc/en/reference/riemannian_geometry/index.rst @@ -2,7 +2,7 @@ Differential Geometry of Curves and Surfaces ============================================ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/geometry/riemannian_manifolds/parametrized_surface3d sage/geometry/riemannian_manifolds/surface3d_generators diff --git a/src/doc/en/reference/rings/index.rst b/src/doc/en/reference/rings/index.rst index 58451740464..d43d1be4003 100644 --- a/src/doc/en/reference/rings/index.rst +++ b/src/doc/en/reference/rings/index.rst @@ -5,7 +5,7 @@ Base Classes for Rings, Algebras and Fields ------------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ring @@ -13,7 +13,7 @@ Ideals ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ideal sage/rings/ideal_monoid @@ -23,7 +23,7 @@ Ring Morphisms -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/morphism sage/rings/homset @@ -32,7 +32,7 @@ Quotient Rings -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/quotient_ring sage/rings/quotient_ring_element @@ -41,7 +41,7 @@ Fraction Fields --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/fraction_field sage/rings/fraction_field_element @@ -50,7 +50,7 @@ Localization --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/localization @@ -58,18 +58,26 @@ Ring Extensions --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ring_extension sage/rings/ring_extension_element sage/rings/ring_extension_morphism +Generic Data Structures and Algorithms for Rings +------------------------------------------------ + +.. toctree:: + :maxdepth: 1 + + sage/rings/generic + Utilities --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/big_oh sage/rings/infinity @@ -79,7 +87,7 @@ Derivation ---------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/derivation diff --git a/src/doc/en/reference/rings_numerical/index.rst b/src/doc/en/reference/rings_numerical/index.rst index 955c61beee6..a20066c2ec7 100644 --- a/src/doc/en/reference/rings_numerical/index.rst +++ b/src/doc/en/reference/rings_numerical/index.rst @@ -16,7 +16,7 @@ which builds on GMP. In many cases the PARI C-library is used to compute special functions when implementations aren't otherwise available. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_mpfr sage/rings/complex_mpfr @@ -32,7 +32,7 @@ Sage implements real and complex interval arithmetic using MPFI ComplexBallField). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_mpfi sage/rings/real_interval_absolute @@ -46,7 +46,7 @@ Exact Real Arithmetic --------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_lazy diff --git a/src/doc/en/reference/rings_standard/index.rst b/src/doc/en/reference/rings_standard/index.rst index 922de453f59..4bb5338609f 100644 --- a/src/doc/en/reference/rings_standard/index.rst +++ b/src/doc/en/reference/rings_standard/index.rst @@ -5,7 +5,7 @@ Integers -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/integer_ring sage/rings/integer @@ -30,7 +30,7 @@ Rationals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/rational_field sage/rings/rational diff --git a/src/doc/en/reference/semirings/index.rst b/src/doc/en/reference/semirings/index.rst index 6cab3c1421a..b40e71c54e1 100644 --- a/src/doc/en/reference/semirings/index.rst +++ b/src/doc/en/reference/semirings/index.rst @@ -2,7 +2,7 @@ Standard Semirings ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/semirings/non_negative_integer_semiring sage/rings/semirings/tropical_semiring diff --git a/src/doc/en/reference/stats/index.rst b/src/doc/en/reference/stats/index.rst index e55454305c6..0020b113d66 100644 --- a/src/doc/en/reference/stats/index.rst +++ b/src/doc/en/reference/stats/index.rst @@ -2,7 +2,7 @@ Statistics ========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/stats/basic_stats sage/stats/intlist diff --git a/src/doc/en/reference/tensor_free_modules/alt_forms.rst b/src/doc/en/reference/tensor_free_modules/alt_forms.rst index a6a2b87818d..c614c8fd531 100644 --- a/src/doc/en/reference/tensor_free_modules/alt_forms.rst +++ b/src/doc/en/reference/tensor_free_modules/alt_forms.rst @@ -2,7 +2,7 @@ Alternating tensors =================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/ext_pow_free_module diff --git a/src/doc/en/reference/tensor_free_modules/index.rst b/src/doc/en/reference/tensor_free_modules/index.rst index 2d2f7db3613..5e67a0e0389 100644 --- a/src/doc/en/reference/tensor_free_modules/index.rst +++ b/src/doc/en/reference/tensor_free_modules/index.rst @@ -9,7 +9,7 @@ not depend upon other SageManifolds classes. In other words, it constitutes a self-consistent subset that can be used independently of SageManifolds. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/finite_rank_free_module diff --git a/src/doc/en/reference/tensor_free_modules/morphisms.rst b/src/doc/en/reference/tensor_free_modules/morphisms.rst index fa661c8978d..e05a1670861 100644 --- a/src/doc/en/reference/tensor_free_modules/morphisms.rst +++ b/src/doc/en/reference/tensor_free_modules/morphisms.rst @@ -2,7 +2,7 @@ Morphisms ========= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/free_module_homset diff --git a/src/doc/en/reference/tensor_free_modules/tensors.rst b/src/doc/en/reference/tensor_free_modules/tensors.rst index 434ea734191..ca4e1df907a 100644 --- a/src/doc/en/reference/tensor_free_modules/tensors.rst +++ b/src/doc/en/reference/tensor_free_modules/tensors.rst @@ -2,7 +2,9 @@ Tensors ======= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + + sage/tensor/modules/reflexive_module sage/tensor/modules/tensor_free_module diff --git a/src/doc/en/reference/topology/index.rst b/src/doc/en/reference/topology/index.rst index 8550c2b0c42..1442b327ed2 100644 --- a/src/doc/en/reference/topology/index.rst +++ b/src/doc/en/reference/topology/index.rst @@ -8,7 +8,7 @@ also available, mainly for developers who want to use it as a base for other types of cell complexes. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/topology/simplicial_complex sage/topology/simplicial_complex_morphism diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 67daac25902..90a8c3c97f7 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -46,7 +46,7 @@ Similarly, valuations can be defined on function fields:: sage: v = K.valuation(x) sage: v(1/x) -1 - + sage: v = K.valuation(1/x) sage: v(1/x) 1 @@ -88,7 +88,7 @@ polynomial the minimum of the coefficient valuations:: sage: R.<x> = QQ[] sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) - + The Gauss valuation can be augmented by specifying that `x - 4` has valuation 1:: sage: v = v.augmentation(x - 4, 1); v @@ -184,7 +184,7 @@ More Details ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/valuation/value_group sage/rings/valuation/valuation diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst index 09e8bce9a95..bdd5cc1d360 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst @@ -5,8 +5,8 @@ The Matrix of Frobenius on Hyperelliptic Curves Sage has a highly optimized implementation of the Harvey-Kedlaya algorithm for computing the matrix of Frobenius associated to a curve over a finite field. This is an implementation by David Harvey, which -is GPL'd and depends only on NTL and zn_poly (a C library in Sage for -fast arithmetic in :math:`(\ZZ/n\ZZ)[x]`). +is GPL'd and depends only on NTL (a C library in Sage for fast +arithmetic in :math:`(\ZZ/n\ZZ)[x]`). We import the hypellfrob function and call it on a polynomial over :math:`\ZZ`. 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 cdcbdb584c9..b8eef385e4f 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 @@ -40,4 +40,3 @@ ('index', name + '.tex', 'Three Lectures about Explicit Methods in\nNumber Theory Using Sage', 'William Stein', 'manual'), ] - diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index a0c98ea3836..ee96fe1831e 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -15,7 +15,7 @@ paper. TikZ is a very versatile tool to draw in scientific documents and Sage can deal easily with 3-dimensional polytopes. Finally sagetex makes everything work together nicely between Sage, TikZ and LaTeX. Since version 6.3 of Sage, there is a function for (projection -of) polytopes to output a TikZ picture of the polytope. Since version 9.7 of +of) polytopes to output a TikZ picture of the polytope. Since version 9.8 of SageMath, the tikz output can be a ``TikzPicture`` object from the sage module ``sage.misc.latex_standalone``. This short tutorial shows how it all works. @@ -61,7 +61,7 @@ You can customize the polytope using the following options in the command ``P.ti - ``opacity`` : real number (default: ``0.8``) between 0 and 1 giving the opacity of the front facets, - ``axis`` : Boolean (default: ``False``) draw the axes at the origin or not. - ``output_type`` : string (default: ``None``) ``None``, ``'LatexExpr'`` or - ``'TikzPicture'``, the type of the output. Since SageMath 9.7, the value ``None`` is deprecated + ``'TikzPicture'``, the type of the output. Since SageMath 9.8, the value ``None`` is deprecated as the default value will soon be changed from ``'LatexExpr'`` to ``'TikzPicture'``. Examples @@ -90,7 +90,7 @@ When you found a good angle, follow the above procedure to obtain the values .. end of output -Note: the ``output_type='TikzPicture'`` is necessary since SagMath 9.7 to avoid +Note: the ``output_type='TikzPicture'`` is necessary since SagMath 9.8 to avoid a deprecation warning message since the default output type will soon change from a ``LatexExpr`` (Python str) to a ``TikzPicture`` object (allowing more versatility, like being able to view it directly in the Jupyter notebook). diff --git a/src/doc/en/thematic_tutorials/group_theory.rst b/src/doc/en/thematic_tutorials/group_theory.rst index ca45acf7082..e9e6b23953f 100644 --- a/src/doc/en/thematic_tutorials/group_theory.rst +++ b/src/doc/en/thematic_tutorials/group_theory.rst @@ -204,7 +204,7 @@ Experiment by running the following code several times:: sage: m = random_prime(10000) sage: n = random_prime(10000) - sage: euler_phi(m*n) == euler_phi(m) * euler_phi(n) + sage: euler_phi(m*n) == euler_phi(m) * euler_phi(n) or m == n True Feel a conjecture coming on? Can you generalize this result? @@ -318,7 +318,7 @@ since `\sigma` is an odd permutation. Many more available functions that can be applied to a permutation can be found via "tab-completion." With ``sigma`` defined as an element of a permutation group, in a Sage cell, type ``sigma.`` (Note the -"``.``") and then press the tab key. You will get a list of available +"``.``") and then press the :kbd:`Tab` key. You will get a list of available functions (you may need to scroll down to see the whole list). Experiment and explore! It is what Sage is all about. You really cannot break anything. @@ -359,13 +359,13 @@ and then a variety of functions become available. After trying the examples below, experiment with tab-completion. Having defined ``H``, type ``H.`` (note the "``.``") and then press -the tab key. You will get a list of available functions (you may need +the :kbd:`Tab` key. You will get a list of available functions (you may need to scroll down to see the whole list). As before, *experiment and explore*---it is really hard to break anything. Here is another couple of ways to experiment and explore. Find a function that looks interesting, say ``is_abelian()``. Type -``H.is_abelian?`` (note the question mark) followed by the enter key. +``H.is_abelian?`` (note the question mark) followed by the :kbd:`Enter` key. This will display a portion of the source code for the ``is_abelian()`` function, describing the inputs and output, possibly illustrated with example uses. diff --git a/src/doc/en/thematic_tutorials/numerical_sage/conf.py b/src/doc/en/thematic_tutorials/numerical_sage/conf.py index 08e174fde3b..5772289f6fa 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/conf.py +++ b/src/doc/en/thematic_tutorials/numerical_sage/conf.py @@ -40,4 +40,3 @@ ('index', name + '.tex', 'Numerical Computing with Sage', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst index 50bff7decd2..5cafdf1ea1b 100644 --- a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst +++ b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst @@ -53,7 +53,7 @@ The *standard types* are :class:`bool`, :class:`int`, :class:`list`, * The type :class:`bool` (*booleans*) has two values: ``True`` and ``False``. The boolean operators are denoted by their names ``or``, ``and``, ``not``. -* The Python types :class:`int` and :class:`long` are used to +* The Python type :class:`int` is used to represent integers of limited size. To handle arbitrary large integers with exact arithmetic, Sage uses its own type named :class:`Integer`. diff --git a/src/doc/en/tutorial/conf.py b/src/doc/en/tutorial/conf.py index b2b525d2c2a..8a4f626f440 100644 --- a/src/doc/en/tutorial/conf.py +++ b/src/doc/en/tutorial/conf.py @@ -36,4 +36,3 @@ ('index', 'sage_tutorial.tex', 'Tutorial', 'The Sage Development Team', 'manual'), ] - diff --git a/src/doc/en/tutorial/interactive_shell.rst b/src/doc/en/tutorial/interactive_shell.rst index 7ae821e416d..4c06c6a6255 100644 --- a/src/doc/en/tutorial/interactive_shell.rst +++ b/src/doc/en/tutorial/interactive_shell.rst @@ -549,7 +549,7 @@ You can also use the following more concise notation: sage: V = QQ^3 Then it is easy to list all member functions for :math:`V` using tab -completion. Just type ``V.``, then type the ``[tab key]`` key on your +completion. Just type ``V.``, then type the :kbd:`Tab` key on your keyboard: .. skip @@ -567,7 +567,7 @@ keyboard: ... V.zero_vector -If you type the first few letters of a function, then ``[tab key]``, +If you type the first few letters of a function, then the :kbd:`Tab` key, you get only functions that begin as indicated. .. skip diff --git a/src/doc/en/tutorial/introduction.rst b/src/doc/en/tutorial/introduction.rst index 10ebef9f5d6..44eecb132c3 100644 --- a/src/doc/en/tutorial/introduction.rst +++ b/src/doc/en/tutorial/introduction.rst @@ -99,7 +99,7 @@ Ways to Use Sage You can use Sage in several ways. -- **Notebook graphical interface:** run `sage -n jupyter`; see +- **Notebook graphical interface:** run ``sage -n jupyter``; see `the Jupyter documentation on-line <https://jupyter-notebook.readthedocs.io/en/latest/notebook.html>`_, - **Interactive command line:** see :ref:`chapter-interactive_shell`, diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index 5c10f4ac183..aafaaea6ac5 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -2,14 +2,9 @@ Sage, LaTeX and Friends ********************************* -AUTHOR: Rob Beezer (2010-05-23) - -Sage and the LaTeX dialect of TeX have an -intensely synergistic relationship. This section aims to -introduce the variety of interactions, beginning with the most -basic and proceeding to the more unusual and arcane. (So you may -not want to read this entire section on your first pass through -this tutorial.) +Sage and the LaTeX dialect of TeX have an intensely synergistic relationship. +This section aims to introduce the variety of interactions, beginning with the +most basic and proceeding to the more unusual. Overview ======== @@ -18,41 +13,36 @@ It may be easiest to understand the various uses of LaTeX with a brief overview of the mechanics of the three principal methods employed by Sage. - #. Every "object" in Sage is required to have a LaTeX representation. - You can access this representation by executing, in the notebook or - at the sage command line, ``latex(foo)`` where ``foo`` is some object - in Sage. The output is a string that should render a reasonably accurate - representation of ``foo`` when used in TeX's math-mode (for example, - when enclosed between a pair of single dollar signs). Some examples of - this follow below. + #. Every "object" in Sage is required to have a LaTeX representation. You + can access this representation by executing ``latex(foo)`` where + ``foo`` is some object in Sage. The output is a string that should + render a reasonably accurate representation of ``foo`` when used in + TeX's math-mode (for example, when enclosed between a pair of single + dollar signs). Some examples of this follow below. In this way, Sage can be used effectively for constructing portions of a LaTeX document: create or compute an object in Sage, print ``latex()`` of the object and cut/paste it into your document. - #. The notebook interface is configured to use - `MathJax <http://www.mathjax.org>`_ - to render mathematics - cleanly in a web browser. MathJax is an open source JavaScript - display engine for mathematics that works in all modern - browsers. It is able to render a large, but not totally - complete, subset of TeX. It has no support for - things like complicated tables, sectioning or document - management, as it is oriented towards accurately rendering - "snippets" of TeX. Seemingly automatic rendering of math in the - notebook is provided by converting the ``latex()`` - representation of an object (as described above) into a form of - HTML palatable to MathJax. - - Since MathJax uses its own scalable fonts, it is superior to other methods that - rely on converting equations, or other snippets of TeX, into static inline images. - - #. At the Sage command-line, or in the notebook when LaTeX code is - more involved than MathJax can handle, a system-wide installation of - LaTeX can be employed. Sage includes almost everything you need to - build and use Sage, but a significant exception is TeX itself. So in these - situations you need to have TeX installed, along with some associated - conversion utilities, to utilize the full power. + #. The Jupyter notebook interface uses `MathJax <http://www.mathjax.org>`_ + to render mathematics cleanly in a web browser. You can start this + automatic rendering by executing ``%display latex`` (and stop by + executing ``%display plain``). + + MathJax is an open source JavaScript display engine for mathematics that + works in all modern browsers. It is able to render a large, but not + totally complete, subset of TeX. It has no support for things like + complicated tables, sectioning or document management, as it is oriented + towards accurately rendering "snippets" of TeX. Seemingly automatic + rendering of math in the notebook is provided by converting the + ``latex()`` representation of an object (as described above) into a form + of HTML palatable to MathJax. + + #. A system-wide installation of LaTeX can be employed. Sage includes + almost everything you need to build and use Sage, but a significant + exception is TeX itself. So in these situations you need to have + TeX installed, along with some associated conversion utilities, to + utilize the full power. Here we demonstrate some basic uses of the ``latex()`` function. :: @@ -72,11 +62,10 @@ Here we demonstrate some basic uses of the ``latex()`` function. :: -1 & -1 & -1 \end{array}\right) -Basic MathJax functionality is largely automatic in the notebook, but -we can partially demonstrate this support with the ``MathJax`` class. -The ``eval`` function of this class converts a Sage object to its -LaTeX representation and then wraps it in HTML that invokes the CSS -"math" class, which then employs MathJax. :: +Basic MathJax functionality is largely automatic in the notebook, but we can +partially demonstrate this support with the ``MathJax`` class. The object of +this class converts a Sage object to its LaTeX representation and then wraps it +in HTML. :: sage: from sage.misc.html import MathJax sage: mj = MathJax() @@ -94,65 +83,28 @@ LaTeX representation and then wraps it in HTML that invokes the CSS Basic Use ========= -As indicated in the overview, the simplest way to exploit Sage's -support of LaTeX is to use the ``latex()`` function to create -legitimate LaTeX code to represent mathematical objects. These -strings can then be incorporated into standalone LaTeX documents. -This works identically in the notebook and at the Sage command -line. - -At the other extreme is the ``view()`` command. At the Sage -command line the command ``view(foo)`` will create the LaTeX -representation of ``foo``, incorporate this into a simple LaTeX -document, and then process that document with your system-wide -TeX installation. Finally, the appropriate viewer will be called -to display the output from the TeX command. Which version of TeX -is used, and therefore the nature of the output and associated -viewer, can be customized (see :ref:`sec-custom-processing`). - -In the notebook, the ``view(foo)`` command creates the -appropriate combination of HTML and CSS so that MathJax will -render the LaTeX representation properly in the worksheet. To the -user, it simply creates a nicely formatted version of the output, -distinct from the default ASCII output of Sage. Not every -mathematical object in Sage has a LaTeX representation amenable to -the limited capabilities of MathJax. In these cases, the MathJax -interpretation can be bypassed, the system-wide TeX called -instead, and the subsequent output converted to a graphic image -for display in the worksheet. Affecting and controlling this -process is discussed below in the section -:ref:`sec-custom-generation`. - -The notebook has two other features for employing TeX. -The first is the "Typeset" button just above the first cell of a -worksheet, to the right of the four drop-down boxes. When -checked, any subsequent evaluations of cells will result in -output interpreted by MathJax, hence of a typeset quality. Note -that this effect is not retroactive -- previously evaluated cells -need to be re-evaluated. Essentially, checking the "Typeset" -button is identical to wrapping the output of each cell in the -``view()`` command. - -A second feature of the notebook is entering TeX as -part of annotating a worksheet. When the cursor is placed -between cells of a worksheet so that a blue bar appears, then a -shift-click will open a mini-word-processor, TinyMCE. This -allows for the entry of text, using a WSIWYG editor to create -HTML and CSS command for styled text. So it is possible to add -formatted text as commentary within a worksheet. However, text -between pairs of dollar signs, or pairs of double dollar signs is -interpreted by MathJax as inline or display math (respectively). +As indicated in the overview, the simplest way to exploit Sage's support of +LaTeX is to use the ``latex()`` function to create legitimate LaTeX code to +represent mathematical objects. These strings can then be incorporated into +standalone LaTeX documents. + +At the other extreme is the ``view()`` command. The command ``view(foo)`` will +create the LaTeX representation of ``foo``, incorporate this into a simple +LaTeX document, and then process that document with your system-wide TeX +installation. Finally, the appropriate viewer will be called to display the +output from the TeX command. Which version of TeX is used, and therefore the +nature of the output and associated viewer, can be customized (see +:ref:`sec-custom-processing`). .. _sec-custom-generation: Customizing LaTeX Generation ============================ -There are several ways to customize the actual LaTeX code generated by -the ``latex()`` command. In the notebook and at the Sage command-line -there is a pre-defined object named ``latex`` which has several methods, -which you can list by typing ``latex.``, followed by the tab key -(note the period). +There are several ways to customize the actual LaTeX code generated by the +``latex()`` command. There is a pre-defined object named ``latex`` which has +several methods, which you can list by typing ``latex.``, followed by the +:kbd:`Tab` key (note the period). A good example is the ``latex.matrix_delimiters`` method. It can be used to change the notation surrounding a matrix -- large parentheses, @@ -192,7 +144,7 @@ done in written work. This is accomplished by redefining the sage: latex(QQ) \Bold{Q} sage: from sage.misc.html import MathJax - sage: mj=MathJax() + sage: mj = MathJax() sage: mj(QQ) <html>\[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\]</html> sage: latex.blackboard_bold(True) @@ -200,10 +152,9 @@ done in written work. This is accomplished by redefining the <html>\[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\]</html> sage: latex.blackboard_bold(False) -It is possible to take advantage of the extensible nature of -TeX by adding in new macros and new packages. First, -individual macros can be added so that they are used when -MathJax interprets a snippet of TeX in the notebook. :: +It is possible to take advantage of the extensible nature of TeX by adding in +new macros and new packages. First, individual macros can be added so that +they are used when MathJax interprets a snippet of TeX in the notebook. :: sage: latex.extra_macros() '' @@ -215,18 +166,16 @@ MathJax interprets a snippet of TeX in the notebook. :: sage: latex(x+y) x + y sage: from sage.misc.html import MathJax - sage: mj=MathJax() + sage: mj = MathJax() sage: mj(x+y) <html>\[\newcommand{\foo}{bar}x + y\]</html> -Additional macros added this way will also be used in the event -that the system-wide version of TeX is called on -something larger than MathJax can handle. The command -``latex_extra_preamble`` is used to build the preamble of a -complete LaTeX document, so the following illustrates -how this is accomplished. As usual note the need for the -double-backslashes in the Python strings. :: - +Additional macros added this way will also be used in the event that the +system-wide version of TeX is called on something larger than MathJax can +handle. The command ``latex_extra_preamble`` is used to build the preamble of +a complete LaTeX document, so the following illustrates how this is +accomplished. As usual note the need for the double-backslashes in the Python +strings. :: sage: latex.extra_macros('') sage: latex.extra_preamble('') @@ -242,20 +191,16 @@ double-backslashes in the Python strings. :: \newcommand{\Bold}[1]{\mathbf{#1}} \newcommand{\foo}{bar} -Again, for larger or more complicated LaTeX -expressions, it is possible to add packages (or anything else) to -the preamble of the LaTeX file. Anything may be -incorporated into the preamble with the ``latex.add_to_preamble`` -command, and the specialized command -``latex.add_package_to_preamble_if_available`` will first check -if a certain package is actually available before trying to add -it to the preamble. - -Here we add the geometry package to the preamble and use it to -set the size of the region on the page that TeX will -use (effectively setting the margins). As usual, note the need -for the double-backslashes in the Python strings. :: +Again, for larger or more complicated LaTeX expressions, it is possible to add +packages (or anything else) to the preamble of the LaTeX file. Anything may be +incorporated into the preamble with the ``latex.add_to_preamble`` command, and +the specialized command ``latex.add_package_to_preamble_if_available`` will +first check if a certain package is actually available before trying to add it +to the preamble. +Here we add the geometry package to the preamble and use it to set the size of +the region on the page that TeX will use (effectively setting the margins). As +usual, note the need for the double-backslashes in the Python strings. :: sage: from sage.misc.latex import latex_extra_preamble sage: latex.extra_macros('') @@ -270,9 +215,9 @@ for the double-backslashes in the Python strings. :: ... \newcommand{\Bold}[1]{\mathbf{#1}} -A particular package may be added along with a check on its existence, -as follows. As an example, we just illustrate an attempt to add to -the preamble a package that presumably does not exist. :: +A particular package may be added along with a check on its existence, as +follows. As an example, we just illustrate an attempt to add to the preamble a +package that presumably does not exist. :: sage: latex.extra_preamble('') sage: latex.extra_preamble() @@ -289,94 +234,30 @@ the preamble a package that presumably does not exist. :: Customizing LaTeX Processing ============================ -It is also possible to control which variant of TeX is -used for system-wide invocations, thus also influencing the -nature of the output. - -The ``latex.engine()`` command can be used to control if the -system-wide executables ``latex``, ``pdflatex`` or ``xelatex`` -are employed for more complicated LaTeX expressions. -When ``view()`` is called from the sage command-line and the -engine is set to ``latex``, a dvi file is produced and Sage will -use a dvi viewer (like xdvi) to display the result. In contrast, -using ``view()`` at the Sage command-line, when the engine is set -to ``pdflatex``, will produce a PDF as the result and Sage will -call your system's utility for displaying PDF files (acrobat, -okular, evince, etc.). - -In the notebook, it is necessary to intervene in the decision as -to whether MathJax will interpret a snippet of TeX, or -if the LaTeX is complicated enough that the system-wide -installation of TeX should do the work instead. The -device is a list of strings, which if any one is discovered in a -piece of LaTeX code signal the notebook to bypass -MathJax and invoke latex (or whichever executable is set by the -``latex.engine()`` command). This list is managed by the -``latex.add_to_mathjax_avoid_list`` and -``latex.mathjax_avoid_list`` commands. :: - - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested - [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested - ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested - ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested - [] - -Suppose a LaTeX expression is produced in the notebook -with ``view()`` or while the "Typeset" button is checked, and -then recognized as requiring the external LaTeX -installation through the "mathjax avoid list." Then the selected -executable (as specified by ``latex.engine()``) will process the -LaTeX. However, instead of then spawning an external -viewer (which is the command-line behavior), Sage will attempt to -convert the result into a single, tightly-cropped image, which is -then inserted into the worksheet as the output of the cell. - -Just how this conversion proceeds depends on several factors -- -mostly which executable you have specified as the engine and -which conversion utilities are available on your system. Four -useful converters that will cover all eventualities are -``dvips``, ``ps2pdf``, ``dvipng`` and from the ``ImageMagick`` suite, -``convert``. The goal is to produce a PNG file as the output for -inclusion back into the worksheet. When a LaTeX -expression can be converted successfully to a dvi by the latex -engine, then dvipng should accomplish the conversion. If the -LaTeX expression and chosen engine creates a dvi with -specials that dvipng cannot handle, then dvips will create a -PostScript file. Such a PostScript file, or a PDF file created by -an engine such as ``pdflatex``, is then processed into a PNG with -the ``convert`` utility. The presence of two of these converters -can be tested with the ``have_dvipng()`` and ``have_convert()`` -routines. - -These conversions are done automatically if you have the necessary -converters installed; if not, then an error message is printed telling -you what's missing and where to download it. - -For a concrete example of how complicated LaTeX -expressions can be processed, see the example in the next section -(:ref:`sec-tkz-graph`) for using the LaTeX -``tkz-graph`` package to produce high-quality renderings of -combinatorial graphs. For other examples, there are some -pre-packaged test cases. To use these, it is necessary to import -the ``sage.misc.latex.latex_examples`` object, which is an -instance of the ``sage.misc.latex.LatexExamples`` class, as -illustrated below. This class currently has examples of -commutative diagrams, combinatorial graphs, knot theory and -pstricks, which respectively exercise the following packages: -xy, tkz-graph, xypic, pstricks. After the import, use -tab-completion on ``latex_examples`` to see the pre-packaged -examples. Calling each example will give you back some -explanation about what is required to make the example render -properly. To actually see the examples, it is necessary to use -``view()`` (once the preamble, engine, etc are all set properly). -:: +It is also possible to control which variant of TeX is used for system-wide +invocations, thus also influencing the nature of the output. + +The ``latex.engine()`` command can be used to control if the system-wide +executables ``latex``, ``pdflatex`` or ``xelatex`` are employed for more +complicated LaTeX expressions. When ``view()`` is called and the engine is set +to ``latex``, a dvi file is produced and Sage will use a dvi viewer (like xdvi) +to display the result. In contrast, using ``view()`` when the engine is set to +``pdflatex`` will produce a PDF as the result and Sage will call your system's +utility for displaying PDF files (acrobat, okular, evince, etc.). + +For a concrete example of how complicated LaTeX expressions can be processed, +see the example in the next section (:ref:`sec-tkz-graph`) for using the LaTeX +``tkz-graph`` package to produce high-quality renderings of combinatorial +graphs. For other examples, there are some pre-packaged test cases. To use +these, it is necessary to import the ``sage.misc.latex.latex_examples`` object, +which is an instance of the ``sage.misc.latex.LatexExamples`` class, as +illustrated below. This class currently has examples of commutative diagrams, +combinatorial graphs, knot theory and pstricks, which respectively exercise the +following packages: xy, tkz-graph, xypic, pstricks. After the import, use +tab-completion on ``latex_examples`` to see the pre-packaged examples. Calling +each example will give you back some explanation about what is required to make +the example render properly. To actually see the examples, it is necessary to +use ``view()`` (once the preamble, engine, etc are all set properly). :: sage: from sage.misc.latex import latex_examples sage: latex_examples.diagram() @@ -393,96 +274,58 @@ properly. To actually see the examples, it is necessary to use An Example: Combinatorial Graphs with tkz-graph =============================================== -High-quality illustrations of combinatorial graphs (henceforth -just "graphs") are possible with the ``tkz-graph`` package. -This package is built on top of the ``tikz`` front-end to the -``pgf`` library. So all of these components need to be part -of a system-wide TeX installation, and it may be possible -that these components may not be at their most current -versions as packaged in some TeX implementations. So for -best results, it could be necessary or advisable to install -these as part of your personal texmf tree. Creating, -maintaining and customizing a system-wide or personal TeX -installation is beyond the scope of this document, but it should -be easy to find instructions. The necessary files are listed in -:ref:`sec-system-wide-tex`. - -Thus, to start we need to insure that the relevant packages -are included by adding them to the preamble of the eventual -LaTeX document. The images of graphs do not form properly -when a dvi file is used as an intermediate format, so it is -best to set the latex engine to the ``pdflatex`` executable. -At this point a command like ``view(graphs.CompleteGraph(4))`` -should succeed at the Sage command-line and produce a PDF +High-quality illustrations of combinatorial graphs (henceforth just "graphs") +are possible with the ``tkz-graph`` package. This package is built on top of +the ``tikz`` front-end to the ``pgf`` library. So all of these components need +to be part of a system-wide TeX installation, and it may be possible that these +components may not be at their most current versions as packaged in some TeX +implementations. So for best results, it could be necessary or advisable to +install these as part of your personal texmf tree. Creating, maintaining and +customizing a system-wide or personal TeX installation is beyond the scope of +this document, but it should be easy to find instructions. The necessary files +are listed in :ref:`sec-system-wide-tex`. + +Thus, to start we need to insure that the relevant packages are included by +adding them to the preamble of the eventual LaTeX document. The images of +graphs do not form properly when a dvi file is used as an intermediate format, +so it is best to set the latex engine to the ``pdflatex`` executable. At this +point a command like ``view(graphs.CompleteGraph(4))`` should produce a PDF with an appropriate image of the complete graph `K_4`. -For a similar experience in the notebook, it is necessary -to disable MathJax processing of the LaTeX code for the graph -by using the "mathjax avoid list." Graphs are included with a -``tikzpicture`` environment, so this is a good choice for -a string to include in the avoidance list. Now, -``view(graphs.CompleteGraph(4))`` in a worksheet -should call pdflatex to create a PDF and then the -``convert`` utility will extract a PNG graphic to -insert into the output cell of the worksheet. -The following commands illustrate the steps to get -graphs processed by LaTeX in the notebook. :: - - sage: from sage.graphs.graph_latex import setup_latex_preamble - sage: setup_latex_preamble() - sage: latex.extra_preamble() # random - depends on system's TeX installation - '\\usepackage{tikz}\n\\usepackage{tkz-graph}\n\\usepackage{tkz-berge}\n' - sage: latex.engine('pdflatex') - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested - ['tikz', 'tikzpicture'] - -At this point, a command like ``view(graphs.CompleteGraph(4))`` -should produce a graphic version of the graph pasted into the -notebook, having used ``pdflatex`` to process ``tkz-graph`` -commands to realize the graph. Note that there is a variety of -options to affect how a graph is rendered in LaTeX via -``tkz-graph``, which is again outside the scope of this section, -see the section of the Reference manual titled "LaTeX Options for -Graphs" for instructions and details. +Note that there is a variety of options to affect how a graph is rendered in +LaTeX via ``tkz-graph``, which is again outside the scope of this section, see +the section of the Reference manual titled "LaTeX Options for Graphs" for +instructions and details. .. _sec-system-wide-tex: A Fully Capable TeX Installation ================================ -Many of the more advanced features of the integration of -TeX with Sage requires a system-wide installation of -TeX. Many versions of Linux have base TeX -packages based on TeX-live, for OSX there is -TeXshop and for Windows there is MikTeX. -The ``convert`` utility is part of the -`ImageMagick <http://www.imagemagick.org/>`_ suite (which -should be a package or an easy download), and the three -programs ``dvipng``, ``ps2pdf``, and ``dvips`` may be -included with your TeX distribution. The first two may -also be obtained, respectively, from -http://sourceforge.net/projects/dvipng/ and as part of + +Many of the more advanced features of the integration of TeX with Sage requires +a system-wide installation of TeX. Many versions of Linux have base TeX +packages based on TeX Live, for macOS there is TeXShop and for Windows there is +MiKTeX. The ``convert`` utility is part of the `ImageMagick +<http://www.imagemagick.org/>`_ suite (which should be a package or an easy +download), and the three programs ``dvipng``, ``ps2pdf``, and ``dvips`` may be +included with your TeX distribution. The first two may also be obtained, +respectively, from http://sourceforge.net/projects/dvipng/ and as part of `Ghostscript <http://www.ghostscript.com/>`_. -Rendering combinatorial graphs requires a recent version of the -PGF library, the file ``tkz-graph.sty`` from -https://www.ctan.org/pkg/tkz-graph, and the files ``tkz-arith.sty`` -and perhaps ``tkz-berge.sty`` from +Rendering combinatorial graphs requires a recent version of the PGF library, +the file ``tkz-graph.sty`` from https://www.ctan.org/pkg/tkz-graph, and the +files ``tkz-arith.sty`` and perhaps ``tkz-berge.sty`` from https://www.ctan.org/pkg/tkz-berge. -External Programs -================= - -There are three programs available to further integrate -TeX and Sage. The first is sagetex. A concise -description of sagetex is that it is a collection of -TeX macros that allow a LaTeX document to -include instructions to have Sage compute various objects and/or -format objects using the ``latex()`` support built in to Sage. -So as an intermediate step of compiling a LaTeX -document, all of the computational and LaTeX-formatting -features of Sage can be handled automatically. As an example, a -mathematics examination can maintain a correct correspondence -between questions and answers by using sagetex to have Sage -compute one from the other. See :ref:`sec-sagetex` for more -information. +SageTeX +======= + +SageTeX is a program available to further integrate TeX and Sage. A concise +description of SageTeX is that it is a collection of TeX macros that allow a +LaTeX document to include instructions to have Sage compute various objects +and/or format objects using the ``latex()`` support built into Sage. So as an +intermediate step of compiling a LaTeX document, all of the computational and +LaTeX-formatting features of Sage can be handled automatically. As an example, +a mathematics examination can maintain a correct correspondence between +questions and answers by using SageTeX to have Sage compute one from the other. +See :ref:`sec-sagetex` for more information. diff --git a/src/doc/en/tutorial/programming.rst b/src/doc/en/tutorial/programming.rst index f0f43e6f09f..57dac68ae1c 100644 --- a/src/doc/en/tutorial/programming.rst +++ b/src/doc/en/tutorial/programming.rst @@ -704,10 +704,9 @@ declares the :math:`1 \in \GF{5}` equal to :math:`1 \in \QQ`. Profiling ========= -Section Author: Martin Albrecht (malb@informatik.uni-bremen.de) - "Premature optimization is the root of all evil." - Donald Knuth +.. sectionauthor:: Martin Albrecht <malb@informatik.uni-bremen.de> Sometimes it is useful to check for bottlenecks in code to understand which parts take the most computational time; this can @@ -764,7 +763,7 @@ closer examination: .. skip -:: +:: sage: %prun -r A*A sage: stats = _ @@ -810,3 +809,4 @@ On a system shell, type The output file ``cachegrind.out.42`` can now be examined with ``kcachegrind``. Please note that the naming convention ``cachegrind.out.XX`` needs to be obeyed. + diff --git a/src/doc/en/tutorial/sagetex.rst b/src/doc/en/tutorial/sagetex.rst index 17460be0b49..e240e4dc277 100644 --- a/src/doc/en/tutorial/sagetex.rst +++ b/src/doc/en/tutorial/sagetex.rst @@ -137,26 +137,22 @@ ways to accomplish this. that if you upgrade Sage and get a new version of SageTeX, the Python code and LaTeX code for SageTeX may no longer match, causing errors. -- The second way is to use the ``TEXINPUTS`` environment variable. If +- The second way is to use the ``TEXMFLOCAL`` environment variable. If you are using the bash shell, you can do .. CODE-BLOCK:: shell-session - $ export TEXINPUTS="SAGE_ROOT/venv/share/texmf//:" + $ export TEXMFLOCAL=SAGE_ROOT/venv/share/texmf + $ mktexlsr # update kpathsea ls-R databases - where ``SAGE_ROOT`` is the location of your Sage installation. Note - that the double slash and colon at the end of that line are important. - Thereafter, TeX and friends will find the SageTeX style file. If you - want to make this change permanent, you can add the above line to your - ``.bashrc`` file. If you are using a different shell, you may have to + where ``SAGE_ROOT`` is the location of your Sage installation. + Thereafter, TeX and friends will find the SageTeX style file. + If you want to make this change persistent, you can add the 1st of the + above lines to your ``.bashrc`` file. + If you are using a different shell, you may have to modify the above command to make the environment variable known; see your shell's documentation for how to do that. - One flaw with this method is that if you use applications like - TeXShop, Kile, or Emacs/AucTeX, they will not necessarily pick up the - environment variable, since when they run LaTeX, they may do so - outside your usual shell environment. - If you ever move your Sage installation, or install a new version into a new directory, you'll need to update the above command to reflect the new value of ``SAGE_ROOT``. @@ -238,16 +234,14 @@ SageTeX and TeXLive ------------------- One potentially confusing issue is that the popular TeX distribution -`TeXLive 2009 <http://www.tug.org/texlive/>`_ includes SageTeX. This may +`TeXLive <http://www.tug.org/texlive/>`_ includes SageTeX. This may seem nice, but with SageTeX, it's important that the Sage bits and LaTeX -bits be synchronized -- which is a problem in this case, since both Sage -and SageTeX are updated frequently, and TeXLive is not. -While at the time of this writing (March 2013), many Linux distributions -have moved on to more recent releases of TeXLive, the 2009 release -lingers and is, in fact, the source of most bug reports about SageTeX! +bits be synchronized -- which is a problem in this case, since +TeXLive, as shipped by your OS distro, or package manager, might be out of sync with +TeXLive distribution, and the latter might also be out of sync with +the current SageTeX. Because of this, it is *strongly recommended* that you always install the LaTeX part of SageTeX from Sage, as described above. The instructions above will insure that both halves of SageTeX are -compatible and will work properly. Using TeXLive to provide the LaTeX -side of SageTeX is not supported. +compatible and will work properly. diff --git a/src/doc/en/tutorial/tour_help.rst b/src/doc/en/tutorial/tour_help.rst index c66c24e470b..87bce8c60e1 100644 --- a/src/doc/en/tutorial/tour_help.rst +++ b/src/doc/en/tutorial/tour_help.rst @@ -94,12 +94,11 @@ question mark: [6 3 5 1 7 2 8 9 4] [4 9 1 8 5 6 7 2 3] -Sage also provides 'Tab completion': type the first few letters of -a function and then hit the tab key. For example, if you type ``ta`` -followed by ``TAB``, Sage will print -``tachyon, tan, tanh, -taylor``. This provides a good way to find -the names of functions and other structures in Sage. +Sage also provides 'Tab completion': type the first few letters of a +function and then hit the :kbd:`Tab` key. For example, if you type +``ta`` followed by :kbd:`Tab`, Sage will print ``tachyon, tan, tanh, +taylor``. This provides a good way to find the names of functions and +other structures in Sage. .. _section-functions: diff --git a/src/doc/en/tutorial/tour_numtheory.rst b/src/doc/en/tutorial/tour_numtheory.rst index 3064d100e23..075e0ac0ad7 100644 --- a/src/doc/en/tutorial/tour_numtheory.rst +++ b/src/doc/en/tutorial/tour_numtheory.rst @@ -157,7 +157,7 @@ NumberField class. Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/en/website/conf.py b/src/doc/en/website/conf.py index 1f7847b232a..099a10865c4 100644 --- a/src/doc/en/website/conf.py +++ b/src/doc/en/website/conf.py @@ -43,4 +43,3 @@ html_additional_pages = { 'index': 'index_furo.html' if html_theme == 'furo' else 'index.html', } - diff --git a/src/doc/es/tutorial/tour_numtheory.rst b/src/doc/es/tutorial/tour_numtheory.rst index a1f7d1a87b9..48e5376cfed 100644 --- a/src/doc/es/tutorial/tour_numtheory.rst +++ b/src/doc/es/tutorial/tour_numtheory.rst @@ -140,7 +140,7 @@ Varios métodos relacionados están implementados en la clase ``NumberField``:: Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/fr/tutorial/conf.py b/src/doc/fr/tutorial/conf.py index b23aaf6841c..7208ddada17 100644 --- a/src/doc/fr/tutorial/conf.py +++ b/src/doc/fr/tutorial/conf.py @@ -45,4 +45,3 @@ # the definition of \\at in the standard preamble of the sphinx doc # conflicts with that in babel/french[b] latex_elements['preamble'] += '\\let\\at\\undefined' - diff --git a/src/doc/fr/tutorial/tour_numtheory.rst b/src/doc/fr/tutorial/tour_numtheory.rst index 871092f5fa5..d1b2fee883e 100644 --- a/src/doc/fr/tutorial/tour_numtheory.rst +++ b/src/doc/fr/tutorial/tour_numtheory.rst @@ -159,7 +159,7 @@ dans la classe NumberField. Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index 72fa3d0e162..e02d8507eb6 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -124,7 +124,7 @@ LaTeXコード生成のカスタマイズ .. There are several ways to customize the actual LaTeX code generated by .. the ``latex()`` command. In the notebook and at the Sage command-line .. there is a pre-defined object named ``latex`` which has several methods, -.. which you can list by typing ``latex.``, followed by the tab key +.. which you can list by typing ``latex.``, followed by the :kbd:`Tab` key .. (note the period). ここでは ``latex.matrix_delimiters`` メソッドに注目してみよう. diff --git a/src/doc/ja/tutorial/tour_numtheory.rst b/src/doc/ja/tutorial/tour_numtheory.rst index 47af68c862c..4d4ed52d50a 100644 --- a/src/doc/ja/tutorial/tour_numtheory.rst +++ b/src/doc/ja/tutorial/tour_numtheory.rst @@ -161,7 +161,7 @@ Sageには :math:`p` \-進数体も組込まれている. Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/pt/tutorial/tour_numtheory.rst b/src/doc/pt/tutorial/tour_numtheory.rst index 6371b491eaf..a3dc973a937 100644 --- a/src/doc/pt/tutorial/tour_numtheory.rst +++ b/src/doc/pt/tutorial/tour_numtheory.rst @@ -157,7 +157,7 @@ NumberField. Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/ru/tutorial/conf.py b/src/doc/ru/tutorial/conf.py index c237e769ed4..0b7a8c4e3db 100644 --- a/src/doc/ru/tutorial/conf.py +++ b/src/doc/ru/tutorial/conf.py @@ -41,4 +41,3 @@ # Additional LaTeX stuff if necessary: #latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - diff --git a/src/doc/ru/tutorial/tour_numtheory.rst b/src/doc/ru/tutorial/tour_numtheory.rst index 652abfbc99e..a985d49fbd0 100644 --- a/src/doc/ru/tutorial/tour_numtheory.rst +++ b/src/doc/ru/tutorial/tour_numtheory.rst @@ -150,7 +150,7 @@ Sage содержит стандартные функции теории чис Univariate Quotient Polynomial Ring in a over Rational Field with modulus x^3 + x^2 - 2*x + 8 sage: K.units() - (3*a^2 + 13*a + 13,) + (-3*a^2 - 13*a - 13,) sage: K.discriminant() -503 sage: K.class_group() diff --git a/src/doc/tr/a_tour_of_sage/conf.py b/src/doc/tr/a_tour_of_sage/conf.py index adb526f1c78..f969a1b516a 100644 --- a/src/doc/tr/a_tour_of_sage/conf.py +++ b/src/doc/tr/a_tour_of_sage/conf.py @@ -35,7 +35,6 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ - ('index', name+'.tex', 'Sage Turu', + ('index', name + '.tex', 'Sage Turu', 'The Sage Development Team', 'manual'), ] - diff --git a/src/sage/algebras/affine_nil_temperley_lieb.py b/src/sage/algebras/affine_nil_temperley_lieb.py index 492043ddf7d..8aff4321abb 100644 --- a/src/sage/algebras/affine_nil_temperley_lieb.py +++ b/src/sage/algebras/affine_nil_temperley_lieb.py @@ -85,12 +85,11 @@ def _element_constructor_(self, w): a2*a1 """ W = self.weyl_group() - assert(w in W) + assert w in W word = w.reduced_word() - if all( self.has_no_braid_relation(W.from_reduced_word(word[:i]), word[i]) for i in range(len(word)) ): + if all(self.has_no_braid_relation(W.from_reduced_word(word[:i]), word[i]) for i in range(len(word))): return self.monomial(w) - else: - return self.zero() + return self.zero() @cached_method def one_basis(self): @@ -203,7 +202,7 @@ def product_on_basis(self, w, w1): ... AssertionError """ - assert(self(w) != self.zero()) + assert self(w) != self.zero() for i in w1.reduced_word(): if self.has_no_braid_relation(w, i): w = w.apply_simple_reflection(i) diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 671282406c6..ac06fa76ab8 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -20,18 +20,18 @@ import sage.algebras.catalog as algebras -from .quantum_groups.all import * from .quatalg.all import * +from .steenrod.all import * +from .fusion_rings.all import * +from .lie_algebras.all import * +from .quantum_groups.all import * +from .lie_conformal_algebras.all import * # Algebra base classes from .algebra import Algebra from .free_algebra import FreeAlgebra from .free_algebra_quotient import FreeAlgebraQuotient -from .steenrod.all import * -from .lie_algebras.all import * -from .quantum_groups.all import * -from .lie_conformal_algebras.all import * from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index cce33d64029..1ccf517e2c0 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -6,7 +6,7 @@ easy way to discover and quickly create the algebras that are available (as listed here). -Let ``<tab>`` indicate pressing the tab key. So begin by typing +Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing ``algebras.<tab>`` to the see the currently implemented named algebras. - :class:`algebras.AlternatingCentralExtensionQuantumOnsager @@ -133,5 +133,4 @@ 'ACEQuantumOnsagerAlgebra', 'AlternatingCentralExtensionQuantumOnsager') lazy_import('sage.algebras.yangian', 'Yangian') -del lazy_import # We remove the object from here so it doesn't appear under tab completion - +del lazy_import # We remove the object from here so it doesn't appear under tab completion diff --git a/src/sage/algebras/cellular_basis.py b/src/sage/algebras/cellular_basis.py index 9e484492954..42ce84307d5 100644 --- a/src/sage/algebras/cellular_basis.py +++ b/src/sage/algebras/cellular_basis.py @@ -1,4 +1,4 @@ -r""" +r""" Cellular Basis ============== diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 8fc5d750dd6..04d9b3f0cae 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -6,8 +6,7 @@ - Travis Scrimshaw (2013-09-06): Initial version - Trevor K. Karn (2022-07-27): Rewrite basis indexing using FrozenBitset """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2022 Travis Scrimshaw <tcscrims at gmail.com> # (C) 2022 Trevor Karn <karnx018 at umn.edu> # @@ -15,9 +14,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.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -631,7 +629,7 @@ def _element_constructor_(self, x): return self.element_class(self, {FrozenBitset((i,)): R.one() for i in x}) try: - return super(CliffordAlgebra, self)._element_constructor_(x) + return super()._element_constructor_(x) except TypeError: raise TypeError(f'do not know how to make {x=} an element of self') @@ -666,7 +664,7 @@ def _basis_index_function(self, x): # if the input is a tuple, assume that it has # entries in {0, ..., 2**Q.dim()-1} if isinstance(x, tuple): - return FrozenBitset(x, capacity = Q.dim()) + return FrozenBitset(x, capacity=Q.dim()) # slice the output of format in order to make conventions # of format and FrozenBitset agree. @@ -1758,16 +1756,26 @@ def interior_product_on_basis(self, a, b): EXAMPLES:: sage: E.<x,y,z> = ExteriorAlgebra(QQ) - sage: E.interior_product_on_basis((0,), (0,)) + sage: k = list(E.basis().keys()) + sage: E.interior_product_on_basis(k[1], k[1]) 1 - sage: E.interior_product_on_basis((0,2), (0,)) + sage: E.interior_product_on_basis(k[5], k[1]) z - sage: E.interior_product_on_basis((1,), (0,2)) + sage: E.interior_product_on_basis(k[2], k[5]) 0 - sage: E.interior_product_on_basis((0,2), (1,)) + sage: E.interior_product_on_basis(k[5], k[2]) 0 - sage: E.interior_product_on_basis((0,1,2), (0,2)) + sage: E.interior_product_on_basis(k[7], k[5]) -y + + Check :trac:`34694`:: + + sage: E = ExteriorAlgebra(SR,'e',3) + sage: E.inject_variables() + Defining e0, e1, e2 + sage: a = (e0*e1).interior_product(e0) + sage: a * e0 + -e0*e1 """ sgn = True t = list(a) @@ -1778,7 +1786,9 @@ def interior_product_on_basis(self, a, b): sgn = not sgn t.remove(i) R = self.base_ring() - return self.term(tuple(t), (R.one() if sgn else - R.one())) + if not t: # catch empty sets + t = None + return self.term(FrozenBitset(t), (R.one() if sgn else - R.one())) def lifted_bilinear_form(self, M): r""" @@ -2994,4 +3004,3 @@ def groebner_basis(self, term_order=None, reduced=True): self._groebner_strategy.compute_groebner(reduced=reduced) self._reduced = reduced return self._groebner_strategy.groebner_basis - diff --git a/src/sage/algebras/clifford_algebra_element.pyx b/src/sage/algebras/clifford_algebra_element.pyx index e065b6b70b9..6a12d5657a3 100644 --- a/src/sage/algebras/clifford_algebra_element.pyx +++ b/src/sage/algebras/clifford_algebra_element.pyx @@ -90,6 +90,15 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): 0 sage: 0*x 0 + + :trac:`34707`:: + + sage: Q = QuadraticForm(QQ, 2, [0,5,0]) + sage: C.<p,q> = CliffordAlgebra(Q) + sage: (q * p) * q + 5*q + sage: q * (p * q) + 5*q """ Q = self._parent._quadratic_form zero = self._parent._base.zero() @@ -110,7 +119,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): if ml.isempty(): return rhs._mul_term_self(ml, cl) if len(rhs._monomial_coefficients) == 1: - mr, cr = next(iter(self._monomial_coefficients.items())) + mr, cr = next(iter(rhs._monomial_coefficients.items())) if mr.isempty(): return self._mul_self_term(mr, cr) @@ -125,7 +134,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): # the dictionary describing the element # ``e[i]`` * (the element described by the dictionary ``cur``) # (where ``e[i]`` is the ``i``-th standard basis vector). - for mr,cr in cur.items(): + for mr, cr in cur.items(): # Commute the factor as necessary until we are in order for j in mr: @@ -161,7 +170,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): cur = next_level # Add the distributed terms to the total - for index,coeff in cur.items(): + for index, coeff in cur.items(): d[index] = d.get(index, zero) + cl * coeff if d[index] == zero: del d[index] @@ -982,4 +991,3 @@ cdef class CohomologyRAAGElement(CliffordAlgebraElement): del d[tp] return self.__class__(self._parent, d) - diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 81615828b56..62340be2072 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -1331,8 +1331,8 @@ def __classcall__(self, data, **kwargs): # mutate_initial to name new cluster variables. splitnames = map(lambda w: w.partition(kwargs['cluster_variable_prefix']), kwargs['cluster_variable_names'] + kwargs['coefficient_names']) - nfi = 1 + max([-1] + [int(v) for u, _, v in splitnames - if u == '' and v.isdigit()]) + nfi = 1 + max((int(v) for u, _, v in splitnames + if u == '' and v.isdigit()), default=-1) kwargs.setdefault('next_free_index', nfi) # Determine scalars diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index a05ede46e52..f0bf211bfc5 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -100,6 +100,8 @@ from sage.rings.quotient_ring_element import QuotientRingElement from sage.misc.cachefunc import cached_function +import sage.interfaces.abc + def sorting_keys(element): r""" @@ -173,34 +175,54 @@ def __classcall__(cls, A, im_gens): sage: d2 = A.cdg_algebra({x: x*y, z: t, y: -x*y, t: 0}).differential() sage: d1 is d2 True + + Check that :trac:`34818` is solved:: + + sage: A.<a,b,x,u> = GradedCommutativeAlgebra(QQ,degrees=(2,2,3,3)) + sage: A = A.quotient(A.ideal([a*u,b*u,x*u])) + sage: A.cdg_algebra({x:a*b,a:u}) + Commutative Differential Graded Algebra with generators ('a', 'b', 'x', 'u') in degrees (2, 2, 3, 3) with relations [a*u, b*u, x*u] over Rational Field with differential: + a --> u + b --> 0 + x --> a*b + u --> 0 + sage: A.cdg_algebra({x:a*b,a:u,u:a^2}) + Traceback (most recent call last): + ... + ValueError: the differential does not preserve the ideal """ if isinstance(im_gens, (list, tuple)): - im_gens = {A.gen(i): x for i, x in enumerate(im_gens)} + im_gens = {A.gen(i): A(x) for i, x in enumerate(im_gens)} + else: + im_gens = {A(a): A(im_gens[a]) for a in im_gens} - R = A.cover_ring() I = A.defining_ideal() - if A.base_ring().characteristic() != 2: - squares = R.ideal([R.gen(i)**2 for i, d in enumerate(A._degrees) - if is_odd(d)], side='twosided') - else: - squares = R.ideal(0, side='twosided') - - if I != squares: - A_free = GCAlgebra(A.base(), names=A._names, degrees=A._degrees) - free_diff = {A_free(a): A_free(im_gens[a]) for a in im_gens} - B = A_free.cdg_algebra(free_diff) - IB = B.ideal([B(g) for g in I.gens()]) - BQ = GCAlgebra.quotient(B, IB) - # We check that the differential respects the - # relations in the quotient method, but we also have - # to check this here, in case a GCAlgebra with - # relations is defined first, and then a differential - # imposed on it. - for g in IB.gens(): - if not BQ(g.differential()).is_zero(): - raise ValueError("The differential does not preserve the ideal") - - im_gens = {A(a): A(im_gens[a]) for a in im_gens} + + def image_monomial(exponent): + i = 0 + cexp = list(exponent) + ell = len(cexp) + while i < ell: + if not cexp[i]: + i +=1 + continue + a = A.gen(i) + try: + da = im_gens[a] + except KeyError: + da = A.zero() + cexp[i] -= 1 + b = A.prod(A.gen(j) ** cexp[j] for j in range(len(cexp))) + db = image_monomial(cexp) + im = da * b + (-1)**A._degrees[i] * a * db + return A(im) + return A.zero() + + for g in I.gens(): + d = g.dict() + 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") for i in im_gens: x = im_gens[i] @@ -208,10 +230,10 @@ def __classcall__(cls, A, im_gens): and (not x.is_homogeneous() or total_degree(x.degree()) != total_degree(i.degree()) + 1)): - raise ValueError("The given dictionary does not determine a degree 1 map") + raise ValueError("the given dictionary does not determine a degree 1 map") - im_gens = tuple(im_gens.get(x, A.zero()) for x in A.gens()) - return super(Differential, cls).__classcall__(cls, A, im_gens) + im_gens = tuple([im_gens.get(x, A.zero()) for x in A.gens()]) + return super().__classcall__(cls, A, im_gens) def __init__(self, A, im_gens): r""" @@ -243,14 +265,14 @@ def __init__(self, A, im_gens): sage: A.cdg_algebra({a:b, b:c}) Traceback (most recent call last): ... - ValueError: The given dictionary does not determine a valid differential + ValueError: the given dictionary does not determine a valid differential """ self._dic_ = {A.gen(i): x for i, x in enumerate(im_gens)} Morphism.__init__(self, Hom(A, A, category=Modules(A.base_ring()))) for i in A.gens(): if not self(self(i)).is_zero(): - raise ValueError("The given dictionary does not determine a valid differential") + raise ValueError("the given dictionary does not determine a valid differential") def _call_(self, x): r""" @@ -600,7 +622,7 @@ def __init__(self, A, im_gens): if y != 0: diff_deg.append(y.degree() - x.degree()) if len(set(diff_deg)) > 1: - raise ValueError("The differential does not have a well-defined degree") + raise ValueError("the differential does not have a well-defined degree") self._degree_of_differential = diff_deg[0] @cached_method @@ -941,7 +963,7 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= """ if names is None: if degrees is None: - raise ValueError("You must specify names or degrees") + raise ValueError("you must specify names or degrees") else: n = len(degrees) names = tuple('x{}'.format(i) for i in range(n)) @@ -958,22 +980,15 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= # Deal with multigrading: convert lists and tuples to elements # of an additive abelian group. if degrees: - multigrade = False try: rank = len(list(degrees[0])) G = AdditiveAbelianGroup([0] * rank) degrees = [G(vector(d)) for d in degrees] - multigrade = True except TypeError: # The entries of degrees are not iterables, so # treat as singly-graded. pass - if multigrade: - if sorted(map(sum, degrees)) != list(map(sum, degrees)): - raise ValueError("the generators should be ordered in increased total degree") - else: - if sorted(degrees) != list(degrees): - raise ValueError("the generators should be ordered in increasing degree") + degrees = tuple(degrees) if not R or not I: if n > 1: @@ -998,9 +1013,9 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= for i in range(n) if is_odd(tot_degs[i])], side='twosided') - return super(GCAlgebra, cls).__classcall__(cls, base=base, names=names, - degrees=degrees, R=R, I=I, - category=category) + return super().__classcall__(cls, base=base, names=names, + degrees=degrees, R=R, I=I, + category=category) def __init__(self, base, R=None, I=None, names=None, degrees=None, category=None): """ @@ -1203,7 +1218,7 @@ def quotient(self, I, check=True): [x^2*z, y^2*z, z*t] """ if check and any(not i.is_homogeneous() for i in I.gens()): - raise ValueError("The ideal must be homogeneous") + raise ValueError("the ideal must be homogeneous") NCR = self.cover_ring() gens1 = list(self.defining_ideal().gens()) gens2 = [i.lift() for i in I.gens()] @@ -1236,7 +1251,7 @@ def _coerce_map_from_(self, other): .gens()): return False return self.cover_ring().has_coerce_map_from(other.cover_ring()) - return super(GCAlgebra, self)._coerce_map_from_(other) + return super()._coerce_map_from_(other) def _element_constructor_(self, x, coerce=True): r""" @@ -1269,8 +1284,7 @@ def _element_constructor_(self, x, coerce=True): R = self.cover_ring() x = R(x) - from sage.interfaces.singular import is_SingularElement - if is_SingularElement(x): + if isinstance(x, sage.interfaces.abc.SingularElement): # self._singular_().set_ring() x = self.element_class(self, x.sage_poly(self.cover_ring())) return x @@ -1447,10 +1461,10 @@ def degree(self, total=False): sage: A(0).degree() Traceback (most recent call last): ... - ValueError: The zero element does not have a well-defined degree + ValueError: the zero element does not have a well-defined degree """ if self.is_zero(): - raise ValueError("The zero element does not have a well-defined degree") + raise ValueError("the zero element does not have a well-defined degree") exps = self.lift().dict().keys() degrees = self.parent()._degrees n = self.parent().ngens() @@ -1581,7 +1595,7 @@ def basis_coefficients(self, total=False): sage: (t + x).basis_coefficients() Traceback (most recent call last): ... - ValueError: This element is not homogeneous + ValueError: this element is not homogeneous sage: B.<c,d> = GradedCommutativeAlgebra(QQ, degrees=((2,0), (0,4))) sage: B.basis(4) @@ -1591,10 +1605,10 @@ def basis_coefficients(self, total=False): sage: (c^2 - 1/2 * d).basis_coefficients() Traceback (most recent call last): ... - ValueError: This element is not homogeneous + ValueError: this element is not homogeneous """ if not self.is_homogeneous(total): - raise ValueError('This element is not homogeneous') + raise ValueError('this element is not homogeneous') basis = self.parent().basis(self.degree(total)) lift = self.lift() @@ -1726,7 +1740,7 @@ def quotient(self, I, check=True): [x^2*z, y^2*z, z*t] """ if check and any(not i.is_homogeneous() for i in I.gens()): - raise ValueError("The ideal must be homogeneous") + raise ValueError("the ideal must be homogeneous") NCR = self.cover_ring() gens1 = list(self.defining_ideal().gens()) gens2 = [i.lift() for i in I.gens()] @@ -1755,7 +1769,7 @@ def _coerce_map_from_(self, other): return False elif isinstance(other, GCAlgebra): # Not multigraded return False - return super(GCAlgebra_multigraded, self)._coerce_map_from_(other) + return super()._coerce_map_from_(other) def basis(self, n, total=False): """ @@ -1880,18 +1894,18 @@ def degree(self, total=False): sage: (a**2*b + c).degree() Traceback (most recent call last): ... - ValueError: This element is not homogeneous + ValueError: this element is not homogeneous sage: (a**2*b + c).degree(total=True) 3 sage: A(0).degree() Traceback (most recent call last): ... - ValueError: The zero element does not have a well-defined degree + ValueError: the zero element does not have a well-defined degree """ if total: return GCAlgebra.Element.degree(self) if self.is_zero(): - raise ValueError("The zero element does not have a well-defined degree") + 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() @@ -1899,7 +1913,7 @@ def degree(self, total=False): if len(set(l)) == 1: return l[0] else: - raise ValueError('This element is not homogeneous') + raise ValueError('this element is not homogeneous') ########################################################### @@ -1995,7 +2009,7 @@ def __init__(self, A, differential): sage: A.cdg_algebra({a: a*b*c}) Traceback (most recent call last): ... - ValueError: The given dictionary does not determine a degree 1 map + ValueError: the given dictionary does not determine a degree 1 map The differential composed with itself must be zero:: @@ -2003,7 +2017,7 @@ def __init__(self, A, differential): sage: A.cdg_algebra({a:b, b:c}) Traceback (most recent call last): ... - ValueError: The given dictionary does not determine a valid differential + ValueError: the given dictionary does not determine a valid differential """ cat = Algebras(A.base()).Graded() & ChainComplexes(A.base()) GCAlgebra.__init__(self, A.base(), names=A._names, @@ -2013,6 +2027,49 @@ def __init__(self, A, differential): self._minimalmodels = {} self._numerical_invariants = {} + def cdg_algebra(self, differential): + r""" + Construct a differential graded commutative algebra from the underlying + graded commutative algebra by specifying a differential. This may be used + to get a new differential over the same algebra structure. + + INPUT: + + - ``differential`` -- a dictionary defining a differential or + a map defining a valid differential + + The keys of the dictionary are generators of the algebra, and + the associated values are their targets under the + differential. Any generators which are not specified are + assumed to have zero differential. Alternatively, the + differential can be defined using the :meth:`differential` + method; see below for an example. + + .. SEEALSO:: + + :meth:`differential` + + EXAMPLES:: + + sage: A.<x,y,z,t> = GradedCommutativeAlgebra(GF(5), degrees=(2, 3, 2, 4)) + sage: B = A.quotient(A.ideal(x^3-z*t)) + sage: C = B.cdg_algebra({y:t}) + sage: C + Commutative Differential Graded Algebra with generators ('x', 'y', 'z', 't') in degrees (2, 3, 2, 4) with relations [x^3 - z*t] over Finite Field of size 5 with differential: + x --> 0 + y --> t + z --> 0 + t --> 0 + sage: C.cdg_algebra({}) + Commutative Differential Graded Algebra with generators ('x', 'y', 'z', 't') in degrees (2, 3, 2, 4) with relations [x^3 - z*t] over Finite Field of size 5 with differential: + x --> 0 + y --> 0 + z --> 0 + t --> 0 + + """ + return self.graded_commutative_algebra().cdg_algebra(differential) + def graded_commutative_algebra(self): """ Return the base graded commutative algebra of ``self``. @@ -2088,17 +2145,17 @@ def quotient(self, I, check=True): sage: B.quotient(B.ideal(y*x)) Traceback (most recent call last): ... - ValueError: The differential does not preserve the ideal + ValueError: the differential does not preserve the ideal sage: B.quotient(B.ideal(x)) Traceback (most recent call last): ... - ValueError: The differential does not preserve the ideal + ValueError: the differential does not preserve the ideal """ J = self.ideal(I) AQ = GCAlgebra.quotient(self, J, check) for g in I.gens(): if not AQ(g.differential()).is_zero(): - raise ValueError("The differential does not preserve the ideal") + raise ValueError("the differential does not preserve the ideal") dic = {AQ(a): AQ(a.differential()) for a in self.gens()} return AQ.cdg_algebra(dic) @@ -2883,10 +2940,10 @@ def is_coboundary(self): sage: (x*z+y**2).is_coboundary() Traceback (most recent call last): ... - ValueError: This element is not homogeneous + ValueError: this element is not homogeneous """ if not self.is_homogeneous(): - raise ValueError('This element is not homogeneous') + raise ValueError('this element is not homogeneous') # To avoid taking the degree of 0, we special-case it. if self.is_zero(): return True @@ -2929,7 +2986,7 @@ def is_cohomologous_to(self, other): return self.is_coboundary() if (not isinstance(other, DifferentialGCAlgebra.Element) or self.parent() is not other.parent()): - raise ValueError('The element {} does not lie in this DGA' + raise ValueError('the element {} does not lie in this DGA' .format(other)) if (self - other).is_homogeneous(): return (self - other).is_coboundary() @@ -3073,7 +3130,7 @@ def __init__(self, A, differential): sage: B = A.cdg_algebra(differential={x:y, t:z}) # bad Traceback (most recent call last): ... - ValueError: The differential does not have a well-defined degree + ValueError: the differential does not have a well-defined degree """ cat = Algebras(A.base()).Graded() & ChainComplexes(A.base()) GCAlgebra_multigraded.__init__(self, A.base(), names=A._names, @@ -3426,7 +3483,7 @@ def GradedCommutativeAlgebra(ring, names=None, degrees=None, max_degree=None, sage: GradedCommutativeAlgebra(QQ) Traceback (most recent call last): ... - ValueError: You must specify names or degrees + ValueError: you must specify names or degrees """ if max_degree: from .finite_gca import FiniteGCAlgebra diff --git a/src/sage/algebras/exterior_algebra_groebner.pyx b/src/sage/algebras/exterior_algebra_groebner.pyx index 98e338d3468..cdd445e7253 100644 --- a/src/sage/algebras/exterior_algebra_groebner.pyx +++ b/src/sage/algebras/exterior_algebra_groebner.pyx @@ -9,7 +9,7 @@ AUTHORS: - Trevor K. Karn, Travis Scrimshaw (July 2022): Initial implementation """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2022 Trevor K. Karn <karnx018 at umn.edu> # (C) 2022 Travis Scrimshaw <tcscrims at gmail.com> # @@ -17,8 +17,8 @@ AUTHORS: # 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 cysignals.signals cimport sig_check from sage.libs.gmp.mpz cimport mpz_sizeinbase, mpz_setbit, mpz_tstbit, mpz_cmp_si, mpz_sgn @@ -137,7 +137,7 @@ cdef class GroebnerStrategy: return self.int_to_bitset(max(self.bitset_to_int(k) for k in mc)) cdef inline partial_S_poly_left(self, GBElement f, GBElement g): - """ + r""" Compute one half of the `S`-polynomial for ``f`` and ``g``. This computes: @@ -154,7 +154,7 @@ cdef class GroebnerStrategy: return ret cdef inline partial_S_poly_right(self, GBElement f, GBElement g): - """ + r""" Compute one half of the `S`-polynomial for ``f`` and ``g``. This computes: @@ -715,4 +715,3 @@ cdef class GroebnerStrategyDegLex(GroebnerStrategy): from sage.combinat.combination import from_rank return FrozenBitset(from_rank(n, self.rank, deg)) - 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 1b466d157b7..9c8090c8929 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py @@ -756,8 +756,8 @@ def maximal_ideal(self): """ if self.degree() == 0: raise ValueError("the zero algebra is not local") - if not(self.is_unitary() and self.is_commutative() - and (self._assume_associative or self.is_associative())): + if not (self.is_unitary() and self.is_commutative() + and (self._assume_associative or self.is_associative())): raise TypeError("algebra must be unitary, commutative and associative") gens = [] for x in self.gens(): diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx index 60148f063c4..b0ae904d3d2 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx @@ -640,4 +640,3 @@ cdef class FiniteDimensionalAlgebraElement(AlgebraElement): True """ return self.matrix().characteristic_polynomial() - diff --git a/src/sage/algebras/finite_gca.py b/src/sage/algebras/finite_gca.py index 0a40e539438..5f21cdc8290 100644 --- a/src/sage/algebras/finite_gca.py +++ b/src/sage/algebras/finite_gca.py @@ -6,17 +6,16 @@ - Michael Jung (2021): initial version """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2021 Michael Jung <m.jung at vu.nl> # # 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 __future__ import annotations from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.algebras import Algebras from sage.misc.cachefunc import cached_method @@ -27,6 +26,7 @@ from sage.sets.condition_set import ConditionSet from sage.rings.integer_ring import ZZ + class FiniteGCAlgebra(CombinatorialFreeModule, Algebra): r""" Finite dimensional graded commutative algebras. @@ -484,16 +484,15 @@ def one_basis(self): n = len(self._degrees) return self._weighted_vectors([0 for _ in range(n)]) - def gens(self): + def gens(self) -> tuple: r""" - Return the generators of ``self`` as a list. + Return the generators of ``self`` as a tuple. EXAMPLES:: sage: A.<x,y,z> = GradedCommutativeAlgebra(QQ, degrees=(4,8,2), max_degree=10) sage: A.gens() - [x, y, z] - + (x, y, z) """ n = len(self._degrees) zero = [0 for _ in range(n)] @@ -502,7 +501,7 @@ def gens(self): ind = list(zero) ind[k] = 1 indices.append(self._weighted_vectors(ind)) - return [self.monomial(ind) for ind in indices] + return tuple([self.monomial(ind) for ind in indices]) @cached_method def gen(self, i): diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index d5f8050a1c9..583eb5f9ae8 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -169,9 +169,9 @@ def _element_constructor_(self, x): sage: a = H._element_constructor_([1,2,3,4]); a 1 + 2*i + 3*j + 4*k """ - return self.element_class(self,x) + return self.element_class(self, x) - def _coerce_map_from_(self,S): + def _coerce_map_from_(self, S): """ EXAMPLES:: @@ -183,7 +183,7 @@ def _coerce_map_from_(self,S): sage: H._coerce_map_from_(GF(7)) False """ - return S==self or self.__free_algebra.has_coerce_map_from(S) + return S == self or self.__free_algebra.has_coerce_map_from(S) def _repr_(self): """ @@ -197,7 +197,7 @@ def _repr_(self): n = self.__ngens r = self.__module.dimension() x = self.variable_names() - return "Free algebra quotient on %s generators %s and dimension %s over %s"%(n,x,r,R) + return "Free algebra quotient on %s generators %s and dimension %s over %s" % (n, x, r, R) def gen(self, i): """ @@ -331,11 +331,13 @@ def hamilton_quatalg(R): constructed as a free algebra quotient. INPUT: - - R -- a commutative ring + + - R -- a commutative ring OUTPUT: - - Q -- quaternion algebra - - gens -- generators for Q + + - Q -- quaternion algebra + - gens -- generators for Q EXAMPLES:: @@ -359,8 +361,10 @@ def hamilton_quatalg(R): A = FreeAlgebra(R, n, 'i') F = A.monoid() i, j, k = F.gens() - mons = [ F(1), i, j, k ] - M = MatrixSpace(R,4) - mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] - H3 = FreeAlgebraQuotient(A,mons,mats, names=('i','j','k')) + mons = [F(1), i, j, k] + M = MatrixSpace(R, 4) + mats = [M([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]), + M([0, 0, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, -1, 0, 0]), + M([0, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0])] + H3 = FreeAlgebraQuotient(A, mons, mats, names=('i', 'j', 'k')) return H3, H3.gens() diff --git a/build/pkgs/scipoptsuite/has_nonfree_dependencies b/src/sage/algebras/fusion_rings/__init__.py similarity index 100% rename from build/pkgs/scipoptsuite/has_nonfree_dependencies rename to src/sage/algebras/fusion_rings/__init__.py diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py new file mode 100644 index 00000000000..9c375f15440 --- /dev/null +++ b/src/sage/algebras/fusion_rings/all.py @@ -0,0 +1,18 @@ +""" +Fusion Rings +""" +# **************************************************************************** +# Copyright (C) 2022 Guillermo Aboumrad <gh_willieab> +# +# 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.lazy_import import lazy_import + +lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) + +del lazy_import diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py new file mode 100644 index 00000000000..82b5d764fa6 --- /dev/null +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -0,0 +1,2459 @@ +r""" +The F-Matrix of a Fusion Ring +""" +# **************************************************************************** +# Copyright (C) 2019 Daniel Bump <bump at match.stanford.edu> +# Guillermo Aboumrad <gh_willieab> +# Travis Scrimshaw <tcscrims at gmail.com> +# Galit Anikeeva <physicstravels@gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + + +from copy import deepcopy +from ctypes import cast, py_object +from itertools import product, zip_longest +from multiprocessing import Pool, cpu_count, set_start_method, shared_memory +import numpy as np +from os import getpid, remove +import pickle + +from sage.algebras.fusion_rings.fast_parallel_fmats_methods import ( + _backward_subs, _solve_for_linear_terms, + executor +) +from sage.algebras.fusion_rings.poly_tup_engine import ( + apply_coeff_map, constant_coeff, + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, tup_to_univ_poly, + _unflatten_coeffs, + poly_tup_sortkey, + resize +) +from sage.algebras.fusion_rings.shm_managers import KSHandler, FvarsHandler +from sage.graphs.graph import Graph +from sage.matrix.constructor import matrix +from sage.misc.misc import get_main_globals +from sage.rings.ideal import Ideal +from sage.structure.sage_object import SageObject +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.polydict import ETuple +from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics + +class FMatrix(SageObject): + r""" + An F-matrix for a :class:`FusionRing`. + + INPUT: + + - ``FR`` -- a :class:`FusionRing` + - ``fusion_label`` -- (optional) a string used to label basis elements + of the :class:`FusionRing` associated to ``self`` + (see :meth:`FusionRing.fusion_labels`) + - ``var_prefix`` -- (optional) a string indicating the desired prefix + for variables denoting F-symbols to be solved + - ``inject_variables`` -- (default: ``False``) a boolean indicating + whether to inject variables (:class:`FusionRing` basis element + labels and F-symbols) into the global namespace + + The :class:`FusionRing` or Verlinde algebra is the + Grothendieck ring of a modular tensor category [BaKi2001]_. + Such categories arise in conformal field theory or in the + representation theories of affine Lie algebras, or + quantum groups at roots of unity. They have applications + to low dimensional topology and knot theory, to conformal + field theory and to topological quantum computing. The + :class:`FusionRing` captures much information about a fusion + category, but to complete the picture, the F-matrices or + 6j-symbols are needed. For example these are required in + order to construct braid group representations. This + can be done using the :class:`FusionRing` method + :meth:`FusionRing.get_braid_generators`, which uses + the F-matrix. + + We only undertake to compute the F-matrix if the + :class:`FusionRing` is *multiplicity free* meaning that + the Fusion coefficients `N^{ij}_k` are bounded + by 1. For Cartan Types `X_r` and level `k`, + the multiplicity-free cases are given by the + following table. + + +------------------------+----------+ + | Cartan Type | `k` | + +========================+==========+ + | `A_1` | any | + +------------------------+----------+ + | `A_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `B_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `C_2` | `\leq 2` | + +------------------------+----------+ + | `C_r, r\geq 3` | `\leq 1` | + +------------------------+----------+ + | `D_r, r\geq 4` | `\leq 2` | + +------------------------+----------+ + | `G_2, F_4, E_6, E_7` | `\leq 2` | + +------------------------+----------+ + | `E_8` | `\leq 3` | + +------------------------+----------+ + + Beyond this limitation, computation of the F-matrix + can involve very large systems of equations. A + rule of thumb is that this code can compute the + F-matrix for systems with `\leq 14` simple objects + (primary fields) on a machine with 16 GB of memory. + (Larger examples can be quite time consuming.) + + The :class:`FusionRing` and its methods capture much + of the structure of the underlying tensor category. + But an important aspect that is not encoded in the + fusion ring is the associator, which is a homomorphism + `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` that + requires an additional tool, the F-matrix or 6j-symbol. + To specify this, we fix a simple object `D` + and represent the transformation + + .. MATH:: + + \text{Hom}(D, (A\otimes B)\otimes C) + \to \text{Hom}(D, A\otimes(B\otimes C)) + + by a matrix `F^{ABC}_D`. This depends on a pair of + additional simple objects `X` and `Y`. Indeed, we can + get a basis for `\text{Hom}(D, (A\otimes B)\otimes C)` + indexed by simple objects `X` in which the corresponding + homomorphism factors through `X\otimes C`, and similarly + `\text{Hom}(D, A\otimes(B\otimes C))` has a basis indexed + by `Y`, in which the basis vector factors through `A\otimes Y`. + + See [TTWL2009]_ for an introduction to this topic, + [EGNO2015]_ Section 4.9 for a precise mathematical + definition, and [Bond2007]_ Section 2.5 for a discussion + of how to compute the F-matrix. In addition to + [Bond2007]_, worked out F-matrices may be found in + [RoStWa2009]_ and [CHW2015]_. + + The F-matrix is only determined up to a *gauge*. This + is a family of embeddings `C \to A\otimes B` for + simple objects `A, B, C` such that `\text{Hom}(C, A\otimes B)` + is nonzero. Changing the gauge changes the F-matrix though + not in a very essential way. By varying the gauge it is + possible to make the F-matrices unitary, or it is possible + to make them cyclotomic. + + Due to the large number of equations we may fail to find a + Groebner basis if there are too many variables. + + EXAMPLES:: + + sage: I = FusionRing("E8", 2, conjugate=True) + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) + sage: f = I.get_fmatrix(inject_variables=True); f + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients + + We have injected two sets of variables to the global namespace. + We created three variables ``i0, p, s`` to represent the + primary fields (simple elements) of the :class:`FusionRing`. Creating + the :class:`FMatrix` factory also created variables + ``fx1, fx2, ..., fx14`` in order to solve the hexagon and pentagon + equations describing the F-matrix. Since we called :class:`FMatrix` + with the parameter ``inject_variables=True``, these have been injected + into the global namespace. This is not necessary for the code to work + but if you want to run the code experimentally you may want access + to these variables. + + EXAMPLES:: + + sage: f.fmatrix(s, s, s, s) + [fx10 fx11] + [fx12 fx13] + + The F-matrix has not been computed at this stage, so + the F-matrix `F^{sss}_s` is filled with variables + ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is + to solve for these. + + As explained above The F-matrix `(F^{ABC}_D)_{X, Y}` + two other variables `X` and `Y`. We have methods to + tell us (depending on `A, B, C, D`) what the possibilities + for these are. In this example with `A=B=C=D=s` + both `X` and `Y` are allowed to be `i_0` or `s`. + + :: + + 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 + `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`. + + The F-matrix is computed by solving the so-called + pentagon and hexagon equations. The *pentagon equations* + reflect the Mac Lane pentagon axiom in the definition + of a monoidal category. The hexagon relations + reflect the axioms of a *braided monoidal category*, + which are constraints on both the F-matrix and on + the R-matrix. Optionally, orthogonality constraints + may be imposed to obtain an orthogonal F-matrix. + + :: + + sage: sorted(f.get_defining_equations("pentagons"))[1:3] + [fx9*fx12 - fx2*fx13, fx4*fx11 - fx2*fx13] + sage: sorted(f.get_defining_equations("hexagons"))[1:3] + [fx6 - 1, fx2 + 1] + sage: sorted(f.get_orthogonality_constraints())[1:3] + [fx10*fx11 + fx12*fx13, fx10*fx11 + fx12*fx13] + + There are two methods available to compute an F-matrix. + The first, :meth:`find_cyclotomic_solution` uses only + the pentagon and hexagon relations. The second, + :meth:`find_orthogonal_solution` uses additionally + the orthogonality relations. There are some differences + that should be kept in mind. + + :meth:`find_cyclotomic_solution` currently works only with + smaller examples. For example the :class:`FusionRing` for `G_2` + at level 2 is too large. When it is available, this method + produces an F-matrix whose entries are in the same + cyclotomic field as the underlying :class:`FusionRing`. :: + + sage: f.find_cyclotomic_solution() + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx1 - 1 + adding equation... fx11 - 1 + Done! + + We now have access to the values of the F-matrix using + the methods :meth:`fmatrix` and :meth:`fmat`:: + + sage: f.fmatrix(s, s, s, s) + [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] + [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] + sage: f.fmat(s, s, s, s, p, p) + (1/2*zeta128^48 - 1/2*zeta128^16) + + :meth:`find_orthogonal_solution` is much more powerful + and is capable of handling large cases, sometimes + quickly but sometimes (in larger cases) after hours of + computation. Its F-matrices are not always in the + cyclotomic field that is the base ring of the underlying + :class:`FusionRing`, but sometimes in an extension field adjoining + some square roots. When this happens, the :class:`FusionRing` is + modified, adding an attribute ``_basecoer`` that is + a coercion from the cyclotomic field to the field + containing the F-matrix. The field containing the F-matrix + is available through :meth:`field`. :: + + sage: f = FusionRing("B3", 2).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False, checkpoint=True) # not tested (~100 s) + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested + True + + sage: f = FusionRing("G2", 2).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: f.field() # long time + Algebraic Field + """ + def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: f = FusionRing("B3", 2).get_fmatrix() + sage: TestSuite(f).run(skip="_test_pickling") + """ + self._FR = fusion_ring + if inject_variables and (self._FR._fusion_labels is None): + self._FR.fusion_labels(fusion_label, inject_variables=True) + if not self._FR.is_multiplicity_free(): + raise NotImplementedError("FMatrix is only available for multiplicity free FusionRings") + # Set up F-symbols entry by entry + n_vars = self.findcases() + self._poly_ring = PolynomialRing(self._FR.field(), n_vars, var_prefix) + if inject_variables: + print("creating variables %s%s..%s%s"%(var_prefix, 1, var_prefix, n_vars)) + self._poly_ring.inject_variables(get_main_globals()) + self._idx_to_sextuple, self._fvars = self.findcases(output=True) + + # Base field attributes + self._field = self._FR.field() + r = self._field.defining_polynomial().roots(ring=QQbar, multiplicities=False)[0] + self._qqbar_embedding = self._field.hom([r], QQbar) + + # Warm starting + self._chkpt_status = -1 + + # Multiprocessing attributes + self.mp_thresh = 10000 + self.pool = None + + ####################### + ### Class utilities ### + ####################### + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: FusionRing("B2", 1).get_fmatrix() + F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients + """ + return "F-Matrix factory for %s"%self._FR + + def clear_equations(self): + r""" + Clear the list of equations to be solved. + + EXAMPLES:: + + sage: f = FusionRing("E6", 1).get_fmatrix() + sage: f.get_defining_equations('hexagons', output=False) + sage: len(f.ideal_basis) + 6 + sage: f.clear_equations() + sage: len(f.ideal_basis) == 0 + True + """ + self.ideal_basis = [] + + def clear_vars(self): + r""" + Reset the F-symbols. + + EXAMPLES:: + + sage: f = FusionRing("C4", 1).get_fmatrix() + sage: fvars = f.get_fvars() + sage: some_key = sorted(fvars)[0] + sage: fvars[some_key] + fx0 + sage: fvars[some_key] = 1 + sage: f.get_fvars()[some_key] + 1 + sage: f.clear_vars() + sage: f.get_fvars()[some_key] + fx0 + """ + self._fvars = {t: self._poly_ring.gen(idx) for idx, t in self._idx_to_sextuple.items()} + self._solved = [False] * self._poly_ring.ngens() + + def _reset_solver_state(self): + r""" + Reset solver state and clear relevant cache. + + Used to ensure state variables are the same for each + orthogonal solver run. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: K = f.field() + sage: len(f._nnz.nonzero_positions()) + 1 + sage: f.find_orthogonal_solution(verbose=False) + sage: K == f.field() + False + sage: f._reset_solver_state() + sage: K == f.field() + True + sage: f.FR()._basecoer is None + True + sage: f._poly_ring.base_ring() == K + True + sage: sum(f._solved) == 0 + True + sage: len(f.ideal_basis) == 0 + True + sage: for k, v in f._ks.items(): + ....: k + sage: len(f._nnz.nonzero_positions()) == 1 + True + sage: all(len(x.q_dimension.cache) == 0 for x in f.FR().basis()) + True + sage: len(f.FR().r_matrix.cache) == 0 + True + sage: len(f.FR().s_ij.cache) == 0 + True + """ + self._FR._basecoer = None + self._field = self._FR.field() + self._non_cyc_roots = [] + self._poly_ring = self._poly_ring.change_ring(self._field) + self._chkpt_status = -1 + self.clear_vars() + self.clear_equations() + n = self._poly_ring.ngens() + self._var_degs = [0] * n + self._kp = {} + self._ks = KSHandler(n, self._field) + self._singles = self.get_fvars_by_size(1, indices=True) + self._nnz = self._get_known_nonz() + + # Clear relevant caches + [x.q_dimension.clear_cache() for x in self._FR.basis()] + self._FR.r_matrix.clear_cache() + self._FR.s_ij.clear_cache() + + def fmat(self, a, b, c, d, x, y, data=True): + r""" + Return the F-Matrix coefficient `(F^{a, b, c}_d)_{x, y}`. + + EXAMPLES:: + + sage: fr = FusionRing("G2", 1, fusion_labels=("i0", "t"), inject_variables=True) + sage: f = fr.get_fmatrix() + sage: [f.fmat(t, t, t, t, x, y) for x in fr.basis() for y in fr.basis()] + [fx1, fx2, fx3, fx4] + sage: f.find_cyclotomic_solution(output=True) + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx2 - 1 + Done! + {(t, t, t, i0, t, t): 1, + (t, t, t, t, i0, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, i0, t): 1, + (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)} + sage: [f.fmat(t, t, t, t, x, y) for x in f._FR.basis() for y in f._FR.basis()] + [(-zeta60^14 + zeta60^6 + zeta60^4 - 1), + 1, + (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (zeta60^14 - zeta60^6 - zeta60^4 + 1)] + """ + if (self._FR.Nk_ij(a, b, x) == 0 or self._FR.Nk_ij(x, c, d) == 0 + or self._FR.Nk_ij(b, c, y) == 0 or self._FR.Nk_ij(a, y, d) == 0): + return 0 + + # Some known zero F-symbols + if a == self._FR.one(): + if x == b and y == d: + return 1 + else: + return 0 + if b == self._FR.one(): + if x == a and y == c: + return 1 + else: + return 0 + if c == self._FR.one(): + if x == d and y == b: + return 1 + else: + return 0 + if data: + # Better to use try/except for speed. Somewhat trivial, but worth + # hours when method is called ~10^11 times + try: + return self._fvars[a, b, c, d, x, y] + except KeyError: + return 0 + else: + return (a, b, c, d, x, y) + + def fmatrix(self, a, b, c, d): + r""" + Return the F-Matrix `F^{a, b, c}_d`. + + INPUT: + + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` + + EXAMPLES:: + + sage: fr = FusionRing("A1", 2, fusion_labels="c", inject_variables=True) + sage: f = fr.get_fmatrix(new=True) + sage: f.fmatrix(c1, c1, c1, c1) + [fx0 fx1] + [fx2 fx3] + sage: f.find_cyclotomic_solution(verbose=False); + adding equation... fx4 - 1 + adding equation... fx10 - 1 + sage: f.f_from(c1, c1, c1, c1) + [c0, c2] + sage: f.f_to(c1, c1, c1, c1) + [c0, c2] + sage: f.fmatrix(c1, c1, c1, c1) + [ (1/2*zeta32^12 - 1/2*zeta32^4) (-1/2*zeta32^12 + 1/2*zeta32^4)] + [ (1/2*zeta32^12 - 1/2*zeta32^4) (1/2*zeta32^12 - 1/2*zeta32^4)] + """ + X = self.f_from(a, b, c, d) + Y = self.f_to(a, b, c, d) + return matrix([[self.fmat(a, b, c, d, x, y) for y in Y] for x in X]) + + def field(self): + r""" + Return the base field containing the F-symbols. + + When ``self`` is initialized, the field is set to be the + cyclotomic field of the :class:`FusionRing` associated + to ``self``. + + The field may change after running :meth:`find_orthogonal_solution`. + At that point, this method could return the + associated :class:`FusionRing`'s cyclotomic field, an + appropriate :func:`NumberField` that was computed on the fly + by the F-matrix solver, or the :class:`QQbar<AlgebraicField>`. + + Depending on the ``CartanType`` of ``self``, the solver may need + to compute an extension field containing certain square roots that + do not belong to the associated :class:`FusionRing`'s cyclotomic field. + + In certain cases we revert to :class:`QQbar<AlgebraicField>` because + the extension field computation does not seem to terminate. See + :meth:`attempt_number_field_computation` for more details. + + The method :meth:`get_non_cyclotomic_roots` returns a list of + roots defining the extension of the :class:`FusionRing`'s + cyclotomic field needed to contain all F-symbols. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f.field() + Cyclotomic Field of order 60 and degree 16 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 + sage: phi = f.get_qqbar_embedding() + sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] + [-0.786151377757423 - 8.92806368517581e-31*I] + + .. NOTE:: + + Consider using ``self.field().optimized_representation()`` to + obtain an equivalent :func:`NumberField` with a defining + polynomial with smaller coefficients, for a more efficient + element representation. + """ + return self._field + + def FR(self): + r""" + Return the :class:`FusionRing` associated to ``self``. + + EXAMPLES:: + + sage: f = FusionRing("D3", 1).get_fmatrix() + sage: f.FR() + The Fusion Ring of Type D3 and level 1 with Integer Ring coefficients + """ + return self._FR + + def findcases(self, output=False): + r""" + Return unknown F-matrix entries. + + If run with ``output=True``, + this returns two dictionaries; otherwise it just returns the + number of unknown values. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1, fusion_labels=("i0", "t")).get_fmatrix() + sage: f.findcases() + 5 + sage: f.findcases(output=True) + ({0: (t, t, t, i0, t, t), + 1: (t, t, t, t, i0, i0), + 2: (t, t, t, t, i0, t), + 3: (t, t, t, t, t, i0), + 4: (t, t, t, t, t, t)}, + {(t, t, t, i0, t, t): fx0, + (t, t, t, t, i0, i0): fx1, + (t, t, t, t, i0, t): fx2, + (t, t, t, t, t, i0): fx3, + (t, t, t, t, t, t): fx4}) + """ + i = 0 + if output: + idx_map = dict() + ret = dict() + id_anyon = self._FR.one() + for (a, b, c, d) in product(self._FR.basis(), repeat=4): + if a == id_anyon or b == id_anyon or c == id_anyon: + continue + for x in self.f_from(a, b, c, d): + for y in self.f_to(a, b, c, d): + if output: + v = self._poly_ring.gen(i) + ret[(a, b, c, d, x, y)] = v + idx_map[i] = (a, b, c, d, x, y) + i += 1 + if output: + return idx_map, ret + else: + return i + + def f_from(self, a, b, c, d): + r""" + Return the possible `x` such that there are morphisms + `d \to x \otimes c \to (a \otimes b) \otimes c`. + + INPUT: + + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` + + EXAMPLES:: + + sage: fr = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) + sage: f = fr.get_fmatrix() + sage: f.fmatrix(a1, a1, a2, a2) + [fx6 fx7] + [fx8 fx9] + sage: f.f_from(a1, a1, a2, a2) + [a0, a2] + sage: f.f_to(a1, a1, a2, a2) + [a1, a3] + """ + return [x for x in self._FR.basis() + if self._FR.Nk_ij(a, b, x) != 0 and self._FR.Nk_ij(x, c, d) != 0] + + def f_to(self, a, b, c, d): + r""" + Return the possible `y` such that there are morphisms + `d \to a \otimes y \to a \otimes (b \otimes c)`. + + INPUT: + + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` + + EXAMPLES:: + + sage: b22 = FusionRing("B2", 2) + sage: b22.fusion_labels("b", inject_variables=True) + sage: B = b22.get_fmatrix() + sage: B.fmatrix(b2, b4, b2, b4) + [fx266 fx267 fx268] + [fx269 fx270 fx271] + [fx272 fx273 fx274] + sage: B.f_from(b2, b4, b2, b4) + [b1, b3, b5] + sage: B.f_to(b2, b4, b2, b4) + [b1, b3, b5] + """ + return [y for y in self._FR.basis() + if self._FR.Nk_ij(b, c, y) != 0 and self._FR.Nk_ij(a, y, d) != 0] + + #################### + ### Data getters ### + #################### + + def get_fvars(self): + r""" + Return a dictionary of F-symbols. + + The keys are sextuples `(a, b, c, d, x, y)` of basis elements of + ``self.FR()`` and the values are the corresponding F-symbols + `(F^{a, b, c}_d)_{xy}`. + + These values reflect the current state of a solver's computation. + + EXAMPLES:: + + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] + fx0 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] + 1 + """ + return self._fvars + + def get_poly_ring(self): + r""" + Return the polynomial ring whose generators denote the desired F-symbols. + + EXAMPLES:: + + sage: f = FusionRing("B6", 1).get_fmatrix() + sage: f.get_poly_ring() + Multivariate Polynomial Ring in fx0, ..., fx13 over + Cyclotomic Field of order 96 and degree 32 + """ + return self._poly_ring + + # TODO: this method is incredibly slow... improve by keeping track of the cyclotomic polynomials, NOT their roots in QQbar + def get_non_cyclotomic_roots(self): + r""" + Return a list of roots that define the extension of the associated + :class:`FusionRing`'s base + :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`, + containing all the F-symbols. + + OUTPUT: + + The list of non-cyclotomic roots is given as a list of elements of the + field returned by :meth:`field()`. + + If ``self.field() == self.FR().field()`` then this method + returns an empty list. + + EXAMPLES:: + + sage: f = FusionRing("E6", 1).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() == f.FR().field() + True + sage: f.get_non_cyclotomic_roots() + [] + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() == f.FR().field() + False + sage: phi = f.get_qqbar_embedding() + sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] + [-0.786151377757423 - 8.92806368517581e-31*I] + + When ``self.field()`` is a ``NumberField``, one may use + :meth:`get_qqbar_embedding` to embed the resulting values into + :class:`QQbar<AlgebraicField>`. + """ + return sorted(set(self._non_cyc_roots)) + + def get_qqbar_embedding(self): + r""" + Return an embedding from the base field containing F-symbols (the + associated :class:`FusionRing`'s + :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`, + a :func:`NumberField`, or :class:`QQbar<AlgebraicField>`) into + :class:`QQbar<AlgebraicField>`. + + This embedding is useful for getting a better sense for the + F-symbols, particularly when they are computed as elements of a + :func:`NumberField`. See also :meth:`get_non_cyclotomic_roots`. + + EXAMPLES:: + + sage: fr = FusionRing("G2", 1) + sage: f = fr.get_fmatrix(fusion_label="g", inject_variables=True, new=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: f.find_orthogonal_solution() + Computing F-symbols for The Fusion Ring of Type G2 and level 1 with Integer Ring coefficients with 5 variables... + Set up 10 hex and orthogonality constraints... + Partitioned 10 equations into 2 components of size: + [4, 1] + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 4 / 5 variables + Set up 0 reduced pentagons... + Pent elim step solved for 4 / 5 variables + Partitioned 0 equations into 0 components of size: + [] + Partitioned 1 equations into 1 components of size: + [1] + Computing appropriate NumberField... + sage: phi = f.get_qqbar_embedding() + sage: phi(f.fmat(g1, g1, g1, g1, g1, g1)).n() + -0.618033988749895 + 1.46674215951686e-29*I + """ + return self._qqbar_embedding + + def get_coerce_map_from_fr_cyclotomic_field(self): + r""" + Return a coercion map from the associated :class:`FusionRing`'s + cyclotomic field into the base field containing all F-symbols + (this could be the :class:`FusionRing`'s + :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`, + a :func:`NumberField`, or :class:`QQbar<AlgebraicField>`). + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) + sage: f.FR().field() + Cyclotomic Field of order 60 and degree 16 + sage: f.field() + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 + sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() + sage: phi.domain() == f.FR().field() + True + sage: phi.codomain() == f.field() + True + + When F-symbols are computed as elements of the associated + :class:`FusionRing`'s base + :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`, + we have ``self.field() == self.FR().field()`` and this + returns the identity map on ``self.field()``. :: + + sage: f = FusionRing("A2", 1).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) + sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() + sage: f.field() + Cyclotomic Field of order 48 and degree 16 + sage: f.field() == f.FR().field() + True + sage: phi.domain() == f.field() + True + sage: phi.is_identity() + True + """ + # If base field is different from associated FusionRing's CyclotomicField, + # return coercion map + try: + return self._coerce_map_from_cyc_field + # Otherwise, return identity map CyclotomicField <-> CyclotomicField + except AttributeError: + F = self._FR.field() + return F.hom([F.gen()], F) + + def get_fvars_in_alg_field(self): + r""" + Return F-symbols as elements of the :class:`QQbar<AlgebraicField>`. + + This method uses the embedding defined by + :meth:`get_qqbar_embedding` to coerce + F-symbols into :class:`QQbar<AlgebraicField>`. + + EXAMPLES:: + + sage: fr = FusionRing("G2", 1) + sage: f = fr.get_fmatrix(fusion_label="g", inject_variables=True, new=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 + sage: f.get_fvars_in_alg_field() + {(g1, g1, g1, g0, g1, g1): 1, + (g1, g1, g1, g1, g0, g0): 0.61803399? + 0.?e-8*I, + (g1, g1, g1, g1, g0, g1): -0.7861514? + 0.?e-8*I, + (g1, g1, g1, g1, g1, g0): -0.7861514? + 0.?e-8*I, + (g1, g1, g1, g1, g1, g1): -0.61803399? + 0.?e-8*I} + """ + return {sextuple: self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items()} + + def get_radical_expression(self): + """ + Return a radical expression of F-symbols. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f.FR().fusion_labels("g", inject_variables=True) + sage: f.find_orthogonal_solution(verbose=False) + sage: radical_fvars = f.get_radical_expression() # long time (~1.5s) + sage: radical_fvars[g1, g1, g1, g1, g1, g0] # long time + -sqrt(1/2*sqrt(5) - 1/2) + """ + return {sextuple: val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()} + + ####################### + ### Private helpers ### + ####################### + + def _get_known_vals(self): + r""" + Construct a dictionary of ``idx``, ``known_val`` pairs used for + substituting into remaining equations. + + EXAMPLES:: + + sage: f = FusionRing("D4", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: len(f._get_known_vals()) == 0 + True + sage: f.find_orthogonal_solution(verbose=False) + sage: len(f._get_known_vals()) == f._poly_ring.ngens() + True + """ + return {i: self._fvars[s] for i, s in self._idx_to_sextuple.items() if self._solved[i]} + + def _get_known_nonz(self): + r""" + Construct an :class:`ETuple` indicating positions of + known nonzero variables. + + .. NOTE:: + + MUST be called after ``self._ks = _get_known_sq()``. + This method is called by the constructor of ``self``. + + EXAMPLES:: + + sage: f = FusionRing("D5", 1).get_fmatrix() # indirect doctest + sage: f._reset_solver_state() + sage: f._nnz + (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) + """ + nonz = {idx: 100 for idx in self._singles} + for idx, v in self._ks.items(): + nonz[idx] = 100 + return ETuple(nonz, self._poly_ring.ngens()) + + ############################## + ### Variables partitioning ### + ############################## + + def largest_fmat_size(self): + r""" + Get the size of the largest F-matrix `F^{abc}_d`. + + EXAMPLES:: + + sage: f = FusionRing("B3", 2).get_fmatrix() + sage: f.largest_fmat_size() + 4 + """ + return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(), repeat=4)) + + def get_fvars_by_size(self, n, indices=False): + r""" + Return the set of F-symbols that are entries of an `n \times n` matrix + `F^{a, b, c}_d`. + + INPUT: + + - `n` -- a positive integer + - ``indices`` -- boolean (default: ``False``) + + If ``indices`` is ``False`` (default), + this method returns a set of sextuples `(a, b, c, d, x, y)` identifying + the corresponding F-symbol. Each sextuple is a key in the + dictionary returned by :meth:`get_fvars`. + + Otherwise the method returns a list of integer indices that + internally identify the F-symbols. The ``indices=True`` option is + meant for internal use. + + EXAMPLES:: + + sage: f = FusionRing("A2", 2).get_fmatrix(inject_variables=True) + creating variables fx1..fx287 + Defining fx0, ..., fx286 + sage: f.largest_fmat_size() + 2 + sage: f.get_fvars_by_size(2) + {(f2, f2, f2, f4, f1, f1), + (f2, f2, f2, f4, f1, f5), + ... + (f4, f4, f4, f4, f4, f0), + (f4, f4, f4, f4, f4, f4)} + """ + var_set = set() + one = self._FR.one() + for a, b, c, d in product(self._FR.basis(), repeat=4): + X = self.f_from(a, b, c, d) + Y = self.f_to(a, b, c, d) + if len(X) == n and len(Y) == n: + for x in X: + for y in Y: + # Discard trivial 1x1 F-matrix + trivial = a == one and x == b and y == d + trivial |= b == one and x == a and y == c + trivial |= c == one and x == d and y == b + if not trivial: + var_set.add((a, b, c, d, x, y)) + if indices: + sext_to_idx = {v: k for k, v in self._idx_to_sextuple.items()} + return {sext_to_idx[fx] for fx in var_set} + return var_set + + ############################ + ### Checkpoint utilities ### + ############################ + + def save_fvars(self, filename): + r""" + Save computed F-symbols for later use. + + INPUT: + + - ``filename`` -- a string specifying the name of the pickle file + to be used + + The current directory is used unless an absolute path to a file in + a different directory is provided. + + .. NOTE:: + + This method should only be used *after* successfully running one + of the solvers, e.g. :meth:`find_cyclotomic_solution` or + :meth:`find_orthogonal_solution`. + + When used in conjunction with :meth:`load_fvars`, this method may + be used to restore state of an :class:`FMatrix` object at the end + of a successful F-matrix solver run. + + EXAMPLES:: + + sage: f = FusionRing("A2", 1).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(verbose=False) + sage: fvars = f.get_fvars() + sage: K = f.field() + sage: filename = f.get_fr_str() + "_solver_results.pickle" + sage: f.save_fvars(filename) + sage: del f + sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True) + sage: f2.load_fvars(filename) + sage: fvars == f2.get_fvars() + True + sage: K == f2.field() + True + sage: os.remove(filename) + """ + final_state = [ + self._fvars, + self._non_cyc_roots, + self.get_coerce_map_from_fr_cyclotomic_field(), + self._qqbar_embedding, + ] + with open(filename, 'wb') as f: + pickle.dump(final_state, f) + + def load_fvars(self, filename): + r""" + Load previously computed F-symbols from a pickle file. + + See :meth:`save_fvars` for more information. + + EXAMPLES:: + + sage: f = FusionRing("A2", 1).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(verbose=False) + sage: fvars = f.get_fvars() + sage: K = f.field() + sage: filename = f.get_fr_str() + "_solver_results.pickle" + sage: f.save_fvars(filename) + sage: del f + sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True) + sage: f2.load_fvars(filename) + sage: fvars == f2.get_fvars() + True + sage: K == f2.field() + True + sage: os.remove(filename) + + .. NOTE:: + + :meth:`save_fvars`. This method does not work with intermediate + checkpoint pickles; it only works with pickles containing *all* + F-symbols, i.e. those created by :meth:`save_fvars` and by + specifying an optional ``save_results`` parameter for + :meth:`find_orthogonal_solution`. + """ + with open(filename, 'rb') as f: + self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f) + # Update state attributes + self._chkpt_status = 7 + self._solved = list(True for v in self._fvars) + self._field = self._qqbar_embedding.domain() + + def get_fr_str(self): + r""" + Auto-generate an identifying key for saving results. + + EXAMPLES:: + + sage: f = FusionRing("B3", 1).get_fmatrix() + sage: f.get_fr_str() + 'B31' + """ + ct = self._FR.cartan_type() + return ct.letter + str(ct.n) + str(self._FR.fusion_level()) + + def _checkpoint(self, do_chkpt, status, verbose=True): + r""" + Pickle current solver state. + + EXAMPLES:: + + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons', output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params() + sage: f._checkpoint(do_chkpt=True, status=2) + Checkpoint 2 reached! + sage: del f + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A13.pickle") + Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + sage: f._chkpt_status == 7 + True + sage: sum(f._solved) == f._poly_ring.ngens() + True + sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons', output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params() + sage: f.get_defining_equations('pentagons', output=False) + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._triangular_elim(verbose=False) + sage: f._checkpoint(do_chkpt=True, status=4) + Checkpoint 4 reached! + sage: del f + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A12.pickle") + Computing F-symbols for The Fusion Ring of Type A1 and level 2 with Integer Ring coefficients with 14 variables... + Partitioned 0 equations into 0 components of size: + [] + Partitioned 2 equations into 2 components of size: + [1, 1] + sage: f._chkpt_status == 7 + True + sage: sum(f._solved) == f._poly_ring.ngens() + True + sage: os.remove("fmatrix_solver_checkpoint_A12.pickle") + """ + if not do_chkpt: + return + filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" + with open(filename, 'wb') as f: + pickle.dump([self._fvars, list(self._solved), self._ks, self.ideal_basis, status], f) + if verbose: + print(f"Checkpoint {status} reached!") + + def _restore_state(self, filename): + r""" + Load solver state from file. Use this method both for warm-starting + :meth:`find_orthogonal_solution` and to load pickled results. + + EXAMPLES:: + + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons', output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params() + sage: fvars = f._fvars + sage: ib = f.ideal_basis + sage: solved = f._solved + sage: ks = f._ks + sage: status = f._chkpt_status + sage: f._checkpoint(do_chkpt=True, status=2) + Checkpoint 2 reached! + sage: del f + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) + sage: f._reset_solver_state() + sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") + sage: for sextuple, fvar in fvars.items(): + ....: assert fvar == f._fvars[sextuple] + ....: + sage: ib == f.ideal_basis + True + sage: ks == f._ks + True + sage: solved == f._solved + True + sage: 2 == f._chkpt_status + True + sage: os.remove("fmatrix_solver_checkpoint_A12.pickle") + + TESTS:: + + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(save_results="test.pickle", verbose=False) # long time + sage: del f + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) + sage: f.find_orthogonal_solution(warm_start="test.pickle") # long time + sage: f._chkpt_status == 7 # long time + True + sage: os.remove("test.pickle") # long time + """ + with open(filename, 'rb') as f: + state = pickle.load(f) + # Loading saved results pickle + if len(state) == 4: + self.load_fvars(filename) + self._chkpt_status = 7 + return + self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state + self._update_reduction_params() + + ################# + ### MapReduce ### + ################# + + def start_worker_pool(self, processes=None): + """ + Initialize a ``multiprocessing`` worker pool for parallel processing, + which may be used e.g. to set up defining equations using + :meth:`get_defining_equations`. + + This method sets ``self``'s ``pool`` attribute. The worker + pool may be used time and again. Upon initialization, each process + in the pool attaches to the necessary shared memory resources. + + When you are done using the worker pool, use + :meth:`shutdown_worker_pool` to close the pool and properly dispose + of shared memory resources. + + .. NOTE:: + + Python 3.8+ is required, since the ``multiprocessing.shared_memory`` + module must be imported. + + INPUT: + + - ``processes`` -- an integer indicating the number of workers + in the pool; if left unspecified, the number of workers is + equals the number of processors available + + OUTPUT: + + This method returns a boolean indicating whether a worker pool + was successfully initialized. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix(new=True) + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') + sage: sorted(he) + [fx0 - 1, + fx2*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx4^2 + (zeta60^6)*fx4, + fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3, + fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2, + fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1] + sage: pe = f.get_defining_equations('pentagons') + sage: f.shutdown_worker_pool() + + .. WARNING:: + + This method is needed to initialize the worker pool using the + necessary shared memory resources. Simply using the + ``multiprocessing.Pool`` constructor will not work with our + class methods. + + .. WARNING:: + + Failure to call :meth:`shutdown_worker_pool` may result in a memory + leak, since shared memory resources outlive the process that created + them. + """ + try: + set_start_method('fork') + except RuntimeError: + pass + if not hasattr(self, '_nnz'): + self._reset_solver_state() + # Set up shared memory resource handlers + n_proc = cpu_count() if processes is None else processes + self._pid_list = shared_memory.ShareableList([0]*(n_proc+1)) + pids_name = self._pid_list.shm.name + self._solved = shared_memory.ShareableList(self._solved) + s_name = self._solved.shm.name + self._var_degs = shared_memory.ShareableList(self._var_degs) + vd_name = self._var_degs.shm.name + n = self._poly_ring.ngens() + self._ks = KSHandler(n, self._field, use_mp=True, init_data=self._ks) + ks_names = self._ks.shm.name + self._shared_fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name, init_data=self._fvars) + fvar_names = self._shared_fvars.shm.name + # Initialize worker pool processes + args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name) + + def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name): + """ + Connect worker process to shared memory resources + """ + fmats_obj = cast(fmats_id, py_object).value + fmats_obj._solved = shared_memory.ShareableList(name=solved_name) + fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) + n = fmats_obj._poly_ring.ngens() + K = fmats_obj._field + fmats_obj._fvars = FvarsHandler(n, K, fmats_obj._idx_to_sextuple, name=fvar_names, use_mp=n_proc, pids_name=pids_name) + fmats_obj._ks = KSHandler(n, K, name=ks_names, use_mp=True) + + self.pool = Pool(processes=n_proc, initializer=init, initargs=args) + self._pid_list[0] = getpid() + for i, p in enumerate(self.pool._pool): + self._pid_list[i+1] = p.pid + # return True + + def shutdown_worker_pool(self): + r""" + Shutdown the given worker pool and dispose of shared memory resources + created when the pool was set up using :meth:`start_worker_pool`. + + .. WARNING:: + + Failure to call this method after using :meth:`start_worker_pool` + to create a process pool may result in a memory + leak, since shared memory resources outlive the process that + created them. + + EXAMPLES:: + + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') + sage: f.shutdown_worker_pool() + """ + if self.pool is not None: + self.pool.close() + self.pool = None + self._solved.shm.unlink() + self._var_degs.shm.unlink() + self._ks.shm.unlink() + self._shared_fvars.shm.unlink() + self._pid_list.shm.unlink() + del self.__dict__['_shared_fvars'] + + def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, mp_thresh=None): + r""" + Apply the given mapper to each element of the given input iterable and + return the results (with no duplicates) in a list. + + INPUT: + + - ``mapper`` -- string specifying the name of a function defined in + the ``fast_parallel_fmats_methods`` module + + .. NOTE:: + + If ``worker_pool`` is not provided, function maps and reduces on a + single process. + If ``worker_pool`` is provided, the function attempts to determine + whether it should use multiprocessing based on the length of the + input iterable. If it can't determine the length of the input + iterable then it uses multiprocessing with the default chunksize of + `1` unless a chunksize is provided. + + EXAMPLES:: + + sage: f = FusionRing("A1", 2).get_fmatrix() + sage: f._reset_solver_state() + sage: len(f._map_triv_reduce('get_reduced_hexagons', [(0, 1, False)])) + 11 + sage: f.start_worker_pool() + sage: mp_params = [(i, f.pool._processes, True) for i in range(f.pool._processes)] + sage: len(f._map_triv_reduce('get_reduced_pentagons', mp_params, worker_pool=f.pool, chunksize=1, mp_thresh=0)) + 33 + sage: f.shutdown_worker_pool() + """ + if mp_thresh is None: + mp_thresh = self.mp_thresh + # Compute multiprocessing parameters + if worker_pool is not None: + try: + n = len(input_iter) + except (TypeError, ValueError, AttributeError): + n = mp_thresh + 1 + if chunksize is None: + chunksize = n // (worker_pool._processes**2) + 1 + no_mp = worker_pool is None or n < mp_thresh + # Map phase + input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) + if no_mp: + mapped = map(executor, input_iter) + else: + mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=chunksize) + # Reduce phase + results = set() + for child_eqns in mapped: + if child_eqns is not None: + results.update(child_eqns) + results = list(results) + return results + + ######################## + ### Equations set up ### + ######################## + + def get_orthogonality_constraints(self, output=True): + r""" + Get equations imposed on the F-matrix by orthogonality. + + INPUT: + + - ``output`` -- a boolean + + OUTPUT: + + If ``output=True``, orthogonality constraints are returned as + polynomial objects. + + Otherwise, the constraints are appended to ``self.ideal_basis``. + They are stored in the internal tuple representation. The + ``output=False`` option is meant mostly for internal use by the + F-matrix solver. + + EXAMPLES:: + + sage: f = FusionRing("B4", 1).get_fmatrix() + sage: f.get_orthogonality_constraints() + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, + fx11^2 + fx13^2 - 1] + """ + eqns = [] + for tup in product(self._FR.basis(), repeat=4): + mat = self.fmatrix(*tup) + eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients()) + if output: + return eqns + self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) + + def get_defining_equations(self, option, output=True): + r""" + Get the equations defining the ideal generated by the hexagon or + pentagon relations. + + INPUT: + + - ``option`` -- a string determining equations to be set up: + + * ``'hexagons'`` - get equations imposed on the F-matrix by + the hexagon relations in the definition of a braided category + + * ``'pentagons'`` - get equations imposed on the F-matrix by + the pentagon relations in the definition of a monoidal category + + - ``output`` -- (default: ``True``) a boolean indicating whether + results should be returned, where the equations will be polynomials. + Otherwise, the constraints are appended to ``self.ideal_basis``. + Constraints are stored in the internal tuple representation. The + ``output=False`` option is meant only for internal use by the + F-matrix solver. When computing the hexagon equations with the + ``output=False`` option, the initial state of the F-symbols is used. + + .. NOTE:: + + To set up the defining equations using parallel processing, + use :meth:`start_worker_pool` to initialize multiple processes + *before* calling this method. + + EXAMPLES:: + + sage: f = FusionRing("B2", 1).get_fmatrix() + sage: sorted(f.get_defining_equations('hexagons')) + [fx7 + 1, + fx6 - 1, + fx2 + 1, + fx0 - 1, + fx11*fx12 + (-zeta32^8)*fx13^2 + (zeta32^12)*fx13, + fx10*fx12 + (-zeta32^8)*fx12*fx13 + (zeta32^4)*fx12, + fx10*fx11 + (-zeta32^8)*fx11*fx13 + (zeta32^4)*fx11, + fx10^2 + (-zeta32^8)*fx11*fx12 + (-zeta32^12)*fx10, + fx4*fx9 + fx7, + fx3*fx8 - fx6, + fx1*fx5 + fx2] + sage: pe = f.get_defining_equations('pentagons') + sage: len(pe) + 33 + """ + if not hasattr(self, '_nnz'): + self._reset_solver_state() + n_proc = self.pool._processes if self.pool is not None else 1 + params = [(child_id, n_proc, output) for child_id in range(n_proc)] + eqns = self._map_triv_reduce('get_reduced_'+option, params, worker_pool=self.pool, chunksize=1, mp_thresh=0) + if output: + F = self._field + for i, eq_tup in enumerate(eqns): + eqns[i] = _unflatten_coeffs(F, eq_tup) + return [self._tup_to_fpoly(p) for p in eqns] + self.ideal_basis.extend(eqns) + + ############################ + ### Equations processing ### + ############################ + + def _tup_to_fpoly(self, eq_tup): + r""" + Assemble a polynomial object from its tuple representation. + + .. WARNING:: + + This method avoids implicit casting when constructing a + polynomial object, and may therefore lead to SEGFAULTs. + It is meant for internal use by the F-matrix solver. + + This method is a left inverse of + :meth:`sage.algebras.fusion_rings.poly_tup_engine.poly_to_tup`. + + EXAMPLES:: + + sage: f = FusionRing("C3", 1).get_fmatrix() + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) + True + sage: f.shutdown_worker_pool() + """ + return _tup_to_poly(eq_tup, parent=self._poly_ring) + + def _update_reduction_params(self, eqns=None): + r""" + Update reduction parameters that are solver state attributes. + + EXAMPLES:: + + sage: f = FusionRing("A1", 3).get_fmatrix() + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons', output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f.mp_thresh = 0 + sage: f._fvars = f._shared_fvars + sage: f._triangular_elim(verbose=False) # indirect doctest + sage: f.ideal_basis + [] + sage: f.shutdown_worker_pool() + """ + if eqns is None: + eqns = self.ideal_basis + self._ks.update(eqns) + for i, d in enumerate(get_variables_degrees(eqns, self._poly_ring.ngens())): + self._var_degs[i] = d + self._nnz = self._get_known_nonz() + self._kp = compute_known_powers(self._var_degs, self._get_known_vals(), self._field.one()) + + def _triangular_elim(self, eqns=None, verbose=True): + r""" + Perform triangular elimination of linear terms in two-term equations + until no such terms exist. + + .. NOTE:: + + For optimal usage of triangular elimination, pass in a + *sorted* list of equations. + + EXAMPLES:: + + sage: f = FusionRing("D3", 1).get_fmatrix() + sage: f.get_defining_equations('hexagons', output=False) + sage: f.get_orthogonality_constraints(output=False) + sage: gb = f._par_graph_gb(verbose=False) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: f._triangular_elim() + Elimination epoch completed... 0 eqns remain in ideal basis + sage: f.ideal_basis + [] + """ + if eqns is None: + eqns = self.ideal_basis + + while True: + linear_terms_exist = _solve_for_linear_terms(self, eqns) + if not linear_terms_exist: + break + _backward_subs(self) + # Compute new reduction params and update eqns + self._update_reduction_params(eqns=eqns) + if self.pool is not None and len(eqns) > self.mp_thresh: + n = self.pool._processes + chunks = [[] for i in range(n)] + for i, eq_tup in enumerate(eqns): + chunks[i%n].append(eq_tup) + eqns = chunks + else: + eqns = [eqns] + eqns = self._map_triv_reduce('update_reduce', eqns, worker_pool=self.pool, mp_thresh=0) + eqns.sort(key=poly_tup_sortkey) + if verbose: + print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) + self.ideal_basis = eqns + + ##################### + ### Graph methods ### + ##################### + + def equations_graph(self, eqns=None): + r""" + Construct a graph corresponding to the given equations. + + Every node corresponds to a variable and nodes are connected when + the corresponding variables appear together in an equation. + + INPUT: + + - ``eqns`` -- a list of polynomials + + Each polynomial is either an object in the ring returned by + :meth:`get_poly_ring` or it is a tuple of pairs representing + a polynomial using the internal representation. + + If no list of equations is passed, the graph is built from the + polynomials in ``self.ideal_basis``. In this case the method assumes + the internal representation of a polynomial as a tuple of pairs is + used. + + This method is crucial to :meth:`find_orthogonal_solution`. The + hexagon equations, obtained using :meth:`get_defining_equations`, + define a disconnected graph that breaks up into many small components. + The :meth:`find_orthogonal_solution` solver exploits this when + undertaking a Groebner basis computation. + + OUTPUT: + + A ``Graph`` object. If a list of polynomial objects was given, + the set of nodes in the output graph is the subset polynomial + ring generators appearing in the equations. + + If the internal representation was used, the set of nodes is + the subset of indices corresponding to polynomial ring generators. + This option is meant for internal use by the F-matrix solver. + + EXAMPLES:: + + sage: f = FusionRing("A3", 1).get_fmatrix() + sage: f.get_poly_ring().ngens() + 27 + sage: he = f.get_defining_equations('hexagons') + sage: graph = f.equations_graph(he) + sage: graph.connected_components_sizes() + [6, 3, 3, 3, 3, 3, 3, 1, 1, 1] + """ + if eqns is None: + eqns = self.ideal_basis + + G = Graph() + if not eqns: + return G + + # Eqns could be a list of poly objects or poly tuples stored in internal repn + if isinstance(eqns[0], tuple): + G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) + else: + G.add_vertices([x for eq in eqns for x in eq.variables()]) + for eq in eqns: + # Eqns could be a list of poly objects or poly tuples stored in internal repn + if isinstance(eq, tuple): + s = [v for v in variables(eq)] + else: + s = [v for v in eq.variables()] + for x in s: + for y in s: + if y!=x: + G.add_edge(x, y) + return G + + def _partition_eqns(self, eqns=None, verbose=True): + r""" + Partition equations corresponding to edges in a disconnected graph. + + OUTPUT: + + This method returns a dictionary of (c, e) pairs, where + c is a tuple denoting a connected component in the graph produced + by calling :meth:`equations_graph` with the given ``eqns`` and + e is a list of all equations with variables in c. + + EXAMPLES:: + + sage: f = FusionRing("C2", 1).get_fmatrix() + sage: f.get_defining_equations('hexagons', output=False) + sage: partition = f._partition_eqns() + Partitioned 11 equations into 5 components of size: + [4, 3, 3, 3, 1] + sage: from sage.algebras.fusion_rings.poly_tup_engine import variables + sage: for c, e in partition.items(): + ....: assert set(v for eq_tup in e for v in variables(eq_tup)) == set(c) + sage: vars_in_partition = set() + sage: eqns_in_partition = set() + sage: for c, e in partition.items(): + ....: vars_in_partition.update(c) + ....: eqns_in_partition.update(e) + sage: vars_in_partition == set(v for eq_tup in f.ideal_basis for v in variables(eq_tup)) + True + sage: eqns_in_partition == set(f.ideal_basis) + True + sage: from itertools import product + sage: for e1, e2 in product(partition.values(), repeat=2): + ....: assert e1 == e2 or set(e1).isdisjoint(set(e2)) + """ + if eqns is None: + eqns = self.ideal_basis + graph = self.equations_graph(eqns) + partition = {tuple(c): [] for c in graph.connected_components()} + for eq_tup in eqns: + partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) + if verbose: + print("Partitioned {} equations into {} components of size:".format(len(eqns), len(graph.connected_components()))) + print(graph.connected_components_sizes()) + return partition + + def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verbose=True): + r""" + Compute a Groebner basis for a list of equations partitioned + according to their corresponding graph. + + .. NOTE:: + + If the graph has more than 50 components, this method computes the + Groebner basis in parallel when a ``worker_pool`` is provided. + + This method will refuse to find a Groebner basis for a component + of size larger than 60, since such a calculation does not seem to + terminate. + + EXAMPLES:: + + sage: f = FusionRing("F4", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons', output=False) + sage: gb = f._par_graph_gb() + Partitioned 10 equations into 2 components of size: + [4, 1] + sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs + sage: ret = [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb] + sage: ret.sort(); ret + [fx4 + (-zeta80^24 + zeta80^16), + fx2 - fx3, + fx1 + (zeta80^24 - zeta80^16), + fx0 - 1, + fx3^2 + (zeta80^24 - zeta80^16)] + sage: f.shutdown_worker_pool() + """ + if eqns is None: + eqns = self.ideal_basis + small_comps = list() + temp_eqns = list() + for comp, comp_eqns in self._partition_eqns(eqns=eqns, verbose=verbose).items(): + # Check if component is too large to process + if len(comp) > largest_comp: + temp_eqns.extend(comp_eqns) + else: + small_comps.append(comp_eqns) + input_iter = zip_longest(small_comps, [], fillvalue=term_order) + small_comp_gb = self._map_triv_reduce('compute_gb', input_iter, worker_pool=self.pool, chunksize=1, mp_thresh=50) + ret = small_comp_gb + temp_eqns + return ret + + def _get_component_variety(self, var, eqns): + r""" + Translate equations in each connected component to smaller polynomial + rings so we can call built-in variety method. + + INPUT: + + - ``var`` -- a list of variable indices + - ``eqns`` -- a list of polynomial equations in the internal + tuple of pairs representation + + EXAMPLES:: + + sage: f = FusionRing("G2", 2).get_fmatrix(new=True) + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons', output=False) # long time + sage: f.shutdown_worker_pool() + sage: partition = f._partition_eqns() # long time + Partitioned 327 equations into 35 components of size: + [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1] + sage: c = (216, 292, 319) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time + sage: f._get_component_variety(c, eqns) # long time + [{216: -1, 292: -1, 319: 1}] + """ + # Define smaller poly ring in component vars + R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex') + + # Zip tuples into R and compute Groebner basis + idx_map = {old: new for new, old in enumerate(sorted(var))} + nvars = len(var) + eqns = [_unflatten_coeffs(self._field, eq_tup) for eq_tup in eqns] + polys = [_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R) for eq_tup in eqns] + var_in_R = Ideal(sorted(polys)).variety(ring=AA) + + # Change back to fmats poly ring and append to temp_eqns + inv_idx_map = {v: k for k, v in idx_map.items()} + return [{inv_idx_map[i]: value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R] + + ####################### + ### Solution method ### + ####################### + + # TODO: this can probably be improved by constructing a set of defining polynomials + # and checking, one by one, if it's irreducible over the current field. + # If it is, we construct an extension. Perhaps it's best to go one by one here... + def attempt_number_field_computation(self): + r""" + Based on the ``CartanType`` of ``self`` and data + known on March 17, 2021, determine whether to attempt + to find a :func:`NumberField` containing all the F-symbols. + + This method is used by :meth:`find_orthogonal_solution` + to determine a field containing all F-symbols. + See :meth:`field` and :meth:`get_non_cyclotomic_roots`. + + For certain :class:`fusion rings <FusionRing>`, the number field + computation does not terminate in reasonable time. + In these cases, we report F-symbols as elements + of the :class:`QQbar<AlgebraicField>`. + + EXAMPLES:: + + sage: f = FusionRing("F4", 2).get_fmatrix() + sage: f.attempt_number_field_computation() + False + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: f.attempt_number_field_computation() + True + + .. NOTE:: + + In certain cases, F-symbols are found in the associated + :class:`FusionRing`'s cyclotomic field and a + :func:`NumberField` computation is not needed. In these + cases this method returns ``True`` but the + :meth:`find_orthogonal_solution` solver does *not* + undertake a :func:`NumberField` computation. + """ + ct = self._FR.cartan_type() + k = self._FR._k + # Don't try when k is large and odd for SU(2)_k + if ct.letter == 'A': + if ct.n == 1 and k >= 9 and k % 2: + return False + if ct.letter == 'C': + if ct.n >= 9 and ct.n % 2 and k == 1: + return False + if ct.letter == 'E': + if ct.n < 8 and k == 2: + return False + if ct.n == 8 and k == 3: + return False + if ct.letter == 'F' and k == 2: + return False + if ct.letter == 'G' and k == 2: + return False + return True + + def _get_explicit_solution(self, eqns=None, verbose=True): + r""" + Construct an explicit solution of ``self``. + + When this method is called, the solution is already found in + terms of Groeber basis. A few degrees of freedom remain. + By specializing the free variables and back substituting, a + solution in the base field is now obtained. + + EXAMPLES:: + + sage: f = FusionRing("A1", 3).get_fmatrix() # indirect doctest + sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... + Set up 134 hex and orthogonality constraints... + Partitioned 134 equations into 17 components of size: + [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 51 / 71 variables + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + """ + if eqns is None: + eqns = self.ideal_basis + # Don't add square fixers when warm starting from a late-stage checkpoint + if self._chkpt_status < 5: + n = self._poly_ring.ngens() + one = self._field.one() + for fx, rhs in self._ks.items(): + if not self._solved[fx]: + lt = (ETuple({fx: 2}, n), one) + eqns.append(((lt, (ETuple({}, n), -rhs)))) + eqns_partition = self._partition_eqns(verbose=verbose) + + F = self._field + R = F['x'] + numeric_fvars = dict() + non_cyclotomic_roots = list() + must_change_base_field = False + phi = F.hom([F.gen()], F) + for comp, part in eqns_partition.items(): + # If component has only one equation in a single variable, get a root + if len(comp) == 1 and len(part) == 1: + # Attempt to find cyclotomic root + univ_poly = tup_to_univ_poly(part[0], R) + roots = univ_poly.roots(multiplicities=False) + if roots: + numeric_fvars[comp[0]] = roots[0] + else: + # A real solution is preferred + roots = univ_poly.roots(ring=AA, multiplicities=False) + if not roots: + roots = univ_poly.roots(ring=QQbar, multiplicities=False) + non_cyclotomic_roots.append((comp[0], roots[0])) + must_change_base_field = True + # Otherwise, compute the component variety and select a point to obtain a numerical solution + else: + sols = self._get_component_variety(comp, part) + for fx, rhs in sols[0].items(): + non_cyclotomic_roots.append((fx, rhs)) + must_change_base_field = True + + if must_change_base_field: + # Attempt to compute smallest number field containing all the F-symbols + # If calculation takes too long, we use QQbar as the base field + if self.attempt_number_field_computation(): + if verbose: + print("Computing appropriate NumberField...") + roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots, minimal=True) + else: + self._field = QQbar + bf_elts = [self._qqbar_embedding(F.gen())] + bf_elts += [rhs for fx, rhs in non_cyclotomic_roots] + self._qqbar_embedding = lambda x : x + self._non_cyc_roots = bf_elts[1:] + + # Embed cyclotomic field into newly constructed base field + cyc_gen_as_bf_elt = bf_elts.pop(0) + phi = self._FR.field().hom([cyc_gen_as_bf_elt], self._field) + self._coerce_map_from_cyc_field = phi + numeric_fvars = {k : phi(v) for k, v in numeric_fvars.items()} + for i, elt in enumerate(bf_elts): + numeric_fvars[non_cyclotomic_roots[i][0]] = elt + # Update polynomial ring + self._poly_ring = self._poly_ring.change_ring(self._field) + + # Ensure all F-symbols are known + for fx in numeric_fvars: + self._solved[fx] = True + nvars = self._poly_ring.ngens() + assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) + + # Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + self._fvars = {sextuple: apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()} + for fx, rhs in numeric_fvars.items(): + self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({}, nvars), rhs), ) + _backward_subs(self, flatten=False) + self._fvars = {sextuple: constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()} + + # Update base field attributes + self._FR._field = self.field() + self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() + if self._FR._basecoer: + self._FR.r_matrix.clear_cache() + + def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=True, verbose=True): + r""" + Solve the the hexagon and pentagon relations, along with + orthogonality constraints, to evaluate an orthogonal F-matrix. + + INPUT: + + - ``checkpoint`` -- (default: ``False``) a boolean indicating whether + the computation should be checkpointed. Depending on the associated + ``CartanType``, the computation may take hours to complete. For + large examples, checkpoints are recommended. This method supports + "warm" starting, so the calculation may be resumed from a checkpoint, + using the ``warm_start`` option. + + Checkpoints store necessary state in the pickle file + ``"fmatrix_solver_checkpoint_" + key + ".pickle"``, where ``key`` + is the result of :meth:`get_fr_str`. + + Checkpoint pickles are automatically deleted when the solver exits + a successful run. + + - ``save_results`` -- (optional) a string indicating the name of a + pickle file in which to store calculated F-symbols for later use. + + If ``save_results`` is not provided (default), F-matrix results + are not stored to file. + + The F-symbols may be saved to file after running the solver using + :meth:`save_fvars`. + + - ``warm_start`` -- (optional) a string indicating the name of a pickle + file containing checkpointed solver state. This file must have been + produced by a previous call to the solver using the ``checkpoint`` + option. + + If no file name is provided, the calculation begins from scratch. + + - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use + multiprocessing to speed up calculation. The default value + ``True`` is highly recommended, since parallel processing yields + results much more quickly. + + - ``verbose`` -- (default: ``True``) a boolean indicating whether the + solver should print out intermediate progress reports. + + OUTPUT: + + This method returns ``None``. If the solver runs successfully, the + results may be accessed through various methods, such as + :meth:`get_fvars`, :meth:`fmatrix`, :meth:`fmat`, etc. + + EXAMPLES:: + + sage: f = FusionRing("B5", 1).get_fmatrix(fusion_label="b", inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: f.find_orthogonal_solution() + Computing F-symbols for The Fusion Ring of Type B5 and level 1 with Integer Ring coefficients with 14 variables... + Set up 25 hex and orthogonality constraints... + Partitioned 25 equations into 5 components of size: + [4, 3, 3, 3, 1] + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 10 / 14 variables + Set up 7 reduced pentagons... + Elimination epoch completed... 0 eqns remain in ideal basis + Pent elim step solved for 12 / 14 variables + Partitioned 0 equations into 0 components of size: + [] + Partitioned 2 equations into 2 components of size: + [1, 1] + sage: f.fmatrix(b2, b2, b2, b2) + [ 1/2*zeta80^30 - 1/2*zeta80^10 -1/2*zeta80^30 + 1/2*zeta80^10] + [ 1/2*zeta80^30 - 1/2*zeta80^10 1/2*zeta80^30 - 1/2*zeta80^10] + sage: f.fmat(b2, b2, b2, b2, b0, b1) + -1/2*zeta80^30 + 1/2*zeta80^10 + + Every F-matrix `F^{a, b, c}_d` is orthogonal and in many cases real. + We may use :meth:`fmats_are_orthogonal` and :meth:`fvars_are_real` + to obtain correctness certificates. + + EXAMPLES:: + + sage: f.fmats_are_orthogonal() + True + + In any case, the F-symbols are obtained as elements of the associated + :class:`FusionRing`'s + :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`, + a computed :func:`NumberField`, or :class:`QQbar<AlgebraicField>`. + Currently, the field containing the F-symbols is determined based + on the ``CartanType`` associated to ``self``. + + .. SEEALSO:: + + :meth:`attempt_number_field_computation` + """ + if self._poly_ring.ngens() == 0: + return + self._reset_solver_state() + + # Resume computation from checkpoint + if warm_start: + self._restore_state(warm_start) + # Loading from a pickle with solved F-symbols + if self._chkpt_status > 5: + return + if use_mp: + self.start_worker_pool() + if verbose: + print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) + + if self._chkpt_status < 1: + # Set up hexagon equations and orthogonality constraints + self.get_orthogonality_constraints(output=False) + self.get_defining_equations('hexagons', output=False) + # Report progress + if verbose: + print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) + + # Unzip _fvars and link to shared_memory structure if using multiprocessing + if use_mp:# and loads_shared_memory: + self._fvars = self._shared_fvars + else: + n = self._poly_ring.ngens() + self._fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, init_data=self._fvars) + self._checkpoint(checkpoint, 1, verbose=verbose) + + if self._chkpt_status < 2: + # Set up equations graph. Find GB for each component in parallel. Eliminate variables + self.ideal_basis = self._par_graph_gb(verbose=verbose) + self.ideal_basis.sort(key=poly_tup_sortkey) + self._triangular_elim(verbose=verbose) + # Report progress + if verbose: + print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) + self._checkpoint(checkpoint, 2, verbose=verbose) + + if self._chkpt_status < 3: + # Set up pentagon equations in parallel + self.get_defining_equations('pentagons', output=False) + # Report progress + if verbose: + print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) + self._checkpoint(checkpoint, 3, verbose=verbose) + + if self._chkpt_status < 4: + # Simplify and eliminate variables + self.ideal_basis.sort(key=poly_tup_sortkey) + self._triangular_elim(verbose=verbose) + # Report progress + if verbose: + print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) + self._checkpoint(checkpoint, 4, verbose=verbose) + + # Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change + + # Set up new equations graph and compute variety for each component + if self._chkpt_status < 5: + self.ideal_basis = self._par_graph_gb(term_order="lex", verbose=verbose) + self.ideal_basis.sort(key=poly_tup_sortkey) + self._triangular_elim(verbose=verbose) + self._checkpoint(checkpoint, 5, verbose=verbose) + self.shutdown_worker_pool() + + # Find numeric values for each F-symbol + self._get_explicit_solution(verbose=verbose) + # The calculation was successful, so we may delete checkpoints + self._chkpt_status = 7 + self.clear_equations() + if checkpoint: + remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") + if save_results: + self.save_fvars(save_results) + + ######################### + ### Cyclotomic method ### + ######################### + + def _fix_gauge(self, algorithm=""): + r""" + Fix the gauge by forcing F-symbols not already fixed to equal `1`. + + .. NOTE:: + + This method should be used *after* adding hexagon and pentagon + equations to ``self.ideal_basis``. + + EXAMPLES:: + + sage: f = FusionRing("A3", 1).get_fmatrix() + sage: f._reset_solver_state() # long time + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} # long time + sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time + sage: f.ideal_basis = set(Ideal(eqns).groebner_basis()) # long time + sage: _, _ = f._substitute_degree_one() # long time + sage: f._fix_gauge() # long time + adding equation... fx1 - 1 + adding equation... fx18 - 1 + adding equation... fx21 - 1 + """ + while not all(v for v in self._solved): + # Get a variable that has not been fixed + # In ascending index order, for consistent results + for i, var in enumerate(self._poly_ring.gens()): + if not self._solved[i]: + break + + # Fix var = 1, substitute, and solve equations + self.ideal_basis.add(var-1) + print("adding equation...", var-1) + self.ideal_basis = set(Ideal(list(self.ideal_basis)).groebner_basis(algorithm=algorithm)) + self._substitute_degree_one() + self._update_equations() + + def _substitute_degree_one(self, eqns=None): + r""" + Substitute known value from linear univariate polynomial and + solve, following [Bond2007]_ p.37, for two-term linear equation + for one of the variables. + + EXAMPLES:: + + sage: fr = FusionRing("D3", 1) + sage: f = fr.get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx27 + Defining fx0, ..., fx26 + sage: f._reset_solver_state() + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} + sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9] + sage: _, _ = f._substitute_degree_one() + sage: f._fvars[f._var_to_sextuple[fx0]] + 8 + sage: f._fvars[f._var_to_sextuple[fx4]] + -fx9 + """ + if eqns is None: + eqns = self.ideal_basis + + new_knowns = set() + useless = set() + for eq in eqns: + if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: + self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() + # Add variable to set of known values and remove this equation + new_knowns.add(eq.lm()) + useless.add(eq) + + # Update fvars depending on other variables + for idx, fx in enumerate(self._poly_ring.gens()): + if fx in new_knowns: + self._solved[idx] = fx + for sextuple, rhs in self._fvars.items(): + d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} + if d: + self._fvars[sextuple] = rhs.subs(d) + return new_knowns, useless + + def _update_equations(self): + r""" + Perform backward substitution on equations in ``self.ideal_basis``. + + EXAMPLES:: + + sage: fr = FusionRing("D3", 1) + sage: f = fr.get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx27 + Defining fx0, ..., fx26 + sage: f._reset_solver_state() + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} + sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2] + sage: _, _ = f._substitute_degree_one() + sage: f._update_equations() + sage: f.ideal_basis + {fx3} + """ + special_values = {known: self._fvars[self._var_to_sextuple[known]] for known in self._solved if known} + self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) + self.ideal_basis.discard(0) + + def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, output=False): + r""" + Solve the hexagon and pentagon relations to evaluate the F-matrix. + + This method (omitting the orthogonality constraints) produces + output in the cyclotomic field, but it is very limited in the size + of examples it can handle: for example, `G_2` at level 2 is + too large for this method. You may use :meth:`find_orthogonal_solution` + to solve much larger examples. + + INPUT: + + - ``equations`` -- (optional) a set of equations to be + solved; defaults to the hexagon and pentagon equations + - ``algorithm`` -- (optional) algorithm to compute Groebner Basis + - ``output`` -- (default: ``False``) output a dictionary of + F-matrix values; this may be useful to see but may be omitted + since this information will be available afterwards via the + :meth:`fmatrix` and :meth:`fmat` methods. + + EXAMPLES:: + + sage: fr = FusionRing("A2", 1, fusion_labels="a", inject_variables=True) + sage: f = fr.get_fmatrix(inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.find_cyclotomic_solution(output=True) + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx4 - 1 + Done! + {(a2, a2, a2, a0, a1, a1): 1, + (a2, a2, a1, a2, a1, a0): 1, + (a2, a1, a2, a2, a0, a0): 1, + (a2, a1, a1, a1, a0, a2): 1, + (a1, a2, a2, a2, a0, a1): 1, + (a1, a2, a1, a1, a0, a0): 1, + (a1, a1, a2, a1, a2, a0): 1, + (a1, a1, a1, a0, a2, a2): 1} + + After you successfully run :meth:`find_cyclotomic_solution` you may + check the correctness of the F-matrix by running + :meth:`get_defining_equations` with ``option='hexagons'`` and + ``option='pentagons'``. These should return empty lists + of equations. + + EXAMPLES:: + + sage: f.get_defining_equations("hexagons") + [] + sage: f.get_defining_equations("pentagons") + [] + """ + if self._poly_ring.ngens() == 0: + return + self._reset_solver_state() + self._var_to_sextuple = {self._poly_ring.gen(i): s for i, s in self._idx_to_sextuple.items()} + + if equations is None: + if verbose: + print("Setting up hexagons and pentagons...") + equations = self.get_defining_equations("hexagons")+self.get_defining_equations("pentagons") + if verbose: + print("Finding a Groebner basis...") + self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) + if verbose: + print("Solving...") + self._substitute_degree_one() + if verbose: + print("Fixing the gauge...") + self._fix_gauge(algorithm=algorithm) + if verbose: + print("Done!") + if output: + return self._fvars + + ##################### + ### Verifications ### + ##################### + + def fmats_are_orthogonal(self): + r""" + Verify that all F-matrices are orthogonal. + + This method should always return ``True`` when called after running + :meth:`find_orthogonal_solution`. + + EXAMPLES:: + + sage: f = FusionRing("D4", 1).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) + sage: f.fmats_are_orthogonal() + True + """ + is_orthog = [] + for a, b, c, d in product(self._FR.basis(), repeat=4): + mat = self.fmatrix(a, b, c, d) + is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) + return all(is_orthog) + + def fvars_are_real(self): + r""" + Test whether all F-symbols are real. + + EXAMPLES:: + + sage: f = FusionRing("A1", 3).get_fmatrix() + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework) + True + """ + try: + for k, v in self._fvars.items(): + AA(self._qqbar_embedding(v)) + except ValueError: + print("the F-symbol {} (key {}) has a nonzero imaginary part".format(v, k)) + return False + return True + + def certify_pentagons(self, use_mp=True, verbose=False): + r""" + Obtain a certificate of satisfaction for the pentagon equations, + up to floating-point error. + + This method converts the computed F-symbols (available through + :meth:`get_fvars`) to native Python floats and then checks whether + the pentagon equations are satisfied using floating point arithmetic. + + When ``self.FR().basis()`` has many elements, verifying satisfaction + of the pentagon relations exactly using :meth:`get_defining_equations` + with ``option="pentagons"`` may take a long time. This method is + faster, but it cannot provide mathematical guarantees. + + EXAMPLES:: + + sage: f = FusionRing("C3", 1).get_fmatrix() + sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... + Set up 134 hex and orthogonality constraints... + Partitioned 134 equations into 17 components of size: + [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 51 / 71 variables + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + sage: f.certify_pentagons() is None # not tested (long time ~1.5s, cypari issue in doctesting framework) + True + """ + fvars_copy = deepcopy(self._fvars) + self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} + if use_mp: + pool = Pool() + else: + pool = None + n_proc = pool._processes if pool is not None else 1 + params = [(child_id, n_proc, verbose) for child_id in range(n_proc)] + pe = self._map_triv_reduce('pent_verify', params, worker_pool=pool, chunksize=1, mp_thresh=0) + if np.all(np.isclose(np.array(pe), 0, atol=1e-7)): + if verbose: + print("Found valid F-symbols for {}".format(self._FR)) + pe = None + else: + if verbose: + print("Something went wrong. Pentagons remain.") + self._fvars = fvars_copy + return pe diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd new file mode 100644 index 00000000000..11dc0253b35 --- /dev/null +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd @@ -0,0 +1,5 @@ +cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) +cpdef _backward_subs(factory, bint flatten=*) +cpdef executor(tuple params) +cpdef _solve_for_linear_terms(factory, list eqns=*) + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx new file mode 100644 index 00000000000..a482c0c4fc1 --- /dev/null +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -0,0 +1,534 @@ +""" +Fast F-Matrix Methods +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad <gh_willieab> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +cimport cython +from sage.algebras.fusion_rings.poly_tup_engine cimport ( + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, + subs, subs_squares, reduce_poly_dict, resize, + _flatten_coeffs, _unflatten_coeffs, + has_appropriate_linear_term, + resize +) +from sage.algebras.fusion_rings.shm_managers cimport KSHandler, FvarsHandler +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular +from sage.rings.polynomial.polydict cimport ETuple + +from ctypes import cast, py_object +from itertools import product +from sage.rings.ideal import Ideal +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +########################## +### Fast class methods ### +########################## + +cpdef _solve_for_linear_terms(factory, list eqns=None): + r""" + Solve for a linear term occurring in a two-term equation, and for + variables appearing in univariate single-term equations. + + EXAMPLES:: + + sage: f = FusionRing("D3", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx27 + Defining fx0, ..., fx26 + sage: f._reset_solver_state() + sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: _solve_for_linear_terms(f) + True + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + fx4 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + fx4 + """ + if eqns is None: + eqns = factory.ideal_basis + + cdef bint linear_terms_exist = False + cdef ETuple exp, rhs_exp + cdef int max_var + cdef tuple eq_tup + cdef FvarsHandler fvars = factory._fvars + cdef NumberFieldElement_absolute coeff, other + cdef tuple rhs_coeff + for eq_tup in eqns: + if len(eq_tup) == 1: + vars = variables(eq_tup) + if len(vars) == 1 and not factory._solved[vars[0]]: + fvars[factory._idx_to_sextuple[vars[0]]] = tuple() + factory._solved[vars[0]] = True + linear_terms_exist = True + + # TESTS: + # s = factory._idx_to_sextuple[vars[0]] + # factory.test_fvars[s] = tuple() + # assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s], factory.test_fvars[s]) + if len(eq_tup) == 2: + idx = has_appropriate_linear_term(eq_tup) + if idx < 0: continue + # The chosen term is guaranteed to be univariate in the largest variable + exp = eq_tup[idx][0] + max_var = exp._data[0] + if not factory._solved[max_var]: + rhs_exp = eq_tup[(idx+1) % 2][0] + coeff = factory._field(list(eq_tup[(idx+1) % 2][1])) + other = factory._field(list(eq_tup[idx][1])) + rhs_coeff = tuple((-coeff / other)._coefficients()) + fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp, rhs_coeff), ) + factory._solved[max_var] = True + linear_terms_exist = True + + # TESTS: + # s = factory._idx_to_sextuple[max_var] + # factory.test_fvars[s] = ((rhs_exp, rhs_coeff), ) + # assert _unflatten_coeffs(factory._field, factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s], fvars[s]) + return linear_terms_exist + +cpdef _backward_subs(factory, bint flatten=True): + r""" + Perform backward substitution on ``self.ideal_basis``, traversing + variables in reverse lexicographical order. + + EXAMPLES:: + + sage: f = FusionRing("D3", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx27 + Defining fx0, ..., fx26 + sage: f._reset_solver_state() + sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: _solve_for_linear_terms(f) + True + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + fx4 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + fx4 + sage: f.ideal_basis.append(poly_to_tup(fx4-1)) + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: _solve_for_linear_terms(f) + True + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _backward_subs + sage: _backward_subs(f) + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + 1 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + 1 + """ + one = factory._field.one() + fvars = factory._fvars + solved = factory._solved + cdef KSHandler _ks = factory._ks + cdef dict idx_to_sextuple = factory._idx_to_sextuple + cdef int nvars = len(idx_to_sextuple) + for i in range(nvars-1, -1, -1): + sextuple = idx_to_sextuple[i] + rhs = fvars[sextuple] + d = {var_idx: fvars[idx_to_sextuple[var_idx]] + for var_idx in variables(rhs) if solved[var_idx]} + if d: + kp = compute_known_powers(get_variables_degrees([rhs], nvars), d, one) + res = tuple(subs_squares(subs(rhs, kp, one), _ks).items()) + if flatten: + res = _flatten_coeffs(res) + fvars[sextuple] = res + + +cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): + """ + Cython version of fmat class method. Using cdef for fastest dispatch + """ + if _Nk_ij(a, b, x) == 0 or _Nk_ij(x, c, d) == 0 or _Nk_ij(b, c, y) == 0 or _Nk_ij(a, y, d) == 0: + return 0 + # Some known F-symbols + if a == id_anyon: + return int(x == b and y == d) + if b == id_anyon: + return int(x == a and y == c) + if c == id_anyon: + return int(x == d and y == b) + return fvars[a, b, c, d, x, y] + +###################################### +# Fast fusion coefficients cache # +###################################### + +# from sage.misc.cachefunc import cached_function +# cdef dict _Nk_ij = dict() + +# cpdef _Nk_ij(factory, proc): +# cdef int coeff +# for a, b, c in product(factory._FR.basis(), repeat=3): +# try: +# coeff = (a*b).monomial_coefficients(copy=False)[c.weight()] +# except: +# coeff = 0 +# _Nk_ij[a, b, c] = coeff + +# cpdef int _Nk_ij(a, b, c): +# try: +# return (a*b).monomial_coefficients(copy=False)[c.weight()] +# except KeyError: +# return 0 +# +# _Nk_ij = cached_function(_Nk_ij, name='_Nk_ij') + +############### +### Mappers ### +############### + +cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): + """ + Given an FMatrix factory and a sextuple, return a hexagon equation + as a polynomial object. + """ + a, b, c, d, e, g = sextuple + # To add typing we need to ensure all fmats.fmat are of the same type? + # Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? + lhs = r_matrix(a, c, e, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, c, b, d, e, g) * r_matrix(b, c, g, base_coercion=False) + rhs = 0 + for f in basis: + rhs += _fmat(fvars, Nk_ij, id_anyon, c, a, b, d, e, f) * r_matrix(f, c, d, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, b, c, d, f, g) + return lhs-rhs + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef get_reduced_hexagons(factory, tuple mp_params): + """ + Set up and reduce the hexagon equations corresponding to this worker. + """ + # Set up multiprocessing parameters + cdef list worker_results = list() + cdef int child_id, n_proc + cdef unsigned long i + child_id, n_proc, output = mp_params + cdef tuple sextuple, red + + # Pre-compute common parameters for speed + cdef tuple basis = tuple(factory._FR.basis()) + cdef dict fvars + cdef bint must_zip_up = False + if not output: + fvars = {s: factory._poly_ring.gen(i) for i, s in factory._idx_to_sextuple.items()} + else: + # Handle both cyclotomic and orthogonal solution method + for k, v in factory._fvars.items(): + must_zip_up = isinstance(v, tuple) + break + if must_zip_up: + fvars = {k: _tup_to_poly(v, parent=factory._poly_ring) for k, v in factory._fvars.items()} + else: + fvars = factory._fvars + r_matrix = factory._FR.r_matrix + _Nk_ij = factory._FR.Nk_ij + id_anyon = factory._FR.one() + _field = factory._field + cdef NumberFieldElement_absolute one = _field.one() + cdef ETuple _nnz = factory._nnz + _ks = factory._ks + + # Computation loop + it = product(basis, repeat=6) + for i in range(len(basis)**6): + sextuple = next(it) + 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) + + # Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + + worker_results.append(red) + + return collect_eqns(worker_results) + +cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, tuple nonuple, bint prune=False): + r""" + Given an FMatrix factory and a nonuple, return a pentagon equation + as a polynomial object. + """ + a, b, c, d, e, f, g, k, l = nonuple + lhs = _fmat(fvars, Nk_ij, id_anyon, f, c, d, e, g, l) * _fmat(fvars, Nk_ij, id_anyon, a, b, l, e, f, k) + if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information + return zero + rhs = zero + for h in basis: + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) + return lhs - rhs + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef get_reduced_pentagons(factory, tuple mp_params): + r""" + Set up and reduce the pentagon equations corresponding to this worker. + """ + # Set up multiprocessing parameters + cdef list worker_results = list() + cdef int child_id, n_proc + child_id, n_proc, output = mp_params + cdef unsigned long i + cdef tuple nonuple, red + cdef MPolynomial_libsingular pe + + # Pre-compute common parameters for speed + cdef tuple basis = tuple(factory._FR.basis()) + # Handle both cyclotomic and orthogonal solution method + cdef bint must_zip_up + for k, v in factory._fvars.items(): + must_zip_up = isinstance(v, tuple) + break + cdef dict fvars + if must_zip_up: + fvars = {k: _tup_to_poly(v, parent=factory._poly_ring) for k, v in factory._fvars.items()} + else: + fvars = factory._fvars + _Nk_ij = factory._FR.Nk_ij + id_anyon = factory._FR.one() + _field = factory._field + cdef NumberFieldElement_absolute one = _field.one() + cdef MPolynomial_libsingular zero = factory._poly_ring.zero() + cdef KSHandler _ks = factory._ks + factory._nnz = factory._get_known_nonz() + cdef ETuple _nnz = factory._nnz + + # Computation loop + it = product(basis, repeat=9) + for i in range(len(basis)**9): + nonuple = next(it) + 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) + + # Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + + worker_results.append(red) + return collect_eqns(worker_results) + +cdef list update_reduce(factory, list eqns): + r""" + Substitute known values, known squares, and reduce. + """ + cdef list res = list() + cdef tuple eq_tup, red, unflat + cdef dict eq_dict + + # Pre-compute common parameters for speed + _field = factory._field + one = _field.one() + cdef KSHandler _ks = factory._ks + # Update reduction params + factory._nnz = factory._get_known_nonz() + factory._kp = compute_known_powers(factory._var_degs, factory._get_known_vals(), factory._field.one()) + cdef dict _kp = factory._kp + cdef ETuple _nnz = factory._nnz + + for i in range(len(eqns)): + eq_tup = eqns[i] + # Construct cyclotomic field elts from list repn + unflat = _unflatten_coeffs(_field, eq_tup) + + eq_dict = subs(unflat, _kp, one) + red = reduce_poly_dict(eq_dict, _nnz, _ks, one) + + # Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + + res.append(red) + return collect_eqns(res) + +cdef list compute_gb(factory, tuple args): + r""" + Compute the reduced Groebner basis for given equations iterable. + """ + cdef list res = list() + cdef list eqns, sorted_vars + eqns, term_order = args + # Define smaller poly ring in component vars + sorted_vars = [] + cdef tuple eq_tup + cdef int fx + for eq_tup in eqns: + for fx in variables(eq_tup): + sorted_vars.append(fx) + sorted_vars = sorted(set(sorted_vars)) + 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) } + nvars = len(sorted_vars) + F = factory.field() + cdef list polys = list() + for eq_tup in eqns: + eq_tup = _unflatten_coeffs(F, eq_tup) + polys.append(_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R)) + gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") + + # Change back to fmats poly ring and append to temp_eqns + cdef dict inv_idx_map = { v : k for k, v in idx_map.items() } + cdef tuple t + nvars = factory._poly_ring.ngens() + for p in gb: + t = resize(poly_to_tup(p), inv_idx_map, nvars) + + # Avoid pickling cyclotomic coefficients + t = _flatten_coeffs(t) + + res.append(t) + return collect_eqns(res) + +################ +### Reducers ### +################ + +cdef inline list collect_eqns(list eqns): + r""" + Helper function for returning processed results back to parent process. + + Trivial reducer: simply collects objects with the same key in the worker. + This method is only useful when called after :meth:`executor`, whose + function argument appends output to the ``worker_results`` list. + """ + # Discard the zero polynomial + reduced = set(eqns) - set([tuple()]) + return list(reduced) + +############################## +### Parallel code executor ### +############################## + +# Hard-coded module __dict__-style attribute with visible cdef methods +cdef dict mappers = { + "get_reduced_hexagons": get_reduced_hexagons, + "get_reduced_pentagons": get_reduced_pentagons, + "update_reduce": update_reduce, + "compute_gb": compute_gb, + "pent_verify": pent_verify + } + +cpdef executor(tuple params): + r""" + 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 + its ``id``. + + INPUT: + + - ``params`` -- a tuple ``((fn_name, fmats_id), fn_args)`` where + ``fn_name`` is the name of the function to be executed, ``fmats_id`` + is the ``id`` of the :class:`FMatrix` object, and ``fn_args`` is a + tuple containing all arguments to be passed to the function ``fn_name``. + + .. NOTE:: + + When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object `X` in + the parent process equals the *virtual* memory address of the copy + of object `X` in each worker, so we may construct references to + forked copies of `X` using an ``id`` obtained in the parent process. + + TESTS:: + + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import executor + sage: fmats = FusionRing("A1", 3).get_fmatrix() + sage: fmats._reset_solver_state() + sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, True)) + sage: len(executor(params)) == 63 + True + sage: fmats = FusionRing("E6", 1).get_fmatrix() + sage: fmats._reset_solver_state() + sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, False)) + sage: len(executor(params)) == 6 + True + """ + (fn_name, fmats_id), args = params + # Construct a reference to global FMatrix object in this worker's memory + fmats_obj = cast(fmats_id, py_object).value + # Bind module method to FMatrix object in worker process, and call the method + return mappers[fn_name](fmats_obj, args) + +#################### +### Verification ### +#################### + +cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): + r""" + Check the pentagon equation corresponding to the given nonuple. + """ + a, b, c, d, e, f, g, k, l = nonuple + cdef float diff, lhs, rhs + + lhs = _fmat(fvars, Nk_ij, id_anyon, f, c, d, e, g, l)*_fmat(fvars, Nk_ij, id_anyon, a, b, l, e, f, k) + rhs = 0.0 + for h in factory._FR.basis(): + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) + diff = lhs - rhs + if diff > tol or diff < -tol: + worker_results.append(diff) + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef pent_verify(factory, tuple mp_params): + r""" + Generate all the pentagon equations assigned to this process, + and reduce them. + """ + child_id, n_proc, verbose = mp_params + cdef float t0 + cdef tuple nonuple + cdef unsigned long long i + cdef list worker_results = list() + + # Pre-compute common parameters for speed + Nk_ij = factory._FR.Nk_ij + cdef dict fvars = factory._fvars + id_anyon = factory._FR.one() + for i, nonuple in enumerate(product(factory._FR.basis(), repeat=9)): + if i % n_proc == child_id: + feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, nonuple) + if i % 50000000 == 0 and i and verbose: + print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000, len(worker_results))) + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd new file mode 100644 index 00000000000..b3eec73b15b --- /dev/null +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd @@ -0,0 +1,3 @@ +cpdef _unflatten_entries(factory, list entries) +cpdef executor(tuple params) + 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 new file mode 100644 index 00000000000..9fcb4408c21 --- /dev/null +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -0,0 +1,329 @@ +""" +Fast Fusion Ring Methods for Computing Braid Group Representations +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad <gh_willieab> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from ctypes import cast, py_object +cimport cython +from sage.algebras.fusion_rings.fast_parallel_fmats_methods cimport _fmat + +from sage.rings.qqbar import QQbar + +############### +### Mappers ### +############### + +cdef mid_sig_ij(fusion_ring, row, col, a, b): + r""" + Compute the (xi, yi), (xj, yj) entry of generator braiding the middle two + strands in the tree b -> xi # yi -> (a # a) # (a # a), which results in + a sum over j of trees b -> xj # yj -> (a # a) # (a # a) + + .. WARNING:: + + This method assumes F-matrices are orthogonal. + """ + # Pre-compute common parameters for efficiency + _fvars = fusion_ring.get_fmatrix()._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + + xi, yi = row + xj, yj = col + entry = 0 + cdef list basis = list(fusion_ring.basis()) + for c in basis: + for d in basis: + # #Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = _fmat(_fvars, _Nk_ij, one, a, a, yi, b, xi, c) + f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yi) + f3 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yj) + f4 = _fmat(_fvars, _Nk_ij, one, a, a, yj, b, xj, c) + r = fusion_ring.r_matrix(a, a, d) + entry += f1 * f2 * r * f3 * f4 + return entry + +cdef odd_one_out_ij(fusion_ring, xi, xj, a, b): + r""" + Compute the `xi`, `xj` entry of the braid generator on the two right-most + strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which + results in a sum over j of trees b -> xj -> (a # a) # (a # a) + + .. WARNING:: + + This method assumes F-matrices are orthogonal. + """ + # Pre-compute common parameters for efficiency + _fvars = fusion_ring.get_fmatrix()._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + + entry = 0 + for c in fusion_ring.basis(): + # #Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xi, c) + f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xj, c) + r = fusion_ring.r_matrix(a, a, c) + entry += f1 * r * f2 + return entry + +# Cache methods (manually for cdef methods) +cdef odd_one_out_ij_cache = dict() +cdef mid_sig_ij_cache = dict() + +cdef cached_mid_sig_ij(fusion_ring, row, col, a, b): + r""" + Cached version of :meth:`mid_sig_ij`. + """ + if (row, col, a, b) in mid_sig_ij_cache: + return mid_sig_ij_cache[row, col, a, b] + entry = mid_sig_ij(fusion_ring, row, col, a, b) + mid_sig_ij_cache[row, col, a, b] = entry + return entry + +cdef cached_odd_one_out_ij(fusion_ring, xi, xj, a, b): + r""" + Cached version of :meth:`odd_one_out_ij`. + """ + if (xi, xj, a, b) in odd_one_out_ij_cache: + return odd_one_out_ij_cache[xi, xj, a, b] + entry = odd_one_out_ij(fusion_ring, xi, xj, a, b) + odd_one_out_ij_cache[xi, xj, a, b] = entry + return entry + +@cython.nonecheck(False) +@cython.cdivision(True) +cdef sig_2k(fusion_ring, tuple args): + r""" + Compute entries of the `2k`-th braid generator + """ + # Pre-compute common parameters for efficiency + _fvars = fusion_ring.get_fmatrix()._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + + cdef int child_id, n_proc + child_id, n_proc, fn_args = args + k, a, b, n_strands = fn_args + cdef int ctr = -1 + cdef list worker_results = list() + # Get computational basis + cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) + cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} + cdef int dim = len(comp_basis) + cdef set coords = set() + cdef int i + # Avoid pickling cyclotomic field element objects + cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar + cdef list basis = list(fusion_ring.basis()) + for i in range(dim): + for f in basis: + for e in basis: + for q in basis: + # Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + # Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[k-1] = f + nnz_pos[k] = e + # Handle the special case k = 1 + if k > 1: + nnz_pos[n_strands//2+k-2] = q + nnz_pos = tuple(nnz_pos) + + # Skip repeated entries when k = 1 + if nnz_pos in comp_basis and (basis_dict[nnz_pos], i) not in coords: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + # A few special cases + top_left = m[0] + if k >= 3: + top_left = l[k-3] + root = b + if k - 1 < len(l): + root = l[k-1] + + # Handle the special case k = 1 + if k == 1: + entry = cached_mid_sig_ij(fusion_ring, m[:2], (f, e), a, root) + + # Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) + coords.add((basis_dict[nnz_pos], i)) + continue + + entry = 0 + for p in fusion_ring.basis(): + f1 = _fmat(_fvars, _Nk_ij, one, top_left, m[k-1], m[k], root, l[k-2], p) + f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, e, root, q, p) + entry += f1 * cached_mid_sig_ij(fusion_ring, (m[k-1], m[k]), (f, e), a, p) * f2 + + # Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) + return worker_results + +@cython.nonecheck(False) +@cython.cdivision(True) +cdef odd_one_out(fusion_ring, tuple args): + r""" + Compute entries of the rightmost braid generator, in case we have an + odd number of strands. + """ + # Pre-compute common parameters for efficiency + _fvars = fusion_ring.get_fmatrix()._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + + cdef list worker_results = [] + cdef list nnz_pos_temp + cdef tuple nnz_pos + cdef int child_id, n_proc, i + child_id, n_proc, fn_args = args + a, b, n_strands = fn_args + cdef int ctr = -1 + # Get computational basis + cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) + cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} + cdef int dim = len(comp_basis) + + # Avoid pickling cyclotomic field element objects + cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar + + cdef list basis = list(fusion_ring.basis()) + for i in range(dim): + for f in basis: + for q in basis: + # Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + # Compute appropriate possible nonzero row index + nnz_pos_temp = list(comp_basis[i]) + nnz_pos_temp[n_strands//2-1] = f + # Handle small special case + if n_strands > 3: + nnz_pos_temp[-1] = q + nnz_pos = tuple(nnz_pos_temp) + + if nnz_pos in comp_basis: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + + # Handle a couple of small special cases + if n_strands == 3: + entry = cached_odd_one_out_ij(fusion_ring, m[-1], f, a, b) + + # Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) + continue + top_left = m[0] + if n_strands > 5: + top_left = l[-2] + root = b + + # Compute relevant entry + entry = 0 + for p in basis: + f1 = _fmat(_fvars, _Nk_ij, one, top_left, m[-1], a, root, l[-1], p) + f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, a, root, q, p) + entry += f1 * cached_odd_one_out_ij(fusion_ring, m[-1], f, a, p) * f2 + + # Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) + return worker_results + +############################## +### Parallel code executor ### +############################## + +# Hard-coded module __dict__-style attribute with visible cdef methods +cdef dict mappers = { + "sig_2k": sig_2k, + "odd_one_out": odd_one_out +} + +cpdef executor(tuple params): + r""" + 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``. + + .. NOTE:: + + When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object `X` in + the parent process equals the *virtual* memory address of the copy of + object `X` in each worker, so we may construct references to forked + copies of `X`. + + TESTS:: + + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor + sage: FR = FusionRing("A1", 4) + sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) + sage: FR.get_fmatrix().find_orthogonal_solution(verbose=False) # long time + sage: params = (('sig_2k', id(FR)), (0, 1, (1, one, one, 5))) # long time + sage: len(executor(params)) == 13 # long time + True + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor + sage: FR = FusionRing("A1", 2) + sage: FR.fusion_labels("a", inject_variables=True) + sage: FR.get_fmatrix().find_orthogonal_solution(verbose=False) + sage: params = (('odd_one_out', id(FR)), (0, 1, (a2, a2, 5))) + sage: len(executor(params)) == 1 + True + """ + (fn_name, fr_id), args = params + # Construct a reference to global FMatrix object in this worker's memory + fusion_ring_obj = cast(fr_id, py_object).value + # Bind module method to FMatrix object in worker process, and call the method + return mappers[fn_name](fusion_ring_obj, args) + +###################################### +### Pickling circumvention helpers ### +###################################### + +cpdef _unflatten_entries(fusion_ring, list entries): + r""" + Restore cyclotomic coefficient object from its tuple of rational + coefficients representation. + + Used to circumvent pickling issue introduced by PARI settigs + in :trac:`30537`. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import _unflatten_entries + sage: fr = FusionRing("B2", 2) + sage: F = fr.field() + sage: coeff = [F.random_element() for i in range(2)] + sage: entries = [((0, 0), coeff[0].list()), ((0, 1), coeff[1].list())] + sage: _unflatten_entries(fr, entries) + sage: all(cyc_elt_obj == c for (coord, cyc_elt_obj), c in zip(entries, coeff)) + True + """ + F = fusion_ring.fvars_field() + fm = fusion_ring.get_fmatrix() + if F != QQbar: + for i, (coord, entry) in enumerate(entries): + entries[i] = (coord, F(entry)) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py similarity index 50% rename from src/sage/combinat/root_system/fusion_ring.py rename to src/sage/algebras/fusion_rings/fusion_ring.py index b261550db21..d36faae2b34 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -6,19 +6,28 @@ # Guillermo Aboumrad <gh_willieab> # Travis Scrimshaw <tcscrims at gmail.com> # Nicolas Thiery <nthiery at users.sf.net> +# 2022 Guillermo Aboumrad <gh_willieab> # # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.root_system.weyl_characters import WeylCharacterRing +from itertools import product, zip_longest +from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int -from sage.matrix.special import diagonal_matrix +from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import ( + executor, + _unflatten_entries +) +from sage.combinat.root_system.weyl_characters import WeylCharacterRing from sage.matrix.constructor import matrix +from sage.matrix.special import diagonal_matrix +from sage.misc.cachefunc import cached_method from sage.misc.misc import inject_variable from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField -from sage.misc.cachefunc import cached_method +from sage.rings.qqbar import QQbar + class FusionRing(WeylCharacterRing): r""" @@ -31,11 +40,17 @@ class FusionRing(WeylCharacterRing): - ``conjugate`` -- (default ``False``) set ``True`` to obtain the complex conjugate ring - ``cyclotomic_order`` -- (default computed depending on ``ct`` and ``k``) + - ``fusion_labels`` -- (default None) either a tuple of strings to use as labels of the + basis of simple objects, or a string from which the labels will be + constructed + - ``inject_variables`` -- (default ``False``): use with ``fusion_labels``. + If ``inject_variables`` is ``True``, the fusion labels will be variables + that can be accessed from the command line The cyclotomic order is an integer `N` such that all computations will return elements of the cyclotomic field of `N`-th roots of unity. Normally you will never need to change this but consider changing it - if :meth:`root_of_unity` ever returns ``None``. + if :meth:`root_of_unity` raises a ``ValueError``. This algebra has a basis (sometimes called *primary fields* but here called *simple objects*) indexed by the weights of level `\leq k`. @@ -57,7 +72,7 @@ class FusionRing(WeylCharacterRing): EXAMPLES:: - sage: A22 = FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: [f1, f2] = A22.fundamental_weights() sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] sage: [M[3] * x for x in M] @@ -78,7 +93,7 @@ class FusionRing(WeylCharacterRing): [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2'], inject_variables=True) + sage: B22.fusion_labels(['I0', 'Y1', 'X', 'Z', 'Xp', 'Y2'], inject_variables=True) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] sage: [(x, x.weight()) for x in b] @@ -97,7 +112,7 @@ class FusionRing(WeylCharacterRing): This is the order used by methods such as :meth:`s_matrix`. You may use :meth:`CombinatorialFreeModule.set_order` to reorder the basis:: - sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: B22.set_order([x.weight() for x in [I0, Y1, Y2, X, Xp, Z]]) sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] @@ -144,7 +159,7 @@ class FusionRing(WeylCharacterRing): .. MATH:: - N^k_{ij} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,\overline{s(k,\ell)}}{s(I,\ell)}, + N^k_{ij} = \sum_l \frac{s(i, \ell)\, s(j, \ell)\, \overline{s(k, \ell)}}{s(I, \ell)}, where `N^k_{ij}` are the fusion coefficients, i.e. the structure constants of the fusion ring, and ``I`` is the unit object. @@ -153,62 +168,64 @@ class FusionRing(WeylCharacterRing): .. MATH:: - s(i*,j) = s(i,j*) = \overline{s(i,j)}. + s(i*, j) = s(i, j*) = \overline{s(i, j)}. This is equation (16.5) in [DFMS1996]_. Thus with `N_{ijk}=N^{k*}_{ij}` the Verlinde formula is equivalent to .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, + N_{ijk} = \sum_l \frac{s(i, \ell)\, s(j, \ell)\, s(k, \ell)}{s(I, \ell)}, In this formula `s` is the normalized unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that corresponds to the right-hand side, except using `\tilde{s}` instead of `s`:: - sage: def V(i,j,k): + sage: def V(i, j, k): ....: R = i.parent() - ....: return sum(R.s_ij(i,l) * R.s_ij(j,l) * R.s_ij(k,l) / R.s_ij(R.one(),l) + ....: return sum(R.s_ij(i, l) * R.s_ij(j, l) * R.s_ij(k, l) / R.s_ij(R.one(), l) ....: for l in R.basis()) - This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the + This does not produce ``self.N_ijk(i, j, k)`` exactly, because of the missing normalization factor. The following code to check the Verlinde formula takes this into account:: sage: def test_verlinde(R): ....: b0 = R.one() ....: c = R.global_q_dimension() - ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() + ....: return all(V(i, j, k) == c * R.N_ijk(i, j, k) for i in R.basis() ....: for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: - sage: test_verlinde(FusionRing("A2",1)) + sage: test_verlinde(FusionRing("A2", 1)) True - sage: test_verlinde(FusionRing("B4",2)) # long time (.56s) + sage: test_verlinde(FusionRing("B4", 2)) # long time (.56s) True As an exercise, the reader may verify the examples in Section 5.3 of [RoStWa2009]_. Here we check the example of the Ising modular tensor category, which is related - to the BPZ minimal model `M(4,3)` or to an `E_8` coset + to the BPZ minimal model `M(4, 3)` or to an `E_8` coset model. See [DFMS1996]_ Sections 7.4.2 and 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to construct it as the conjugate of the `E_8` level 2 :class:`FusionRing`:: - sage: I = FusionRing("E8",2,conjugate=True) - sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: I = FusionRing("E8", 2, conjugate=True) + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) sage: b = I.basis().list(); b [i0, p, s] - sage: [[x*y for x in b] for y in b] - [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: Matrix([[x*y for x in b] for y in b]) # long time (.93s) + [ i0 p s] + [ p i0 s] + [ s s i0 + p] sage: [x.twist() for x in b] [0, 1, 1/8] sage: [x.ribbon() for x in b] [1, -1, zeta128^8] - sage: [I.r_matrix(i, j, k) for (i,j,k) in [(s,s,i0), (p,p,i0), (p,s,s), (s,p,s), (s,s,p)]] + sage: [I.r_matrix(i, j, k) for (i, j, k) in [(s, s, i0), (p, p, i0), (p, s, s), (s, p, s), (s, s, p)]] [-zeta128^56, -1, -zeta128^32, -zeta128^32, zeta128^24] sage: I.r_matrix(s, s, i0) == I.root_of_unity(-1/8) True @@ -229,11 +246,11 @@ class FusionRing(WeylCharacterRing): The term *modular tensor category* refers to the fact that associated with the category there is a projective representation of the modular - group `SL(2,\ZZ)`. We recall that this group is generated by + group `SL(2, \ZZ)`. We recall that this group is generated by .. MATH:: - S = \begin{pmatrix} & -1\\1\end{pmatrix},\qquad + S = \begin{pmatrix} & -1\\1\end{pmatrix}, \qquad T = \begin{pmatrix} 1 & 1\\ &1 \end{pmatrix} subject to the relations `(ST)^3 = S^2`, `S^2T = TS^2`, and `S^4 = I`. @@ -265,7 +282,7 @@ class FusionRing(WeylCharacterRing): of `SL(2, \ZZ)`. Let us confirm these identities for the Fibonacci MTC ``FusionRing("G2", 1)``:: - sage: R = FusionRing("G2",1) + sage: R = FusionRing("G2", 1) sage: S = R.s_matrix(unitary=True) sage: T = R.twists_matrix() sage: C = R.conj_matrix() @@ -279,7 +296,7 @@ class FusionRing(WeylCharacterRing): True """ @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None): + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ Normalize input to ensure a unique representation. @@ -318,10 +335,12 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: E81 = FusionRing('E8', 1) sage: TestSuite(E81).run() """ - return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, - conjugate=conjugate, - cyclotomic_order=cyclotomic_order) + return super().__classcall__(cls, ct, base_ring=base_ring, + prefix=prefix, style=style, k=k, + conjugate=conjugate, + cyclotomic_order=cyclotomic_order, + fusion_labels=fusion_labels, + inject_variables=inject_variables) def _test_verlinde(self, **options): """ @@ -329,7 +348,7 @@ def _test_verlinde(self, **options): EXAMPLES:: - sage: G22 = FusionRing("G2",2) + sage: G22 = FusionRing("G2", 2) sage: G22._test_verlinde() """ tester = self._tester(**options) @@ -337,9 +356,9 @@ def _test_verlinde(self, **options): i0 = self.one() from sage.misc.misc import some_tuples B = self.basis() - for x,y,z in some_tuples(B, 3, tester._max_runs): - v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) - tester.assertEqual(v, c * self.N_ijk(x,y,z)) + for x, y, z in some_tuples(B, 3, tester._max_runs): + v = sum(self.s_ij(x, w) * self.s_ij(y, w) * self.s_ij(z, w) / self.s_ij(i0, w) for w in B) + tester.assertEqual(v, c * self.N_ijk(x, y, z)) def _test_total_q_order(self, **options): r""" @@ -351,13 +370,67 @@ def _test_total_q_order(self, **options): EXAMPLES:: - sage: G22 = FusionRing("G2",2) + sage: G22 = FusionRing("G2", 2) sage: G22._test_total_q_order() """ tester = self._tester(**options) - tqo = self.total_q_order() + tqo = self.total_q_order(base_coercion=False) tester.assertTrue(tqo.is_real_positive()) - tester.assertEqual(tqo**2, self.global_q_dimension()) + tester.assertEqual(tqo**2, self.global_q_dimension(base_coercion=False)) + + def test_braid_representation(self, max_strands=6, anyon=None): + """ + Check that we can compute valid braid group representations. + + INPUT: + + - ``max_strands`` -- (default: 6): maximum number of braid group strands + - ``anyon`` -- (optional) run this test on this particular simple object + + Create a braid group representation using :meth:`get_braid_generators` + and confirms the braid relations. This test indirectly partially + verifies the correctness of the orthogonal F-matrix solver. If the + code were incorrect the method would not be deterministic because the + fusing anyon is chosen randomly. (A different choice is made for each + number of strands tested.) However the doctest is deterministic since + it will always return ``True``. If the anyon parameter is omitted, + a random anyon is tested for each number of strands up to ``max_strands``. + + EXAMPLES:: + + sage: A21 = FusionRing("A2", 1) + sage: A21.test_braid_representation(max_strands=4) + True + sage: F41 = FusionRing("F4", 1) # long time + sage: F41.test_braid_representation() # long time + True + """ + if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free + raise NotImplementedError("only implemented for multiplicity free fusion rings") + b = self.basis() + results = [] + # Test with different numbers of strands + for n_strands in range(3, max_strands+1): + # Randomly select a fusing anyon. Skip the identity element, since + # its braiding matrices are trivial + if anyon is not None: + a = anyon + else: + while True: + a = b.random_element() + if a != self.one(): + break + pow = a ** n_strands + d = pow.monomials()[0] + # Try to find 'interesting' braid group reps i.e. skip 1-d reps + for k, v in pow.monomial_coefficients().items(): + if v > 1: + d = self(k) + break + comp_basis, sig = self.get_braid_generators(a, d, n_strands, verbose=False) + results.append(len(comp_basis) > 0) + results.append(self.gens_satisfy_braid_gp_rels(sig)) + return all(results) def fusion_labels(self, labels=None, inject_variables=False): r""" @@ -437,14 +510,72 @@ def field(self): EXAMPLES:: - sage: FusionRing("A2",2).field() + sage: FusionRing("A2", 2).field() Cyclotomic Field of order 60 and degree 16 - sage: FusionRing("B2",2).field() + sage: FusionRing("B2", 2).field() Cyclotomic Field of order 40 and degree 16 """ + # if self._field is None: + # self._field = CyclotomicField(4 * self._cyclotomic_order) + # return self._field return CyclotomicField(4 * self._cyclotomic_order) - def root_of_unity(self, r): + def fvars_field(self): + r""" + Return a field containing the ``CyclotomicField`` computed by + :meth:`field` as well as all the F-symbols of the associated + ``FMatrix`` factory object. + + This method is only available if ``self`` is multiplicity-free. + + OUTPUT: + + Depending on the ``CartanType`` associated to ``self`` and whether + a call to an F-matrix solver has been made, this method + will return the same field as :meth:`field`, a :func:`NumberField`, + or the :class:`QQbar<AlgebraicField>`. + See :meth:`FMatrix.attempt_number_field_computation` for more details. + + Before running an F-matrix solver, the output of this method matches + that of :meth:`field`. However, the output may change upon successfully + computing F-symbols. Requesting braid generators triggers a call to + :meth:`FMatrix.find_orthogonal_solution`, so the output of this method + may change after such a computation. + + By default, the output of methods like :meth:`r_matrix`, + :meth:`s_matrix`, :meth:`twists_matrix`, etc. will lie in the + ``fvars_field``, unless the ``base_coercion`` option is set to + ``False``. + + This method does not trigger a solver run. + + EXAMPLES:: + + sage: A13 = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) + sage: A13.fvars_field() + Cyclotomic Field of order 40 and degree 16 + sage: A13.field() + Cyclotomic Field of order 40 and degree 16 + sage: a2**4 + 2*a0 + 3*a2 + sage: comp_basis, sig = A13.get_braid_generators(a2, a2, 3, verbose=False) # long time (<3s) + sage: A13.fvars_field() # long time + Number Field in a with defining polynomial y^32 - ... - 500*y^2 + 25 + sage: a2.q_dimension().parent() # long time + Number Field in a with defining polynomial y^32 - ... - 500*y^2 + 25 + sage: A13.field() + Cyclotomic Field of order 40 and degree 16 + + In some cases, the :meth:`NumberField.optimized_representation() + <sage.rings.number_field.number_field.NumberField_absolute.optimized_representation>` + may be used to obtain a better defining polynomial for the + computed :func:`NumberField`. + """ + if self.is_multiplicity_free(): + return self.get_fmatrix().field() + raise NotImplementedError("method is only available for multiplicity free fusion rings") + + def root_of_unity(self, r, base_coercion=True): r""" Return `e^{i\pi r}` as an element of ``self.field()`` if possible. @@ -454,17 +585,29 @@ def root_of_unity(self, r): EXAMPLES:: - sage: A11 = FusionRing("A1",1) + sage: A11 = FusionRing("A1", 1) sage: A11.field() Cyclotomic Field of order 24 and degree 8 - sage: [A11.root_of_unity(2/x) for x in [1..7]] - [1, -1, zeta24^4 - 1, zeta24^6, None, zeta24^4, None] + sage: for n in [1..7]: + ....: try: + ....: print(n, A11.root_of_unity(2/n)) + ....: except ValueError as err: + ....: print(n, err) + 1 1 + 2 -1 + 3 zeta24^4 - 1 + 4 zeta24^6 + 5 not a root of unity in the field + 6 zeta24^4 + 7 not a root of unity in the field """ n = 2 * r * self._cyclotomic_order - if n in ZZ: - return self.field().gen() ** n - else: - return None + if n not in ZZ: + raise ValueError("not a root of unity in the field") + ret = self.field().gen() ** n + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) def get_order(self): r""" @@ -474,12 +617,12 @@ def get_order(self): EXAMPLES:: - sage: A14 = FusionRing("A1",4) - sage: w = A14.get_order(); w - [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) - sage: [A14(x) for x in A14.get_order()] - [A14(0), A14(4), A14(1), A14(3), A14(2)] + sage: A15 = FusionRing("A1", 5) + sage: w = A15.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2), (5/2, -5/2)] + sage: A15.set_order([w[k] for k in [0, 4, 1, 3, 5, 2]]) + sage: [A15(x) for x in A15.get_order()] + [A15(0), A15(4), A15(1), A15(3), A15(5), A15(2)] .. WARNING:: @@ -513,7 +656,7 @@ def fusion_level(self): EXAMPLES:: - sage: B22 = FusionRing('B2',2) + sage: B22 = FusionRing('B2', 2) sage: B22.fusion_level() 2 """ @@ -532,10 +675,10 @@ def fusion_l(self): EXAMPLES:: - sage: B22 = FusionRing('B2',2) + sage: B22 = FusionRing('B2', 2) sage: B22.fusion_l() 10 - sage: D52 = FusionRing('D5',2) + sage: D52 = FusionRing('D5', 2) sage: D52.fusion_l() 10 """ @@ -589,7 +732,7 @@ def conj_matrix(self): EXAMPLES:: - sage: FusionRing("A2",1).conj_matrix() + sage: FusionRing("A2", 1).conj_matrix() [1 0 0] [0 0 1] [0 1 0] @@ -604,7 +747,7 @@ def twists_matrix(self): EXAMPLES:: - sage: B21=FusionRing("B2",1) + sage: B21=FusionRing("B2", 1) sage: [x.twist() for x in B21.basis().list()] [0, 1, 5/8] sage: [B21.root_of_unity(x.twist()) for x in B21.basis().list()] @@ -637,11 +780,11 @@ def N_ijk(self, elt_i, elt_j, elt_k): sage: G23.fusion_labels("g") sage: b = G23.basis().list(); b [g0, g1, g2, g3, g4, g5] - sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z) > 1] + sage: [(x, y, z) for x in b for y in b for z in b if G23.N_ijk(x, y, z) > 1] [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] - sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b) + sage: all(G23.N_ijk(x, y, z)==G23.N_ijk(y, z, x) for x in b for y in b for z in b) True - sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b) + sage: all(G23.N_ijk(x, y, z)==G23.N_ijk(y, x, z) for x in b for y in b for z in b) True """ return (elt_i * elt_j).monomial_coefficients().get(elt_k.dual().weight(), 0) @@ -661,13 +804,13 @@ def Nk_ij(self, elt_i, elt_j, elt_k): sage: A22 = FusionRing("A2", 2) sage: b = A22.basis().list() - sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) + sage: all(x*y == sum(A22.Nk_ij(x, y, k)*k for k in b) for x in b for y in b) True """ return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) @cached_method - def s_ij(self, elt_i, elt_j): + def s_ij(self, elt_i, elt_j, base_coercion=True): r""" Return the element of the S-matrix of this fusion ring corresponding to the given elements. @@ -676,7 +819,7 @@ def s_ij(self, elt_i, elt_j): .. MATH:: - s_{i,j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, + s_{i, j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, where `\theta_k` is the twist and `d_k` is the quantum dimension. See [Row2006]_ Equation (2.2) or [EGNO2015]_ @@ -694,11 +837,55 @@ def s_ij(self, elt_i, elt_j): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ ijtwist = elt_i.twist() + elt_j.twist() - return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) - * self.root_of_unity(k.twist() - ijtwist) + ret = sum(k.q_dimension(base_coercion=False) * self.Nk_ij(elt_i, k, elt_j) + * self.root_of_unity(k.twist() - ijtwist, base_coercion=False) for k in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + + def s_ijconj(self, elt_i, elt_j, base_coercion=True): + """ + Return the conjugate of the element of the S-matrix given by + ``self.s_ij(elt_i, elt_j, base_coercion=base_coercion)``. + + See :meth:`s_ij`. + + EXAMPLES:: + + sage: G21 = FusionRing("G2", 1) + sage: b = G21.basis() + sage: [G21.s_ijconj(x, y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + + This method works with all possible types of fields returned by + ``self.fmats.field()``. - def s_matrix(self, unitary=False): + TESTS:: + + sage: E62 = FusionRing("E6", 2) + sage: E62.fusion_labels("e", inject_variables=True) + sage: E62.s_ij(e8, e1).conjugate() == E62.s_ijconj(e8, e1) + True + sage: F41 = FusionRing("F4", 1) + sage: fmats = F41.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) + sage: b = F41.basis() + sage: all(F41.s_ijconj(x, y) == F41._basecoer(F41.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) + True + sage: G22 = FusionRing("G2", 2) + sage: fmats = G22.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: b = G22.basis() # long time + sage: all(G22.s_ijconj(x, y) == fmats.field()(G22.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) # long time + True + """ + ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + + def s_matrix(self, unitary=False, base_coercion=True): r""" Return the S-matrix of this fusion ring. @@ -730,14 +917,14 @@ def s_matrix(self, unitary=False): [0 0 0 1] """ b = self.basis() - S = matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) + S = matrix([[self.s_ij(b[x], b[y], base_coercion=base_coercion) + for x in self.get_order()] for y in self.get_order()]) if unitary: - return S / self.total_q_order() - else: - return S + return S / self.total_q_order(base_coercion=base_coercion) + return S @cached_method - def r_matrix(self, i, j, k): + def r_matrix(self, i, j, k, base_coercion=True): r""" Return the R-matrix entry corresponding to the subobject ``k`` in the tensor product of ``i`` with ``j``. @@ -770,49 +957,61 @@ def r_matrix(self, i, j, k): EXAMPLES:: sage: I = FusionRing("E8", 2, conjugate=True) # Ising MTC - sage: I.fusion_labels(["i0","p","s"], inject_variables=True) - sage: I.r_matrix(s,s,i0) == I.root_of_unity(-1/8) + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) + sage: I.r_matrix(s, s, i0) == I.root_of_unity(-1/8) True - sage: I.r_matrix(p,p,i0) + sage: I.r_matrix(p, p, i0) -1 - sage: I.r_matrix(p,s,s) == I.root_of_unity(-1/2) + sage: I.r_matrix(p, s, s) == I.root_of_unity(-1/2) True - sage: I.r_matrix(s,p,s) == I.root_of_unity(-1/2) + sage: I.r_matrix(s, p, s) == I.root_of_unity(-1/2) True - sage: I.r_matrix(s,s,p) == I.root_of_unity(3/8) + sage: I.r_matrix(s, s, p) == I.root_of_unity(3/8) True """ if self.Nk_ij(i, j, k) == 0: - return 0 + return self.field().zero() if (not base_coercion) or (self._basecoer is None) else self.fvars_field().zero() if i != j: - return self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) - i0 = self.one() - B = self.basis() - return sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) - * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ij(x,z).conjugate() - * self.s_ij(k,x).conjugate() * self.s_ij(y,z).conjugate() / self.s_ij(i0,z) - for x in B for y in B for z in B) / (self.total_q_order()**4) - - def global_q_dimension(self): + ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2, base_coercion=False) + else: + i0 = self.one() + B = self.basis() + ret = sum(y.ribbon(base_coercion=False)**2 / (i.ribbon(base_coercion=False) * x.ribbon(base_coercion=False)**2) + * self.s_ij(i0, y, base_coercion=False) * self.s_ij(i, z, base_coercion=False) * self.s_ijconj(x, z, base_coercion=False) + * self.s_ijconj(k, x, base_coercion=False) * self.s_ijconj(y, z, base_coercion=False) / self.s_ij(i0, z, base_coercion=False) + for x in B for y in B for z in B) / (self.total_q_order(base_coercion=False)**4) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + + def global_q_dimension(self, base_coercion=True): r""" Return `\sum d_i^2`, where the sum is over all simple objects - and `d_i` is the quantum dimension. It is a positive real number. + and `d_i` is the quantum dimension. + + The global `q`-dimension is a positive real number. EXAMPLES:: - sage: FusionRing("E6",1).global_q_dimension() + sage: FusionRing("E6", 1).global_q_dimension() 3 """ - return sum(x.q_dimension()**2 for x in self.basis()) + ret = sum(x.q_dimension(base_coercion=False) ** 2 for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) - def total_q_order(self): + def total_q_order(self, base_coercion=True): r""" - Return the positive square root of ``self.global_q_dimension()`` - as an element of ``self.field()``. + Return the positive square root of :meth:`self.global_q_dimension() + <global_q_dimension>` as an element of :meth:`self.field() <field>`. + + This is implemented as `D_{+}e^{-i\pi c/4}`, where `D_+` is + :meth:`D_plus()` and `c` is :meth:`virasoro_central_charge()`. EXAMPLES:: - sage: F = FusionRing("G2",1) + sage: F = FusionRing("G2", 1) sage: tqo=F.total_q_order(); tqo zeta60^15 - zeta60^11 - zeta60^9 + 2*zeta60^3 + zeta60 sage: tqo.is_real_positive() @@ -821,9 +1020,12 @@ def total_q_order(self): True """ c = self.virasoro_central_charge() - return self.D_plus() * self.root_of_unity(-c/4) + ret = self.D_plus(base_coercion=False) * self.root_of_unity(-c/4, base_coercion=False) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) - def D_plus(self): + def D_plus(self, base_coercion=True): r""" Return `\sum d_i^2\theta_i` where `i` runs through the simple objects, `d_i` is the quantum dimension and `\theta_i` is the twist. @@ -832,7 +1034,7 @@ def D_plus(self): EXAMPLES:: - sage: B31 = FusionRing("B3",1) + sage: B31 = FusionRing("B3", 1) sage: Dp = B31.D_plus(); Dp 2*zeta48^13 - 2*zeta48^5 sage: Dm = B31.D_minus(); Dm @@ -844,9 +1046,12 @@ def D_plus(self): sage: Dp/Dm == B31.root_of_unity(c/2) True """ - return sum((x.q_dimension())**2 * x.ribbon() for x in self.basis()) + ret = sum((x.q_dimension(base_coercion=False))**2 * x.ribbon(base_coercion=False) for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) - def D_minus(self): + def D_minus(self, base_coercion=True): r""" Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple objects, `d_i` is the quantum dimension and `\theta_i` is the twist. @@ -855,8 +1060,8 @@ def D_minus(self): EXAMPLES:: - sage: E83 = FusionRing("E8",3,conjugate=True) - sage: [Dp,Dm] = [E83.D_plus(), E83.D_minus()] + sage: E83 = FusionRing("E8", 3, conjugate=True) + sage: [Dp, Dm] = [E83.D_plus(), E83.D_minus()] sage: Dp*Dm == E83.global_q_dimension() True sage: c = E83.virasoro_central_charge(); c @@ -864,7 +1069,341 @@ def D_minus(self): sage: Dp*Dm == E83.global_q_dimension() True """ - return sum((x.q_dimension())**2 / x.ribbon() for x in self.basis()) + ret = sum((x.q_dimension(base_coercion=False))**2 / x.ribbon(base_coercion=False) for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + + def is_multiplicity_free(self): + r""" + Return ``True`` if the fusion multiplicities + :meth:`Nk_ij` are bounded by 1. + + The :class:`FMatrix` is available only for multiplicity free + instances of :class:`FusionRing`. + + EXAMPLES:: + + sage: [FusionRing(ct, k).is_multiplicity_free() for ct in ("A1", "A2", "B2", "C3") for k in (1, 2, 3)] + [True, True, True, True, True, False, True, True, False, True, False, False] + """ + ct = self.cartan_type() + k = self.fusion_level() + if ct.letter == 'A': + if ct.n == 1: + return True + return k <= 2 + # if ct.letter in ['B', 'D', 'G', 'F', 'E']: + if ct.letter in ['B', 'D', 'F', 'G']: + return k <= 2 + if ct.letter == 'C': + if ct.n == 2: + return k <= 2 + return k == 1 + if ct.letter == 'E': + if ct.n == 8: + return k <= 3 + return k <= 2 + + ################################### + ### Braid group representations ### + ################################### + + def get_computational_basis(self, a, b, n_strands): + r""" + Return the so-called computational basis for `\text{Hom}(b, a^n)`. + + INPUT: + + - ``a`` -- a basis element + - ``b`` -- another basis element + - ``n_strands`` -- the number of strands for a braid group + + Let `n=` ``n_strands`` and let `k` be the greatest integer `\leq n/2`. + The braid group acts on `\text{Hom}(b, a^n)`. This action + is computed in :meth:`get_braid_generators`. This method + returns the computational basis in the form of a list of + fusion trees. Each tree is represented by an `(n-2)`-tuple + + .. MATH:: + + (m_1, \ldots, m_k, l_1, \ldots, l_{k-2}) + + such that each `m_j` is an irreducible constituent in `a \otimes a` + and + + .. MATH:: + + \begin{array}{l} + b \in l_{k-2} \otimes m_{k}, \\ + l_{k-2} \in l_{k-3} \otimes m_{k-1}, \\ + \cdots, \\ + l_2 \in l_1 \otimes m_3, \\ + l_1 \in m_1 \otimes m_2, + \end{array} + + where `z \in x \otimes y` means `N_{xy}^z \neq 0`. + + As a computational device when ``n_strands`` is odd, we pad the + vector `(m_1, \ldots, m_k)` with an additional `m_{k+1}` equal to `a`. + However, this `m_{k+1}` does *not* appear in the output of this method. + + The following example appears in Section 3.1 of [CW2015]_. + + EXAMPLES:: + + sage: A14 = FusionRing("A1", 4) + sage: A14.get_order() + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.fusion_labels(["zero", "one", "two", "three", "four"], inject_variables=True) + sage: [A14(x) for x in A14.get_order()] + [zero, one, two, three, four] + sage: A14.get_computational_basis(one, two, 4) + [(two, two), (two, zero), (zero, two)] + """ + def _get_trees(fr, top_row, root): + if len(top_row) == 2: + m1, m2 = top_row + return [[]] if fr.Nk_ij(m1, m2, root) else [] + else: + m1, m2 = top_row[:2] + return [tuple([l, *b]) for l in fr.basis() for b in _get_trees(fr, [l]+top_row[2:], root) if fr.Nk_ij(m1, m2, l)] + + comp_basis = list() + for top in product((a*a).monomials(), repeat=n_strands//2): + # If the n_strands is odd, we must extend the top row by a fusing anyon + top_row = list(top)+[a]*(n_strands%2) + comp_basis.extend(tuple([*top, *levels]) for levels in _get_trees(self, top_row, b)) + return comp_basis + + def get_fmatrix(self, *args, **kwargs): + r""" + Construct an :class:`FMatrix` factory to solve the pentagon relations + and organize the resulting F-symbols. + + We only need this attribute to compute braid group representations. + + EXAMPLES:: + + sage: A15 = FusionRing("A1", 5) + sage: A15.get_fmatrix() + F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients + """ + # Initialize fresh FMatrix object. Useful if you need to reset + # FMatrix properties and there are various FusionRing objects (unique) + # associated to same level and algebra. + if not hasattr(self, 'fmats') or kwargs.get('new', False): + kwargs.pop('new', None) + from sage.algebras.fusion_rings.f_matrix import FMatrix + self.fmats = FMatrix(self, *args, **kwargs) + return self.fmats + + def _emap(self, mapper, input_args, worker_pool=None): + r""" + Apply the given mapper to each element of the given input iterable + and return the results (with no duplicates) in a list. + + INPUT: + + - ``mapper`` -- a string specifying the name of a function defined + in the ``fast_parallel_fusion_ring_braid_repn`` module + - ``input_args`` -- a tuple of arguments to be passed to mapper + + This method applies the mapper in parallel if a ``worker_pool`` + is provided. + + .. NOTE:: + + If ``worker_pool`` is not provided, function maps and reduces on + a single process. If ``worker_pool`` is provided, the function + attempts to determine whether it should use multiprocessing + based on the length of the input iterable. If it cannot determine + the length of the input iterable then it uses multiprocessing + with the default chunksize of `1` if chunksize is not + explicitly provided. + + EXAMPLES:: + + sage: FR = FusionRing("A1", 4) + sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) + sage: fmats = FR.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) # long time + sage: len(FR._emap('sig_2k', (1, one, one, 5))) # long time + 13 + sage: FR = FusionRing("A1", 2) + sage: FR.fusion_labels("a", inject_variables=True) + sage: fmats = FR.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) + sage: len(FR._emap('odd_one_out', (a1, a1, 7))) + 16 + """ + n_proc = worker_pool._processes if worker_pool is not None else 1 + input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] + no_mp = worker_pool is None + # Map phase + input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) + results = list() + if no_mp: + mapped = map(executor, input_iter) + else: + mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=1) + # Reduce phase + for worker_results in mapped: + results.extend(worker_results) + return results + + def get_braid_generators(self, + fusing_anyon, + total_charge_anyon, + n_strands, + checkpoint=False, + save_results="", + warm_start="", + use_mp=True, + verbose=True): + r""" + Compute generators of the Artin braid group on ``n_strands`` strands. + + If `a = ` ``fusing_anyon`` and `b = ` ``total_charge_anyon`` + the generators are endomorphisms of `\text{Hom}(b, a^n)`. + + INPUT: + + - ``fusing_anyon`` -- a basis element of ``self`` + - ``total_charge_anyon`` -- a basis element of ``self`` + - ``n_strands`` -- a positive integer greater than 2 + - ``checkpoint`` -- (default: ``False``) a boolean indicating + whether the F-matrix solver should pickle checkpoints + - ``save_results`` -- (optional) a string indicating the name of + a file in which to pickle computed F-symbols for later use + - ``warm_start`` -- (optional) a string indicating the name of a + pickled checkpoint file to "warm" start the F-matrix solver. + The pickle may be a checkpoint generated by the solver, or + a file containing solver results. If all F-symbols are known, + we don't run the solver again. + - ``use_mp`` -- (default: ``True``) a boolean indicating whether + to use multiprocessing to speed up the computation; this is + highly recommended. Python 3.8+ is required. + - ``verbose`` -- (default: ``True``) boolean indicating whether + to be verbose with the computation + + For more information on the optional parameters, see + :meth:`FMatrix.find_orthogonal_solution`. + + Given a simple object in the fusion category, here called + ``fusing_anyon`` allowing the universal R-matrix to act on adjacent + pairs in the fusion of ``n_strands`` copies of ``fusing_anyon`` + produces an action of the braid group. This representation can + be decomposed over another anyon, here called ``total_charge_anyon``. + See [CHW2015]_. + + OUTPUT: + + The method outputs a pair of data ``(comp_basis, sig)`` where + ``comp_basis`` is a list of basis elements of the braid group + module, parametrized by a list of fusion ring elements describing + a fusion tree. For example with 5 strands the fusion tree + is as follows. See :meth:`get_computational_basis` + for more information. + + .. IMAGE:: ../../../media/fusiontree.png + :scale: 45 + :align: center + + ``sig`` is a list of braid group generators as matrices. In + some cases these will be represented as sparse matrices. + + In the following example we compute a 5-dimensional braid group + representation on 5 strands associated to the spin representation + in the modular tensor category `SU(2)_4 \cong SO(3)_2`. + + EXAMPLES:: + + sage: A14 = FusionRing("A1", 4) + sage: A14.get_order() + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.fusion_labels(["one", "two", "three", "four", "five"], inject_variables=True) + sage: [A14(x) for x in A14.get_order()] + [one, two, three, four, five] + sage: two ** 5 + 5*two + 4*four + sage: comp_basis, sig = A14.get_braid_generators(two, two, 5, verbose=False) # long time + sage: A14.gens_satisfy_braid_gp_rels(sig) # long time + True + sage: len(comp_basis) == 5 # long time + True + """ + if n_strands < 3: + raise ValueError("the number of strands must be an integer at least 3") + # Construct associated FMatrix object and solve for F-symbols + self.get_fmatrix() + if self.fmats._chkpt_status < 7: + self.fmats.find_orthogonal_solution(checkpoint=checkpoint, + save_results=save_results, + warm_start=warm_start, + use_mp=use_mp, + verbose=verbose) + + # Set multiprocessing parameters. Context can only be set once, so we try to set it + try: + set_start_method('fork') + except RuntimeError: + pass + # Turn off multiprocessing when field is QQbar due to pickling issues introduced by PARI upgrade in trac ticket #30537 + pool = Pool() if use_mp and self.fvars_field() != QQbar else None + + # Set up computational basis and compute generators one at a time + a, b = fusing_anyon, total_charge_anyon + comp_basis = self.get_computational_basis(a, b, n_strands) + d = len(comp_basis) + if verbose: + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d, n_strands)) + + # Compute diagonal odd-indexed generators using the 3j-symbols + gens = {2*i+1: diagonal_matrix(self.r_matrix(a, a, c[i]) for c in comp_basis) for i in range(n_strands//2)} + + # Compute even-indexed generators using F-matrices + for k in range(1, n_strands//2): + entries = self._emap('sig_2k', (k, a, b, n_strands), pool) + + # Build cyclotomic field element objects from tuple of rationals repn + _unflatten_entries(self, entries) + gens[2*k] = matrix(dict(entries)) + + # If n_strands is odd, we compute the final generator + if n_strands % 2: + entries = self._emap('odd_one_out', (a, b, n_strands), pool) + + # Build cyclotomic field element objects from tuple of rationals repn + _unflatten_entries(self, entries) + gens[n_strands-1] = matrix(dict(entries)) + + return comp_basis, [gens[k] for k in sorted(gens)] + + def gens_satisfy_braid_gp_rels(self, sig): + r""" + Return ``True`` if the matrices in the list ``sig`` satisfy + the braid relations. + + This if `n` is the cardinality of ``sig``, this + confirms that these matrices define a representation of + the Artin braid group on `n+1` strands. Tests correctness of + :meth:`get_braid_generators`. + + EXAMPLES:: + + sage: F41 = FusionRing("F4", 1, fusion_labels="f", inject_variables=True) + sage: f1*f1 + f0 + f1 + sage: comp, sig = F41.get_braid_generators(f1, f0, 4, verbose=False) + sage: F41.gens_satisfy_braid_gp_rels(sig) + True + """ + n = len(sig) + braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) + far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n), repeat=2) if abs(i-j) > 1 and i > j) + singular = any(s.is_singular() for s in sig) + return braid_rels and far_comm and not singular class Element(WeylCharacterRing.Element): """ @@ -877,7 +1416,7 @@ def is_simple_object(self): EXAMPLES:: sage: A22 = FusionRing("A2", 2) - sage: x = A22(1,0); x + sage: x = A22(1, 0); x A22(1,0) sage: x.is_simple_object() True @@ -896,7 +1435,7 @@ def weight(self): EXAMPLES:: - sage: A21 = FusionRing("A2",1) + sage: A21 = FusionRing("A2", 1) sage: [x.weight() for x in A21.basis().list()] [(0, 0, 0), (2/3, -1/3, -1/3), (1/3, 1/3, -2/3)] """ @@ -947,7 +1486,7 @@ def twist(self, reduced=True): P = self.parent() rho = P.space().rho() # We copy self.weight() to skip the test (which was already done - # by self.is_simple_object()). + # by self.is_simple_object()). lam = next(iter(self._monomial_coefficients)) inner = lam.inner_product(lam + 2*rho) twist = P._conj * P._nf * inner / P.fusion_l() @@ -956,10 +1495,9 @@ def twist(self, reduced=True): f = twist.floor() twist -= f return twist + (f % 2) - else: - return twist + return twist - def ribbon(self): + def ribbon(self, base_coercion=True): r""" Return the twist or ribbon element of ``self``. @@ -972,28 +1510,31 @@ def ribbon(self): EXAMPLES:: - sage: F = FusionRing("A1",3) + sage: F = FusionRing("A1", 3) sage: [x.twist() for x in F.basis()] [0, 3/10, 4/5, 3/2] - sage: [x.ribbon() for x in F.basis()] + sage: [x.ribbon(base_coercion=False) for x in F.basis()] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] - sage: [F.root_of_unity(x) for x in [0, 3/10, 4/5, 3/2]] + sage: [F.root_of_unity(x, base_coercion=False) for x in [0, 3/10, 4/5, 3/2]] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] """ - return self.parent().root_of_unity(self.twist()) + ret = self.parent().root_of_unity(self.twist(), base_coercion=False) + if (not base_coercion) or (self.parent()._basecoer is None): + return ret + return self.parent()._basecoer(ret) @cached_method - def q_dimension(self): + def q_dimension(self, base_coercion=True): r""" Return the quantum dimension as an element of the cyclotomic field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or + with `m=1, 2, 3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. EXAMPLES:: - sage: B22 = FusionRing("B2",2) + sage: B22 = FusionRing("B2", 2) sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 4, 5, 1, 5, 4] """ @@ -1027,4 +1568,8 @@ def q_dimension(self): expr = R(expr) expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) - return expr.substitute(q=zet) + ret = expr.substitute(q=zet) + + if (not base_coercion) or (self.parent()._basecoer is None): + return ret + return self.parent()._basecoer(ret) diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pxd b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd new file mode 100644 index 00000000000..22c6449385a --- /dev/null +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd @@ -0,0 +1,24 @@ +from sage.algebras.fusion_rings.shm_managers cimport KSHandler +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular +from sage.rings.polynomial.polydict cimport ETuple + +cpdef tuple poly_to_tup(MPolynomial_libsingular poly) +cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) +cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) +cpdef list get_variables_degrees(list eqns, int nvars) +cpdef list variables(tuple eq_tup) +cpdef constant_coeff(tuple eq_tup, field) +cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) +# cpdef bint tup_fixes_sq(tuple eq_tup) +cdef bint tup_fixes_sq(tuple eq_tup) +cdef dict subs_squares(dict eq_dict, KSHandler known_sq) +cpdef dict compute_known_powers(max_degs, dict val_dict, one) +cdef dict subs(tuple poly_tup, dict known_powers, one) +cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) +cpdef tuple poly_tup_sortkey(tuple eq_tup) +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, KSHandler known_sq, NumberFieldElement_absolute one) +cdef tuple _flatten_coeffs(tuple eq_tup) +cpdef tuple _unflatten_coeffs(field, tuple eq_tup) +cdef int has_appropriate_linear_term(tuple eq_tup) + diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx new file mode 100644 index 00000000000..ac465e14a77 --- /dev/null +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -0,0 +1,579 @@ +""" +Arithmetic Engine for Polynomials as Tuples +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad <gh_willieab> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +########### +### API ### +########### + +cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): + r""" + Convert a polynomial object into the internal representation as tuple of + ``(ETuple exp, NumberFieldElement coeff)`` pairs. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: R.<x, y> = PolynomialRing(QQ) + sage: poly_to_tup(x**2 + 1) + (((2, 0), 1), ((0, 0), 1)) + 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()) + +cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): + r""" + Return a polynomial object from its tuple of pairs representation. + + Inverse of :meth:`poly_to_tup`: + + - ``poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup`` and + - ``tup_to_poly(poly_to_tup(eq), eq.parent()) == eq``. + + .. NOTE:: + + Assumes ``all(parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup)``. + This method is meant for internal use. + + .. WARNING:: + + Unsafe for client use, since it avoids implicit casting and + it may lead to segmentation faults. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import _tup_to_poly + sage: K = CyclotomicField(20) + sage: R.<x, y> = PolynomialRing(K) + sage: poly_tup = (((2, 0), K.one()), ((0, 0), K.one())) + sage: _tup_to_poly(poly_tup, parent=R) + x^2 + 1 + sage: poly = x**2*y**4 - 4/5*x*y**2 + 1/3 * y + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: _tup_to_poly(poly_to_tup(poly), parent=R) == poly + True + + TESTS:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: R.<x, y, z> = PolynomialRing(CyclotomicField(20)) + sage: r = R.random_element() + sage: _tup_to_poly(poly_to_tup(r), parent=R) == r + True + sage: R = PolynomialRing(QQ, 'fx', 100) + sage: r = R.random_element() + sage: _tup_to_poly(poly_to_tup(r), parent=R) == r + True + """ + return parent._element_constructor_(dict(eq_tup), check=False) + +cdef inline tuple _flatten_coeffs(tuple eq_tup): + r""" + Flatten cyclotomic coefficients to a representation as a tuple of rational + coefficients. + + This is used to avoid pickling cyclotomic coefficient objects, which fails + with new PARI settings introduced in :trac:`30537`. + """ + cdef list flat = [] + cdef NumberFieldElement_absolute cyc_coeff + for exp, cyc_coeff in eq_tup: + flat.append((exp, tuple(cyc_coeff._coefficients()))) + return tuple(flat) + +cpdef tuple _unflatten_coeffs(field, tuple eq_tup): + r""" + Restore cyclotomic coefficient object from its tuple of rational + coefficients representation. + + Used to circumvent pickling issue introduced by PARI settigs + in :trac:`30537`. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs + sage: fm = FusionRing("A2", 2).get_fmatrix() + sage: p = fm._poly_ring.random_element() + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: flat_poly_tup = list() + sage: for exp, cyc_coeff in poly_to_tup(p): + ....: flat_poly_tup.append((exp, tuple(cyc_coeff._coefficients()))) + sage: flat_poly_tup = tuple(flat_poly_tup) + sage: _unflatten_coeffs(fm.field(), flat_poly_tup) == poly_to_tup(p) + True + """ + cdef list unflat = [] + for exp, coeff_tup in eq_tup: + unflat.append((exp, field(list(coeff_tup)))) + return tuple(unflat) + +################################# +### Useful private predicates ### +################################# + +cdef inline int has_appropriate_linear_term(tuple eq_tup): + r""" + Determine whether the given tuple of pairs (of length 2) contains + an *appropriate* linear term. + + In this context, a linear term is said to be *appropriate* if + it is in the largest variable in the given polynomial (w.r.t. + the degrevlex ordering), the monomial in which the linear term + appears is univariate, and the linear term is not a common factor in + the polynomial. + + OUTPUT: + + If the given polynomial contains an appropriate linear term, this method + returns the index of the monomial in which the term appears. + + Otherwise, the method returns `-1`. + """ + max_var = degrees(eq_tup).nonzero_positions()[0] + cdef ETuple m + cdef int i + for i in range(2): + m = eq_tup[i][0] + if m._nonzero == 1 and m._data[1] == 1 and m._data[0] == max_var and eq_tup[(i+1) % 2][0][max_var] == 0: + return i + return -1 + +###################### +### "Change rings" ### +###################### + +cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): + r""" + Given a tuple of pairs representing a univariate polynomial and a univariate + polynomial ring, return a univariate polynomial object. + + Each pair in the tuple is assumed to be of the form ``(ETuple, coeff)``, + where ``coeff`` is an element of ``univ_poly_ring.base_ring()``. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import tup_to_univ_poly + sage: from sage.rings.polynomial.polydict import ETuple + sage: K = CyclotomicField(56) + sage: poly_tup = ((ETuple([0, 3, 0]), K(2)), (ETuple([0, 1, 0]), K(-1)), (ETuple([0, 0, 0]), K(-2/3))) + sage: R = K['b'] + sage: tup_to_univ_poly(poly_tup, R) + 2*b^3 - b - 2/3 + + TESTS:: + + sage: poly_tup = ((ETuple([0, 0, 0]), K(-1/5)), ) + sage: tup_to_univ_poly(poly_tup, R) + -1/5 + """ + cdef ETuple exp + cdef NumberFieldElement_absolute c + return univ_poly_ring({exp._data[1] if exp._nonzero else 0: c for exp, c in eq_tup}) + +cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): + r""" + Return a tuple representing a polynomial in a ring with + ``len(sorted_vars)`` generators. + + This method is used for creating polynomial objects with the + "right number" of variables for computing Groebner bases of the + partitioned equations graph and for adding constraints ensuring certain + F-symbols are nonzero. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import resize + sage: from sage.rings.polynomial.polydict import ETuple + sage: K = CyclotomicField(56) + sage: poly_tup = ((ETuple([0, 3, 0, 2]), K(2)), (ETuple([0, 1, 0, 1]), K(-1)), (ETuple([0, 0, 0, 0]), K(-2/3))) + sage: idx_map = {1: 0, 3: 1} + sage: resize(poly_tup, idx_map, 2) + (((3, 2), 2), ((1, 1), -1), ((0, 0), -2/3)) + + sage: R = PolynomialRing(K, 'fx', 20) + sage: R.inject_variables() + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19 + sage: sparse_poly = R(fx0**2 * fx17 + fx3) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: S.<x, y, z> = PolynomialRing(K) + sage: _tup_to_poly(resize(poly_to_tup(sparse_poly), {0:0, 3:1, 17:2}, 3), parent=S) + x^2*z + y + """ + cdef ETuple exp, new_e + cdef NumberFieldElement_absolute c + cdef list resized = list() + for exp, c in eq_tup: + new_e = ETuple({idx_map[pos]: d for pos, d in exp.sparse_iter()}, nvars) + resized.append((new_e, c)) + return tuple(resized) + +########################### +### Convenience methods ### +########################### + +cdef inline ETuple degrees(tuple poly_tup): + r""" + Return the maximal degree of each variable in the polynomial. + """ + # Deal with the empty tuple, representing the zero polynomial + if not poly_tup: + return ETuple() + cdef ETuple max_degs, exp + cdef int i + max_degs = <ETuple> (<tuple> poly_tup[0])[0] + for i in range(1, len(poly_tup)): + max_degs = max_degs.emax(<ETuple> (<tuple> poly_tup[i])[0]) + return max_degs + +cpdef list get_variables_degrees(list eqns, int nvars): + r""" + Find maximum degrees for each variable in equations. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import get_variables_degrees + sage: R.<x, y, z> = PolynomialRing(QQ) + sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: get_variables_degrees([poly_to_tup(p) for p in polys], 3) + [2, 1, 3] + """ + if not eqns: + return [0]*nvars + cdef ETuple max_deg + cdef int i + max_deg = degrees(eqns[0]) + for i in range(1, len(eqns)): + max_deg = max_deg.emax(degrees( <tuple>(eqns[i]) )) + cdef list dense = [0] * len(max_deg) + for i in range(max_deg._nonzero): + dense[max_deg._data[2*i]] = max_deg._data[2*i+1] + return dense + +cpdef list variables(tuple eq_tup): + """ + Return indices of all variables appearing in eq_tup + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import variables + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0, 3, 0]), 2), (ETuple([0, 1, 0]), -1), (ETuple([0, 0, 0]), -2/3)) + sage: variables(poly_tup) + [1] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: R.<x, y, z> = PolynomialRing(QQ) + sage: variables(poly_to_tup(x*2*y + y**3 - 4/3*x)) + [0, 1] + sage: variables(poly_to_tup(R(1/4))) + [] + """ + return degrees(eq_tup).nonzero_positions() + +cpdef constant_coeff(tuple eq_tup, field): + r""" + Return the constant coefficient of the polynomial represented by + given tuple. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import constant_coeff + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0, 3, 0]), 2), (ETuple([0, 1, 0]), -1), (ETuple([0, 0, 0]), -2/3)) + sage: constant_coeff(poly_tup, QQ) + -2/3 + sage: R.<x, y, z> = PolynomialRing(QQ) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9), QQ) + -9 + """ + cdef ETuple exp + for exp, coeff in eq_tup: + if exp.is_constant(): + return coeff + return field.zero() + +cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): + """ + Apply ``coeff_map`` to coefficients. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import apply_coeff_map + sage: sq = lambda x : x**2 + sage: R.<x, y, z> = PolynomialRing(ZZ) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: _tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) + x + 4*y + 9*z + """ + cdef ETuple exp + cdef list new_tup = [] + for exp, coeff in eq_tup: + new_tup.append((exp, coeff_map(coeff))) + return tuple(new_tup) + +# cpdef inline bint tup_fixes_sq(tuple eq_tup): +cdef inline bint tup_fixes_sq(tuple eq_tup): + r""" + Determine if given equation fixes the square of a variable. + + An equation fixes the sq of a variable if it is of the form `a*x^2 + c` + for *nonzero* constants `a`, `c`. + """ + if len(eq_tup) != 2: + return False + # To access _attributes, we must cdef ETuple + cdef ETuple lm = eq_tup[0][0] + if lm._nonzero != 1 or lm._data[1] != 2: + return False + cdef ETuple tm = eq_tup[1][0] + if tm._nonzero != 0: + return False + return True + +###################### +### Simplification ### +###################### + +cdef dict subs_squares(dict eq_dict, KSHandler known_sq): + r""" + Substitute for known squares into a given polynomial. + + INPUT: + + - ``eq_dict`` -- a dictionary of ``(ETuple, coeff)`` pairs representing + a polynomial + - ``known_sq`` -- a dictionary of ``(int i, NumberFieldElement a)`` pairs + such that `x_i^2 - a = 0` + + OUTPUT: + + A dictionary of ``(ETuple, coeff)`` pairs. + """ + cdef dict subbed, new_e + cdef ETuple exp, lm + cdef int idx, power + subbed = dict() + for exp, coeff in eq_dict.items(): + new_e = dict() + for idx, power in exp.sparse_iter(): + if known_sq.contains(idx): + coeff *= pow(known_sq.get(idx), power // 2) + # New power is 1 if power is odd + if power & True: + new_e[idx] = 1 + else: + new_e[idx] = power + exp = ETuple(new_e, len(exp)) + # If exponent tuple is already present in dictionary, coefficients are added + if exp in subbed: + subbed[exp] += coeff + else: + subbed[exp] = coeff + return subbed + +cdef dict remove_gcf(dict eq_dict, ETuple nonz): + r""" + Return a dictionary of ``(ETuple, coeff)`` pairs describing the + polynomial ``eq / GCF(eq)``. + + The input ``nonz`` is an ``ETuple`` indicating the positions of + variables known to be nonzero. The entries of ``nonz`` are assumed to + be some relatively large number, like 100. + """ + # Find common variables, filtered according to known nonzeros + cdef ETuple common_powers, exp + cdef NumberFieldElement_absolute c + common_powers = nonz + for exp, c in eq_dict.items(): + common_powers = common_powers.emin(exp) + cdef dict ret = {} + for exp, c in eq_dict.items(): + ret[exp.esub(common_powers)] = c + return ret + +cdef tuple to_monic(dict eq_dict, one): + """ + Return tuple of pairs ``(ETuple, coeff)`` describing the monic polynomial + associated to ``eq_dict``. + + Here, the leading coefficient is chosen according to the degree reverse + lexicographic ordering (default for multivariate polynomial rings). + """ + if not eq_dict: + return () + cdef list ord_monoms = sorted(eq_dict, key=monom_sortkey) + cdef ETuple lm = ord_monoms[-1] + cdef NumberFieldElement_absolute lc = eq_dict[lm] + if not lc: + return () + cdef list ret = [(lm, one)] + inv_lc = lc.inverse_of_unit() + cdef int i, n + n = len(ord_monoms) + for i in range(n-1): + ret.append((ord_monoms[n-2-i], inv_lc * eq_dict[ord_monoms[n-2-i]])) + return tuple(ret) + +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, KSHandler known_sq, NumberFieldElement_absolute one): + """ + Return a tuple describing a monic polynomial with no known nonzero + gcf and no known squares. + """ + if not eq_dict: + return () + cdef dict sq_rmvd = subs_squares(eq_dict, known_sq) + cdef dict gcf_rmvd = remove_gcf(sq_rmvd, nonz) + return to_monic(gcf_rmvd, one) + +#################### +### Substitution ### +#################### + +cpdef dict compute_known_powers(max_degs, dict val_dict, one): + """ + Pre-compute powers of known values for efficiency when preparing to + substitute into a list of polynomials. + + INPUT: + + - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of + each variable + - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value pairs + - ``poly_tup`` -- a tuple of ``(ETuple, coeff)`` pairs reperesenting a + multivariate polynomial + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.poly_tup_engine import compute_known_powers + sage: R.<x, y, z> = PolynomialRing(QQ) + sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } + sage: from sage.algebras.fusion_rings.poly_tup_engine import get_variables_degrees + sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys], 3) + sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) + {0: [(((0, 0, 0), 1),), + (((0, 0, 0), -1),), + (((0, 0, 0), 1),), + (((0, 0, 0), -1),)], + 2: [(((0, 0, 0), 1),), + (((0, 2, 0), 1),), + (((0, 4, 0), 1),), + (((0, 6, 0), 1),)]} + """ + assert (max_degs and max(max_degs) <= 100) or True, "cannot substitute for degree larger than 100" + cdef ETuple max_deg = ETuple(list(max_degs)) + max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) + cdef dict known_powers + # Get polynomial unit as tuple to initialize list elements + cdef tuple one_tup = ((max_deg._new(), one), ) + cdef int d, power, var_idx + known_powers = {var_idx: [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter()} + for var_idx, d in max_deg.sparse_iter(): + for power in range(d): + known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power], val_dict[var_idx]) + return known_powers + +cdef dict subs(tuple poly_tup, dict known_powers, one): + """ + Substitute given variables into a polynomial tuple. + """ + cdef dict subbed = {} + cdef ETuple exp, m, shifted_exp + cdef int var_idx, power + cdef tuple temp + for exp, coeff in poly_tup: + # Get polynomial unit as tuple + temp = ((exp._new(), one), ) + for var_idx, power in exp.sparse_iter(): + if var_idx in known_powers: + exp = exp.eadd_p(-power, var_idx) + temp = tup_mul(temp, known_powers[var_idx][power]) + for m, c in temp: + shifted_exp = exp.eadd(m) + if shifted_exp in subbed: + subbed[shifted_exp] += coeff * c + else: + subbed[shifted_exp] = coeff * c + return subbed + +cdef tuple tup_mul(tuple p1, tuple p2): + r""" + Multiplication of two polynomial tuples using schoolbook multiplication. + """ + cdef dict prod = {} + cdef ETuple xi, yj, shifted_exp + for xi, ai in p1: + for yj, bj in p2: + shifted_exp = xi.eadd(yj) + if shifted_exp in prod: + prod[shifted_exp] += ai * bj + else: + prod[shifted_exp] = ai * bj + return tuple(prod.items()) + +############### +### Sorting ### +############### + +cdef tuple monom_sortkey(ETuple exp): + r""" + Produce a sortkey for a monomial exponent with respect to degree + reversed lexicographic ordering. + """ + cdef int deg = exp.unweighted_degree() + # for i in range(exp._nonzero): + # exp._data[2*i+1] = -exp._data[2*i+1] + cdef ETuple rev = exp.reversed().emul(-1) + return (deg, rev) + +cpdef tuple poly_tup_sortkey(tuple eq_tup): + r""" + Return the sortkey of a polynomial represented as a tuple of + ``(ETuple, coeff)`` pairs with respect to the degree + lexicographical term order. + + Using this key to sort polynomial tuples results in comparing polynomials + term by term (we assume the tuple representation is sorted so that the + leading term with respect to the degree reverse lexicographical order + comes first). For each term, we first compare degrees, then the monomials + themselves. Different polynomials can have the same sortkey. + + EXAMPLES:: + + sage: F = CyclotomicField(20) + sage: zeta20 = F.gen() + sage: R.<x, y, z> = PolynomialRing(F) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: p = (zeta20 + 1)*x^2 + (zeta20^3 + 6)*x*z + (zeta20^2 + 7*zeta20)*z^2 + (2/3*zeta20 + 1/4)*x + y + sage: p1 = poly_to_tup(p); p1 + (((2, 0, 0), zeta20 + 1), + ((1, 0, 1), zeta20^3 + 6), + ((0, 0, 2), zeta20^2 + 7*zeta20), + ((1, 0, 0), 2/3*zeta20 + 1/4), + ((0, 1, 0), 1)) + sage: poly_tup_sortkey(p1) + (2, 0, 2, 2, 0, 1, -2, 1, 2, -2, 2, 1, 0, 1, 1, -1, 1) + """ + cdef ETuple exp + cdef int i, l, nnz + cdef list key = [] + for exp, c in eq_tup: + # Compare by term degree + key.append(exp.unweighted_degree()) + # Next compare by term w.r.t. lex order + for i in range(exp._nonzero): + # key.append(exp._length-1-exp._data[2*(nnz-i-1)]) + # key.append(-exp._data[2*(nnz-i-1)+1]) + key.append(-exp._data[2*i]) + key.append(exp._data[2*i+1]) + return tuple(key) + diff --git a/src/sage/algebras/fusion_rings/shm_managers.pxd b/src/sage/algebras/fusion_rings/shm_managers.pxd new file mode 100644 index 00000000000..342b533acae --- /dev/null +++ b/src/sage/algebras/fusion_rings/shm_managers.pxd @@ -0,0 +1,24 @@ +cimport numpy as np +from sage.rings.number_field.number_field_base cimport NumberField +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute + +cdef class KSHandler: + cdef list obj_cache + cdef np.ndarray ks_dat + cdef NumberField field + cdef public object shm + + cdef bint contains(self, int idx) + cdef NumberFieldElement_absolute get(self, int idx) + cdef setitem(self, int idx, rhs) + cpdef update(self, list eqns) + +cdef class FvarsHandler: + cdef dict sext_to_idx, obj_cache + cdef int bytes, ngens + cdef np.ndarray fvars + cdef NumberField field + cdef object fvars_t, pid_list + cdef Py_ssize_t child_id + cdef public object shm + diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx new file mode 100644 index 00000000000..91aba7ba59f --- /dev/null +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -0,0 +1,776 @@ +r""" +Shared Memory Managers for F-Symbol Attributes + +This module provides an implementation for shared dictionary like +state attributes required by the orthogonal F-matrix solver. + +Currently, the attributes only work when the base field of the +:class:`FMatrix` factory is a cyclotomic field. +""" + +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad <gh_willieab> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +cimport cython +cimport numpy as np +from cysignals.memory cimport sig_malloc +from multiprocessing import shared_memory +from sage.algebras.fusion_rings.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs +from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular +from sage.rings.polynomial.polydict cimport ETuple + +import numpy as np +from os import getpid + +cdef class KSHandler: + r""" + A shared memory backed dict-like structure to manage the + ``_ks`` attribute of an F-matrix. + + This structure implements a representation of the known squares dictionary + using a structured NumPy array backed by a contiguous shared memory + object. + + The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each + integer index corresponds to a variable and each ``known_sq`` is an + element of the F-matrix factory's base cyclotomic field. + + Each cyclotomic coefficient is stored as a list of numerators and a + list of denominators representing the rational coefficients. The + structured array also maintains ``known`` attribute that indicates + whether the structure contains an entry corresponding to the given index. + + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + INPUT: + + - ``n_slots`` -- the total number of F-symbols + - ``field`` -- F-matrix's base cyclotomic field + - ``use_mp`` -- a boolean indicating whether to construct a shared + memory block to back ``self``. Requires Python 3.8+, since we + must import the ``multiprocessing.shared_memory`` module. + - ``init_data`` -- a dictionary or :class:`KSHandler` object containing + known squares for initialization, e.g., from a solver checkpoint + - ``name`` -- the name of a shared memory object (used by child processes + for attaching) + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. WARNING:: + + This structure *cannot* modify an entry that + has already been set. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler + sage: # Create shared data structure + sage: f = FusionRing("A1", 2).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: n = f._poly_ring.ngens() + sage: f.start_worker_pool() + sage: ks = KSHandler(n, f._field, use_mp=True) + sage: # In the same shell or in a different shell, attach to fvars + sage: name = ks.shm.name + sage: ks2 = KSHandler(n, f._field, name=name, use_mp=True) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] + sage: ks.update([poly_to_tup(p) for p in eqns]) + sage: for idx, sq in ks.items(): + ....: print("Index: {}, square: {}".format(idx, sq)) + ....: + Index: 1, square: 4 + Index: 3, square: -zeta32^4 + 1/19*zeta32^2 + sage: ks.shm.unlink() + sage: f.shutdown_worker_pool() + """ + def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler + sage: # Create shared data structure + sage: f = FusionRing("A1", 2).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: n = f._poly_ring.ngens() + sage: f.start_worker_pool() + sage: ks = KSHandler(n, f._field, use_mp=True) + sage: TestSuite(ks).run() + sage: ks.shm.unlink() + sage: f.shutdown_worker_pool() + """ + cdef int n, d + self.field = field + n = n_slots + d = self.field.degree() + ks_t = np.dtype([ + ('known', 'bool', (1, )), + ('nums', 'i8', (d, )), + ('denoms', 'u8', (d, )) + ]) + self.obj_cache = [None]*n + if use_mp: + if name is None: + self.shm = shared_memory.SharedMemory(create=True, size=n*ks_t.itemsize) + else: + self.shm = shared_memory.SharedMemory(name=name) + self.ks_dat = np.ndarray((n, ), dtype=ks_t, buffer=self.shm.buf) + else: + self.ks_dat = np.ndarray((n, ), dtype=ks_t) + if name is None: + self.ks_dat['known'] = np.zeros((n, 1), dtype='bool') + self.ks_dat['nums'] = np.zeros((n, d), dtype='i8') + self.ks_dat['denoms'] = np.ones((n, d), dtype='u8') + # Populate initializer data + for idx, sq in init_data.items(): + self.setitem(idx, sq) + + @cython.nonecheck(False) + @cython.wraparound(False) + @cython.boundscheck(False) + cdef NumberFieldElement_absolute get(self, int idx): + r""" + Retrieve the known square corresponding to the given index, + if it exists. + """ + if not self.ks_dat['known'][idx]: + raise KeyError('index {} does not correspond to a known square'.format(idx)) + if self.obj_cache[idx] is not None: + return self.obj_cache[idx] + cdef int d + cdef list rat + cdef Py_ssize_t i + cdef np.ndarray[np.int64_t, ndim=1] nums = self.ks_dat['nums'][idx] + # cdef np.int64_t[::1] num_view = nums + cdef np.ndarray[np.uint64_t, ndim=1] denoms = self.ks_dat['denoms'][idx] + # cdef np.uint64_t[::1] denom_view = denoms + cdef np.int64_t num + cdef np.uint64_t denom + cdef NumberFieldElement_absolute cyc_coeff + cdef Rational quo + d = self.field.degree() + rat = list() + for i in range(d): + num = nums[i] + denom = denoms[i] + quo = Integer(num) / Integer(denom) + rat.append(quo) + cyc_coeff = self.field(rat) + self.obj_cache[idx] = cyc_coeff + return cyc_coeff + + cpdef update(self, list eqns): + r""" + Update ```self``'s ``shared_memory``-backed dictionary of known + squares. Keys are variable indices and corresponding values + are the squares. + + EXAMPLES:: + + sage: f = FusionRing("B5", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: for idx, sq in f._ks.items(): + ....: k + ....: + sage: f.get_orthogonality_constraints() + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, + fx11^2 + fx13^2 - 1] + sage: f.get_orthogonality_constraints(output=False) + sage: f._ks.update(f.ideal_basis) + sage: for idx, sq in f._ks.items(): + ....: print(idx, "-->", sq) + ....: + 0 --> 1 + 1 --> 1 + 2 --> 1 + 3 --> 1 + 4 --> 1 + 5 --> 1 + 6 --> 1 + 7 --> 1 + 8 --> 1 + 9 --> 1 + + .. WARNING:: + + This method assumes every polynomial in ``eqns`` is *monic*. + """ + cdef ETuple lm + cdef list rhs + cdef Py_ssize_t i, idx + cdef tuple eq_tup + for i in range(len(eqns)): + eq_tup = eqns[i] + if tup_fixes_sq(eq_tup): + rhs = [-v for v in eq_tup[-1][1]] + # eq_tup is guaranteed univariate, so we extract variable idx from lm + lm = eq_tup[0][0] + idx = lm._data[0] + try: + self.setitem(idx, rhs) + except OverflowError: + print("KS overflowed on index {} with value {}".format(idx, self.field(rhs))) + + @cython.nonecheck(False) + @cython.wraparound(False) + @cython.infer_types(False) + cdef setitem(self, int idx, rhs): + """ + Create an entry corresponding to the given index. + + The ``rhs`` parameter may be a cyclotomic coefficient or its + list/tuple representation. + """ + cdef Py_ssize_t i + cdef np.ndarray[np.int64_t, ndim=1] nums = self.ks_dat['nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=1] denoms = self.ks_dat['denoms'][idx] + cdef np.int64_t num + cdef np.uint64_t denom + cdef Rational quo + self.ks_dat['known'][idx] = True + if not isinstance(rhs, list): + rhs = rhs._coefficients() + for i in range(len(rhs)): + quo = rhs[i] + num = quo.numerator() + denom = quo.denominator() + if num > 2**32: + print("WARNING: Large num encountered in KS", num) + if denom > 2**32: + print("WARNING: Large denom encountered in KS", denom) + nums[i] = num + denoms[i] = denom + + cdef bint contains(self, int idx): + r""" + Determine whether ``self`` contains entry corresponding to given + ``idx``. + """ + return self.ks_dat[idx]['known'] + + def __eq__(self, KSHandler other): + r""" + Test for equality. + + TESTS:: + + sage: f = FusionRing("C2", 2).get_fmatrix() + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler + sage: n = f._poly_ring.ngens() + sage: f.start_worker_pool() + sage: ks = KSHandler(n, f._field, use_mp=True, init_data=f._ks) + sage: # In the same shell or in a different one, attach to shared memory handler + sage: name = ks.shm.name + sage: k2 = KSHandler(n, f._field, name=name, use_mp=True) + sage: ks == k2 + True + sage: ks.shm.unlink() + sage: f.shutdown_worker_pool() + """ + return all(other.get(idx) == sq for idx, sq in self.items()) + + def __reduce__(self): + r""" + Provide pickling / unpickling support for ``self.`` + + TESTS:: + + sage: f = FusionRing("A3", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: loads(dumps(f._ks)) == f._ks + True + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: loads(dumps(f._ks)) == f._ks + True + """ + d = {i: sq for i, sq in self.items()} + return make_KSHandler, (self.ks_dat.size, self.field, d) + + def items(self): + r""" + Iterate through existing entries using Python dict-style syntax. + + EXAMPLES:: + + sage: f = FusionRing("A3", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f._ks.update(f.ideal_basis) + sage: for idx, sq in f._ks.items(): + ....: print("Index: {}, sq: {}".format(idx, sq)) + ....: + Index: 0, sq: 1 + Index: 1, sq: 1 + Index: 2, sq: 1 + Index: 3, sq: 1 + Index: 4, sq: 1 + ... + Index: 25, sq: 1 + Index: 26, sq: 1 + """ + cdef Py_ssize_t i + for i in range(self.ks_dat.size): + if self.ks_dat['known'][i]: + yield i, self.get(i) + +def make_KSHandler(n_slots, field, init_data): + r""" + Provide pickling / unpickling support for :class:`KSHandler`. + + TESTS:: + + sage: f = FusionRing("B4", 1).get_fmatrix() + sage: f._reset_solver_state() + sage: loads(dumps(f._ks)) == f._ks # indirect doctest + True + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: loads(dumps(f._ks)) == f._ks # indirect doctest + True + """ + return KSHandler(n_slots, field, init_data=init_data) + +cdef class FvarsHandler: + r""" + A shared memory backed dict-like structure to manage the + ``_fvars`` attribute of an F-matrix. + + This structure implements a representation of the F-symbols dictionary + using a structured NumPy array backed by a contiguous shared memory + object. + + The monomial data is stored in the ``exp_data`` structure. Monomial + exponent data is stored contiguously and ``ticks`` are used to + indicate different monomials. + + Coefficient data is stored in the ``coeff_nums`` and ``coeff_denom`` + arrays. The ``coeff_denom`` array stores the value + ``d = coeff.denominator()`` for each cyclotomic coefficient. The + ``coeff_nums`` array stores the values + ``c.numerator() * d for c in coeff._coefficients()``, the abridged + list representation of the cyclotomic coefficient ``coeff``. + + Each entry also has a boolean ``modified`` attribute, indicating + whether it has been modified by the parent process. Entry retrieval + is cached in each process, so each process must check whether + entries have been modified before attempting retrieval. + + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + Multiprocessing requires Python 3.8+, since we must import the + ``multiprocessing.shared_memory`` module. + + INPUT: + + - ``n_slots`` -- number of generators of the underlying polynomial ring + - ``field`` -- base field for polynomial ring + - ``idx_to_sextuple`` -- map relating a single integer index to a sextuple + of ``FusionRing`` elements + - ``init_data`` -- a dictionary or :class:`FvarsHandler` object containing + known squares for initialization, e.g., from a solver checkpoint + - ``use_mp`` -- an integer indicating the number of child processes + used for multiprocessing; if running serially, use 0. + - ``pids_name`` -- the name of a ``ShareableList`` contaning the + process ``pid``'s for every process in the pool (including the + parent process) + - ``name`` -- the name of a shared memory object + (used by child processes for attaching) + - ``max_terms`` -- maximum number of terms in each entry; since + we use contiguous C-style memory blocks, the size of the block + must be known in advance + - ``n_bytes`` -- the number of bytes that should be allocated for + each numerator and each denominator stored by the structure + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. NOTE:: + + If you ever encounter an ``OverflowError`` when running the + :meth:`FMatrix.find_orthogonal_solution` solver, consider + increasing the parameter ``n_bytes``. + + .. WARNING:: + + The current data structure supports up to `2^16` entries, + with each monomial in each entry having at most 254 + nonzero terms. On average, each of the ``max_terms`` monomials + can have at most 30 terms. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: # Create shared data structure + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) + sage: # In the same shell or in a different shell, attach to fvars + sage: name = fvars.shm.name + sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name , use_mp=n_proc, pids_name=pids_name) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) + sage: fvars[f2, f1, f2, f2, f0, f0] = rhs + sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) + fx5^5 + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, + pids_name=None, name=None, max_terms=20, n_bytes=32): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: # Create shared data structure + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) + sage: TestSuite(fvars).run(skip="_test_pickling") + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + self.field = field + self.obj_cache = dict() + cdef int d = self.field.degree() + self.bytes = n_bytes + cdef int slots = self.bytes // 8 + cdef int n_proc = use_mp + 1 + self.fvars_t = np.dtype([ + ('modified', np.int8, (n_proc, )), + ('ticks', 'u1', (max_terms, )), + ('exp_data', 'u2', (max_terms*30, )), + ('coeff_nums', np.int64, (max_terms, d, slots)), + ('coeff_denom', np.uint64, (max_terms, d, slots)) + ]) + self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} + self.ngens = n_slots + if use_mp: + if name is None: + self.shm = shared_memory.SharedMemory(create=True, size=self.ngens*self.fvars_t.itemsize) + else: + self.shm = shared_memory.SharedMemory(name=name) + self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t, buffer=self.shm.buf) + self.pid_list = shared_memory.ShareableList(name=pids_name) + self.child_id = -1 + else: + self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t) + self.child_id = 0 + # Populate with initialziation data + for sextuple, fvar in init_data.items(): + if isinstance(fvar, MPolynomial_libsingular): + fvar = _flatten_coeffs(poly_to_tup(fvar)) + if isinstance(fvar, NumberFieldElement_absolute): + fvar = ((ETuple({}, self.ngens), tuple(fvar._coefficients())), ) + if isinstance(fvar, tuple): + transformed = list() + for exp, c in fvar: + if isinstance(c, NumberFieldElement_absolute): + transformed.append((exp, tuple(c._coefficients()))) + if transformed: + fvar = tuple(transformed) + self[sextuple] = fvar + + @cython.nonecheck(False) + @cython.wraparound(False) + @cython.boundscheck(False) + def __getitem__(self, sextuple): + r""" + Retrieve a record from the shared memory data structure by + unflattening its representation and constructing relevant Python + objects. + + This method returns a tuple of ``(ETuple, coeff)`` pairs, + where ``coeff`` is an element of ``self.field``. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: f = FusionRing("B7", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(14, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) + sage: rhs = tuple((exp, tuple(c._coefficients())) + ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) + sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) + sage: fvars[f2, f2, f2, f2, f0, f0] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(-1/19*f._poly_ring.one())) + sage: fvars[f2, f1, f2, f1, f2, f2] = rhs + sage: s, t, r = (f1, f2, f1, f2, f2, f2), (f2, f2, f2, f2, f0, f0), (f2, f1, f2, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10 + True + sage: f._tup_to_fpoly(fvars[t]) == 0 + True + sage: f._tup_to_fpoly(fvars[r]) == -1/19 + True + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + + .. NOTE:: + + This method implements caching. Only the parent process is allowed + to modify the shared fvars structure. Each process builds its own + cache, so each process must update its cache before retrieving a + modified entry, tagged via its ``modified`` property. + """ + if sextuple not in self.sext_to_idx: + raise KeyError('invalid sextuple {}'.format(sextuple)) + cdef Py_ssize_t idx = self.sext_to_idx[sextuple] + # Each process builds its own cache, so each process must know + # whether the entry it wants to retrieve has been modified. + # Each process needs to know where to look, so we use an index + # every process agrees on. The pid_list[0] belongs to the main process. + if self.child_id < 0: + self.child_id = self.pid_list.index(getpid()) + if idx in self.obj_cache: + if self.fvars['modified'][idx, self.child_id]: + del self.obj_cache[idx] + else: + return self.obj_cache[idx] + cdef ETuple e, exp + cdef int count, nnz + cdef Integer d, num + cdef list poly_tup, rats + cdef NumberFieldElement_absolute cyc_coeff + cdef Py_ssize_t cum, i, j, k + cdef Rational quo + cdef tuple ret + # Define memory views to reduce Python overhead and ensure correct typing + cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t, ndim=1] modified = self.fvars['modified'][idx] + e = ETuple({}, self.ngens) + poly_tup = list() + cum = 0 + count = np.count_nonzero(ticks) + for i in range(count): + # Construct new ETuple for each monomial + exp = e._new() + # Handle constant coeff + nnz = ticks[i] if ticks[i] < 255 else 0 + exp._nonzero = nnz + if nnz: + exp._data = <int*>sig_malloc(sizeof(int)*nnz*2) + for j in range(2*nnz): + exp._data[j] = <int>exp_data[cum] + cum += 1 + + # Construct cyclotomic field coefficient + rats = list() + for k in range(self.field.degree()): + num = Integer(list(nums[i, k]), 2**63) + denom = Integer(list(denoms[i, k]), 2**64) + quo = num / denom + rats.append(quo) + cyc_coeff = self.field(rats) + poly_tup.append((exp, cyc_coeff)) + ret = tuple(poly_tup) + # Cache object and reset modified + self.obj_cache[idx] = ret + modified[self.child_id] = 0 + return ret + + @cython.nonecheck(False) + @cython.wraparound(False) + def __setitem__(self, sextuple, fvar): + r""" + Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, + create or overwrite an entry in the shared data structure + corresponding to the given sextuple. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: f = FusionRing("A3", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx27 + Defining fx0, ..., fx26 + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(27, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) + sage: rhs = tuple((exp, tuple(c._coefficients())) + ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) + sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) + sage: fvars[f3, f2, f3, f0, f1, f1] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(-1/19*f._poly_ring.one())) + sage: fvars[f3, f3, f3, f1, f2, f2] = rhs + sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 + True + sage: f._tup_to_fpoly(fvars[t]) == 0 + True + sage: f._tup_to_fpoly(fvars[r]) == -1/19 + True + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + cdef ETuple exp + cdef Integer num, denom + cdef tuple coeff_tup + cdef Py_ssize_t cum, i, idx, j, k, t + cdef Rational r + idx = self.sext_to_idx[sextuple] + # Clear entry before inserting + self.fvars[idx] = np.zeros((1, ), dtype=self.fvars_t) + # Define memory views to reduce Python overhead and ensure correct typing + cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t, ndim=1] modified = self.fvars['modified'][idx] + cdef list digits + # Initialize denominators to 1 + denoms[:, :, 0] = 1 + cum = 0 + i = 0 + for exp, coeff_tup in fvar: + # Handle constant coefficient + if exp._nonzero > 0: + ticks[i] = exp._nonzero + else: + ticks[i] = -1 + for j in range(2*exp._nonzero): + exp_data[cum] = exp._data[j] + cum += 1 + k = 0 + for r in coeff_tup: + num, denom = r.as_integer_ratio() + if abs(num) > 2**63 or denom > 2**63: + print("Large integers encountered in FvarsHandler", num, denom) + if abs(num) < 2**63: + nums[i, k, 0] = num + else: + digits = num.digits(2**63) + # assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num, num.nbits()//8+1) + for t in range(len(digits)): + nums[i, k, t] = <np.int64_t>digits[t] + if denom < 2**64: + denoms[i, k, 0] = denom + else: + digits = denom.digits(2**64) + # assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom, denom.nbits()//8+1) + for t in range(len(digits)): + denoms[i, k, t] = <np.uint64_t>digits[t] + k += 1 + i += 1 + modified[:] = 1 + + def __reduce__(self): + r""" + Provide pickling / unpickling support for ``self.`` + + TESTS:: + + sage: f = FusionRing("F4", 1).get_fmatrix() + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars, use_mp=n_proc, pids_name=pids_name) + sage: for s, fvar in loads(dumps(fvars)).items(): + ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) + ....: + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + n = self.fvars.size + idx_map = {i: s for s, i in self.sext_to_idx.items()} + d = {s: fvar for s, fvar in self.items()} + return make_FvarsHandler, (n, self.field, idx_map, d) + + def items(self): + r""" + Iterates through key-value pairs in the data structure as if it + were a Python dict. + + As in a Python dict, the key-value pairs are yielded in no particular + order. + + EXAMPLES:: + + sage: f = FusionRing("G2", 1).get_fmatrix(inject_variables=True, new=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: shared_fvars = FvarsHandler(5, f._field, f._idx_to_sextuple, init_data=f._fvars) + sage: for sextuple, fvar in shared_fvars.items(): + ....: if sextuple == (f1, f1, f1, f1, f1, f1): + ....: f._tup_to_fpoly(fvar) + ....: + fx4 + """ + for sextuple in self.sext_to_idx: + yield sextuple, self[sextuple] + +def make_FvarsHandler(n, field, idx_map, init_data): + r""" + Provide pickling / unpickling support for :class:`FvarsHandler`. + + TESTS:: + + sage: f = FusionRing("G2", 1).get_fmatrix() + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name + sage: fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars, use_mp=n_proc, pids_name=pids_name) + sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest + ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) + ....: + sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + return FvarsHandler(n, field, idx_map, init_data=init_data) + diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py index 29ed81ff47d..29d444e745f 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py @@ -113,17 +113,15 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms <seb.oehms@gmail.com> # # 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 warnings import warn from sage.combinat.free_module import CombinatorialFreeModule @@ -188,7 +186,8 @@ def __invert__(self): inverse_Tietze = () len_self = len(self_Tietze) - inverse_Tietze = tuple([-1*self_Tietze[len_self - i - 1] for i in range(len_self)]) + inverse_Tietze = tuple([-1 * self_Tietze[len_self - i - 1] + for i in range(len_self)]) P = self.parent() return P(inverse_Tietze) @@ -829,9 +828,9 @@ def __classcall_private__(cls, n=None, names='c', cubic_equation_parameters=None from sage.structure.category_object import normalize_names names = tuple(normalize_names(n, names)) - return super(CubicHeckeAlgebra, cls).__classcall__(cls, names, - cubic_equation_parameters=cubic_equation_parameters, - cubic_equation_roots=cubic_equation_roots) + return super().__classcall__(cls, names, + cubic_equation_parameters=cubic_equation_parameters, + cubic_equation_roots=cubic_equation_roots) def __init__(self, names, cubic_equation_parameters=None, cubic_equation_roots=None): r""" @@ -1757,11 +1756,11 @@ def _test_ring_constructions(self, **options): raise RuntimeError('fatal: base ring embedding %s does not work' % bri) test_eleBgenEmb = self._tester(**options) - test_eleBgenEmb.assertTrue(eleBgenEmb == eleB) + test_eleBgenEmb.assertEqual(eleBgenEmb, eleB) test_eleEgenEmb = self._tester(**options) - test_eleEgenEmb.assertTrue(eleEgenEmb == eleE) + test_eleEgenEmb.assertEqual(eleEgenEmb, eleE) test_eleBembE = self._tester(**options) - test_eleBembE.assertTrue(eleBembE == eleB) + test_eleBembE.assertEqual(eleBembE, eleB) # -------------------------------------------------------------------------- # _test_matrix_constructions @@ -1806,7 +1805,7 @@ def check_matrix(representation_type): m12mult = m1*m2 m12mat = b12.matrix(representation_type=representation_type) test_matrix = self._tester(**options) - test_matrix.assertTrue(m12mult == m12mat) + test_matrix.assertEqual(m12mult, m12mat) from sage.combinat.root_system.reflection_group_real import is_chevie_available @@ -3272,7 +3271,7 @@ def cubic_hecke_subalgebra(self, nstrands=None): if nstrands == self._nstrands - 1 and self._cubic_hecke_subalgebra is not None: return self._cubic_hecke_subalgebra - names_red = names[:nstrands-1] + names_red = names[:nstrands - 1] if self.base_ring() == self.base_ring(generic=True): SubHeckeAlg = CubicHeckeAlgebra(names=names_red) else: @@ -3535,4 +3534,3 @@ def char_function(ele): return char_function irrs = [irr for irr in self.irred_repr if irr.number_gens() == self._nstrands - 1] return [self.characters(irrs[i], original=original) for i in range(len(irrs))] - 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 d30633cad55..6388fa79e60 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py @@ -11,17 +11,15 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms <seb.oehms@gmail.com> # # 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.category_object import normalize_names from sage.structure.element import get_coercion_model from sage.categories.action import Action @@ -152,7 +150,7 @@ def _act_(self, perm, pol): for key, value in pol.dict().items(): newkey = [0] * len(key) for pos, k in enumerate(key): - newkey[perm(pos+1)-1] = k + newkey[perm(pos + 1) - 1] = k pol_dict[tuple(newkey)] = value return self.domain()(pol_dict) @@ -313,7 +311,7 @@ def _element_constructor_(self, x, mon=None): from sage.interfaces.gap3 import GAP3Element if isinstance(x, GAP3Element): return self._convert_from_gap3_mvp(x) - return super(CubicHeckeExtensionRing, self)._element_constructor_(x, mon=mon) + return super()._element_constructor_(x, mon=mon) def _coerce_map_from_(self, R): r""" @@ -338,8 +336,8 @@ def _coerce_map_from_(self, R): markov = R.markov_trace_version() a, b, c, *rem = self.gens() iu = a + b + c - iv = a*b + a*c + b*c - iw = a*b*c + iv = a * b + a * c + b * c + iw = a * b * c im_gens = [iu, iv, iw] if markov: if self.markov_trace_version(): @@ -351,7 +349,7 @@ def _coerce_map_from_(self, R): else: embedding_into_extension_ring = R.hom(im_gens) return embedding_into_extension_ring - return super(CubicHeckeExtensionRing, self)._coerce_map_from_(R) + return super()._coerce_map_from_(R) def hom(self, im_gens, codomain=None, check=True, base_map=None): r""" @@ -383,13 +381,13 @@ def hom(self, im_gens, codomain=None, check=True, base_map=None): e3, *im_remain = im_gens hom_cycl_gen = self.base_ring().hom([e3], codomain=e3.parent(), check=check, base_map=base_map) verbose("hom_cycl_gen %s" % hom_cycl_gen, level=2) - return super(CubicHeckeExtensionRing, self).hom(im_remain, codomain=codomain, check=check, base_map=hom_cycl_gen) + 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 ' 'third root of unity at first position) or a ' 'base_map (on %s) must be given' % self.base_ring()) - return super(CubicHeckeExtensionRing, self).hom(im_gens, codomain=codomain, check=check, base_map=base_map) + return super().hom(im_gens, codomain=codomain, check=check, base_map=base_map) def _an_element_(self): r""" @@ -1164,10 +1162,10 @@ def mirror_involution(self): if self._mirror is None: if self._is_markov_trace_version(): u, v, w, s = self.gens() - self._mirror = self.hom([v/w, u/w, ~w, ~s]) + self._mirror = self.hom([v / w, u / w, ~w, ~s]) else: u, v, w = self.gens() - self._mirror = self.hom([v/w, u/w, ~w]) + self._mirror = self.hom([v / w, u / w, ~w]) return self._mirror def create_specialization(self, im_cubic_equation_parameters, im_writhe_parameter=None): @@ -1477,7 +1475,7 @@ def specialize_links_gould(self): L = LaurentPolynomialRing(ZZ, 't0, t1') t0, t1 = L.gens() lu = t0 + t1 - 1 - lv = t0*t1 - t0 - t1 + lv = t0 * t1 - t0 - t1 lw = -t0 * t1 LL = L.localization((lu, lv)) u = LL(lu) @@ -1486,4 +1484,3 @@ def specialize_links_gould(self): phi = self.hom((u, v, w, LL.one())) inc = L.convert_map_from(LL) return inc * phi - 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 01c72e26098..ef8ba7316c9 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py @@ -14,18 +14,17 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms <seb.oehms@gmail.com> # # 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 enum import Enum + from sage.misc.cachefunc import cached_method from sage.misc.verbose import verbose from sage.rings.integer import Integer @@ -136,10 +135,10 @@ def number_of_representations(self, nstrands): raise ValueError("nstrands must be between 1 and 4") return self.value['num_rep'][nstrands - 1] - RegularLeft = {'split': False, 'regular': True, 'data': sc.regular_left, 'num_rep': [1, 1, 1, 1]} - RegularRight = {'split': False, 'regular': True, 'data': sc.regular_right, 'num_rep': [1, 1, 1, 1]} - SplitIrredMarin = {'split': True, 'regular': False, 'data': sc.split_irred, 'num_rep': [1, 3, 7, 24]} - SplitIrredChevie = {'split': True, 'regular': False, 'data': None, 'num_rep': [1, 3, 7, 24, 30]} + RegularLeft = {'split': False, 'regular': True, 'data': sc.regular_left, 'num_rep': [1, 1, 1, 1]} + RegularRight = {'split': False, 'regular': True, 'data': sc.regular_right, 'num_rep': [1, 1, 1, 1]} + SplitIrredMarin = {'split': True, 'regular': False, 'data': sc.split_irred, 'num_rep': [1, 3, 7, 24]} + SplitIrredChevie = {'split': True, 'regular': False, 'data': None, 'num_rep': [1, 3, 7, 24, 30]} # --------------------------------------------- @@ -275,42 +274,42 @@ def internal_index(self): # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 2 strands # ------------------------------------------------------------------------------------------------- - W2_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W2_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W2_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W2_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W2_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W2_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 3 strands # ------------------------------------------------------------------------------------------------- - W3_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W3_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W3_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W3_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W3_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W3_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} - W3_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} - W3_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} - W3_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} + W3_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} + W3_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} + W3_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} - W3_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 2, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} + W3_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 2, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 4 strands # ------------------------------------------------------------------------------------------------- - W4_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W4_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W4_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W4_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W4_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W4_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} - W4_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} - W4_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} - W4_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} + W4_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} + W4_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} + W4_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} - W4_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 3, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} + W4_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 3, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} - W4_120 = {'alt_name': 'Uba', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} - W4_201 = {'alt_name': 'Uac', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} - W4_012 = {'alt_name': 'Ucb', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} - W4_102 = {'alt_name': 'Uca', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} - W4_210 = {'alt_name': 'Uab', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} - W4_021 = {'alt_name': 'Ubc', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} + W4_120 = {'alt_name': 'Uba', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} + W4_201 = {'alt_name': 'Uac', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} + W4_012 = {'alt_name': 'Ucb', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} + W4_102 = {'alt_name': 'Uca', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} + W4_210 = {'alt_name': 'Uab', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} + W4_021 = {'alt_name': 'Ubc', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} W4_213 = {'alt_name': 'Vcab', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 13, 'intern_ind': 13} W4_132 = {'alt_name': 'Vbca', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 14, 'intern_ind': 14} @@ -319,11 +318,11 @@ def internal_index(self): W4_123 = {'alt_name': 'Vcba', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 17, 'intern_ind': 17} W4_312 = {'alt_name': 'Vacb', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 18, 'intern_ind': 18} - W4_422 = {'alt_name': 'Wa', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 19, 'intern_ind': 19} - W4_224 = {'alt_name': 'Wc', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 20, 'intern_ind': 20} - W4_242 = {'alt_name': 'Wb', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 21, 'intern_ind': 21} + W4_422 = {'alt_name': 'Wa', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 19, 'intern_ind': 19} + W4_224 = {'alt_name': 'Wc', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 20, 'intern_ind': 20} + W4_242 = {'alt_name': 'Wb', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 21, 'intern_ind': 21} - W4_333 = {'alt_name': 'X', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 22, 'intern_ind': 22} + W4_333 = {'alt_name': 'X', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 22, 'intern_ind': 22} W4_333bar = {'alt_name': 'Xbar', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 23, 'intern_ind': 23} # ------------------------------------------------------------------------------------------------- @@ -340,7 +339,7 @@ def internal_index(self): W5_103 = {'alt_name': None, 'dim': 4, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} W5_310 = {'alt_name': None, 'dim': 4, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} - W5_203 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} + W5_203 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} W5_032 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} W5_320 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} W5_230 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} @@ -513,7 +512,7 @@ def __getitem__(self, item): elif isinstance(item, (Integer, int)): return self._get_block(item) - return super(CubicHeckeMatrixRep, self).__getitem__(item) + return super().__getitem__(item) @cached_method def block_diagonal_list(self): @@ -799,12 +798,12 @@ def __call__(self, entries=None, coerce=True, copy=None): """ from sage.algebras.hecke_algebras.cubic_hecke_algebra import CubicHeckeAlgebra if entries is None: - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) if not hasattr(entries, 'parent'): - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) ele_parent = entries.parent() if not isinstance(ele_parent, (CubicHeckeAlgebra, MatrixSpace)): - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) return self._element_constructor_(entries) @cached_method @@ -890,12 +889,12 @@ def invert_gen(matr): Return the inverse matrix of generators. """ cfs = ch_algebra.cubic_equation(as_coefficients=True, generic=True) - fac = - 1/cfs[0] - cf0, cf1, cf2, cf3 = [original_base_ring(cf*fac) for cf in cfs] + fac = - 1 / cfs[0] + cf0, cf1, cf2, cf3 = [original_base_ring(cf * fac) for cf in cfs] - matri = cf1*matr.parent().one() - matri += cf2*matr - matri += cf3*matr**2 + matri = cf1 * matr.parent().one() + matri += cf2 * matr + matri += cf3 * matr**2 d1, d2 = matr.dimensions() matrI = matrix(original_base_ring, d1, d2, lambda i, j: original_base_ring(matri[i, j])) return matrI @@ -912,7 +911,7 @@ def invert_gen(matr): num_rep = representation_type.number_of_representations(n) if representation_type == RepresentationType.SplitIrredChevie: - rep_list = [ch_algebra._fetch_matrix_list_from_chevie(i+1) for i in range(num_rep)] + rep_list = [ch_algebra._fetch_matrix_list_from_chevie(i + 1) for i in range(num_rep)] if gen_ind > 0: matrix_list = [rep[gen_ind - 1] for rep in rep_list] else: @@ -1003,7 +1002,7 @@ def zero(self): [0 0 0] ] """ - z = self.element_class(self, super(CubicHeckeMatrixSpace, self).zero()) + z = self.element_class(self, super().zero()) z._cubic_hecke_element = self._cubic_hecke_algebra.zero() z.set_immutable() return z @@ -1031,7 +1030,7 @@ def one(self): [0 0 1] ] """ - o = self.element_class(self, super(CubicHeckeMatrixSpace, self).one()) + o = self.element_class(self, super().one()) o._cubic_hecke_element = self._cubic_hecke_algebra.one() o.set_immutable() return o @@ -1079,4 +1078,3 @@ def some_elements(self): True """ return tuple([self(x) for x in self._cubic_hecke_algebra.some_elements()]) - diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index 671cd52fecf..900039c7909 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -2282,9 +2282,14 @@ def _decompose_into_generators(self, u): {(1,): -1, (1, 2, 1): 1} sage: Cp._decompose_into_generators(W([1,2,3,1,2])) # optional - coxeter3 {(1,): 1, (1, 2, 1): -1, (1, 2, 1, 3, 2): 1, (1, 3, 2): -1} + + TESTS:: + + sage: Cp._decompose_into_generators(W([])) # optional - coxeter3 + {(): 1} """ # l(y) = 0 or 1 - if not u: + if not len(u): return {(): 1} if len(u) == 1: return {(u[0],): 1} diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index 0923934c514..f5824069797 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -397,7 +397,7 @@ def _latex_(self): from sage.misc.latex import latex return latex(self._x) - def __bool__(self): + def __bool__(self) -> bool: """ Return if ``self`` is non-zero. @@ -411,8 +411,6 @@ def __bool__(self): """ return bool(self._x) - - def __eq__(self, other): """ Check equality. @@ -817,7 +815,7 @@ def _latex_(self): from sage.misc.latex import latex return "{} + {}".format(latex(self._s), latex(self._v)) - def __bool__(self): + def __bool__(self) -> bool: """ Return if ``self`` is non-zero. @@ -834,8 +832,6 @@ def __bool__(self): """ return bool(self._s) or bool(self._v) - - def __eq__(self, other): """ Check equality. diff --git a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx index fc1d43c574c..41444812c49 100644 --- a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx @@ -107,9 +107,9 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: loads(dumps(x*y*x)) == x*y*x # indirect doctest True - """ - return self.__class__, (self._parent,self._poly) + return self.__class__, (self._parent, self._poly) + def __copy__(self): """ TESTS:: @@ -117,10 +117,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: copy(x*y*z+z*y*x) == x*y*z+z*y*x # indirect doctest True - """ self._poly = (<FreeAlgebra_letterplace>self._parent)._current_ring(self._poly) - return self.__class__(self._parent,self._poly,check=False) + return self.__class__(self._parent, self._poly, check=False) + def __hash__(self): """ TESTS:: @@ -128,7 +128,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: set([x*y*z, z*y+x*z,x*y*z]) # indirect doctest {x*z + z*y, x*y*z} - """ return hash(self._poly) @@ -163,66 +162,65 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): w + (z + 1)*x - y sage: print(a+b*(z+1)-c) a + (z + 1)*b - c - """ cdef list L = [] cdef FreeAlgebra_letterplace P = self._parent cdef int ngens = P.__ngens if P._base._repr_option('element_is_atomic'): - for E,c in zip(self._poly.exponents(),self._poly.coefficients()): + for E, c in zip(self._poly.exponents(), self._poly.coefficients()): monstr = P.exponents_to_string(E) if monstr: - if c==1: + if c == 1: if L: - L.extend(['+',monstr]) + L.extend(['+', monstr]) else: L.append(monstr) - elif c==-1: + elif c == -1: if L: - L.extend(['-',monstr]) + L.extend(['-', monstr]) else: - L.append('-'+monstr) + L.append('-' + monstr) else: if L: - if c>=0: - L.extend(['+',repr(c)+'*'+monstr]) + if c >= 0: + L.extend(['+', repr(c) + '*' + monstr]) else: - L.extend(['-',repr(-c)+'*'+monstr]) + L.extend(['-', repr(-c) + '*' + monstr]) else: - L.append(repr(c)+'*'+monstr) + L.append(repr(c) + '*' + monstr) else: - if c>=0: + if c >= 0: if L: - L.extend(['+',repr(c)]) + L.extend(['+', repr(c)]) else: L.append(repr(c)) else: if L: - L.extend(['-',repr(-c)]) + L.extend(['-', repr(-c)]) else: L.append(repr(c)) else: - for E,c in zip(self._poly.exponents(),self._poly.coefficients()): + for E, c in zip(self._poly.exponents(), self._poly.coefficients()): monstr = P.exponents_to_string(E) if monstr: - if c==1: + if c == 1: if L: - L.extend(['+',monstr]) + L.extend(['+', monstr]) else: L.append(monstr) - elif c==-1: + elif c == -1: if L: - L.extend(['-',monstr]) + L.extend(['-', monstr]) else: - L.append('-'+monstr) + L.append('-' + monstr) else: if L: - L.extend(['+','('+repr(c)+')*'+monstr]) + L.extend(['+', '(' + repr(c) + ')*' + monstr]) else: - L.append('('+repr(c)+')*'+monstr) + L.append('(' + repr(c) + ')*' + monstr) else: if L: - L.extend(['+',repr(c)]) + L.extend(['+', repr(c)]) else: L.append(repr(c)) if L: @@ -245,60 +243,60 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): cdef int ngens = P.__ngens from sage.misc.latex import latex if P._base._repr_option('element_is_atomic'): - for E,c in zip(self._poly.exponents(),self._poly.coefficients()): + for E, c in zip(self._poly.exponents(), self._poly.coefficients()): monstr = P.exponents_to_latex(E) if monstr: - if c==1: + if c == 1: if L: - L.extend(['+',monstr]) + L.extend(['+', monstr]) else: L.append(monstr) - elif c==-1: + elif c == -1: if L: - L.extend(['-',monstr]) + L.extend(['-', monstr]) else: - L.append('-'+monstr) + L.append('-' + monstr) else: if L: - if c>=0: - L.extend(['+',repr(latex(c))+' '+monstr]) + if c >= 0: + L.extend(['+', repr(latex(c)) + ' ' + monstr]) else: - L.extend(['-',repr(latex(-c))+' '+monstr]) + L.extend(['-', repr(latex(-c)) + ' ' + monstr]) else: - L.append(repr(latex(c))+' '+monstr) + L.append(repr(latex(c)) + ' ' + monstr) else: - if c>=0: + if c >= 0: if L: - L.extend(['+',repr(latex(c))]) + L.extend(['+', repr(latex(c))]) else: L.append(repr(latex(c))) else: if L: - L.extend(['-',repr(latex(-c))]) + L.extend(['-', repr(latex(-c))]) else: L.append(repr(c)) else: - for E,c in zip(self._poly.exponents(),self._poly.coefficients()): + for E, c in zip(self._poly.exponents(), self._poly.coefficients()): monstr = P.exponents_to_latex(E) if monstr: - if c==1: + if c == 1: if L: - L.extend(['+',monstr]) + L.extend(['+', monstr]) else: L.append(monstr) - elif c==-1: + elif c == -1: if L: - L.extend(['-',monstr]) + L.extend(['-', monstr]) else: - L.append('-'+monstr) + L.append('-' + monstr) else: if L: - L.extend(['+','\\left('+repr(latex(c))+'\\right) '+monstr]) + L.extend(['+', '\\left(' + repr(latex(c)) + '\\right) ' + monstr]) else: - L.append('\\left('+repr(latex(c))+'\\right) '+monstr) + L.append('\\left(' + repr(latex(c)) + '\\right) ' + monstr) else: if L: - L.extend(['+',repr(latex(c))]) + L.extend(['+', repr(latex(c))]) else: L.append(repr(latex(c))) if L: @@ -309,10 +307,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): """ Return the degree of this element. - NOTE: + .. NOTE:: - Generators may have a positive integral degree weight. All - elements must be weighted homogeneous. + Generators may have a positive integral degree weight. All + elements must be weighted homogeneous. EXAMPLES:: @@ -322,7 +320,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: ((x*y+z)^3).degree() 9 - """ return self._poly.degree() @@ -342,7 +339,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: ((x*y+z)^2).letterplace_polynomial() x*x__1*y_2*x_3*x__4*y_5 + x*x__1*y_2*z_3*x__4*x__5 + z*x__1*x__2*x_3*x__4*y_5 + z*x__1*x__2*z_3*x__4*x__5 - """ return self._poly @@ -358,7 +354,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: ((2*x*y+z)^2).lm() x*y*x*y - """ return FreeAlgebraElement_letterplace(self._parent, self._poly.lm()) @@ -375,7 +370,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: ((2*x*y+z)^2).lt() 4*x*y*x*y - """ return FreeAlgebraElement_letterplace(self._parent, self._poly.lt()) @@ -394,7 +388,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: ((2*x*y+z)^2).lc() 4 - """ return self._poly.lc() @@ -407,7 +400,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): True sage: bool(F.zero()) False - """ return bool(self._poly) @@ -416,10 +408,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): Tell whether or not the leading monomial of self divides the leading monomial of another element. - NOTE: + .. NOTE:: - A free algebra element `p` divides another one `q` if there are - free algebra elements `s` and `t` such that `spt = q`. + A free algebra element `p` divides another one `q` if there are + free algebra elements `s` and `t` such that `spt = q`. EXAMPLES:: @@ -430,7 +422,6 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): y*x*y sage: (y*x*y-y^4).lm_divides((2*x*y+z)^2*z) True - """ if self._parent is not p._parent: raise TypeError("the two arguments must be elements in the same free algebra") @@ -440,16 +431,16 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): s_poly = self._poly = P(self._poly) cdef int p_d = p_poly.degree() cdef int s_d = s_poly.degree() - if s_d>p_d: + if s_d > p_d: return False cdef int i - if P.monomial_divides(s_poly,p_poly): + if P.monomial_divides(s_poly, p_poly): return True realngens = A._commutative_ring.ngens() CG = CyclicPermutationGroup(P.ngens()) - for i from 0 <= i < p_d-s_d: + for i in range(p_d - s_d): s_poly = s_poly * CG[realngens] - if P.monomial_divides(s_poly,p_poly): + if P.monomial_divides(s_poly, p_poly): return True return False @@ -469,7 +460,7 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): return PyObject_RichCompare(left, right, op) ################################ - ## Arithmetic + # Arithmetic cpdef _neg_(self): """ TESTS:: @@ -483,7 +474,9 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): -3*x*y - 2*z*z """ - return FreeAlgebraElement_letterplace(self._parent,-self._poly,check=False) + return FreeAlgebraElement_letterplace(self._parent, -self._poly, + check=False) + cpdef _add_(self, other): """ Addition, under the side condition that either one summand @@ -502,20 +495,21 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): x sage: 0+x x - """ if not other: return self if not self: return other cdef FreeAlgebraElement_letterplace right = other - if right._poly.degree()!=self._poly.degree(): + if right._poly.degree() != self._poly.degree(): raise ArithmeticError("can only add elements of the same weighted degree") # update the polynomials cdef FreeAlgebra_letterplace A = self._parent self._poly = A._current_ring(self._poly) right._poly = A._current_ring(right._poly) - return FreeAlgebraElement_letterplace(self._parent,self._poly+right._poly,check=False) + return FreeAlgebraElement_letterplace(self._parent, + self._poly + right._poly, + check=False) cpdef _sub_(self, other): """ @@ -541,20 +535,21 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: x*y+z x*y + z - """ if not other: return self if not self: return -other cdef FreeAlgebraElement_letterplace right = other - if right._poly.degree()!=self._poly.degree(): + if right._poly.degree() != self._poly.degree(): raise ArithmeticError("can only subtract elements of the same degree") # update the polynomials cdef FreeAlgebra_letterplace A = self._parent self._poly = A._current_ring(self._poly) right._poly = A._current_ring(right._poly) - return FreeAlgebraElement_letterplace(self._parent,self._poly-right._poly,check=False) + return FreeAlgebraElement_letterplace(self._parent, + self._poly - right._poly, + check=False) cpdef _lmul_(self, Element right): """ @@ -566,9 +561,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace') sage: (a+b)*(z+1) # indirect doctest (z + 1)*a + (z + 1)*b - """ - return FreeAlgebraElement_letterplace(self._parent,self._poly._lmul_(right),check=False) + return FreeAlgebraElement_letterplace(self._parent, + self._poly._lmul_(right), + check=False) cpdef _rmul_(self, Element left): """ @@ -580,9 +576,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace') sage: (z+1)*(a+b) # indirect doctest (z + 1)*a + (z + 1)*b - """ - return FreeAlgebraElement_letterplace(self._parent,self._poly._rmul_(left),check=False) + return FreeAlgebraElement_letterplace(self._parent, + self._poly._rmul_(left), + check=False) cpdef _mul_(self, other): """ @@ -598,14 +595,15 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): cdef FreeAlgebraElement_letterplace left = self cdef FreeAlgebraElement_letterplace right = other cdef FreeAlgebra_letterplace A = left._parent - A.set_degbound(left._poly.degree()+right._poly.degree()) + A.set_degbound(left._poly.degree() + right._poly.degree()) # we must put the polynomials into the same ring left._poly = A._current_ring(left._poly) right._poly = A._current_ring(right._poly) realngens = A._commutative_ring.ngens() CG = CyclicPermutationGroup(A._current_ring.ngens()) rshift = right._poly * CG[left._poly.degree() * realngens] - return FreeAlgebraElement_letterplace(A,left._poly*rshift, check=False) + return FreeAlgebraElement_letterplace(A, left._poly * rshift, + check=False) def __pow__(FreeAlgebraElement_letterplace self, int n, k): """ @@ -615,30 +613,29 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace') sage: (a+z*b)^3 # indirect doctest a*a*a + (z)*a*a*b + (z)*a*b*a + (z + 3)*a*b*b + (z)*b*a*a + (z + 3)*b*a*b + (z + 3)*b*b*a + (4*z + 3)*b*b*b - """ cdef FreeAlgebra_letterplace A = self._parent - if n<0: + if n < 0: raise ValueError("negative exponents are not allowed") - if n==0: + if n == 0: return FreeAlgebraElement_letterplace(A, A._current_ring(1), check=False) - if n==1: + if n == 1: return self - A.set_degbound(self._poly.degree()*n) - cdef MPolynomial_libsingular p,q + A.set_degbound(self._poly.degree() * n) + cdef MPolynomial_libsingular p, q self._poly = A._current_ring(self._poly) cdef int d = self._poly.degree() q = p = self._poly realngens = A._commutative_ring.ngens() cdef int i CG = CyclicPermutationGroup(A._current_ring.ngens()) - for i from 0<i<n: + for i in range(1, n): q = q * CG[d * realngens] p *= q return FreeAlgebraElement_letterplace(A, p, check=False) - ## Groebner related stuff + # Groebner related stuff def reduce(self, G): """ Reduce this element by a list of elements or by a @@ -689,32 +686,31 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): y*y*z*z*z + y*z*y*z*z - y*z*z*y*z + y*z*z*z*z sage: p.reduce(I) != p.reduce(G) != p.normal_form(I) != p.reduce(I) True - """ cdef FreeAlgebra_letterplace P = self._parent - if not isinstance(G,(list,tuple)): - if G==P: + if not isinstance(G, (list, tuple)): + if G == P: return P.zero() - if not (isinstance(G,MPolynomialIdeal) and G.ring()==P._current_ring): + if not (isinstance(G, MPolynomialIdeal) and G.ring() == P._current_ring): G = G.gens() C = P.current_ring() cdef int selfdeg = self._poly.degree() - if isinstance(G,MPolynomialIdeal): + if isinstance(G, MPolynomialIdeal): gI = G else: - gI = P._reductor_(G,selfdeg) #C.ideal(g,coerce=False) + gI = P._reductor_(G, selfdeg) # C.ideal(g,coerce=False) from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() - bck = (libsingular_options['redTail'],libsingular_options['redSB']) + bck = (libsingular_options['redTail'], libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True - poly = poly_reduce(C(self._poly),gI, ring=C, - attributes={gI:{"isSB":1}}) + poly = poly_reduce(C(self._poly), gI, ring=C, + attributes={gI: {"isSB": 1}}) libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] - return FreeAlgebraElement_letterplace(P,poly,check=False) + return FreeAlgebraElement_letterplace(P, poly, check=False) - def normal_form(self,I): + def normal_form(self, I): """ Return the normal form of this element with respect to a twosided weighted homogeneous ideal. @@ -728,10 +724,10 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): The normal form of `x` wrt. `I`. - NOTE: + .. NOTE:: - The normal form is computed by reduction with respect - to a Groebnerbasis of `I` with degree bound `deg(x)`. + The normal form is computed by reduction with respect + to a Groebnerbasis of `I` with degree bound `deg(x)`. EXAMPLES:: diff --git a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx index 7c83201cedb..0e1e47efc11 100644 --- a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx @@ -3,7 +3,7 @@ # Copyright (C) 2011 Simon King <simon.king@uni-jena.de> # Distributed under the terms of the GNU General Public License (GPL), # version 2 or any later version. The full text of the GPL is available at: -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ############################################################################### @@ -133,13 +133,13 @@ freeAlgebra = singular_function("freeAlgebra") # unfortunately we cannot set Singular attributes for MPolynomialRing_libsingular # Hence, we must constantly work around Letterplace's sanity checks, # and cannot use the following library functions: -#set_letterplace_attributes = singular_function("setLetterplaceAttributes") -#lpMult = singular_function("lpMult") +# set_letterplace_attributes = singular_function("setLetterplaceAttributes") +# lpMult = singular_function("lpMult") ##################### -# Auxiliar functions +# Auxiliary functions -cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks): +cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring, blocks): """ Create a polynomial ring in block order. @@ -177,7 +177,6 @@ cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks): Block term order with blocks: (Lexicographic term order of length 3, Lexicographic term order of length 3) - """ n = base_ring.ngens() T0 = base_ring.term_order() @@ -185,11 +184,11 @@ cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks): cdef i cdef tuple names0 = base_ring.variable_names() cdef list names = list(names0) - for i from 1<=i<blocks: + for i in range(1, blocks): T += T0 - names.extend([x+'_'+str(i) for x in names0]) + names.extend([x + '_' + str(i) for x in names0]) return PolynomialRing(base_ring.base_ring(), names, order=T, - implementation="singular") + implementation="singular") ##################### @@ -270,10 +269,10 @@ cdef class FreeAlgebra_letterplace(Algebra): Algebra.__init__(self, base_ring, varnames, normalize=False, category=Algebras(base_ring)) self._commutative_ring = R - self._current_ring = make_letterplace_ring(R,1) + self._current_ring = make_letterplace_ring(R, 1) self._degbound = 1 if degrees is None: - self._degrees = tuple([int(1)]*self.__ngens) + self._degrees = tuple([int(1)] * self.__ngens) else: if (not isinstance(degrees, (tuple, list))) \ or len(degrees) != self.__ngens - self._nb_slackvars \ @@ -291,12 +290,13 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace') sage: loads(dumps(F)) is F # indirect doctest True - """ from sage.algebras.free_algebra import FreeAlgebra - if self._nb_slackvars==0: - return FreeAlgebra,(self._commutative_ring,) - return FreeAlgebra,(self._commutative_ring,None,None,None,None,None,None,None,self._degrees) + if self._nb_slackvars == 0: + return FreeAlgebra, (self._commutative_ring,) + return FreeAlgebra, (self._commutative_ring, None, None, None, + None, None, None, None, self._degrees) + # Small methods def ngens(self): """ @@ -307,10 +307,10 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace') sage: F.ngens() 3 - """ - return self.__ngens-self._nb_slackvars - def gen(self,i): + return self.__ngens - self._nb_slackvars + + def gen(self, i): """ Return the `i`-th generator. @@ -329,21 +329,21 @@ cdef class FreeAlgebra_letterplace(Algebra): True sage: F.gen(2) c - """ - if i>=self.__ngens-self._nb_slackvars: + if i >= self.__ngens - self._nb_slackvars: raise ValueError("this free algebra only has %d generators" % (self.__ngens - self._nb_slackvars)) if self._gens is not None: return self._gens[i] deg = self._degrees[i] - #self.set_degbound(deg) + # self.set_degbound(deg) p = self._current_ring.gen(i) cdef int n - cdef int j = self.__ngens-1 - for n from 1<=n<deg: + cdef int j = self.__ngens - 1 + for n in range(1, deg): j += self.__ngens p *= self._current_ring.gen(j) return FreeAlgebraElement_letterplace(self, p) + def current_ring(self): """ Return the commutative ring that is used to emulate @@ -361,9 +361,9 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.set_degbound(3) sage: F.current_ring() Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1, a_2, b_2, c_2 over Rational Field - """ return self._current_ring + def commutative_ring(self): """ Return the commutative version of this free algebra. @@ -382,9 +382,9 @@ cdef class FreeAlgebra_letterplace(Algebra): Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2 sage: FreeAlgebra(F.commutative_ring()) is F True - """ return self._commutative_ring + def term_order_of_block(self): """ Return the term order that is used for the commutative version of this free algebra. @@ -397,7 +397,6 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace',order='lex') sage: L.term_order_of_block() Lexicographic term order - """ return self._commutative_ring.term_order() @@ -416,27 +415,25 @@ cdef class FreeAlgebra_letterplace(Algebra): False sage: FreeAlgebra(QQ, implementation='letterplace', names=['x']).is_commutative() True - """ - return self.__ngens-self._nb_slackvars <= 1 + return self.__ngens - self._nb_slackvars <= 1 def is_field(self, proof=True): """ Tell whether this free algebra is a field. - NOTE: + .. NOTE:: - This would only be the case in the degenerate case of no generators. - But such an example cannot be constructed in this implementation. + This would only be the case in the degenerate case of no generators. + But such an example cannot be constructed in this implementation. TESTS:: sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: F.is_field() False - """ - return (not (self.__ngens-self._nb_slackvars)) and self._base.is_field(proof=proof) + return (not (self.__ngens - self._nb_slackvars)) and self._base.is_field(proof=proof) def _repr_(self): """ @@ -451,10 +448,8 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3]) sage: F Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field - - """ - return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens-self._nb_slackvars,self.gens(),self._base) + return "Free Associative Unital Algebra on %d generators %s over %s" % (self.__ngens - self._nb_slackvars, self.gens(), self._base) def _latex_(self): r""" @@ -467,17 +462,17 @@ cdef class FreeAlgebra_letterplace(Algebra): \Bold{Q}\langle \mathit{bla}, \alpha, z\rangle """ from sage.misc.latex import latex - return "%s\\langle %s\\rangle"%(latex(self.base_ring()),', '.join(self.latex_variable_names())) + return "%s\\langle %s\\rangle" % (latex(self.base_ring()), ', '.join(self.latex_variable_names())) def degbound(self): """ Return the degree bound that is currently used. - NOTE: + .. NOTE:: - When multiplying two elements of this free algebra, the degree - bound will be dynamically adapted. It can also be set by - :meth:`set_degbound`. + When multiplying two elements of this free algebra, the degree + bound will be dynamically adapted. It can also be set by + :meth:`set_degbound`. EXAMPLES: @@ -495,16 +490,16 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.set_degbound(4) sage: F.degbound() 4 - """ return self._degbound - def set_degbound(self,d): + + def set_degbound(self, d): """ Increase the degree bound that is currently in place. - NOTE: + .. NOTE:: - The degree bound cannot be decreased. + The degree bound cannot be decreased. EXAMPLES: @@ -525,19 +520,18 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.set_degbound(2) sage: F.degbound() 4 - """ - if d<=self._degbound: + if d <= self._degbound: return self._degbound = d - self._current_ring = make_letterplace_ring(self._commutative_ring,d) + self._current_ring = make_letterplace_ring(self._commutative_ring, d) # def base_extend(self, R): # if self._base.has_coerce_map_from(R): # return self ################################################ - ## Ideals + # Ideals def _ideal_class_(self, n=0): """ @@ -551,7 +545,6 @@ cdef class FreeAlgebra_letterplace(Algebra): Right Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field sage: type(I) is F._ideal_class_() True - """ from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal return LetterplaceIdeal @@ -567,7 +560,6 @@ cdef class FreeAlgebra_letterplace(Algebra): Monoid of ideals of Free Associative Unital Algebra on 2 generators (x, y) over Finite Field of size 2 sage: F.ideal_monoid() is F.ideal_monoid() True - """ if self.__monoid is None: self.__monoid = IdealMonoid_nc(self) @@ -591,16 +583,15 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace sage: P = F.commutative_ring() sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3) # indirect doctest - <repr(<sage.algebras.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace at 0x...>) failed: NotImplementedError: + <repr(<sage.algebras.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace at 0x...>) failed: NotImplementedError: Apparently you tried to view the letterplace algebra with shift-multiplication as the free algebra over a finitely generated free abelian monoid. In principle, this is correct, but it is not implemented, yet.> - """ cdef int ngens = self.__ngens - cdef int nblocks = len(E)/ngens - cdef int i,j,base, exp, var_ind + cdef int nblocks = len(E) // ngens + cdef int i, j, base, exp, var_ind cdef list out = [] cdef list tmp for i from 0<=i<nblocks: @@ -609,7 +600,7 @@ cdef class FreeAlgebra_letterplace(Algebra): if not tmp: continue var_ind, exp = tmp[0] - if len(tmp)>1 or exp>1: + if len(tmp) > 1 or exp > 1: raise NotImplementedError("\n Apparently you tried to view the letterplace algebra with\n shift-multiplication as the free algebra over a finitely\n generated free abelian monoid.\n In principle, this is correct, but it is not implemented, yet.") out.append(self._names[var_ind]) @@ -631,8 +622,8 @@ cdef class FreeAlgebra_letterplace(Algebra): \left(2 z + 1\right) a b a b + \left(z + 1\right) a b c + \left(z + 1\right) c a b - c c """ cdef int ngens = self.__ngens - cdef int nblocks = len(E)/ngens - cdef int i,j,base, exp, var_ind + cdef int nblocks = len(E) // ngens + cdef int i, j, base, exp, var_ind cdef list out = [] cdef list tmp cdef list names = self.latex_variable_names() @@ -642,11 +633,11 @@ cdef class FreeAlgebra_letterplace(Algebra): if not tmp: continue var_ind, exp = tmp[0] - if len(tmp)>1 or exp>1: + if len(tmp) > 1 or exp > 1: raise NotImplementedError("\n Apparently you tried to view the letterplace algebra with\n shift-multiplication as the free algebra over a finitely\n generated free abelian monoid.\n In principle, this is correct, but it is not implemented, yet.") out.append(names[var_ind]) - i += (self._degrees[var_ind]-1) + i += (self._degrees[var_ind] - 1) return ' '.join(out) def _reductor_(self, g, d): @@ -686,7 +677,6 @@ cdef class FreeAlgebra_letterplace(Algebra): y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2 sage: p.reduce(I).letterplace_polynomial() == q True - """ cdef list out = [] C = self.current_ring() @@ -697,12 +687,13 @@ cdef class FreeAlgebra_letterplace(Algebra): from sage.groups.perm_gps.all import CyclicPermutationGroup CG = CyclicPermutationGroup(C.ngens()) for y in G: - out.extend([y]+[y * CG[ngens*(n+1)] for n in xrange(d-y.degree())]) + out.extend([y] + [y * CG[ngens * (n + 1)] + for n in range(d - y.degree())]) return C.ideal(out) ########################### - ## Coercion - cpdef _coerce_map_from_(self,S): + # Coercion + cpdef _coerce_map_from_(self, S): """ A ring ``R`` coerces into self, if @@ -729,7 +720,7 @@ cdef class FreeAlgebra_letterplace(Algebra): t*x """ - if self==S or self._current_ring.has_coerce_map_from(S): + if self == S or self._current_ring.has_coerce_map_from(S): return True cdef int i # Do we have another letterplace algebra? @@ -747,7 +738,7 @@ cdef class FreeAlgebra_letterplace(Algebra): # Do the degrees match degs = self._degrees Sdegs = (<FreeAlgebra_letterplace>S)._degrees - for i from 0<=i<S.ngens(): + for i in range(S.ngens()): if degs[names.index(Snames[i])] != Sdegs[i]: return False return True @@ -761,7 +752,6 @@ cdef class FreeAlgebra_letterplace(Algebra): sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: F.an_element() # indirect doctest x - """ return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False) @@ -825,11 +815,11 @@ cdef class FreeAlgebra_letterplace(Algebra): l = len(e) break cdef dict out = {} - self.set_degbound(l/self.__ngens) + self.set_degbound(l // self.__ngens) cdef Py_ssize_t n = self._current_ring.ngens() for e, c in D.iteritems(): - out[tuple(e) + (0,)*(n-l)] = c - return FreeAlgebraElement_letterplace(self,self._current_ring(out), + out[tuple(e) + (0,) * (n - l)] = c + return FreeAlgebraElement_letterplace(self, self._current_ring(out), check=check) def _element_constructor_(self, x): @@ -875,7 +865,7 @@ cdef class FreeAlgebra_letterplace(Algebra): """ if isinstance(x, basestring): from sage.misc.sage_eval import sage_eval - return sage_eval(x,locals=self.gens_dict()) + return sage_eval(x, locals=self.gens_dict()) try: P = x.parent() except AttributeError: @@ -890,9 +880,10 @@ cdef class FreeAlgebra_letterplace(Algebra): Names = self._current_ring.variable_names() PNames = list(Ppoly.variable_names()) # translate the slack variables - PNames[P.ngens(): len(PNames): P.ngens()+1] = list(Names[self.ngens(): len(Names): self.ngens()+1])[:P.degbound()] + PNames[P.ngens(): len(PNames): P.ngens() + 1] = list(Names[self.ngens(): len(Names): self.ngens() + 1])[:P.degbound()] x = Ppoly.hom([Gens[Names.index(asdf)] for asdf in PNames])(x.letterplace_polynomial()) - return FreeAlgebraElement_letterplace(self,self._current_ring(x)) + return FreeAlgebraElement_letterplace(self, self._current_ring(x)) + cdef class FreeAlgebra_letterplace_libsingular(): """ diff --git a/src/sage/algebras/letterplace/letterplace_ideal.pyx b/src/sage/algebras/letterplace/letterplace_ideal.pyx index 56ec1c3d399..747ba2ca6e0 100644 --- a/src/sage/algebras/letterplace/letterplace_ideal.pyx +++ b/src/sage/algebras/letterplace/letterplace_ideal.pyx @@ -1,9 +1,8 @@ """ Homogeneous ideals of free algebras -For twosided ideals and when the base ring is a field, this -implementation also provides Groebner bases and ideal containment -tests. +For twosided ideals and when the base ring is a field, this implementation +also provides Groebner bases and ideal containment tests. EXAMPLES:: @@ -31,9 +30,7 @@ forms and can test containment in the ideal:: AUTHOR: - Simon King (2011-03-22): See :trac:`7797`. - """ - # **************************************************************************** # Copyright (C) 2011 Simon King <simon.king@uni-jena.de> # @@ -43,7 +40,6 @@ AUTHOR: # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.rings.noncommutative_ideals import Ideal_nc from sage.libs.singular.function import lib, singular_function from sage.algebras.letterplace.free_algebra_letterplace cimport FreeAlgebra_letterplace, FreeAlgebra_letterplace_libsingular @@ -53,8 +49,9 @@ from sage.rings.infinity import Infinity ##################### # Define some singular functions lib("freegb.lib") -singular_twostd=singular_function("twostd") -poly_reduce=singular_function("NF") +singular_twostd = singular_function("twostd") +poly_reduce = singular_function("NF") + class LetterplaceIdeal(Ideal_nc): """ @@ -159,7 +156,6 @@ class LetterplaceIdeal(Ideal_nc): 0 sage: (z*I.0-x*y*z).normal_form(I) -y*x*z + z*z - """ def __init__(self, ring, gens, coerce=True, side="twosided"): """ @@ -198,6 +194,7 @@ class LetterplaceIdeal(Ideal_nc): Ideal_nc.__init__(self, ring, gens, coerce=coerce, side=side) self.__GB = self self.__uptodeg = 0 + def groebner_basis(self, degbound=None): """ Twosided Groebner basis with degree bound. @@ -287,17 +284,18 @@ class LetterplaceIdeal(Ideal_nc): return self.__GB if not A.base().is_field(): raise TypeError("Currently, we can only compute Groebner bases if the ring of coefficients is a field") - if self.side()!='twosided': + if self.side() != 'twosided': raise TypeError("This ideal is not two-sided. We can only compute two-sided Groebner bases") if degbound == Infinity: - while self.__uptodeg<Infinity: - test_bound = 2*max([x._poly.degree() for x in self.__GB.gens()]) + while self.__uptodeg < Infinity: + test_bound = 2 * max([x._poly.degree() + for x in self.__GB.gens()]) self.groebner_basis(test_bound) return self.__GB # Set the options required by letterplace from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() - bck = (libsingular_options['redTail'],libsingular_options['redSB']) + bck = (libsingular_options['redTail'], libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True A.set_degbound(degbound) @@ -330,14 +328,14 @@ class LetterplaceIdeal(Ideal_nc): libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] - self.__GB = A.ideal(out,side='twosided',coerce=False) - if degbound >= 2*max([x._poly.degree() for x in out]): + self.__GB = A.ideal(out, side='twosided', coerce=False) + if degbound >= 2 * max([x._poly.degree() for x in out]): degbound = Infinity self.__uptodeg = degbound self.__GB.__uptodeg = degbound return self.__GB - def __contains__(self,x): + def __contains__(self, x): """ The containment test is based on a normal form computation. @@ -349,7 +347,6 @@ class LetterplaceIdeal(Ideal_nc): True sage: 1 in I False - """ R = self.ring() return (x in R) and R(x).normal_form(self).is_zero() @@ -384,25 +381,26 @@ class LetterplaceIdeal(Ideal_nc): Twosided Ideal (y*z, x*x - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field sage: I.reduce(F*[x^2+x*y,y^2+y*z]*F) Twosided Ideal (x*y + y*z, -y*x + y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field - """ P = self.ring() - if not isinstance(G,(list,tuple)): - if G==P: + if not isinstance(G, (list, tuple)): + if G == P: return P.ideal([P.zero()]) if G in P: return G.normal_form(self) G = G.gens() C = P.current_ring() - sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], coerce=False) + sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], + coerce=False) selfdeg = max([x.degree() for x in sI.gens()]) gI = P._reductor_(G, selfdeg) from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() - bck = (libsingular_options['redTail'],libsingular_options['redSB']) + bck = (libsingular_options['redTail'], libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True - sI = poly_reduce(sI,gI, ring=C, attributes={gI:{"isSB":1}}) + sI = poly_reduce(sI, gI, ring=C, attributes={gI: {"isSB": 1}}) libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] - return P.ideal([FreeAlgebraElement_letterplace(P,x,check=False) for x in sI], coerce=False) + return P.ideal([FreeAlgebraElement_letterplace(P, x, check=False) + for x in sI], coerce=False) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 1ae47fa7a31..39f7be6c856 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -1332,7 +1332,7 @@ def _unicode_art_(self): from sage.typeset.unicode_art import unicode_art return unicode_art(self._combined_matrix()) - def __bool__(self): + def __bool__(self) -> bool: r""" Return if ``self`` is nonzero. @@ -1346,8 +1346,6 @@ def __bool__(self): """ return bool(self._real) or bool(self._imag) - - def __hash__(self): r""" Return the hash of ``self``. diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index d466856a7b9..2017ad3c6d2 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -41,7 +41,7 @@ from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra as ClassicalMatrix -# the next 6 lines are here to silent pyflakes and lgtm warnings +# the next 6 lines are here to silence pyflakes warnings assert VirasoroAlgebra assert RankTwoHeisenbergVirasoro assert OnsagerAlgebra @@ -97,6 +97,7 @@ def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): s_coeff = {(X,Y): {Z:a, Y:d}, (Y,Z): {X:b}, (Z,X): {Y:c, Z:d}} return LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + def cross_product(R, names=['X', 'Y', 'Z']): r""" The Lie algebra of `\RR^3` defined by the usual cross product @@ -160,7 +161,7 @@ def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): return AbelianLieAlgebra(R, names=names) if n == 1: - L = three_dimensional(R, 0, 1, 0, 0, names=names) # Strictly upper triangular matrices + L = three_dimensional(R, 0, 1, 0, 0, names=names) # Strictly upper triangular matrices L.rename("Lie algebra of 3x3 strictly upper triangular matrices over {}".format(R)) return L @@ -184,18 +185,19 @@ def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): return L if n == 3: - #return sl(R, 2) + # return sl(R, 2) from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients E = names[0] F = names[1] H = names[2] - s_coeff = { (E,F): {H:R.one()}, (H,E): {E:R(2)}, (H,F): {F:R(-2)} } + s_coeff = {(E, F): {H: R.one()}, (H, E): {E: R(2)}, (H, F): {F: R(-2)}} L = LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) L.rename("sl2 over {}".format(R)) return L raise ValueError("Invalid rank") + def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): """ The Lie algebra of affine transformations of the line. @@ -233,6 +235,7 @@ def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): L.rename("Lie algebra of affine transformations of a line over {}".format(R)) return L + def abelian(R, names=None, index_set=None): """ Return the abelian Lie algebra generated by ``names``. @@ -365,6 +368,7 @@ def upper_triangular_matrices(R, n): L.rename("Lie algebra of {}-dimensional upper triangular matrices over {}".format(n, L.base_ring())) return L + def strictly_upper_triangular_matrices(R, n): r""" Return the Lie algebra `\mathfrak{n}_k` of strictly `k \times k` upper @@ -401,13 +405,13 @@ def strictly_upper_triangular_matrices(R, n): MS = MatrixSpace(R, n, sparse=True) one = R.one() names = tuple('n{}'.format(i) for i in range(n-1)) - gens = tuple(MS({(i,i+1):one}) for i in range(n-1)) + gens = tuple(MS({(i,i+1): one}) for i in range(n-1)) L = LieAlgebraFromAssociative(MS, gens, names=names) L.rename("Lie algebra of {}-dimensional strictly upper triangular matrices over {}".format(n, L.base_ring())) return L ##################################################################### -## Classical Lie algebras +# Classical Lie algebras def sl(R, n, representation='bracket'): @@ -511,6 +515,7 @@ def su(R, n, representation='matrix'): return MatrixCompactRealForm(R, CartanType(['A', n-1])) raise ValueError("invalid representation") + def so(R, n, representation='bracket'): r""" The Lie algebra `\mathfrak{so}_n`. diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index bb2859ba6bd..ffe27c69797 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -1997,4 +1997,3 @@ cdef class LyndonBracket(GradedLieBracket): if self._hash == -1: self._hash = hash(self._index_word) return self._hash - diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 9f4500d28b4..35863aece41 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -110,23 +110,23 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, sage: V = lie_conformal_algebras.Virasoro(QQ) sage: TestSuite(V).run() """ - is_super = kwds.get('super',None) + is_super = kwds.get('super', None) default_category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated().Graded() if is_super or parity: category = default_category.Super().or_subcategory(category) else: category = default_category.or_subcategory(category) - LieConformalAlgebraWithStructureCoefficients.__init__(self,R, - s_coeff,index_set=index_set,central_elements=central_elements, + LieConformalAlgebraWithStructureCoefficients.__init__(self, R, + s_coeff, index_set=index_set, central_elements=central_elements, category=category, prefix=prefix, names=names, latex_names=latex_names, parity=parity, **kwds) if weights is None: - weights = (1,)* (len(self._generators) - - len(self.central_elements())) - if len (weights) != (len(self._generators) - - len(self.central_elements())): - raise ValueError("weights and (non-central) generator lists "\ + weights = (1,) * (len(self._generators) - + len(self.central_elements())) + if len(weights) != (len(self._generators) - + len(self.central_elements())): + raise ValueError("weights and (non-central) generator lists " "must be of same length") self._weights = weights diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index b11c912f36d..c40e654e157 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -14,7 +14,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.arith.all import factorial from sage.misc.misc_c import prod from sage.misc.repr import repr_lincomb @@ -27,7 +26,7 @@ class LCAWithGeneratorsElement(IndexedFreeModuleElement): The element class of a Lie conformal algebra with a preferred set of generators. """ - def T(self,n=1): + def T(self, n=1): r""" The n-th derivative of this element. @@ -135,7 +134,7 @@ def _bracket_(self, right): /factorial(m+k+j-l)/factorial(l-k-j)/factorial(j)*\ mbr[j].T(m+k+j-l) for j in mbr if j >= l-m-k and\ j <= l-k) for l in range(m+k+pole+1)} - return {k:v for k,v in ret.items() if v} + return {k: v for k, v in ret.items() if v} diclist = [i._bracket_(j) for i in self.terms() for j in right.terms()] @@ -143,8 +142,8 @@ def _bracket_(self, right): pz = p.zero() for d in diclist: for k in d: - ret[k] = ret.get(k,pz) + d[k] - return {k:v for k,v in ret.items() if v} + ret[k] = ret.get(k, pz) + d[k] + return {k: v for k, v in ret.items() if v} def _repr_(self): r""" @@ -172,17 +171,15 @@ def _repr_(self): return "0" p = self.parent() if p._names: - terms = [("T^({0}){1}".format(k[1], - p._names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else("T{}".format(p._names[p._index_to_pos[k[0]]]),v) \ - if k[1] == 1 \ - else ("{}".format(p._names[p._index_to_pos[k[0]]]),v)\ - for k,v in self.monomial_coefficients().items()] + terms = [("T^({}){}".format(k1, p._names[p._index_to_pos[k0]]), v) if k1 > 1 + else ("T{}".format(p._names[p._index_to_pos[k0]]), v) if k1 == 1 + else ("{}".format(p._names[p._index_to_pos[k0]]), v) + for (k0, k1), v in self.monomial_coefficients().items()] else: - terms = [("T^({0}){1}".format(k[1], p._repr_generator(k[0])),v)\ - if k[1] > 1 else("T{}".format(p._repr_generator(k[0])),v)\ - if k[1] == 1 else ("{}".format(p._repr_generator(k[0])), - v) for k,v in self.monomial_coefficients().items()] + terms = [("T^({}){}".format(k1, p._repr_generator(k0)), v) if k1 > 1 + else ("T{}".format(p._repr_generator(k0)), v) if k1 == 1 + else ("{}".format(p._repr_generator(k0)), v) + for (k0, k1), v in self.monomial_coefficients().items()] return repr_lincomb(terms, strip_one=True) @@ -222,16 +219,14 @@ def _latex_(self): except ValueError: names = None if names: - terms = [("T^{{({0})}}{1}".format(k[1], - names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else("T{}".format(names[p._index_to_pos[k[0]]]),v)\ - if k[1] == 1\ - else ("{}".format(names[p._index_to_pos[k[0]]]),v)\ - for k, v in self.monomial_coefficients().items()] + terms = [("T^{{({0})}}{1}".format(k1, names[p._index_to_pos[k0]]), v) if k1 > 1 + else ("T{}".format(names[p._index_to_pos[k0]]), v) if k1 == 1 + else ("{}".format(names[p._index_to_pos[k0]]), v) + for (k0, k1), v in self.monomial_coefficients().items()] else: - terms = [("T^{{({0})}}{1}".format(k[1], latex(k[0])),v) if k[1] > 1 \ - else("T{}".format(latex(k[0])),v) if k[1] == 1 \ - else ("{}".format(latex(k[0])),v)\ - for k, v in self.monomial_coefficients().items()] + terms = [("T^{{({0})}}{1}".format(k1, latex(k0)), v) if k1 > 1 + else ("T{}".format(latex(k0)), v) if k1 == 1 + else ("{}".format(latex(k0)), v) + for (k0, k1), v in self.monomial_coefficients().items()] return repr_lincomb(terms, is_latex=True, strip_one=True) diff --git a/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py index 5ee696bee17..23af0d3f7dd 100644 --- a/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py @@ -19,18 +19,19 @@ - Reimundo Heluani (2020-06-03): Initial implementation. """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2020 Reimundo Heluani <heluani@potuz.net> # # 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra + class N2LieConformalAlgebra(GradedLieConformalAlgebra): """ The N=2 super Lie conformal algebra. @@ -70,7 +71,7 @@ class N2LieConformalAlgebra(GradedLieConformalAlgebra): sage: G.bracket(G) {0: 2*L, 2: 2/3*C} """ - def __init__(self,R): + def __init__(self, R): """ Initialize self. @@ -79,24 +80,25 @@ def __init__(self,R): sage: V = lie_conformal_algebras.N2(QQ) sage: TestSuite(V).run() """ - n2dict =\ - {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, - 3:{('C', 0):R(2).inverse_of_unit()}}, - ('L','G1'):{0:{('G1',1):1}, 1:{('G1',0):3*R(2).\ - inverse_of_unit()}}, - ('L','G2'):{0:{('G2',1):1}, 1:{('G2',0):3*R(2).\ - inverse_of_unit()}}, - ('G1','G2'): {0:{('L',0):1,('J',1):R(2).inverse_of_unit()}, - 1:{('J',0):1}, 2:{('C',0):R(3).inverse_of_unit()}}, - ('L','J'): {0:{('J',1):1},1:{('J',0):1}}, - ('J','J'): {1:{('C',0):R(3).inverse_of_unit()}}, - ('J','G1'): {0:{('G1',0):1}}, - ('J','G2'): {0:{('G2',0):-1}}} + n2dict = {('L', 'L'): {0: {('L', 1): 1}, + 1: {('L', 0): 2}, + 3: {('C', 0): R(2).inverse_of_unit()}}, + ('L', 'G1'): {0: {('G1', 1): 1}, + 1: {('G1', 0): 3 * R(2).inverse_of_unit()}}, + ('L', 'G2'): {0: {('G2', 1): 1}, + 1: {('G2', 0): 3 * R(2).inverse_of_unit()}}, + ('G1', 'G2'): {0: {('L', 0): 1, ('J', 1): R(2).inverse_of_unit()}, + 1: {('J', 0): 1}, + 2: {('C', 0): R(3).inverse_of_unit()}}, + ('L', 'J'): {0: {('J', 1): 1}, 1: {('J', 0): 1}}, + ('J', 'J'): {1: {('C', 0): R(3).inverse_of_unit()}}, + ('J', 'G1'): {0: {('G1', 0): 1}}, + ('J', 'G2'): {0: {('G2', 0): -1}}} from sage.rings.rational_field import QQ - weights = (2,1,QQ(3/2),QQ(3/2)) - parity = (0,0,1,1) - GradedLieConformalAlgebra.__init__(self,R,n2dict, - names=('L', 'J','G1','G2'), + weights = (2, 1, QQ(3) / 2, QQ(3) / 2) + parity = (0, 0, 1, 1) + GradedLieConformalAlgebra.__init__(self, R, n2dict, + names=('L', 'J', 'G1', 'G2'), central_elements=('C',), weights=weights, parity=parity) @@ -108,7 +110,5 @@ def _repr_(self): sage: R = lie_conformal_algebras.N2(QQbar); R The N=2 super Lie conformal algebra over Algebraic Field - """ - return "The N=2 super Lie conformal algebra over {}".\ - format(self.base_ring()) + return f"The N=2 super Lie conformal algebra over {self.base_ring()}" diff --git a/src/sage/algebras/q_commuting_polynomials.py b/src/sage/algebras/q_commuting_polynomials.py index b772f04a57e..d1aae987d61 100644 --- a/src/sage/algebras/q_commuting_polynomials.py +++ b/src/sage/algebras/q_commuting_polynomials.py @@ -346,6 +346,5 @@ def product_on_basis(self, x, y): Ly = y.list() # This could be made more efficient - qpow = sum(exp * sum(self._B[j,i] * val for j, val in enumerate(Ly[:i])) for i,exp in enumerate(Lx)) + qpow = sum(exp * sum(self._B[j, i] * val for j, val in enumerate(Ly[:i])) for i, exp in enumerate(Lx)) return self.term(x * y, self._q ** qpow) - diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index f646b0c2a52..1e57682f255 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -948,8 +948,7 @@ def inverse(self): if any(p[i] != 0 for i in range(Cl._n)): return super().__invert__() tk = 2 * Cl._k - w = tuple([tk-val if val else 0 for val in w]) - return Cl.element_class(Cl, {(p, w) : coeff.inverse_of_unit()}) + w = tuple([tk - val if val else 0 for val in w]) + return Cl.element_class(Cl, {(p, w): coeff.inverse_of_unit()}) __invert__ = inverse - diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index 0a647ffee17..b901c3b8225 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -1402,7 +1402,7 @@ def monomial_coefficients(self, copy=True): data = [R(str(c)) for c in libgap.Coefficients(B, self._libgap)] return {i: c for i, c in enumerate(data) if c != 0} - def _vector_(self, R=None): + def _vector_(self, R=None, order=None, sparse=False): """ Return ``self`` as a vector. @@ -1419,11 +1419,29 @@ def _vector_(self, R=None): 1*v0 + F[a1]*v0 + (q^2)*F[a1]*F[a2]*v0 + (q)*F[a1+a2]*v0 sage: vector(x) # optional - gap_packages (1, 1, 0, q^2, q, 0, 0, 0) + + sage: v._vector_(sparse=True) # optional - gap_packages + (1, 0, 0, 0, 0, 0, 0, 0) + sage: x._vector_(sparse=True) # optional - gap_packages + (1, 1, 0, q^2, q, 0, 0, 0) + + sage: M = V.submodule([V.an_element()]) # optional - gap_packages + sage: M # optional - gap_packages + Free module generated by {0} over Fraction Field of Univariate Polynomial Ring in q over Rational Field """ V = self.parent()._dense_free_module(R) + if sparse: + V = V.sparse_module() + if order is None: + return V(self.monomial_coefficients()) + v = copy(V.zero()) - for i, c in self.monomial_coefficients().items(): - v[i] = c + if order is None: + for i, c in self.monomial_coefficients().items(): + v[i] = c + else: + for i, c in self.monomial_coefficients().items(): + v[order[i]] = c return v @@ -1917,6 +1935,21 @@ def highest_weight_decomposition(self): for wt, vecs in zip(*self._highest_weights_and_vectors) for v in vecs] + def tensor_factors(self): + r""" + Return the factors of ``self``. + + EXAMPLES:: + + sage: Q = QuantumGroup(['A',2]) # optional - gap_packages + sage: V = Q.highest_weight_module([1,0]) # optional - gap_packages + sage: T = tensor([V,V]) # optional - gap_packages + sage: T.tensor_factors() # optional - gap_packages + (Highest weight module of weight Lambda[1] of Quantum Group of type ['A', 2] with q=q, + Highest weight module of weight Lambda[1] of Quantum Group of type ['A', 2] with q=q) + """ + return self._modules + Element = QuaGroupRepresentationElement @@ -1934,7 +1967,11 @@ def __init__(self, ambient, gen, weight): sage: TestSuite(S).run() # optional - gap_packages """ self._ambient = ambient - cat = ambient.category() + # We do not use the generic ambient category since submodules of tensor + # products are considered to be tensor products. + # This should be reverted after this has changed. + #cat = ambient.category() + cat = Modules(ambient.base_ring()).FiniteDimensional().WithBasis() QuantumGroupModule.__init__(self, ambient._Q, cat.Subobjects()) self._gen = gen diff --git a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx index 88f65813eac..0d110aad7e3 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx @@ -260,5 +260,3 @@ def rational_quaternions_from_integral_matrix_and_denom(A, Matrix_integer_dense v.append(x) mpz_clear(tmp) return v - - diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 8feec0d6d98..49b281d9a49 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -505,7 +505,7 @@ def __classcall__(self, p=2, basis='milnor', **kwds): std_generic = generic if p != 2: std_generic = True - if not(std_generic is True or std_generic is False): + if not (std_generic is True or std_generic is False): raise ValueError("option 'generic' is not a boolean") std_basis = get_basis_name(basis, p, generic=std_generic) @@ -525,7 +525,9 @@ def __init__(self, p=2, basis='milnor', **kwds): - ``precision`` - (optional, default ``None``) - ``generic`` - (optional, default 'auto') - OUTPUT: mod `p` Steenrod algebra with basis, or a sub-Hopf + OUTPUT: + + mod `p` Steenrod algebra with basis, or a sub-Hopf algebra of the mod `p` Steenrod algebra defined by the given profile function. @@ -1036,8 +1038,9 @@ def homogeneous_component(self, n): - `n` - integer - OUTPUT: a vector space spanned by the basis for this algebra - in dimension `n` + OUTPUT: + + a vector space spanned by the basis for this algebra in dimension `n` EXAMPLES:: @@ -1128,7 +1131,9 @@ def product_on_basis(self, t1, t2): - ``t1``, ``t2`` -- tuples, the indices of two basis elements of self - OUTPUT: the product of the two corresponding basis elements, + OUTPUT: + + the product of the two corresponding basis elements, as an element of self ALGORITHM: If the two elements are represented in the Milnor @@ -1251,7 +1256,9 @@ def coproduct_on_basis(self, t, algorithm=None): Which of these methods is used is controlled by whether ``algorithm`` is 'milnor' or 'serre-cartan'. - OUTPUT: the coproduct of the corresponding basis element, + OUTPUT: + + the coproduct of the corresponding basis element, as an element of ``self`` tensor ``self``. EXAMPLES:: @@ -1433,7 +1440,9 @@ def antipode_on_basis(self, t): - ``t`` -- tuple, the index of a basis element of self - OUTPUT: the antipode of the corresponding basis element, + OUTPUT: + + the antipode of the corresponding basis element, as an element of self. ALGORITHM: according to a result of Milnor's, the antipode of @@ -2184,7 +2193,9 @@ def basis(self, d=None): - `d` -- integer or ``None``, optional (default ``None``) - OUTPUT: If `d` is ``None``, then return a basis of the algebra. + OUTPUT: + + If `d` is ``None``, then return a basis of the algebra. Otherwise, return the basis in degree `d`. EXAMPLES:: @@ -2301,7 +2312,9 @@ def P(self, *nums): - ``a, b, c, ...`` - non-negative integers - OUTPUT: element of the Steenrod algebra given by the Milnor + OUTPUT: + + element of the Steenrod algebra given by the Milnor single basis element `P(a, b, c, ...)` Note that at the prime 2, this is the same element as @@ -3794,7 +3807,9 @@ def SteenrodAlgebra(p=2, basis='milnor', generic='auto', **kwds): - ``precision`` - integer or ``None`` (optional, default ``None``) - ``generic`` - (optional, default 'auto') - OUTPUT: mod `p` Steenrod algebra or one of its sub-Hopf algebras, + OUTPUT: + + mod `p` Steenrod algebra or one of its sub-Hopf algebras, elements of which are printed using ``basis`` See below for information about ``basis``, ``profile``, etc. @@ -4166,7 +4181,9 @@ def AA(n=None, p=2): - `n` - non-negative integer, optional (default ``None``) - `p` - prime number, optional (default 2) - OUTPUT: If `n` is ``None``, then return the full Steenrod algebra. + OUTPUT: + + If `n` is ``None``, then return the full Steenrod algebra. Otherwise, return `A(n)`. When `p=2`, `A(n)` is the sub-Hopf algebra generated by the diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index 4cfbe2f83e4..f028e6d5df5 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -193,8 +193,9 @@ def convert_from_milnor_matrix(n, basis, p=2, generic='auto'): - ``p`` - positive prime number (optional, default 2) - OUTPUT: ``matrix`` - change-of-basis matrix, a square matrix over - GF(p) + OUTPUT: + + ``matrix`` - change-of-basis matrix, a square matrix over GF(p) .. note:: @@ -1082,6 +1083,7 @@ def sorting_pair(s,t,basis): # pair used for sorting the basis result.append((tuple(q_mono), tuple(p_mono))) return tuple(result) + ############################################################################# def steenrod_basis_error_check(dim, p, **kwds): """ diff --git a/src/sage/algebras/steenrod/steenrod_algebra_misc.py b/src/sage/algebras/steenrod/steenrod_algebra_misc.py index 850081f6e67..d8fedad70b8 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_misc.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_misc.py @@ -311,7 +311,9 @@ def normalize_profile(profile, precision=None, truncation_type='auto', p=2, gene - `p` - prime, optional, default 2 - `generic` - boolean, optional, default ``None`` - OUTPUT: a triple ``profile, precision, truncation_type``, in + OUTPUT: + + a triple ``profile, precision, truncation_type``, in standard form as described below. The "standard form" is as follows: ``profile`` should be a tuple @@ -638,6 +640,7 @@ def milnor_mono_to_string(mono, latex=False, generic=False): string = string + ")" return string.strip(" ") + def serre_cartan_mono_to_string(mono, latex=False, generic=False): r""" String representation of element of the Serre-Cartan basis. @@ -730,7 +733,9 @@ def wood_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq^{2^s (2^{t+1}-1)}`` for each pair (s,t) EXAMPLES:: @@ -773,8 +778,9 @@ def wall_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings ``Q^{m}_{k}`` for - each pair (m,k) + OUTPUT: + + ``string`` - concatenation of strings ``Q^{m}_{k}`` for each pair (m,k) EXAMPLES:: @@ -812,8 +818,9 @@ def wall_long_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``Sq^(2^m)`` + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq^(2^m)`` EXAMPLES:: @@ -855,8 +862,10 @@ def arnonA_mono_to_string(mono, latex=False, p=2): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``X^{m}_{k}`` for each pair (m,k) + OUTPUT: + + ``string`` - concatenation of strings of the form ``X^{m}_{k}`` + for each pair (m,k) EXAMPLES:: @@ -894,8 +903,9 @@ def arnonA_long_mono_to_string(mono, latex=False, p=2): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``Sq(2^m)`` + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq(2^m)`` EXAMPLES:: @@ -939,8 +949,10 @@ def pst_mono_to_string(mono, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form - ``P^{s}_{t}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``P^{s}_{t}`` + for each pair (s,t) EXAMPLES:: @@ -999,8 +1011,10 @@ def comm_mono_to_string(mono, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form - ``c_{s,t}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``c_{s,t}`` + for each pair (s,t) EXAMPLES:: @@ -1059,8 +1073,10 @@ def comm_long_mono_to_string(mono, p, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form ``s_{2^s - ... 2^(s+t-1)}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``s_{2^s... 2^(s+t-1)}`` + for each pair (s,t) EXAMPLES:: @@ -1121,7 +1137,9 @@ def convert_perm(m): - ``m`` - tuple of non-negative integers with no repetitions - OUTPUT: ``list`` - conversion of ``m`` to a permutation of the set + OUTPUT: + + ``list`` - conversion of ``m`` to a permutation of the set 1,2,...,len(m) If ``m=(3,7,4)``, then one can view ``m`` as representing the diff --git a/src/sage/algebras/yangian.py b/src/sage/algebras/yangian.py index b63648bbb95..55bb61fccf5 100644 --- a/src/sage/algebras/yangian.py +++ b/src/sage/algebras/yangian.py @@ -19,10 +19,9 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod -from sage.structure.unique_representation import UniqueRepresentation from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis -from sage.rings.integer_ring import ZZ +from sage.categories.cartesian_product import cartesian_product from sage.rings.infinity import infinity from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.sets.family import Family @@ -31,157 +30,6 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.algebras.associated_graded import AssociatedGradedAlgebra -import itertools - - -class GeneratorIndexingSet(UniqueRepresentation): - """ - Helper class for the indexing set of the generators. - """ - def __init__(self, index_set, level=None): - """ - Initialize ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - """ - self._index_set = index_set - self._level = level - - def __repr__(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: GeneratorIndexingSet((1,2)) - Cartesian product of Positive integers, (1, 2), (1, 2) - sage: GeneratorIndexingSet((1,2), 4) - Cartesian product of (1, 2, 3, 4), (1, 2), (1, 2) - """ - if self._level is None: - L = PositiveIntegers() - else: - L = tuple(range(1, self._level + 1)) - return "Cartesian product of {L}, {I}, {I}".format(L=L, I=self._index_set) - - def an_element(self): - """ - Initialize ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - sage: I.an_element() - (3, 1, 1) - sage: I = GeneratorIndexingSet((1,2), 5) - sage: I.an_element() - (3, 1, 1) - sage: I = GeneratorIndexingSet((1,2), 1) - sage: I.an_element() - (1, 1, 1) - """ - if self._level is not None and self._level < 3: - return (1, self._index_set[0], self._index_set[0]) - return (3, self._index_set[0], self._index_set[0]) - - def cardinality(self): - """ - Return the cardinality of ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - sage: I.cardinality() - +Infinity - sage: I = GeneratorIndexingSet((1,2), level=3) - sage: I.cardinality() == 3 * 2 * 2 - True - """ - if self._level is not None: - return self._level * len(self._index_set)**2 - return infinity - - __len__ = cardinality - - def __call__(self, x): - """ - Call ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - sage: I([1, 2]) - (1, 2) - """ - return tuple(x) - - def __contains__(self, x): - """ - Check containment of ``x`` in ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - sage: (4, 1, 2) in I - True - sage: [4, 2, 1] in I - True - sage: (-1, 1, 1) in I - False - sage: (1, 3, 1) in I - False - - :: - - sage: I3 = GeneratorIndexingSet((1,2), 3) - sage: (1, 1, 2) in I3 - True - sage: (3, 1, 1) in I3 - True - sage: (4, 1, 1) in I3 - False - """ - return (isinstance(x, (tuple, list)) and len(x) == 3 - and x[0] in ZZ and x[0] > 0 - and (self._level is None or x[0] <= self._level) - and x[1] in self._index_set - and x[2] in self._index_set) - - def __iter__(self): - """ - Iterate over ``self``. - - TESTS:: - - sage: from sage.algebras.yangian import GeneratorIndexingSet - sage: I = GeneratorIndexingSet((1,2)) - sage: it = iter(I) - sage: [next(it) for dummy in range(5)] - [(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1)] - - sage: I = GeneratorIndexingSet((1,2), 3) - sage: list(I) - [(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), - (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2), - (3, 1, 1), (3, 1, 2), (3, 2, 1), (3, 2, 2)] - """ - I = self._index_set - if self._level is not None: - for x in itertools.product(range(1, self._level + 1), I, I): - yield x - return - for i in PositiveIntegers(): - for x in itertools.product(I, I): - yield (i, x[0], x[1]) - class Yangian(CombinatorialFreeModule): r""" @@ -392,7 +240,7 @@ def __init__(self, base_ring, n, variable_name, filtration): category = category.Connected() self._index_set = tuple(range(1, n + 1)) # The keys for the basis are tuples (l, i, j) - indices = GeneratorIndexingSet(self._index_set) + indices = cartesian_product([PositiveIntegers(), self._index_set, self._index_set]) # We note that the generators are non-commutative, but we always sort # them, so they are, in effect, indexed by the free abelian monoid basis_keys = IndexedFreeAbelianMonoid(indices, bracket=False, @@ -501,7 +349,7 @@ def _element_constructor_(self, x): True sage: Y6 = Yangian(QQ, 4, level=6, filtration='natural') sage: Y(Y6.an_element()) - t(1)[1,1]*t(1)[1,2]^2*t(1)[1,3]^3*t(3)[1,1] + t(1)[1,1]^2*t(1)[1,2]^2*t(1)[1,3]^3 + 2*t(1)[1,1] + 3*t(1)[1,2] + 1 """ if isinstance(x, CombinatorialFreeModule.Element): if isinstance(x.parent(), Yangian) and x.parent()._n <= self._n: @@ -543,8 +391,8 @@ def algebra_generators(self): sage: Y = Yangian(QQ, 4) sage: Y.algebra_generators() - Lazy family (generator(i))_{i in Cartesian product of - Positive integers, (1, 2, 3, 4), (1, 2, 3, 4)} + Lazy family (generator(i))_{i in The Cartesian product of + (Positive integers, {1, 2, 3, 4}, {1, 2, 3, 4})} """ return Family(self._indices._indices, self.gen, name="generator") @@ -816,7 +664,8 @@ def __init__(self, base_ring, n, level, variable_name, filtration): category = HopfAlgebrasWithBasis(base_ring).Filtered() self._index_set = tuple(range(1,n+1)) # The keys for the basis are tuples (l, i, j) - indices = GeneratorIndexingSet(self._index_set, level) + L = range(1, self._level + 1) + indices = cartesian_product([L, self._index_set, self._index_set]) # We note that the generators are non-commutative, but we always sort # them, so they are, in effect, indexed by the free abelian monoid basis_keys = IndexedFreeAbelianMonoid(indices, bracket=False, prefix=variable_name) @@ -1152,7 +1001,10 @@ def __init__(self, Y): EXAMPLES:: sage: grY = Yangian(QQ, 4).graded_algebra() - sage: TestSuite(grY).run() # long time + sage: g = grY.indices().gens() + sage: x = grY(g[1,1,1] * g[1,1,2]^2 * g[1,1,3]^3 * g[3,1,1]) + sage: elts = [grY(g[1,1,1]), grY(g[2,1,1]), x] + sage: TestSuite(grY).run(elements=elts) # long time """ if Y._filtration != 'loop': raise ValueError("the Yangian must have the loop filtration") @@ -1170,6 +1022,17 @@ def antipode_on_basis(self, m): -tbar(2)[1,1] sage: x = grY.an_element(); x + tbar(1)[1,1]*tbar(1)[1,2]^2*tbar(1)[1,3]^3*tbar(42)[1,1] + sage: grY.antipode_on_basis(x.leading_support()) + -tbar(1)[1,1]*tbar(1)[1,2]^2*tbar(1)[1,3]^3*tbar(42)[1,1] + - 2*tbar(1)[1,1]*tbar(1)[1,2]*tbar(1)[1,3]^3*tbar(42)[1,2] + - 3*tbar(1)[1,1]*tbar(1)[1,2]^2*tbar(1)[1,3]^2*tbar(42)[1,3] + + 5*tbar(1)[1,2]^2*tbar(1)[1,3]^3*tbar(42)[1,1] + + 10*tbar(1)[1,2]*tbar(1)[1,3]^3*tbar(42)[1,2] + + 15*tbar(1)[1,2]^2*tbar(1)[1,3]^2*tbar(42)[1,3] + + sage: g = grY.indices().gens() + sage: x = grY(g[1,1,1] * g[1,1,2]^2 * g[1,1,3]^3 * g[3,1,1]); x tbar(1)[1,1]*tbar(1)[1,2]^2*tbar(1)[1,3]^3*tbar(3)[1,1] sage: grY.antipode_on_basis(x.leading_support()) -tbar(1)[1,1]*tbar(1)[1,2]^2*tbar(1)[1,3]^3*tbar(3)[1,1] diff --git a/src/sage/all.py b/src/sage/all.py index 6aef26c42a9..93588df1b93 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -104,6 +104,32 @@ message='The distutils(.sysconfig module| package) is deprecated', module='Cython|distutils|numpy|sage.env|sage.features') +# triggered by cython 0.29.32 +warnings.filterwarnings('ignore', category=DeprecationWarning, + message="'cgi' is deprecated and slated for removal in Python 3.13", + module='Cython') + +# triggered by pyparsing 2.4.7 +warnings.filterwarnings('ignore', category=DeprecationWarning, + message="module 'sre_constants' is deprecated", + module='pyparsing') + +# importlib.resources.path and ...read_binary are deprecated in python 3.11, +# but the replacement importlib.resources.files needs python 3.9 +warnings.filterwarnings('ignore', category=DeprecationWarning, + message=r'(path|read_binary) is deprecated\. Use files\(\) instead\.', + module='sage.repl.rich_output.output_(graphics|graphics3d|video)') + +# triggered by sphinx +warnings.filterwarnings('ignore', category=DeprecationWarning, + message="'imghdr' is deprecated and slated for removal in Python 3.13", + module='sphinx.util.images') + +# triggered by docutils 0.19 on Python 3.11 +warnings.filterwarnings('ignore', category=DeprecationWarning, + message=r"Use setlocale\(\), getencoding\(\) and getlocale\(\) instead", + module='docutils.io') + ################ end setup warnings ############################### @@ -290,6 +316,12 @@ def quit_sage(verbose=True): set_random_seed() +# Relink imported lazy_import objects to point to the appropriate namespace + +from sage.misc.lazy_import import clean_namespace +clean_namespace() +del clean_namespace + # From now on it is ok to resolve lazy imports sage.misc.lazy_import.finish_startup() diff --git a/src/sage/all_cmdline.py b/src/sage/all_cmdline.py index 81c56b018ab..197d34ebc90 100644 --- a/src/sage/all_cmdline.py +++ b/src/sage/all_cmdline.py @@ -17,4 +17,16 @@ from sage.all import * from sage.calculus.predefined import x +from sage.misc.lazy_import import lazy_import + +for pkg in ['axiom', 'fricas', 'gap' , 'gap3', 'giac', 'gp', + 'gnuplot', 'kash', 'magma', 'macaulay2', 'maple', + 'mathematica', 'mathics', 'matlab', + 'mupad', 'mwrank', 'octave', 'qepcad', 'singular', + 'sage0', 'lie', 'r']: + lazy_import(f'sage.interfaces.{pkg}', f'{pkg}_console') +del pkg + +lazy_import('sage.interfaces.maxima_abstract', 'maxima_console') + sage.misc.session.init() diff --git a/src/sage/arith/all.py b/src/sage/arith/all.py index d89f401f211..c93e9f05bc9 100644 --- a/src/sage/arith/all.py +++ b/src/sage/arith/all.py @@ -9,8 +9,8 @@ inverse_mod, get_gcd, get_inverse_mod, power_mod, rational_reconstruction, mqrr_rational_reconstruction, trial_division, factor, prime_divisors, odd_part, prime_to_m_part, - is_square, is_squarefree, euler_phi, crt, CRT, CRT_list, CRT_basis, - CRT_vectors, multinomial, multinomial_coefficients, + is_square, is_squarefree, euler_phi, carmichael_lambda, crt, CRT, + CRT_list, CRT_basis, CRT_vectors, multinomial, multinomial_coefficients, binomial, factorial, kronecker_symbol, kronecker, legendre_symbol, primitive_root, nth_prime, quadratic_residues, moebius, continuant, number_of_divisors, hilbert_symbol, hilbert_conductor, diff --git a/src/sage/arith/functions.pyx b/src/sage/arith/functions.pyx index 8d1d25528f8..2fe15bbb974 100644 --- a/src/sage/arith/functions.pyx +++ b/src/sage/arith/functions.pyx @@ -195,7 +195,7 @@ cpdef LCM_list(v): sig_check() if isinstance(elt, Integer): x = <Integer>elt - elif isinstance(elt, (int, long)): + elif isinstance(elt, int): x = Integer(elt) else: # The result is no longer an Integer, pass to generic code diff --git a/src/sage/arith/long.pxd b/src/sage/arith/long.pxd index b0c80f61480..1c9a53387a0 100644 --- a/src/sage/arith/long.pxd +++ b/src/sage/arith/long.pxd @@ -124,7 +124,7 @@ cdef inline bint integer_check_long(x, long* value, int* err) except -1: ....: if err == 0: ....: return value ....: elif err == ERR_OVERFLOW: - ....: raise OverflowError("integer_check_long: overflow") + ....: raise OverflowError(f"integer_check_long: overflow ({x})") ....: elif err == ERR_TYPE: ....: raise TypeError("integer_check_long: wrong type") ....: elif err == ERR_INDEX: @@ -136,24 +136,23 @@ cdef inline bint integer_check_long(x, long* value, int* err) except -1: ....: def long_max(): ....: return smallInteger(LONG_MAX) ....: ''') - sage: types = (ZZ, QQ, int) sage: L = [1, 12345, 10^9, 2^30, long_max()//9, long_max()//3, long_max()] sage: L += [-x for x in L] + [0, long_min()] sage: for v in L: - ....: for t in (Integer, int): + ....: for t in (Integer, int, QQ): ....: assert check_long(t(v)) == v sage: check_long(2^100) Traceback (most recent call last): ... - OverflowError: integer_check_long: overflow + OverflowError: integer_check_long: overflow (...) sage: check_long(long_max() + 1) Traceback (most recent call last): ... - OverflowError: integer_check_long: overflow + OverflowError: integer_check_long: overflow (...) sage: check_long(long_min() - 1) Traceback (most recent call last): ... - OverflowError: integer_check_long: overflow + OverflowError: integer_check_long: overflow (...) sage: check_long("hello") Traceback (most recent call last): ... @@ -162,6 +161,36 @@ cdef inline bint integer_check_long(x, long* value, int* err) except -1: Traceback (most recent call last): ... TypeError: integer_check_long: bad __index__ + + Repeat the overflow tests with python integers: + + sage: check_long(int(2^100)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) + sage: check_long(int(long_max() + 1)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) + sage: check_long(int(long_min() - 1)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) + + And again with rationals: + + sage: check_long(QQ(2^100)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) + sage: check_long(QQ(long_max() + 1)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) + sage: check_long(QQ(long_min() - 1)) + Traceback (most recent call last): + ... + OverflowError: integer_check_long: overflow (...) """ cdef int c = integer_check_long_py(x, value, err) if c: @@ -193,35 +222,93 @@ cdef inline long dig(const digit* D, int n): cdef inline bint integer_check_long_py(x, long* value, int* err): """ - Part of ``integer_check_long`` in ``long.pxd``, checking only for - Python objects of type ``int`` and ``long``. See that function for - documentation and tests. + Return whether ``x`` is a python object of type ``int``. + + If possible, compute the value of this integer as C long and store + it in ``*value``. + + Errors are returned as an error indicator ``*err`` (without raising + any Python exception). + + Possible errors when returning ``True``: + + - ``0``: ``x`` was successfully converted to a C long and its value + is stored in ``*value``. + + - ``ERR_OVERFLOW``: ``x`` is a python object of type ``int`` but + too large to store in a C long. + + Possible errors when returning ``False``: + + - ``ERR_TYPE``: ``x`` is not a python object of type ``int``. + + EXAMPLES: + + We create a pure Python wrapper of this function:: + + sage: cython(''' # optional - sage.misc.cython + ....: from sage.arith.long cimport * + ....: def check_long_py(x): + ....: cdef long value + ....: cdef int err + ....: cdef bint c = integer_check_long_py(x, &value, &err) + ....: if c: + ....: if err == 0: + ....: return value + ....: elif err == ERR_OVERFLOW: + ....: return f"Overflow ({x})" + ....: elif err == ERR_TYPE: + ....: return f"Bad type ({x})" + ....: return f"This should never happen ({x})" + ....: from libc.limits cimport LONG_MIN, LONG_MAX + ....: def long_min(): + ....: return LONG_MIN + ....: def long_max(): + ....: return LONG_MAX + ....: ''') + sage: L = [1, 12345, 10^9, 2^30, long_max()//9, long_max()//3, long_max()] + sage: L += [-x for x in L] + [0, long_min()] + sage: for v in L: + ....: assert check_long_py(int(v)) == v + sage: check_long_py(int(2^100)) + 'Overflow (...)' + sage: check_long_py(int(long_max() + 1)) + 'Overflow (...)' + sage: check_long_py(int(long_min() - 1)) + 'Overflow (...)' + sage: check_long_py(389) + 'Bad type (...)' + sage: check_long_py("hello") + 'Bad type (...)' + sage: check_long_py(2/3) + 'Bad type (...)' """ - if not isinstance(x, long): - if isinstance(x, int): - # This can happen only on Python 2 - value[0] = PyInt_AS_LONG(x) - err[0] = 0 - return 1 + if not isinstance(x, int): err[0] = ERR_TYPE return 0 - # x is a Python "long" (called "int" on Python 3) + # x is a Python "int" (aka PyLongObject or py_long in cython) cdef const digit* D = (<py_long>x).ob_digit cdef Py_ssize_t size = Py_SIZE(x) - # We assume that PyLong_SHIFT is 15 on a 32-bit system and 30 on a - # 64-bit system. This is not guaranteed by Python, but it is the - # default configuration. + # We assume PyLong_SHIFT <= BITS_IN_LONG <= 3 * PyLong_SHIFT. + # This is true in all the default configurations: + # - BITS_IN_LONG = 63, PyLong_SHIFT = 30 + # - BITS_IN_LONG = 31, PyLong_SHIFT = 15 (python <= 3.10) + # - BITS_IN_LONG = 31, PyLong_SHIFT = 30 (new in python 3.11) + # cf. https://trac.sagemath.org/ticket/33842#comment:130 # - # This way, we know that 1 and 2 digits certainly fit in a C long - # and 4 or more digits never fit. For 3 digits, we need an explicit - # overflow check. + # This way, we know that 1 digit certainly fits in a C long + # and 4 or more digits never fit. + # For 2 or 3 digits, we need an explicit overflow check. cdef int BITS_IN_LONG = 8 * sizeof(long) - 1 - if not (2 * PyLong_SHIFT <= BITS_IN_LONG < 4 * PyLong_SHIFT): - raise AssertionError + if not (PyLong_SHIFT <= BITS_IN_LONG <= 3 * PyLong_SHIFT): + raise AssertionError( + f"PyLong_SHIFT = {PyLong_SHIFT}, " + f"BITS_IN_LONG = {BITS_IN_LONG}") cdef long lead + cdef long lead_2_overflow = (<long>1) << (BITS_IN_LONG - PyLong_SHIFT) cdef long lead_3_overflow = (<long>1) << (BITS_IN_LONG - 2 * PyLong_SHIFT) if size == 0: value[0] = 0 @@ -233,9 +320,20 @@ cdef inline bint integer_check_long_py(x, long* value, int* err): value[0] = -dig(D, 0) err[0] = 0 elif size == 2: + if BITS_IN_LONG < 2 * PyLong_SHIFT and D[1] >= lead_2_overflow: + err[0] = ERR_OVERFLOW + return 1 value[0] = dig(D, 0) + dig(D, 1) err[0] = 0 elif size == -2: + if BITS_IN_LONG < 2 * PyLong_SHIFT and D[1] >= lead_2_overflow: + if D[0] == 0 and D[1] == lead_2_overflow: + # Special case for LONG_MIN + value[0] = (<long>-1) << BITS_IN_LONG + err[0] = 0 + else: + err[0] = ERR_OVERFLOW + return 1 value[0] = -(dig(D, 0) + dig(D, 1)) err[0] = 0 elif size == 3: diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index e57076646f4..8be5f952d68 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- r""" Miscellaneous arithmetic functions + +AUTHORS: + +- Kevin Stueve (2010-01-17): in ``is_prime(n)``, delegated calculation to ``n.is_prime()`` """ # **************************************************************************** @@ -29,6 +33,7 @@ from sage.rings.abc import RealField, ComplexField from sage.rings.fast_arith import arith_int, arith_llong, prime_range +from sage.arith.functions import LCM_list ################################################################## @@ -471,25 +476,29 @@ def factorial(n, algorithm='gmp'): def is_prime(n): r""" - Return ``True`` if `n` is a prime number, and ``False`` otherwise. - - Use a provable primality test or a strong pseudo-primality test depending - on the global :mod:`arithmetic proof flag <sage.structure.proof.proof>`. + Determine whether `n` is a prime element of its parent ring. INPUT: - - ``n`` - the object for which to determine primality + - ``n`` -- the object for which to determine primality + + Exceptional special cases: + + - For integers, determine whether `n` is a *positive* prime. + - For number fields except `\QQ`, determine whether `n` + is a prime element *of the maximal order*. + + ALGORITHM: + + For integers, this function uses a provable primality test + or a strong pseudo-primality test depending on the global + :mod:`arithmetic proof flag <sage.structure.proof.proof>`. .. SEEALSO:: - :meth:`is_pseudoprime` - :meth:`sage.rings.integer.Integer.is_prime` - AUTHORS: - - - Kevin Stueve kstueve@uw.edu (2010-01-17): - delegated calculation to ``n.is_prime()`` - EXAMPLES:: sage: is_prime(389) @@ -505,18 +514,60 @@ def is_prime(n): sage: is_prime(-2) False + :: + sage: a = 2**2048 + 981 sage: is_prime(a) # not tested - takes ~ 1min sage: proof.arithmetic(False) sage: is_prime(a) # instantaneous! True sage: proof.arithmetic(True) + + TESTS: + + Make sure the warning from :trac:`25046` works as intended:: + + sage: is_prime(7/1) + doctest:warning + ... + UserWarning: Testing primality in Rational Field, which is a field, + hence the result will always be False. To test whether n is a prime + integer, use is_prime(ZZ(n)) or ZZ(n).is_prime(). Using n.is_prime() + instead will silence this warning. + False + sage: ZZ(7/1).is_prime() + True + sage: QQ(7/1).is_prime() + False + + However, number fields redefine ``.is_prime()`` in an incompatible fashion + (cf. :trac:`32340`) and we should not warn:: + + sage: K.<i> = NumberField(x^2+1) + sage: is_prime(1+i) + True """ try: - return n.is_prime() + ret = n.is_prime() except (AttributeError, NotImplementedError): return ZZ(n).is_prime() + R = n.parent() + if R.is_field(): + # number fields redefine .is_prime(), see #32340 + from sage.rings.number_field.number_field import NumberField_generic + if not isinstance(R, NumberField_generic): + import warnings + s = f'Testing primality in {R}, which is a field, ' \ + 'hence the result will always be False. ' + if R is QQ: + s += 'To test whether n is a prime integer, use ' \ + 'is_prime(ZZ(n)) or ZZ(n).is_prime(). ' + s += 'Using n.is_prime() instead will silence this warning.' + warnings.warn(s) + + return ret + def is_pseudoprime(n): r""" @@ -1465,13 +1516,13 @@ def divisors(n): sage: K.<a> = QuadraticField(7) sage: divisors(K.ideal(7)) - [Fractional ideal (1), Fractional ideal (-a), Fractional ideal (7)] + [Fractional ideal (1), Fractional ideal (a), Fractional ideal (7)] sage: divisors(K.ideal(3)) [Fractional ideal (1), Fractional ideal (3), - Fractional ideal (-a + 2), Fractional ideal (-a - 2)] + Fractional ideal (a - 2), Fractional ideal (a + 2)] sage: divisors(K.ideal(35)) - [Fractional ideal (1), Fractional ideal (5), Fractional ideal (-a), - Fractional ideal (7), Fractional ideal (-5*a), Fractional ideal (35)] + [Fractional ideal (1), Fractional ideal (5), Fractional ideal (a), + Fractional ideal (7), Fractional ideal (5*a), Fractional ideal (35)] TESTS:: @@ -2569,7 +2620,7 @@ def factor(n, proof=None, int_=False, algorithm='pari', verbose=0, **kwds): sage: K.<i> = QuadraticField(-1) sage: factor(122 - 454*i) - (-3*i - 2) * (-i - 2)^3 * (i + 1)^3 * (i + 4) + (-i) * (-i - 2)^3 * (i + 1)^3 * (-2*i + 3) * (i + 4) To access the data in a factorization:: @@ -2643,7 +2694,7 @@ def radical(n, *args, **kwds): sage: radical(0) Traceback (most recent call last): ... - ArithmeticError: Radical of 0 not defined. + ArithmeticError: radical of 0 is not defined sage: K.<i> = QuadraticField(-1) sage: radical(K(2)) i + 1 @@ -3085,6 +3136,156 @@ def plot(self, xmin=1, xmax=50, pointsize=30, rgbcolor=(0, 0, 1), euler_phi = Euler_Phi() +def carmichael_lambda(n): + r""" + Return the Carmichael function of a positive integer ``n``. + + The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest + positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all + `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` + is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. + + INPUT: + + - ``n`` -- a positive integer. + + OUTPUT: + + - The Carmichael function of ``n``. + + ALGORITHM: + + If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd + prime and let `k` be a positive integer. Then + `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then + `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is + composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the + prime factorization of `n`. Then + + .. MATH:: + + \lambda(n) + = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) + = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) + + EXAMPLES: + + The Carmichael function of all positive integers up to and including 10:: + + sage: from sage.arith.misc import carmichael_lambda + sage: list(map(carmichael_lambda, [1..10])) + [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] + + The Carmichael function of the first ten primes:: + + sage: list(map(carmichael_lambda, primes_first_n(10))) + [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] + + Cases where the Carmichael function is equivalent to the Euler phi + function:: + + sage: carmichael_lambda(2) == euler_phi(2) + True + sage: carmichael_lambda(4) == euler_phi(4) + True + sage: p = random_prime(1000, lbound=3, proof=True) + sage: k = randint(1, 1000) + sage: carmichael_lambda(p^k) == euler_phi(p^k) + True + + A case where `\lambda(n) \neq \varphi(n)`:: + + sage: k = randint(3, 1000) + sage: carmichael_lambda(2^k) == 2^(k - 2) + True + sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) + False + + Verifying the current implementation of the Carmichael function using + another implementation. The other implementation that we use for + verification is an exhaustive search for the exponent of the + multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: + + sage: from sage.arith.misc import carmichael_lambda + sage: n = randint(1, 500) + sage: c = carmichael_lambda(n) + sage: def coprime(n): + ....: return [i for i in range(n) if gcd(i, n) == 1] + sage: def znpower(n, k): + ....: L = coprime(n) + ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) + sage: def my_carmichael(n): + ....: if n == 1: + ....: return 1 + ....: for k in range(1, n): + ....: L = znpower(n, k) + ....: ones = [1] * len(L) + ....: T = [L[i] == ones[i] for i in range(len(L))] + ....: if all(T): + ....: return k + sage: c == my_carmichael(n) + True + + Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` + for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. + Here, we verify Carmichael's theorem. :: + + sage: from sage.arith.misc import carmichael_lambda + sage: n = randint(2, 1000) + sage: c = carmichael_lambda(n) + sage: ZnZ = IntegerModRing(n) + sage: M = ZnZ.list_of_elements_of_multiplicative_group() + sage: ones = [1] * len(M) + sage: P = [power_mod(a, c, n) for a in M] + sage: P == ones + True + + TESTS: + + The input ``n`` must be a positive integer:: + + sage: from sage.arith.misc import carmichael_lambda + sage: carmichael_lambda(0) + Traceback (most recent call last): + ... + ValueError: Input n must be a positive integer. + sage: carmichael_lambda(randint(-10, 0)) + Traceback (most recent call last): + ... + ValueError: Input n must be a positive integer. + + Bug reported in :trac:`8283`:: + + sage: from sage.arith.misc import carmichael_lambda + sage: type(carmichael_lambda(16)) + <class 'sage.rings.integer.Integer'> + + REFERENCES: + + - :wikipedia:`Carmichael_function` + """ + n = Integer(n) + # sanity check + if n < 1: + raise ValueError("Input n must be a positive integer.") + + L = list(n.factor()) + t = [] + + # first get rid of the prime factor 2 + if n & 1 == 0: + two,e = L.pop(0) + assert two == 2 + k = e - 2 if e >= 3 else e - 1 + t.append(1 << k) + + # then other prime factors + t += [p**(k - 1) * (p - 1) for p, k in L] + + # finish the job + return LCM_list(t) + + def crt(a, b, m=None, n=None): r""" Return a solution to a Chinese Remainder Theorem problem. @@ -3224,10 +3425,10 @@ def crt(a, b, m=None, n=None): CRT = crt -def CRT_list(v, moduli): - r""" Given a list ``v`` of elements and a list of corresponding +def CRT_list(values, moduli): + r""" Given a list ``values`` of elements and a list of corresponding ``moduli``, find a single element that reduces to each element of - ``v`` modulo the corresponding moduli. + ``values`` modulo the corresponding moduli. .. SEEALSO:: @@ -3289,22 +3490,37 @@ def CRT_list(v, moduli): sage: from gmpy2 import mpz sage: CRT_list([mpz(2),mpz(3),mpz(2)], [mpz(3),mpz(5),mpz(7)]) 23 + + Make sure we are not mutating the input lists:: + + sage: xs = [1,2,3] + sage: ms = [5,7,9] + sage: CRT_list(xs, ms) + 156 + sage: xs + [1, 2, 3] + sage: ms + [5, 7, 9] """ - if not isinstance(v, list) or not isinstance(moduli, list): + if not isinstance(values, list) or not isinstance(moduli, list): raise ValueError("arguments to CRT_list should be lists") - if len(v) != len(moduli): + if len(values) != len(moduli): raise ValueError("arguments to CRT_list should be lists of the same length") - if not v: + if not values: return ZZ.zero() - if len(v) == 1: - return moduli[0].parent()(v[0]) - x = v[0] - m = moduli[0] + if len(values) == 1: + return moduli[0].parent()(values[0]) + + # The result is computed using a binary tree. In typical cases, + # this scales much better than folding the list from one side. from sage.arith.functions import lcm - for i in range(1, len(v)): - x = CRT(x, v[i], m, moduli[i]) - m = lcm(m, moduli[i]) - return x % m + while len(values) > 1: + vs, ms = values[::2], moduli[::2] + for i, (v, m) in enumerate(zip(values[1::2], moduli[1::2])): + vs[i] = CRT(vs[i], v, ms[i], m) + ms[i] = lcm(ms[i], m) + values, moduli = vs, ms + return values[0] % moduli[0] def CRT_basis(moduli): diff --git a/src/sage/arith/power.pyx b/src/sage/arith/power.pyx index 3c9219a5f11..2900d9f2a45 100644 --- a/src/sage/arith/power.pyx +++ b/src/sage/arith/power.pyx @@ -24,7 +24,7 @@ cpdef generic_power(a, n): """ Return `a^n`. - If `n` is negative, return `(1/a)^(-n)`. + If `n` is negative, return `(1/a)^{-n}`. INPUT: diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 76cd8986123..00da3f9d4af 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -2252,7 +2252,6 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: sefms("x # 3") == SR(x != 3) True sage: solve([x != 5], x) - #0: solve_rat_ineq(ineq=_SAGE_VAR_x # 5) [[x - 5 != 0]] sage: solve([2*x==3, x != 5], x) [[x == (3/2), (-7/2) != 0]] diff --git a/src/sage/calculus/transforms/dwt.pyx b/src/sage/calculus/transforms/dwt.pyx index 6c34b1a288f..61e0f83d331 100644 --- a/src/sage/calculus/transforms/dwt.pyx +++ b/src/sage/calculus/transforms/dwt.pyx @@ -21,9 +21,6 @@ AUTHOR: # https://www.gnu.org/licenses/ # **************************************************************************** -import sage.plot.all - - def WaveletTransform(n, wavelet_type, wavelet_k): r""" This function initializes an GSLDoubleArray of length n which @@ -136,19 +133,20 @@ cdef class DiscreteWaveletTransform(GSLDoubleArray): def backward_transform(self): gsl_wavelet_transform_inverse(self.wavelet,self.data,self.stride,self.n,self.workspace) - def plot(self,xmin=None,xmax=None,**args): + def plot(self, xmin=None, xmax=None, **args): + from sage.plot.point import point + cdef int i cdef double x v = [] - point = sage.plot.all.point if xmin is None: x_min = 0 if xmax is None: - x_max=self.n - for i from x_min <=i < x_max: + x_max = self.n + for i from x_min <= i < x_max: x = self.data[i] - if i >0: - v.append(point([(i,x)],hue=(1,1,1),**args)) + if i > 0: + v.append(point([(i, x)], hue=(1, 1, 1), **args)) return sum(v) diff --git a/src/sage/calculus/transforms/fft.pyx b/src/sage/calculus/transforms/fft.pyx index c5a6108d222..1ea2cb6b2b2 100644 --- a/src/sage/calculus/transforms/fft.pyx +++ b/src/sage/calculus/transforms/fft.pyx @@ -22,11 +22,11 @@ AUTHORS: from cysignals.memory cimport sig_malloc, sig_free -import sage.plot.all import sage.libs.pari.all from sage.rings.integer import Integer from sage.rings.complex_mpfr import ComplexNumber + def FastFourierTransform(size, base_ring=None): """ Create an array for fast Fourier transform conversion using gsl. @@ -247,10 +247,11 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): Graphics object consisting of 2 graphics primitives """ + from sage.plot.point import point + cdef int i v = [] - point = sage.plot.all.point pi = sage.symbolic.constants.pi.n() I = sage.symbolic.constants.I.n() s = 1/(3*pi) # so arg gets scaled between -1/3 and 1/3. diff --git a/src/sage/calculus/var.pyx b/src/sage/calculus/var.pyx index 85359ac098d..ed7a4305188 100644 --- a/src/sage/calculus/var.pyx +++ b/src/sage/calculus/var.pyx @@ -343,11 +343,11 @@ def function(s, **kwds): TESTS: Make sure that :trac:`15860` is fixed and whitespaces are removed:: - + sage: function('A, B') (A, B) sage: B - B + B """ G = globals() # this is the reason the code must be in Cython. v = new_function(s, **kwds) diff --git a/src/sage/categories/additive_magmas.py b/src/sage/categories/additive_magmas.py index e81dcf9d4b9..8d2c46d0771 100644 --- a/src/sage/categories/additive_magmas.py +++ b/src/sage/categories/additive_magmas.py @@ -519,7 +519,7 @@ def algebra_generators(self): An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() - Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} + Family (B[a], B[b], B[c], B[d]) .. TODO:: @@ -730,7 +730,7 @@ def is_empty(self): TESTS: - We check that the method `is_empty` is inherited from this + We check that the method ``is_empty`` is inherited from this category in both examples above:: sage: A.is_empty.__module__ diff --git a/src/sage/categories/additive_semigroups.py b/src/sage/categories/additive_semigroups.py index fde92f27896..0527867154b 100644 --- a/src/sage/categories/additive_semigroups.py +++ b/src/sage/categories/additive_semigroups.py @@ -153,7 +153,7 @@ def algebra_generators(self): An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() - Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} + Family (B[a], B[b], B[c], B[d]) """ return self.basis().keys().additive_semigroup_generators().map(self.monomial) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index eef5e7acc70..f70455cf1a0 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1886,11 +1886,10 @@ def _is_subclass(self, c): sage: Algebras(QQ)._is_subclass(ModulesWithBasis) False """ - assert( isinstance(c, Category) or (issubclass(c.__class__, type) and issubclass(c, Category)) ) + assert (isinstance(c, Category) or (issubclass(c.__class__, type) and issubclass(c, Category))) if isinstance(c, Category): return self.is_subcategory(c) - else: - return any(isinstance(cat, c) for cat in self._all_super_categories) + return any(isinstance(cat, c) for cat in self._all_super_categories) @cached_method def _meet_(self, other): @@ -2236,7 +2235,7 @@ def _sort(categories): .. NOTE:: - The auxiliary function `_flatten_categories` used in the test + The auxiliary function ``_flatten_categories`` used in the test below expects a second argument, which is a type such that instances of that type will be replaced by its super categories. Usually, this type is :class:`JoinCategory`. @@ -2954,8 +2953,8 @@ def __init__(self, super_categories, **kwds): sage: TestSuite(C).run() """ - assert(len(super_categories) >= 2) - assert(all(not isinstance(category, JoinCategory) for category in super_categories)) + assert len(super_categories) >= 2 + assert all(not isinstance(category, JoinCategory) for category in super_categories) # Use __super_categories to not overwrite the lazy attribute Category._super_categories # Maybe this would not be needed if the flattening/sorting is does consistently? self.__super_categories = list(super_categories) diff --git a/src/sage/categories/commutative_algebras.py b/src/sage/categories/commutative_algebras.py index 0f24e90c524..cff24e1298a 100644 --- a/src/sage/categories/commutative_algebras.py +++ b/src/sage/categories/commutative_algebras.py @@ -89,4 +89,3 @@ def extra_super_categories(self): True """ return [CommutativeRings()] - diff --git a/src/sage/categories/covariant_functorial_construction.py b/src/sage/categories/covariant_functorial_construction.py index 4ed3a678986..40d661945e8 100644 --- a/src/sage/categories/covariant_functorial_construction.py +++ b/src/sage/categories/covariant_functorial_construction.py @@ -139,7 +139,7 @@ def category_from_parents(self, parents): Category of tensor products of finite dimensional vector spaces with basis over Rational Field """ from sage.structure.parent import Parent - assert(all(isinstance(parent, Parent) for parent in parents)) + assert all(isinstance(parent, Parent) for parent in parents) # Should we pass a set of categories to reduce the cache size? # But then this would impose that, for any constructor, the # category of the result does not depend on the order/repetition @@ -169,7 +169,7 @@ def category_from_categories(self, categories): sage: cartesian_product.category_from_categories((Cat1, Cat2)) Category of Cartesian products of monoids """ - assert(len(categories) > 0) + assert len(categories) > 0 return self.category_from_category(Category.meet(categories)) def category_from_category(self, category): @@ -205,6 +205,7 @@ def __call__(self, args, **kwargs): Functorial construction application INPUT: + - ``self``: a covariant functorial construction `F` - ``args``: a tuple (or iterable) of parents or elements @@ -216,11 +217,12 @@ def __call__(self, args, **kwargs): sage: tensor((E, E, E)) E # E # E """ - args = tuple(args) # a bit brute force; let's see if this becomes a bottleneck later - assert(all( hasattr(arg, self._functor_name) for arg in args)) - assert(len(args) > 0) + args = tuple(args) # a bit brute force; let's see if this becomes a bottleneck later + assert all(hasattr(arg, self._functor_name) for arg in args) + assert len(args) > 0 return getattr(args[0], self._functor_name)(*args[1:], **kwargs) + class FunctorialConstructionCategory(Category): # Should this be CategoryWithBase? """ Abstract class for categories `F_{Cat}` obtained through a diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 0aacd2aeda5..98fe35a0ab3 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -236,11 +236,11 @@ def braid_group_as_finitely_presented_group(self): sage: W.braid_group_as_finitely_presented_group() Finitely presented group < S1, S2 | (S1*S2)^2*(S1^-1*S2^-1)^2 > - sage: W = ReflectionGroup(['B',3], index_set=["AA","BB",5]) # optional - gap3 - sage: W.braid_group_as_finitely_presented_group() # optional - gap3 + sage: W = ReflectionGroup(['B',3], index_set=["AA","BB","5"]) # optional - gap3 + sage: W.braid_group_as_finitely_presented_group() # optional - gap3 Finitely presented group < SAA, SBB, S5 | - SAA*SBB*SAA*SBB^-1*SAA^-1*SBB^-1, SAA*S5*SAA^-1*S5^-1, - (SBB*S5)^2*(SBB^-1*S5^-1)^2 > + (SAA*SBB)^2*(SAA^-1*SBB^-1)^2, SAA*S5*SAA^-1*S5^-1, + SBB*S5*SBB*S5^-1*SBB^-1*S5^-1 > """ from sage.groups.free_group import FreeGroup from sage.misc.misc_c import prod @@ -263,8 +263,10 @@ def braid_orbit(self, word): - ``word``: a list (or iterable) of indices in ``self.index_set()`` - OUTPUT: a list of all lists that can be obtained from - ``word`` by replacements of braid relations + OUTPUT: + + a list of all lists that can be obtained from + ``word`` by replacements of braid relations See :meth:`braid_relations` for the definition of braid relations. @@ -283,25 +285,25 @@ def braid_orbit(self, word): sage: sorted(W.braid_orbit([2,1,1,2,1])) [[1, 2, 1, 1, 2], [2, 1, 1, 2, 1], [2, 1, 2, 1, 2], [2, 2, 1, 2, 2]] - sage: W = ReflectionGroup(['A',3], index_set=["AA","BB",5]) # optional - gap3 - sage: w = W.long_element() # optional - gap3 - sage: W.braid_orbit(w.reduced_word()) # optional - gap3 - [['AA', 5, 'BB', 5, 'AA', 'BB'], - ['AA', 'BB', 5, 'BB', 'AA', 'BB'], - [5, 'BB', 'AA', 5, 'BB', 5], - ['BB', 5, 'AA', 'BB', 5, 'AA'], - [5, 'BB', 5, 'AA', 'BB', 5], - ['BB', 5, 'AA', 'BB', 'AA', 5], - [5, 'AA', 'BB', 'AA', 5, 'BB'], - ['BB', 'AA', 5, 'BB', 5, 'AA'], - ['AA', 'BB', 'AA', 5, 'BB', 'AA'], - [5, 'BB', 'AA', 'BB', 5, 'BB'], - ['BB', 'AA', 5, 'BB', 'AA', 5], - [5, 'AA', 'BB', 5, 'AA', 'BB'], - ['AA', 'BB', 5, 'AA', 'BB', 'AA'], - ['BB', 5, 'BB', 'AA', 'BB', 5], - ['AA', 5, 'BB', 'AA', 5, 'BB'], - ['BB', 'AA', 'BB', 5, 'BB', 'AA']] + sage: W = ReflectionGroup(['A',3], index_set=["AA","BB","5"]) # optional - gap3 + sage: w = W.long_element() # optional - gap3 + sage: W.braid_orbit(w.reduced_word()) # optional - gap3 + [['BB', '5', 'AA', 'BB', '5', 'AA'], + ['5', 'BB', '5', 'AA', 'BB', '5'], + ['BB', 'AA', 'BB', '5', 'BB', 'AA'], + ['AA', '5', 'BB', 'AA', '5', 'BB'], + ['5', 'AA', 'BB', 'AA', '5', 'BB'], + ['AA', 'BB', '5', 'AA', 'BB', 'AA'], + ['AA', 'BB', 'AA', '5', 'BB', 'AA'], + ['AA', 'BB', '5', 'BB', 'AA', 'BB'], + ['BB', 'AA', '5', 'BB', 'AA', '5'], + ['BB', '5', 'AA', 'BB', 'AA', '5'], + ['AA', '5', 'BB', '5', 'AA', 'BB'], + ['5', 'BB', 'AA', '5', 'BB', '5'], + ['5', 'BB', 'AA', 'BB', '5', 'BB'], + ['5', 'AA', 'BB', '5', 'AA', 'BB'], + ['BB', '5', 'BB', 'AA', 'BB', '5'], + ['BB', 'AA', '5', 'BB', '5', 'AA']] .. TODO:: @@ -668,7 +670,7 @@ def _test_reduced_word(self, **options): def simple_projection(self, i, side='right', length_increasing=True): r""" - Return the simple projection `\pi_i` (or `\overline\pi_i` if `length_increasing` is ``False``). + Return the simple projection `\pi_i` (or `\overline\pi_i` if ``length_increasing`` is ``False``). INPUT: @@ -1233,6 +1235,15 @@ def _test_has_descent(self, **options): sage: W = CoxeterGroups().example() sage: W._test_has_descent() + + sage: W = Permutations(4) + sage: W._test_has_descent() + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: W._test_has_descent() + sage: sage.combinat.permutation.Permutations.options._reset() + + sage: W = SignedPermutations(3) + sage: W._test_has_descent() """ tester = self._tester(**options) s = self.simple_reflections() @@ -1252,12 +1263,12 @@ def _test_has_descent(self, **options): tester.assertEqual(s[i].has_descent(j, positive=True), i != j) if i == j: continue - u = s[i] * s[j] - v = s[j] * s[i] - tester.assertTrue((s[i] * s[j]).has_descent(i, side='left')) - tester.assertTrue((s[i] * s[j]).has_descent(j, side='right')) - tester.assertEqual((s[i] * s[j]).has_descent(j, side='left'), u == v) - tester.assertEqual((s[i] * s[j]).has_descent(i, side='right'), u == v) + u = s[i].apply_simple_reflection_right(j) + v = s[j].apply_simple_reflection_right(i) + tester.assertTrue(u.has_descent(i, side='left')) + tester.assertTrue(u.has_descent(j, side='right')) + tester.assertEqual(u.has_descent(j, side='left'), u == v) + tester.assertEqual(u.has_descent(i, side='right'), u == v) def _test_descents(self, **options): """ @@ -1295,6 +1306,15 @@ def _test_coxeter_relations(self, **options): sage: cm = CartanMatrix([[2,-5,0],[-2,2,-1],[0,-1,2]]) sage: W = WeylGroup(cm) sage: W._test_coxeter_relations() + + sage: W = Permutations(4) + sage: W._test_coxeter_relations() + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: W._test_coxeter_relations() + sage: sage.combinat.permutation.Permutations.options._reset() + + sage: W = SignedPermutations(3) + sage: W._test_coxeter_relations() """ tester = self._tester(**options) s = self.simple_reflections() @@ -1588,25 +1608,25 @@ def reduced_words(self): sage: sorted(w.reduced_words()) [[2, 3, 4, 2], [3, 2, 4, 2], [3, 4, 2, 4]] - sage: W = ReflectionGroup(['A',3], index_set=["AA","BB",5]) # optional - gap3 - sage: w = W.long_element() # optional - gap3 - sage: w.reduced_words() # optional - gap3 - [['AA', 5, 'BB', 5, 'AA', 'BB'], - ['AA', 'BB', 5, 'BB', 'AA', 'BB'], - [5, 'BB', 'AA', 5, 'BB', 5], - ['BB', 5, 'AA', 'BB', 5, 'AA'], - [5, 'BB', 5, 'AA', 'BB', 5], - ['BB', 5, 'AA', 'BB', 'AA', 5], - [5, 'AA', 'BB', 'AA', 5, 'BB'], - ['BB', 'AA', 5, 'BB', 5, 'AA'], - ['AA', 'BB', 'AA', 5, 'BB', 'AA'], - [5, 'BB', 'AA', 'BB', 5, 'BB'], - ['BB', 'AA', 5, 'BB', 'AA', 5], - [5, 'AA', 'BB', 5, 'AA', 'BB'], - ['AA', 'BB', 5, 'AA', 'BB', 'AA'], - ['BB', 5, 'BB', 'AA', 'BB', 5], - ['AA', 5, 'BB', 'AA', 5, 'BB'], - ['BB', 'AA', 'BB', 5, 'BB', 'AA']] + sage: W = ReflectionGroup(['A',3], index_set=["AA","BB","5"]) # optional - gap3 + sage: w = W.long_element() # optional - gap3 + sage: w.reduced_words() # optional - gap3 + [['BB', '5', 'AA', 'BB', '5', 'AA'], + ['5', 'BB', '5', 'AA', 'BB', '5'], + ['BB', 'AA', 'BB', '5', 'BB', 'AA'], + ['AA', '5', 'BB', 'AA', '5', 'BB'], + ['5', 'AA', 'BB', 'AA', '5', 'BB'], + ['AA', 'BB', '5', 'AA', 'BB', 'AA'], + ['AA', 'BB', 'AA', '5', 'BB', 'AA'], + ['AA', 'BB', '5', 'BB', 'AA', 'BB'], + ['BB', 'AA', '5', 'BB', 'AA', '5'], + ['BB', '5', 'AA', 'BB', 'AA', '5'], + ['AA', '5', 'BB', '5', 'AA', 'BB'], + ['5', 'BB', 'AA', '5', 'BB', '5'], + ['5', 'BB', 'AA', 'BB', '5', 'BB'], + ['5', 'AA', 'BB', '5', 'AA', 'BB'], + ['BB', '5', 'BB', 'AA', 'BB', '5'], + ['BB', 'AA', '5', 'BB', '5', 'AA']] .. TODO:: @@ -1866,6 +1886,31 @@ def absolute_le(self, other): False sage: w1.absolute_le(w1) True + + TESTS: + + Check that this is independent of the implementation of the group, see :trac:`34799`:: + + sage: W1 = WeylGroup(['A',2]) + sage: W2 = Permutations(3) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = set((P(w1), P(w2)) for w1 in W1 for w2 in W1 if w1.absolute_le(w2)) + sage: d2 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d2 + True + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: d3 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d3 + True + sage: sage.combinat.permutation.Permutations.options._reset() + + sage: W1 = WeylGroup(['B',2]) + sage: W2 = SignedPermutations(2) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = set((P(w1), P(w2)) for w1 in W1 for w2 in W1 if w1.absolute_le(w2)) + sage: d2 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d2 + True """ if self == other: return True @@ -2053,6 +2098,21 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): sage: w0.binary_factorizations().category() Category of finite enumerated sets + + Check that this is independent of the implementation of the group, see :trac:`34799`:: + + sage: W1 = WeylGroup(['A',3]) + sage: W2 = Permutations(4) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = {P(pi): set((P(w[0]), P(w[1])) for w in pi.binary_factorizations()) for pi in W1} + sage: d2 = {pi: set(pi.binary_factorizations()) for pi in W2} + sage: d1 == d2 + True + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: d3 = {pi: set(pi.binary_factorizations()) for pi in W2} + sage: d1 == d3 + True + sage: sage.combinat.permutation.Permutations.options._reset() """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest W = self.parent() @@ -2062,11 +2122,11 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): s = W.simple_reflections() def succ(u_v): - (u, v) = u_v + u, v = u_v for i in v.descents(side='left'): - u1 = u * s[i] + u1 = u.apply_simple_reflection_right(i) if i == u1.first_descent() and predicate(u1): - yield (u1, s[i] * v) + yield u1, v.apply_simple_reflection_left(i) return RecursivelyEnumeratedSet_forest(((W.one(), self),), succ, category=FiniteEnumeratedSets()) @@ -2212,7 +2272,6 @@ def lower_cover_reflections(self, side='right'): [s2*s3*s2, s3, s1] """ - if side == 'left': self = self.inverse() return [x[1] for x in self.bruhat_lower_covers_reflections()] @@ -2259,7 +2318,6 @@ def cover_reflections(self, side='right'): [s4, s2, s1*s2*s1, s3*s4*s3] """ - if side == 'left': self = self.inverse() return [x[1] for x in self.bruhat_upper_covers_reflections()] @@ -2594,7 +2652,6 @@ def apply_demazure_product(self, element, side='right', s2*s3*s4*s1*s2*s3*s4*s2*s3*s2*s1 """ - # if self and element have the same parent if self.parent().is_parent_of(element): the_word = element.reduced_word() @@ -2637,7 +2694,6 @@ def min_demazure_product_greater(self, element): s4*s2 """ - # if self and element have the same parent if self.parent().is_parent_of(element): the_word = element.reduced_word() @@ -2770,7 +2826,6 @@ def deodhar_lift_down(self, w, index_set): s2*s3*s2 """ - vmin = self.coset_representative(index_set) wmin = w.coset_representative(index_set) if not wmin.bruhat_le(vmin): diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index 1cfbf9175ab..980bebc9f84 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -1563,7 +1563,9 @@ def to_highest_weight(self, index_set=None): r""" Return the highest weight element `u` and a list `[i_1,...,i_k]` such that `self = f_{i_1} ... f_{i_k} u`, where `i_1,...,i_k` are - elements in `index_set`. By default the index set is assumed to be + elements in ``index_set``. + + By default the index set is assumed to be the full index set of self. EXAMPLES:: @@ -1602,8 +1604,10 @@ def to_lowest_weight(self, index_set=None): r""" Return the lowest weight element `u` and a list `[i_1,...,i_k]` such that `self = e_{i_1} ... e_{i_k} u`, where `i_1,...,i_k` are - elements in `index_set`. By default the index set is assumed to be - the full index set of self. + elements in ``index_set``. + + By default the index set is assumed to be the full index + set of self. EXAMPLES:: @@ -1642,7 +1646,7 @@ def to_lowest_weight(self, index_set=None): def all_paths_to_highest_weight(self, index_set=None): r""" Iterate over all paths to the highest weight from ``self`` - with respect to `index_set`. + with respect to ``index_set``. INPUT: @@ -2183,7 +2187,7 @@ def _call_(self, x): cur = cur.e_string(s) return cur - def __bool__(self): + def __bool__(self) -> bool: """ Return if ``self`` is a non-zero morphism. @@ -2200,8 +2204,6 @@ def __bool__(self): """ return any(self._on_gens(mg) is not None for mg in self._gens) - - # TODO: Does this belong in the element_class of the Crystals() category? def to_module_generator(self, x): """ diff --git a/src/sage/categories/examples/commutative_additive_monoids.py b/src/sage/categories/examples/commutative_additive_monoids.py index c0930bec5d5..945826c22a5 100644 --- a/src/sage/categories/examples/commutative_additive_monoids.py +++ b/src/sage/categories/examples/commutative_additive_monoids.py @@ -112,7 +112,7 @@ def zero(self): return self(()) class Element(FreeCommutativeAdditiveSemigroup.Element): - def __bool__(self): + def __bool__(self) -> bool: """ Check if ``self`` is not the zero of the monoid @@ -126,6 +126,5 @@ def __bool__(self): """ return any(x for x in self.value.values()) - Example = FreeCommutativeAdditiveMonoid diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index c135c877b70..71ca54c8a5b 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -1,14 +1,13 @@ r""" Examples of finite Coxeter groups """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Nicolas M. Thiery <nthiery at users.sf.net> # Copyright (C) 2009 Nicolas Borie <nicolas dot borie at math.u-psud.fr> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** - +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper @@ -87,10 +86,11 @@ class DihedralGroup(UniqueRepresentation, Parent): def __init__(self, n=5): r""" + Construct the `n`-th DihedralGroup of order `2 n`. + INPUT: - - ``n`` - an integer with `n>=2` - Construct the n-th DihedralGroup of order 2*n + - `n` -- an integer with `n \geq 2` EXAMPLES:: @@ -238,12 +238,13 @@ def apply_simple_reflection_right(self, i): return self.parent()(reduced_word[:-1]) else: return self.parent()(reduced_word[1:]) - elif (len(reduced_word) == n-1 and (not self.has_descent(i))) and (reduced_word[0] == 2): - return self.parent()((1,)+reduced_word) + elif (len(reduced_word) == n - 1 and (not self.has_descent(i))) and (reduced_word[0] == 2): + return self.parent()((1,) + reduced_word) else: if self.has_descent(i): return self.parent()(reduced_word[:-1]) else: - return self.parent()(reduced_word+(i,)) + return self.parent()(reduced_word + (i,)) + Example = DihedralGroup diff --git a/src/sage/categories/examples/finite_enumerated_sets.py b/src/sage/categories/examples/finite_enumerated_sets.py index 91896c77d25..6a606f7b9e3 100644 --- a/src/sage/categories/examples/finite_enumerated_sets.py +++ b/src/sage/categories/examples/finite_enumerated_sets.py @@ -146,7 +146,8 @@ def ambient(self): def lift(self, x): """ INPUT: - - ``x`` -- an element of ``self`` + + - ``x`` -- an element of ``self`` Lifts ``x`` to the ambient space for ``self``, as per :meth:`Sets.Subquotients.ParentMethods.lift() @@ -164,7 +165,8 @@ def lift(self, x): def retract(self, x): """ INPUT: - - ``x`` -- an element of the ambient space for ``self`` + + - ``x`` -- an element of the ambient space for ``self`` Retracts ``x`` from the ambient space to ``self``, as per :meth:`Sets.Subquotients.ParentMethods.retract() diff --git a/src/sage/categories/examples/lie_algebras.py b/src/sage/categories/examples/lie_algebras.py index cdde62e6760..3eafe0787f3 100644 --- a/src/sage/categories/examples/lie_algebras.py +++ b/src/sage/categories/examples/lie_algebras.py @@ -193,7 +193,7 @@ def __ne__(self, rhs): """ return not self.__eq__(rhs) - def __bool__(self): + def __bool__(self) -> bool: """ Check non-zero. @@ -207,8 +207,6 @@ def __bool__(self): """ return bool(self.value) - - def _add_(self, rhs): """ Add ``self`` and ``rhs``. diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index eee40b1bfbe..93cc264580d 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -249,7 +249,7 @@ def next(self, i): sage: x.parent() Set of prime numbers """ - assert(i in self) + assert i in self return self._from_integer_((Integer(i) + 1).next_prime()) def some_elements(self): diff --git a/src/sage/categories/examples/with_realizations.py b/src/sage/categories/examples/with_realizations.py index e72b2f5b3d0..06e060f8925 100644 --- a/src/sage/categories/examples/with_realizations.py +++ b/src/sage/categories/examples/with_realizations.py @@ -166,8 +166,8 @@ def __init__(self, R, S): From: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis To: The subset algebra of {1, 2, 3} over Rational Field in the Out basis """ - assert(R in Rings()) - self._base = R # Won't be needed when CategoryObject won't override anymore base_ring + assert R in Rings() + self._base = R # Won't be needed when CategoryObject won't override anymore base_ring self._S = S Parent.__init__(self, category=Algebras(R).Commutative().WithRealizations()) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 0ea91e393ff..4bbbd08ae95 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -2,7 +2,7 @@ r""" Fields """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu> # William Stein <wstein@math.ucsd.edu> # 2008 Teresa Gomez-Diaz (CNRS) <Teresa.Gomez-Diaz@univ-mlv.fr> @@ -10,8 +10,8 @@ # 2012-2014 Julian Rueth <julian.rueth@fsfe.org> # # 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_attribute import lazy_class_attribute from sage.misc.lazy_import import LazyImport @@ -22,6 +22,7 @@ from sage.structure.element import coerce_binop + class Fields(CategoryWithAxiom): """ The category of (commutative) fields, i.e. commutative rings where @@ -474,7 +475,7 @@ def _squarefree_decomposition_univariate_polynomial(self, f): if f.degree() == 0: return Factorization([], unit=f[0]) if self.characteristic() != 0: - raise NotImplementedError("square-free decomposition not implemented for this polynomial.") + raise NotImplementedError("square-free decomposition not implemented for this polynomial") factors = [] cur = f 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 2edc78c26e3..bfe0de224b2 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -837,7 +837,7 @@ def derived_subalgebra(self): the 0-dimensional abelian Lie algebra over Rational Field with basis matrix: [] - + If ``self`` is semisimple, then the derived subalgebra is ``self``:: sage: sl3 = LieAlgebra(QQ, cartan_type=['A',2]) @@ -1107,8 +1107,8 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: E,F,H = g.basis() sage: n = g.subalgebra([F,H]) sage: ascii_art(n.chevalley_eilenberg_complex()) - [0] - [0 0] [2] + [0] + [0 0] [2] 0 <-- C_0 <------ C_1 <---- C_2 <-- 0 REFERENCES: diff --git a/src/sage/categories/finite_monoids.py b/src/sage/categories/finite_monoids.py index c58cfc3e4e8..a300339ae22 100644 --- a/src/sage/categories/finite_monoids.py +++ b/src/sage/categories/finite_monoids.py @@ -35,7 +35,9 @@ def nerve(self): r""" The nerve (classifying space) of this monoid. - OUTPUT: the nerve `BG` (if `G` denotes this monoid), as a + OUTPUT: + + the nerve `BG` (if `G` denotes this monoid), as a simplicial set. The `k`-dimensional simplices of this object are indexed by products of `k` elements in the monoid: diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 9269bc8ac1c..59c00471e3f 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -232,7 +232,6 @@ def cycle_index(self, parent=None): return parent.sum_of_terms([C.an_element().cycle_type(), base_ring(C.cardinality())] for C in self.conjugacy_classes() ) / self.cardinality() - @cached_method def profile_series(self, variable='z'): diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index caf76e44893..b5422adfd4e 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -9,16 +9,17 @@ - An *order ideal* (or *lower set*) of a poset `P` is a subset `S` of `P` such that if `x \leq y` and `y\in S` then `x\in S`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.abstract_method import abstract_method from sage.categories.category_with_axiom import CategoryWithAxiom + class FinitePosets(CategoryWithAxiom): r""" The category of finite posets i.e. finite sets with a partial @@ -1949,5 +1950,5 @@ def directed_subsets(self, direction): [[]] """ if direction != 'up' and direction != 'down': - raise ValueError("Direction must be either 'up' or 'down'.") + raise ValueError("direction must be either 'up' or 'down'") return self.antichains().map(lambda elements: self.directed_subset(elements, direction)) diff --git a/src/sage/categories/function_fields.py b/src/sage/categories/function_fields.py index 3141500a511..6c30067e53f 100644 --- a/src/sage/categories/function_fields.py +++ b/src/sage/categories/function_fields.py @@ -42,7 +42,7 @@ def super_categories(self): sage: FunctionFields().super_categories() [Category of fields] """ - return[Fields()] + return [Fields()] def _call_(self, x): r""" diff --git a/src/sage/categories/functor.pyx b/src/sage/categories/functor.pyx index 77b95f0718c..ccf01cc9177 100644 --- a/src/sage/categories/functor.pyx +++ b/src/sage/categories/functor.pyx @@ -681,11 +681,9 @@ def ForgetfulFunctor(domain, codomain): sage: ForgetfulFunctor(abgrps, abgrps) == IdentityFunctor(abgrps) True - """ if domain == codomain: return IdentityFunctor(domain) if not domain.is_subcategory(codomain): raise ValueError("Forgetful functor not supported for domain %s" % domain) return ForgetfulFunctor_generic(domain, codomain) - diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index ef681dbb07a..97d57799f8c 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -233,11 +233,11 @@ def cayley_table(self, names='letters', elements=None): this can be used when the base set is infinite. OUTPUT: + An object representing the multiplication table. This is an :class:`~sage.matrix.operation_table.OperationTable` object and even more documentation can be found there. - EXAMPLES: Permutation groups, matrix groups and abelian groups diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index e063e388998..b8afdee2117 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -322,14 +322,9 @@ def q_dimension(self, q=None, prec=None, use_product=False): 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 + 9*q^7 + 13*q^8 + 16*q^9 + O(q^10) sage: qdim = C.q_dimension(); qdim - 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 - + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + O(x^11) - sage: qdim.compute_coefficients(15) - sage: qdim - 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 - + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + 27*q^11 - + 36*q^12 + 44*q^13 + 57*q^14 + 70*q^15 + O(x^16) - + 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 + O(q^7) + sage: qdim[:16] + [1, 1, 2, 2, 4, 5, 7, 9, 13, 16, 22, 27, 36, 44, 57, 70] """ from sage.rings.integer_ring import ZZ WLR = self.weight_lattice_realization() @@ -375,7 +370,7 @@ def iter_by_deg(gens): elif prec is None: # If we're here, we may not be a finite crystal. # In fact, we're probably infinite. - from sage.combinat.species.series import LazyPowerSeriesRing + from sage.rings.lazy_series_ring import LazyPowerSeriesRing if q is None: P = LazyPowerSeriesRing(ZZ, names='q') else: @@ -383,14 +378,13 @@ def iter_by_deg(gens): if not isinstance(P, LazyPowerSeriesRing): raise TypeError("the parent of q must be a lazy power series ring") ret = P(iter_by_deg(mg)) - ret.compute_coefficients(10) return ret from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic if q is None: q = PowerSeriesRing(ZZ, 'q', default_prec=prec).gen(0) P = q.parent() - ret = P.sum(c * q**deg for deg,c in enumerate(iter_by_deg(mg))) + ret = P.sum(c * q**deg for deg, c in enumerate(iter_by_deg(mg))) if ret.degree() == max_deg and isinstance(P, PowerSeriesRing_generic): ret = P(ret, prec) return ret diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 4fe021b86ca..1b488a03b69 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -47,8 +47,10 @@ - Simon King (2013-02): added examples """ + # **************************************************************************** -# Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu>, William Stein <wstein@gmail.com> +# Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu>, +# William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # @@ -62,7 +64,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.categories.category import Category, JoinCategory from . import morphism from sage.structure.parent import Parent, Set_generic @@ -80,13 +81,13 @@ from sage.structure.coerce_dict import TripleDict _cache = TripleDict(weak_values=True) + def Hom(X, Y, category=None, check=True): """ Create the space of homomorphisms from X to Y in the category ``category``. INPUT: - - ``X`` -- an object of a category - ``Y`` -- an object of a category @@ -734,7 +735,7 @@ def __hash__(self): """ return hash((self._domain, self._codomain, self.base())) - def __bool__(self): + def __bool__(self) -> bool: """ TESTS:: @@ -743,8 +744,6 @@ def __bool__(self): """ return True - - def homset_category(self): """ Return the category that this is a Hom in, i.e., this is typically @@ -1083,7 +1082,7 @@ def __ne__(self, other): True """ return not (self == other) - + def __contains__(self, x): """ Test whether the parent of the argument is ``self``. @@ -1143,7 +1142,7 @@ def identity(self): sage: H.identity() Traceback (most recent call last): ... - TypeError: Identity map only defined for endomorphisms. Try natural_map() instead. + TypeError: identity map only defined for endomorphisms; try natural_map() instead sage: H.natural_map() Natural morphism: From: Integer Ring @@ -1151,8 +1150,7 @@ def identity(self): """ if self.is_endomorphism_set(): return morphism.IdentityMorphism(self) - else: - raise TypeError("Identity map only defined for endomorphisms. Try natural_map() instead.") + raise TypeError("identity map only defined for endomorphisms; try natural_map() instead") def one(self): """ diff --git a/src/sage/categories/integral_domains.py b/src/sage/categories/integral_domains.py index 8cf87ffe94f..2d5d7730693 100644 --- a/src/sage/categories/integral_domains.py +++ b/src/sage/categories/integral_domains.py @@ -144,4 +144,3 @@ def _test_fraction_field(self, **options): class ElementMethods: pass - diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index c1f255a6eb9..975ea0491b3 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -774,12 +774,13 @@ def product(self, x, y): def __init_extra__(self): """ + EXAMPLES:: + sage: S = Semigroups().example("free") sage: S('a') * S('b') # indirect doctest 'ab' sage: S('a').__class__._mul_ == S('a').__class__._mul_parent True - """ # This should instead register the multiplication to the coercion model # But this is not yet implemented in the coercion model @@ -856,6 +857,7 @@ def multiplication_table(self, names='letters', elements=None): this can be used when the base set is infinite. OUTPUT: + The multiplication table as an object of the class :class:`~sage.matrix.operation_table.OperationTable` which defines several methods for manipulating and @@ -1126,8 +1128,8 @@ def product(self, x, y): sage: B[3] * B[2] 4*B[2] + 6*B[3] + 5*B[6] """ - assert(x in self) - assert(y in self) + assert x in self + assert y in self return self.retract(self.lift(x) * self.lift(y)) class Realizations(RealizationsCategory): diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index 1bb34eabf3b..a569cc83849 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -11,16 +11,15 @@ AUTHORS: - Sebastian Oehms (2019-01-19): :meth:`section` added to :class:`FormalCompositeMap`. See :trac:`27081`. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu> # # 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 . import homset import weakref @@ -52,6 +51,7 @@ def unpickle_map(_class, parent, _dict, _slots): mor.__dict__ = _dict return mor + def is_Map(x): """ Auxiliary function: Is the argument a map? @@ -66,6 +66,7 @@ def is_Map(x): """ return isinstance(x, Map) + cdef class Map(Element): """ Basic class for all maps. @@ -1214,7 +1215,7 @@ cdef class Map(Element): sage: psi^2 Traceback (most recent call last): ... - TypeError: self must be an endomorphism. + TypeError: self must be an endomorphism sage: K.<a> = NumberField(x^4 - 5*x + 5) sage: C5.<z> = CyclotomicField(5) @@ -1230,7 +1231,7 @@ cdef class Map(Element): Defn: z |--> 3/11*a^3 + 4/11*a^2 + 9/11*a - 14/11 """ if self.domain() is not self._codomain and n != 1 and n != -1: - raise TypeError("self must be an endomorphism.") + raise TypeError("self must be an endomorphism") if n == 0: from sage.categories.morphism import IdentityMorphism return IdentityMorphism(self._parent) @@ -1870,7 +1871,7 @@ cdef class FormalCompositeMap(Map): sage: c2.is_injective() Traceback (most recent call last): ... - NotImplementedError: Not enough information to deduce injectivity. + NotImplementedError: not enough information to deduce injectivity If the first map is surjective and the second map is not injective, then the composition is not injective:: @@ -1911,7 +1912,7 @@ cdef class FormalCompositeMap(Map): if all(f.is_surjective() for f in injectives): return False - raise NotImplementedError("Not enough information to deduce injectivity.") + raise NotImplementedError("not enough information to deduce injectivity") def is_surjective(self): """ @@ -1953,7 +1954,7 @@ cdef class FormalCompositeMap(Map): ....: V2.hom(Matrix([[1], [1]]), V1)).is_surjective() Traceback (most recent call last): ... - NotImplementedError: Not enough information to deduce surjectivity. + NotImplementedError: not enough information to deduce surjectivity """ try: # we try the category first @@ -1977,7 +1978,7 @@ cdef class FormalCompositeMap(Map): if all(f.is_injective() for f in surjectives): return False - raise NotImplementedError("Not enough information to deduce surjectivity.") + raise NotImplementedError("not enough information to deduce surjectivity") def domains(self): """ diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 26b51ef4b87..99b57ff8c8e 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -2126,9 +2126,9 @@ def tensor(*elements): FIXME: is this a policy that we want to enforce on all parents? """ - assert(all(isinstance(element, Element) for element in elements)) + assert all(isinstance(element, Element) for element in elements) parents = [parent(element) for element in elements] - return tensor(parents)._tensor_of_elements(elements) # good name??? + return tensor(parents)._tensor_of_elements(elements) # good name ? class Homsets(HomsetsCategory): class ParentMethods: diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 93638b04078..dc548a72e66 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -556,18 +556,17 @@ def algebra_generators(self): sage: Z12.semigroup_generators() Family (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: Z12.algebra(QQ).algebra_generators() - Finite family {0: B[0], 1: B[1], 2: B[2], 3: B[3], 4: B[4], 5: B[5], - 6: B[6], 7: B[7], 8: B[8], 9: B[9], 10: B[10], 11: B[11]} + Family (B[0], B[1], B[2], B[3], B[4], B[5], B[6], B[7], B[8], B[9], B[10], B[11]) sage: GroupAlgebras(QQ).example(AlternatingGroup(10)).algebra_generators() - Finite family {0: (8,9,10), 1: (1,2,3,4,5,6,7,8,9)} + Family ((8,9,10), (1,2,3,4,5,6,7,8,9)) sage: A = DihedralGroup(3).algebra(QQ); A Algebra of Dihedral group of order 6 as a permutation group over Rational Field sage: A.algebra_generators() - Finite family {0: (1,2,3), 1: (1,3)} + Family ((1,2,3), (1,3)) """ monoid = self.basis().keys() try: diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py index 69fe01147f4..c29e4c57255 100644 --- a/src/sage/categories/number_fields.py +++ b/src/sage/categories/number_fields.py @@ -209,5 +209,25 @@ def zeta_function(self, prec=53, raise ValueError('algorithm must be "gp" or "pari"') + def _test_absolute_disc(self, **options): + r""" + Run basic tests for the method :meth:`absolute_discriminant` of ``self``. + + See the documentation for :class:`TestSuite` for information on + further options. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES:: + + sage: S = NumberField(x**3-x-1, 'a') + sage: S._test_absolute_disc() + """ + from sage.rings.integer import Integer + tester = self._tester(**options) + tester.assertIsInstance(self.absolute_discriminant(), Integer) + class ElementMethods: pass diff --git a/src/sage/categories/poor_man_map.py b/src/sage/categories/poor_man_map.py index 507705fb9f9..d0fca4b658d 100644 --- a/src/sage/categories/poor_man_map.py +++ b/src/sage/categories/poor_man_map.py @@ -14,6 +14,7 @@ # **************************************************************************** import sage.structure.sage_object + class PoorManMap(sage.structure.sage_object.SageObject): """ A class for maps between sets which are not (yet) modeled by parents @@ -184,6 +185,7 @@ def __mul__(self, other): Composition INPUT: + - ``self`` -- a map `f` - ``other`` -- a map `g` @@ -200,12 +202,12 @@ def __mul__(self, other): Note that the compatibility of the domains and codomains is for performance reasons only checked for proper parents. For example, the incompatibility is not detected here:: - + sage: f*g A map from (2, 3, 4) to (2, 3, 4) - + But it is detected here:: - + sage: g = PoorManMap(factorial, domain = ZZ, codomain = ZZ) sage: h = PoorManMap(sqrt, domain = RR, codomain = CC) sage: g*h @@ -214,7 +216,6 @@ def __mul__(self, other): ValueError: the codomain Complex Field with 53 bits of precision does not coerce into the domain Integer Ring sage: h*g A map from Integer Ring to Complex Field with 53 bits of precision - """ self_domain = self.domain() @@ -227,7 +228,7 @@ def __mul__(self, other): from sage.structure.parent import is_Parent if is_Parent(self_domain) and is_Parent(other_codomain): if not self_domain.has_coerce_map_from(other_codomain): - raise ValueError("the codomain %r does not coerce into the domain %r"%(other_codomain, self_domain)) + raise ValueError("the codomain %r does not coerce into the domain %r" % (other_codomain, self_domain)) codomain = self.codomain() try: diff --git a/src/sage/categories/posets.py b/src/sage/categories/posets.py index b09d920c863..b1a2ba1942d 100644 --- a/src/sage/categories/posets.py +++ b/src/sage/categories/posets.py @@ -1,19 +1,19 @@ r""" Posets """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** - +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method from sage.misc.lazy_import import LazyImport from sage.categories.category import Category from sage.categories.sets_cat import Sets + class Posets(Category): r""" The category of posets i.e. sets with a partial order structure. @@ -326,12 +326,20 @@ def directed_subset(self, elements, direction): [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] sage: B.directed_subset([7, 10], 'down') [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] + + TESTS:: + + sage: B = posets.BooleanLattice(3) + sage: B.directed_subset([3, 1], 'banana') + Traceback (most recent call last): + ... + ValueError: direction must be either 'up' or 'down' """ if direction == 'up': return self.order_filter(elements) if direction == 'down': return self.order_ideal(elements) - raise ValueError("Direction must be either 'up' or 'down'.") + raise ValueError("direction must be either 'up' or 'down'") def principal_order_ideal(self, x): r""" diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 3136160f39d..86e33e45b1e 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -1,7 +1,6 @@ """ Coercion via construction functors """ - # **************************************************************************** # Copyright (C) 2007-2014 Robert Bradshaw # 2007-2018 David Roe @@ -833,7 +832,7 @@ class PolynomialFunctor(ConstructionFunctor): """ rank = 9 - def __init__(self, var, multi_variate=False, sparse=False): + def __init__(self, var, multi_variate=False, sparse=False, implementation=None): """ TESTS:: @@ -857,6 +856,7 @@ def __init__(self, var, multi_variate=False, sparse=False): self.var = var self.multi_variate = multi_variate self.sparse = sparse + self.implementation = implementation def _apply_functor(self, R): """ @@ -870,7 +870,10 @@ def _apply_functor(self, R): """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - return PolynomialRing(R, self.var, sparse=self.sparse) + kwds = {} + if self.implementation: + kwds['implementation'] = self.implementation + return PolynomialRing(R, self.var, sparse=self.sparse, **kwds) def _apply_functor_to_morphism(self, f): """ @@ -1289,10 +1292,9 @@ def _apply_functor_to_morphism(self, f): sage: R.construction()[0](f) # indirect doctest Traceback (most recent call last): ... - NotImplementedError: Morphisms for infinite polynomial rings are not implemented yet. - + NotImplementedError: morphisms for infinite polynomial rings are not implemented yet """ - raise NotImplementedError("Morphisms for infinite polynomial rings are not implemented yet.") + raise NotImplementedError("morphisms for infinite polynomial rings are not implemented yet") def _apply_functor(self, R): """ @@ -1349,7 +1351,7 @@ def __ne__(self, other): sage: F != sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse') True """ - return not(self == other) + return not (self == other) __hash__ = ConstructionFunctor.__hash__ @@ -2851,7 +2853,7 @@ def __init__(self, I, names=None, as_field=False, domain=None, if codomain is None: codomain = Rings() Functor.__init__(self, domain, codomain) - + self.I = I if names is None: self.names = None @@ -3021,7 +3023,7 @@ def merge(self, other): # quotient by I would result in the trivial ring/group/... # Rather than create the zero ring, we claim they can't be merged # TODO: Perhaps this should be detected at a higher level... - raise TypeError("Trivial quotient intersection.") + raise TypeError("trivial quotient intersection") # GF(p) has a coercion from Integers(p). Hence, merging should # yield a field if either self or other yields a field. return QuotientFunctor(I, names=self.names, as_field=as_field, @@ -4730,10 +4732,10 @@ def type_to_parent(P): sage: type_to_parent(list) Traceback (most recent call last): ... - TypeError: Not a scalar type. + TypeError: not a scalar type """ from sage.structure.coerce import py_scalar_parent parent = py_scalar_parent(P) if parent is None: - raise TypeError("Not a scalar type.") + raise TypeError("not a scalar type") return parent diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 88ce6ef5bc0..6a7589bb109 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1,22 +1,22 @@ r""" Rings """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu> # William Stein <wstein@math.ucsd.edu> # 2008 Teresa Gomez-Diaz (CNRS) <Teresa.Gomez-Diaz@univ-mlv.fr> # 2008-2011 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from functools import reduce 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 functools import reduce class Rings(CategoryWithAxiom): @@ -49,19 +49,18 @@ class Rings(CategoryWithAxiom): .. TODO:: - (see: http://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) + (see :trac:`sage_trac/wiki/CategoriesRoadMap`) - Make Rings() into a subcategory or alias of Algebras(ZZ); - A parent P in the category ``Rings()`` should automatically be in the category ``Algebras(P)``. """ - _base_category_class_and_axiom = (Rngs, "Unital") class MorphismMethods: @cached_method - def is_injective(self): + def is_injective(self) -> bool: """ Return whether or not this morphism is injective. @@ -186,7 +185,7 @@ def is_injective(self): raise NotImplementedError - def _is_nonzero(self): + def _is_nonzero(self) -> bool: r""" Return whether this is not the zero morphism. @@ -255,7 +254,6 @@ def extend_to_fraction_field(self): parent = domain.Hom(codomain) # category = category=self.category_for() ??? return RingHomomorphism_from_fraction_field(parent, self) - class SubcategoryMethods: def NoZeroDivisors(self): @@ -273,11 +271,6 @@ def NoZeroDivisors(self): sage: Rings().NoZeroDivisors() Category of domains - .. NOTE:: - - This could be generalized to - :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. - TESTS:: sage: TestSuite(Rings().NoZeroDivisors()).run() @@ -293,11 +286,6 @@ def Division(self): A ring satisfies the *division axiom* if all non-zero elements have multiplicative inverses. - .. NOTE:: - - This could be generalized to - :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. - EXAMPLES:: sage: Rings().Division() @@ -313,15 +301,14 @@ def Division(self): """ return self._with_axiom('Division') - NoZeroDivisors = LazyImport('sage.categories.domains', 'Domains', at_startup=True) - Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) - Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) + Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) + Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) class ParentMethods: - def is_ring(self): + def is_ring(self) -> bool: """ - Return True, since this in an object of the category of rings. + Return ``True``, since this in an object of the category of rings. EXAMPLES:: @@ -330,7 +317,7 @@ def is_ring(self): """ return True - def is_zero(self): + def is_zero(self) -> bool: """ Return ``True`` if this is the zero ring. @@ -355,11 +342,11 @@ def is_zero(self): def bracket(self, x, y): """ - Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. + Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`. INPUT: - - ``x``, ``y`` -- elements of ``self`` + - ``x``, ``y`` -- elements of ``self`` EXAMPLES:: @@ -377,11 +364,11 @@ def bracket(self, x, y): sage: F.bracket( F.bracket(a,b), c) + F.bracket(F.bracket(b,c),a) + F.bracket(F.bracket(c,a),b) 0 """ - return x*y - y*x + return x * y - y * x def _Hom_(self, Y, category): r""" - Returns the homset from ``self`` to ``Y`` in the category ``category`` + Return the homset from ``self`` to ``Y`` in the category ``category``. INPUT: @@ -415,9 +402,9 @@ def _Hom_(self, Y, category): sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest """ if category is not None and not category.is_subcategory(Rings()): - raise TypeError("%s is not a subcategory of Rings()"%category) + raise TypeError(f"{category} is not a subcategory of Rings()") if Y not in Rings(): - raise TypeError("%s is not a ring" % Y) + raise TypeError(f"{Y} is not a ring") from sage.rings.homset import RingHomset return RingHomset(self, Y, category=category) @@ -428,16 +415,15 @@ def _mul_(self, x, switch_sides=False): """ Multiplication of rings with, e.g., lists. - NOTE: + .. NOTE:: - This method is used to create ideals. It is - the same as the multiplication method for - :class:`~sage.rings.ring.Ring`. However, not - all parents that belong to the category of - rings also inherits from the base class of - rings. Therefore, we implemented a ``__mul__`` - method for parents, that calls a ``_mul_`` - method implemented here. See :trac:`7797`. + This method is used to create ideals. It is the same + as the multiplication method for + :class:`~sage.rings.ring.Ring`. However, not all + parents that belong to the category of rings also + inherits from the base class of rings. Therefore, we + implemented a ``__mul__`` method for parents, that + calls a ``_mul_`` method implemented here. See :trac:`7797`. INPUT: @@ -480,12 +466,11 @@ def _mul_(self, x, switch_sides=False): AUTHOR: - Simon King (2011-03-22) - """ try: if self.is_commutative(): return self.ideal(x) - except (AttributeError,NotImplementedError): + except (AttributeError, NotImplementedError): pass try: side = x.side() @@ -495,19 +480,19 @@ def _mul_(self, x, switch_sides=False): try: x = x.gens() except (AttributeError, NotImplementedError): - pass # ... not an ideal + pass # ... not an ideal if switch_sides: - if side in ['right','twosided']: - return self.ideal(x,side=side) - elif side=='left': - return self.ideal(x,side='twosided') + if side in ['right', 'twosided']: + return self.ideal(x, side=side) + elif side == 'left': + return self.ideal(x, side='twosided') else: - if side in ['left','twosided']: - return self.ideal(x,side=side) - elif side=='right': - return self.ideal(x,side='twosided') + if side in ['left', 'twosided']: + return self.ideal(x, side=side) + elif side == 'right': + return self.ideal(x, side='twosided') # duck typing failed - raise TypeError("Don't know how to transform %s into an ideal of %s"%(x,self)) + raise TypeError("do not know how to transform %s into an ideal of %s" % (x, self)) def __pow__(self, n): """ @@ -537,12 +522,12 @@ def ideal_monoid(self): """ The monoid of the ideals of this ring. - NOTE: + .. NOTE:: - The code is copied from the base class of rings. - This is since there are rings that do not inherit - from that class, such as matrix algebras. See - :trac:`7797`. + The code is copied from the base class of rings. + This is since there are rings that do not inherit + from that class, such as matrix algebras. See + :trac:`7797`. EXAMPLES:: @@ -559,7 +544,6 @@ def ideal_monoid(self): sage: MS.ideal_monoid() is MS.ideal_monoid() True - """ try: from sage.rings.ideal_monoid import IdealMonoid @@ -602,7 +586,8 @@ def _test_characteristic(self, **options): try: characteristic = self.characteristic() except AttributeError: - return # raised when self.one() does not have a additive_order() + # raised when self.one() does not have a additive_order() + return except NotImplementedError: return @@ -614,13 +599,13 @@ def ideal(self, *args, **kwds): """ Create an ideal of this ring. - NOTE: + .. NOTE:: - 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 :trac:`7797`. + 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 :trac:`7797`. INPUT: @@ -656,7 +641,6 @@ def ideal(self, *args, **kwds): [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field - """ if 'coerce' in kwds: coerce = kwds['coerce'] @@ -690,12 +674,12 @@ def ideal(self, *args, **kwds): else: try: if self.has_coerce_map_from(first): - gens = first.gens() # we have a ring as argument + 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 + raise ArithmeticError("there is no coercion from %s to %s" % (first, self)) + except TypeError: # first may be a ring element pass break if coerce: @@ -706,7 +690,7 @@ def ideal(self, *args, **kwds): 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. + g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. except (AttributeError, NotImplementedError): pass else: @@ -722,15 +706,15 @@ def ideal(self, *args, **kwds): gens = gens[0] return C(self, gens, **kwds) - def _ideal_class_(self,n=0): + def _ideal_class_(self, n=0): """ Return the class that is used to implement ideals of this ring. - NOTE: + .. NOTE:: - We copy the code from :class:`~sage.rings.ring.Ring`. This is - necessary because not all rings inherit from that class, such - as matrix algebras. + We copy the code from :class:`~sage.rings.ring.Ring`. This is + necessary because not all rings inherit from that class, such + as matrix algebras. INPUT: @@ -742,10 +726,10 @@ def _ideal_class_(self,n=0): The class that is used to implement ideals of this ring with ``n`` generators. - NOTE: + .. NOTE:: - Often principal ideals (``n==1``) are implemented via a different - class. + Often principal ideals (``n==1``) are implemented via + a different class. EXAMPLES:: @@ -753,7 +737,7 @@ def _ideal_class_(self,n=0): sage: MS._ideal_class_() <class 'sage.rings.noncommutative_ideals.Ideal_nc'> - We don't know of a commutative ring in Sage that does not inherit + We do not know of a commutative ring in Sage that does not inherit from the base class of rings. So, we need to cheat in the next example:: @@ -765,19 +749,17 @@ def _ideal_class_(self,n=0): <class 'sage.rings.ideal.Ideal_principal'> sage: super(Ring,QQ)._ideal_class_(2) <class 'sage.rings.ideal.Ideal_generic'> - """ from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc - except (NotImplementedError,AttributeError): + except (NotImplementedError, AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal - else: - return Ideal_generic + return Ideal_generic ## # Quotient rings @@ -848,9 +830,9 @@ def quo(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. - NOTE: + .. NOTE:: - This is a synonym for :meth:`quotient`. + This is a synonym for :meth:`quotient`. EXAMPLES:: @@ -894,15 +876,15 @@ def quo(self, I, names=None, **kwds): sage: a == b False """ - return self.quotient(I,names=names,**kwds) + return self.quotient(I, names=names, **kwds) def quotient_ring(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. - NOTE: + .. NOTE:: - This is a synonym for :meth:`quotient`. + This is a synonym for :meth:`quotient`. INPUT: @@ -980,14 +962,14 @@ def __truediv__(self, I): sage: MS/I Traceback (most recent call last): ... - TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. + TypeError: use self.quotient(I) to construct the quotient ring sage: QQ['x'] / ZZ Traceback (most recent call last): ... - TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. + TypeError: use self.quotient(I) to construct the quotient ring """ - raise TypeError("Use self.quo(I) or self.quotient(I) to construct the quotient ring.") + raise TypeError("use self.quotient(I) to construct the quotient ring") def __getitem__(self, arg): """ @@ -1153,13 +1135,12 @@ def __getitem__(self, arg): """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): - # Allowing arbitrary iterables would create confusion, but we - # may want to support a few more. + # Allowing arbitrary iterables would create confusion, + # but we may want to support a few more. return tuple(arg) - elif isinstance(arg, str): + if isinstance(arg, str): return tuple(arg.split(',')) - else: - return (arg,) + return (arg,) # 1. If arg is a list, try to return a power series ring. @@ -1221,7 +1202,7 @@ def normalize_arg(arg): # right ordered ring structure. from sage.rings.real_lazy import CLF, RLF if (iv.imag().is_zero() or iv.imag().contains_zero() - and elt.imag().is_zero()): + and elt.imag().is_zero()): emb = RLF(elt) else: emb = CLF(elt) @@ -1289,11 +1270,11 @@ def free_module(self, base=None, basis=None, map=True): if basis is not None: if isinstance(basis, (list, tuple)): if len(basis) != 1: - raise ValueError("Basis must have length 1") + raise ValueError("basis must have length 1") basis = basis[0] basis = self(basis) if not basis.is_unit(): - raise ValueError("Basis element must be a unit") + raise ValueError("basis element must be a unit") from sage.modules.free_module_morphism import BaseIsomorphism1D_from_FM, BaseIsomorphism1D_to_FM Hfrom = V.Hom(self) Hto = self.Hom(V) @@ -1306,7 +1287,7 @@ def free_module(self, base=None, basis=None, map=True): raise NotImplementedError class ElementMethods: - def is_unit(self): + def is_unit(self) -> bool: r""" Return whether this element is a unit in the ring. @@ -1314,7 +1295,7 @@ def is_unit(self): This is a generic implementation for (non-commutative) rings which only works for the one element, its additive inverse, and - the zero element. Most rings should provide a more specialized + the zero element. Most rings should provide a more specialized implementation. EXAMPLES:: @@ -1329,7 +1310,7 @@ def is_unit(self): """ if self.is_one() or (-self).is_one(): return True - if self.is_zero(): # now 0 != 1 + if self.is_zero(): # now 0 != 1 return False raise NotImplementedError @@ -1369,7 +1350,6 @@ def inverse_of_unit(self): Rational Field sage: (1/a).parent() Rational Field - """ try: if not self.is_unit(): @@ -1399,8 +1379,6 @@ def _divide_if_possible(self, y): sage: _.parent() Integer Ring - :: - sage: 4._divide_if_possible(3) Traceback (most recent call last): ... @@ -1408,9 +1386,10 @@ def _divide_if_possible(self, y): """ q, r = self.quo_rem(y) if r != 0: - raise ValueError("%s is not divisible by %s"%(self, y)) + raise ValueError("%s is not divisible by %s" % (self, y)) return q + def _gen_names(elts): r""" Used to find a name for a generator when rings are created using the @@ -1419,9 +1398,9 @@ def _gen_names(elts): EXAMPLES:: sage: from sage.categories.rings import _gen_names - sage: list(_gen_names([sqrt(5)])) # optional - sage.symbolic + sage: list(_gen_names([sqrt(5)])) # optional - sage.symbolic ['sqrt5'] - sage: list(_gen_names([sqrt(-17), 2^(1/3)])) # optional - sage.symbolic + sage: list(_gen_names([sqrt(-17), 2^(1/3)])) # optional - sage.symbolic ['a', 'b'] sage: list(_gen_names((1..27)))[-1] 'aa' @@ -1430,7 +1409,7 @@ def _gen_names(elts): from sage.structure.category_object import certify_names from sage.combinat.words.words import Words it = iter(Words("abcdefghijklmnopqrstuvwxyz", infinite=False)) - next(it) # skip empty word + next(it) # skip empty word for x in elts: name = str(x) m = re.match(r'^sqrt\((\d+)\)$', name) diff --git a/src/sage/categories/schemes.py b/src/sage/categories/schemes.py index bfd7eb4369b..df2ec0603cf 100644 --- a/src/sage/categories/schemes.py +++ b/src/sage/categories/schemes.py @@ -137,11 +137,11 @@ def _call_(self, x): from sage.schemes.generic.morphism import is_SchemeMorphism if is_SchemeMorphism(x): return x - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.spec import Spec from sage.categories.map import Map from sage.categories.all import Rings - if isinstance(x, CommutativeRing): + if x in CommutativeRings(): return Spec(x) elif isinstance(x, Map) and x.category_for().is_subcategory(Rings()): # x is a morphism of Rings diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 007401e5474..d053b20e3c0 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -882,7 +882,7 @@ def algebra_generators(self): sage: M.semigroup_generators() Family ('a', 'b', 'c', 'd') sage: M.algebra(ZZ).algebra_generators() - Finite family {0: B['a'], 1: B['b'], 2: B['c'], 3: B['d']} + Family (B['a'], B['b'], B['c'], B['d']) """ return self.basis().keys().semigroup_generators().map(self.monomial) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index d096a950773..9f9bab84e23 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1616,7 +1616,7 @@ def algebra(self, base_ring, category=None, **kwds): of `S`, or ``None`` This returns the space of formal linear combinations of - elements of `G` with coefficients in `R`, endowed with + elements of `S` with coefficients in `K`, endowed with whatever structure can be induced from that of `S`. See the documentation of :mod:`sage.categories.algebra_functor` for details. @@ -1878,13 +1878,14 @@ def image(self, domain_subset=None): cls = ImageSubobject return cls(self, domain_subset) + # Lazy imports to avoid circularity issues. Enumerated = LazyImport('sage.categories.enumerated_sets', 'EnumeratedSets', at_startup=True) - Facade = LazyImport('sage.categories.facade_sets', 'FacadeSets') Finite = LazyImport('sage.categories.finite_sets', 'FiniteSets', at_startup=True) Topological = LazyImport('sage.categories.topological_spaces', 'TopologicalSpaces', 'Topological', at_startup=True) Metric = LazyImport('sage.categories.metric_spaces', 'MetricSpaces', 'Metric', at_startup=True) + from sage.categories.facade_sets import FacadeSets as Facade class Infinite(CategoryWithAxiom): diff --git a/src/sage/categories/sets_with_grading.py b/src/sage/categories/sets_with_grading.py index 8ef7b8b0256..5d01bfab999 100644 --- a/src/sage/categories/sets_with_grading.py +++ b/src/sage/categories/sets_with_grading.py @@ -212,11 +212,21 @@ def generating_series(self): Non negative integers sage: N.generating_series() 1/(-z + 1) + + sage: Permutations().generating_series() + 1 + z + 2*z^2 + 6*z^3 + 24*z^4 + 120*z^5 + 720*z^6 + O(z^7) + + .. TODO:: + + - Very likely, this should always return a lazy power series. """ - from sage.combinat.species.series import LazyPowerSeriesRing + from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.rings.lazy_series_ring import LazyPowerSeriesRing from sage.rings.integer_ring import ZZ - R = LazyPowerSeriesRing(ZZ) - R(self.graded_component(grade).cardinality() for grade in self.grading_set()) + if isinstance(self.grading_set(), NonNegativeIntegers): + R = LazyPowerSeriesRing(ZZ, names="z") + return R(lambda n: self.graded_component(n).cardinality()) + raise NotImplementedError # TODO: # * asymptotic behavior: we need an object for asymptotic behavior and diff --git a/src/sage/categories/super_lie_conformal_algebras.py b/src/sage/categories/super_lie_conformal_algebras.py index cc6167d6830..849a0fbdb13 100644 --- a/src/sage/categories/super_lie_conformal_algebras.py +++ b/src/sage/categories/super_lie_conformal_algebras.py @@ -47,7 +47,7 @@ def extra_super_categories(self): sage: LieConformalAlgebras(QQ).Super().super_categories() [Category of super modules over Rational Field, - Category of Lambda bracket algebras over Rational Field] + Category of Lambda bracket algebras over Rational Field] """ return [LambdaBracketAlgebras(self.base_ring())] diff --git a/src/sage/categories/unique_factorization_domains.py b/src/sage/categories/unique_factorization_domains.py index 49a41b97098..280320474d8 100644 --- a/src/sage/categories/unique_factorization_domains.py +++ b/src/sage/categories/unique_factorization_domains.py @@ -1,12 +1,12 @@ r""" Unique factorization domains """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) <Teresa.Gomez-Diaz@univ-mlv.fr> # # 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_attribute import lazy_class_attribute from sage.misc.misc_c import prod @@ -14,11 +14,14 @@ from sage.categories.category_singleton import Category_contains_method_by_parent_class from sage.categories.gcd_domains import GcdDomains + class UniqueFactorizationDomains(Category_singleton): """ - The category of unique factorization domains - constructive unique factorization domains, i.e. where one can constructively - factor members into a product of a finite number of irreducible elements + The category of (constructive) unique factorization domains. + + In a constructive unique factorization domain we can + constructively factor members into a product of a finite number + of irreducible elements. EXAMPLES:: @@ -30,6 +33,7 @@ class UniqueFactorizationDomains(Category_singleton): TESTS:: sage: TestSuite(UniqueFactorizationDomains()).run() + """ def super_categories(self): @@ -239,7 +243,7 @@ def radical(self, *args, **kwds): sage: Integer(0).radical() Traceback (most recent call last): ... - ArithmeticError: Radical of 0 not defined. + ArithmeticError: radical of 0 is not defined The next example shows how to compute the radical of a number, assuming no prime > 100000 has exponent > 1 in the factorization:: @@ -256,7 +260,7 @@ def radical(self, *args, **kwds): 10 """ if self.is_zero(): - raise ArithmeticError("Radical of 0 not defined.") + raise ArithmeticError("radical of 0 is not defined") try: decomp = self.squarefree_decomposition() except AttributeError: diff --git a/src/sage/categories/weyl_groups.py b/src/sage/categories/weyl_groups.py index 47ca72306f0..c6b0338e441 100644 --- a/src/sage/categories/weyl_groups.py +++ b/src/sage/categories/weyl_groups.py @@ -1,10 +1,5 @@ r""" Weyl Groups - -REFERENCES: - -.. [Dye] Dyer. *Bruhat intervals, polyhedral cones and Kazhdan-Lusztig-Stanley polynomials*. Math.Z., 215(2):223-236, 1994. -.. [JahStu] Jahn and Stump. *Bruhat intervals, subword complexes and brick polyhedra for finite Coxeter groups*. Preprint, available at :arxiv:`2103.03715`, 2021. """ #***************************************************************************** # Copyright (C) 2009 Nicolas M. Thiery <nthiery at users.sf.net> @@ -155,7 +150,7 @@ def pieri_factors(self, *args, **keywords): if hasattr(ct, "PieriFactors"): return ct.PieriFactors(self, *args, **keywords) raise NotImplementedError("Pieri factors for type {}".format(ct)) - + def bruhat_cone(self, x, y, side='upper', backend='cdd'): r""" Return the (upper or lower) Bruhat cone associated to the interval ``[x,y]``. @@ -207,8 +202,8 @@ def bruhat_cone(self, x, y, side='upper', backend='cdd'): REFERENCES: - - [Dye]_ - - [JahStu]_ + - [Dy1994]_ + - [JS2021]_ """ from sage.modules.free_module_element import vector if side == 'upper': diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index ba2ec68a038..238a165c021 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -123,7 +123,7 @@ def _explain_constructor(cl): reqs = "The constructor requires the arguments {}.".format(args) else: reqs = "The constructor requires no arguments." - if argspec.varargs or argspec.keywords: + if argspec.varargs or argspec.varkw: var = "It accepts unspecified arguments as well.\n" else: var = "" diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx index b1833b7a34a..54e3c1d8810 100644 --- a/src/sage/coding/ag_code_decoders.pyx +++ b/src/sage/coding/ag_code_decoders.pyx @@ -2636,4 +2636,3 @@ cdef class DifferentialAGCodeDecoder_K_extension(Decoder_K_extension): sage: TestSuite(circuit).run(skip='_test_pickling') # long time """ super().__init__(pls, G, Q, DifferentialAGCodeDecoder_K, verbose=verbose) - diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 66192643aae..408c1f55634 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -4093,7 +4093,7 @@ cdef class BinaryCodeClassifier: from sage.rings.integer_ring import ZZ from sage.groups.perm_gps.permgroup import PermutationGroup from sage.groups.perm_gps.constructor import PermutationGroupElement - from sage.interfaces.gap import gap + from sage.libs.gap.libgap import libgap rs = [] for i from 0 <= i < B.nrows: r = [] @@ -4104,7 +4104,7 @@ cdef class BinaryCodeClassifier: m_aut_gp_gens, m_labeling, m_size, m_base = self._aut_gp_and_can_label(m) from sage.arith.all import factorial - if True:#size*factorial(n-B.ncols) == m_size: + if True: # size*factorial(n-B.ncols) == m_size: if len(m_aut_gp_gens) == 0: aut_m = PermutationGroup([()]) @@ -4112,17 +4112,13 @@ cdef class BinaryCodeClassifier: aut_m = PermutationGroup([PermutationGroupElement([a+1 for a in g]) for g in m_aut_gp_gens]) if len(aug_aut_gp_gens) == 0: - aut_B_aug = PermutationGroup([()]) + aut_B_aug = libgap(PermutationGroup([()])) else: - aut_B_aug = PermutationGroup([PermutationGroupElement([a+1 for a in g]) for g in aug_aut_gp_gens]) - H = aut_m._gap_(gap).Intersection2(aut_B_aug._gap_(gap)) - rt_transversal = list(gap('List(RightTransversal( %s,%s ));'\ - %(str(aut_B_aug.__interface[gap]),str(H)))) - rt_transversal = [PermutationGroupElement(g) for g in rt_transversal if str(g) != '()'] - rt_transversal = [[a-1 for a in g.domain()] for g in rt_transversal] - rt_transversal = [g + list(xrange(len(g), n)) - for g in rt_transversal] + aut_B_aug = libgap(PermutationGroup([PermutationGroupElement([a+1 for a in g]) for g in aug_aut_gp_gens])) + H = libgap(aut_m).Intersection2(aut_B_aug) + rt_transversal = [[int(a) - 1 for a in g.ListPerm(n)] for g in aut_B_aug.RightTransversal(H) if not g.IsOne()] rt_transversal.append(list(xrange(n))) + bingo2 = 0 for coset_rep in rt_transversal: hwp = create_word_perm(coset_rep) @@ -4182,6 +4178,3 @@ cdef class BinaryCodeClassifier: sig_free(ortho_basis) sig_free(temp_basis) return output - - - diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index f143aae7765..430f3a44de1 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -180,6 +180,7 @@ from sage.misc.functional import sqrt, log from .delsarte_bounds import (delsarte_bound_hamming_space, delsarte_bound_additive_hamming_space) +from sage.features.gap import GapPackage def _check_n_q_d(n, q, d, field_based=True): @@ -277,6 +278,7 @@ def codesize_upper_bound(n, d, q, algorithm=None): """ _check_n_q_d(n, q, d, field_based=False) if algorithm == "gap": + GapPackage("guava", spkg="gap_packages").require() libgap.load_package('guava') return int(libgap.UpperBound(n, d, q)) if algorithm == "LP": @@ -377,6 +379,7 @@ def plotkin_upper_bound(n,q,d, algorithm=None): """ _check_n_q_d(n, q, d, field_based=False) if algorithm == "gap": + GapPackage("guava", spkg="gap_packages").require() libgap.load_package("guava") return QQ(libgap.UpperBoundPlotkin(n, d, q)) else: @@ -433,6 +436,7 @@ def griesmer_upper_bound(n,q,d,algorithm=None): """ _check_n_q_d(n, q, d) if algorithm == "gap": + GapPackage("guava", spkg="gap_packages").require() libgap.load_package("guava") return QQ(libgap.UpperBoundGriesmer(n, d, q)) else: @@ -467,6 +471,7 @@ def elias_upper_bound(n,q,d,algorithm=None): _check_n_q_d(n, q, d, field_based=False) r = 1-1/q if algorithm == "gap": + GapPackage("guava", spkg="gap_packages").require() libgap.load_package("guava") return QQ(libgap.UpperBoundElias(n, d, q)) else: diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 8483d29637c..ec50c75dd9c 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -731,11 +731,16 @@ def ToricCode(P,F): [36, 5] linear code over GF(7) sage: C.minimum_distance() 24 + sage: C.minimum_distance(algorithm="guava") # optional - gap_packages (Guava package) + ... + 24 sage: C = codes.ToricCode([[-2,-2],[-1,-2],[-1,-1],[-1,0],[0,-1],[0,0],[0,1],[1,-1],[1,0]],GF(5)) sage: C [16, 9] linear code over GF(5) sage: C.minimum_distance() 6 + sage: C.minimum_distance(algorithm="guava") # optional - gap_packages (Guava package) + 6 sage: C = codes.ToricCode([ [0,0],[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[3,1],[3,2],[4,1]],GF(8,"a")) sage: C [49, 11] linear code over GF(8) diff --git a/src/sage/coding/cyclic_code.py b/src/sage/coding/cyclic_code.py index a5c3a4c88fb..93ce9730f49 100644 --- a/src/sage/coding/cyclic_code.py +++ b/src/sage/coding/cyclic_code.py @@ -47,6 +47,7 @@ from sage.misc.cachefunc import cached_method from sage.rings.all import Zmod + def find_generator_polynomial(code, check=True): r""" Returns a possible generator polynomial for ``code``. @@ -334,7 +335,7 @@ def __init__(self, generator_pol=None, length=None, code=None, check=True, ... ValueError: The code is not cyclic. - If the `primitive_root` does not lie in an extension of `field`, + If the ``primitive_root`` does not lie in an extension of ``field``, or is not a primitive `n`-th root of unity, then an exception is raised:: @@ -1321,7 +1322,7 @@ def decoding_radius(self): return self._bch_decoder.decoding_radius() -####################### registration ############################### +# ###################### registration ############################## CyclicCode._registered_encoders["Polynomial"] = CyclicCodePolynomialEncoder CyclicCode._registered_encoders["Vector"] = CyclicCodeVectorEncoder diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index c5b82526a63..ee753504f8b 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -33,8 +33,10 @@ def best_linear_code_in_guava(n, k, F): sage: codes.databases.best_linear_code_in_guava(10,5,GF(2)) # long time; optional - gap_packages (Guava package) [10, 5] linear code over GF(2) - sage: gap.eval("C:=BestKnownLinearCode(10,5,GF(2))") # long time; optional - gap_packages (Guava package) - 'a linear [10,5,4]2..4 shortened code' + sage: libgap.LoadPackage('guava') # long time; optional - gap_packages (Guava package) + ... + sage: libgap.BestKnownLinearCode(10,5,libgap.GF(2)) # long time; optional - gap_packages (Guava package) + a linear [10,5,4]2..4 shortened code This means that the best possible binary linear code of length 10 and dimension 5 is a code with minimum distance 4 and covering radius s somewhere diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index e8e32f82c95..a3ac3ca1547 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -209,7 +209,6 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never from copy import copy from sage.cpython.string import bytes_to_str -from sage.interfaces.gap import gap from sage.categories.cartesian_product import cartesian_product from sage.categories.fields import Fields from sage.matrix.matrix_space import MatrixSpace @@ -586,8 +585,8 @@ def assmus_mattson_designs(self, t, mode=None): if mode=="verbose": for w in nonzerowts: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) wtsp = Cp.weight_distribution() dp = min([i for i in range(1,len(wtsp)) if wtsp[i]!=0]) nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i]!=0 and i<=n-t and i>=dp] @@ -595,8 +594,8 @@ def assmus_mattson_designs(self, t, mode=None): if mode=="verbose": for w in nonzerowtsp: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) if s<=d-t: des = [[t,(n,w,wts[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowts] ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des] @@ -885,6 +884,7 @@ def covering_radius(self): sage: C = codes.HammingCode(GF(2), 5) sage: C.covering_radius() # optional - gap_packages (Guava package) + ... 1 sage: C = codes.random_linear_code(GF(263), 5, 1) @@ -893,15 +893,16 @@ def covering_radius(self): ... NotImplementedError: the GAP algorithm that Sage is using is limited to computing with fields of size at most 256 """ + from sage.libs.gap.libgap import libgap GapPackage("guava", spkg="gap_packages").require() - gap.load_package("guava") + libgap.LoadPackage("guava") F = self.base_ring() if F.cardinality() > 256: raise NotImplementedError("the GAP algorithm that Sage is using " "is limited to computing with fields " "of size at most 256") - gapG = gap(self.generator_matrix()) - C = gapG.GeneratorMatCode(gap(F)) + gapG = libgap(self.generator_matrix()) + C = gapG.GeneratorMatCode(libgap(F)) r = C.CoveringRadius() try: return ZZ(r) @@ -1342,7 +1343,9 @@ def minimum_distance(self, algorithm=None): sage: C.minimum_distance(algorithm="gap") 3 + sage: libgap.SetAllInfoLevels(0) # to suppress extra info messages sage: C.minimum_distance(algorithm="guava") # optional - gap_packages (Guava package) + ... 3 TESTS:: @@ -1370,7 +1373,7 @@ def minimum_distance(self, algorithm=None): # This is done only if algorithm is None. if algorithm not in (None, "gap", "guava"): raise ValueError("The algorithm argument must be one of None, " - "'gap' or 'guava'; got '{0}'".format(algorithm)) + "'gap' or 'guava'; got '{0}'".format(algorithm)) F = self.base_ring() q = F.order() @@ -1380,14 +1383,15 @@ def minimum_distance(self, algorithm=None): "of size at most 256") G = self.generator_matrix() - if (q == 2 or q == 3) and algorithm=="guava": - gap.load_package("guava") - C = gap(G).GeneratorMatCode(gap(F)) + if (q == 2 or q == 3) and algorithm == "guava": + from sage.libs.gap.libgap import libgap + libgap.LoadPackage("guava") + C = libgap(G).GeneratorMatCode(libgap(F)) d = C.MinimumWeight() return ZZ(d) return self._minimum_weight_codeword(algorithm).hamming_weight() - def _minimum_weight_codeword(self, algorithm = None): + def _minimum_weight_codeword(self, algorithm=None): r""" Return a minimum weight codeword of ``self``. @@ -1425,33 +1429,29 @@ def _minimum_weight_codeword(self, algorithm = None): - David Joyner (11-2005) """ + from sage.libs.gap.libgap import libgap n, k = self.length(), self.dimension() F = self.base_field() - Gmat = self.generator_matrix()._gap_init_() + Gmat = libgap(self.generator_matrix()) current_randstate().set_seed_gap() - if algorithm=="guava": + if algorithm == "guava": GapPackage("guava", spkg="gap_packages").require() - gap.load_package("guava") - from sage.interfaces.gap import gfq_gap_to_sage - gap.eval("G:="+Gmat) - C = gap(Gmat).GeneratorMatCode(F) + libgap.LoadPackage("guava") + C = Gmat.GeneratorMatCode(F) cg = C.MinimumDistanceCodeword() - c = [gfq_gap_to_sage(cg[j],F) for j in range(1,n+1)] + c = [cg[j].sage(ring=F) for j in range(n)] return vector(F, c) q = F.order() ans = None - dist_min = n + 1 - gap.eval('Gmat:='+Gmat) - gap.eval('K:=GF({})'.format(q)) - gap.eval('v:=Z({})*{}'.format(q,[0]*n)) + dist_min = libgap(n + 1) + K = libgap.GF(q) + v0 = (K**n).Zero() for i in range(1,k+1): - gap.eval("P:=AClosestVectorCombinationsMatFFEVecFFECoords(Gmat,K,v,{},1)".format(i)) - gap.eval("d:=WeightVecFFE(P[1])") - v = gap("P[1]") - dist = gap("d") + v = Gmat.AClosestVectorCombinationsMatFFEVecFFECoords(K,v0,i,1)[0] + dist = v.WeightVecFFE() if dist and dist < dist_min: dist_min = dist ans = list(v) @@ -1464,8 +1464,11 @@ def _minimum_weight_codeword(self, algorithm = None): def module_composition_factors(self, gp): r""" - Prints the GAP record of the Meataxe composition factors module in - Meataxe notation. This uses GAP but not Guava. + Print the GAP record of the Meataxe composition factors module. + + This is displayed in Meataxe notation. + + This uses GAP but not Guava. EXAMPLES:: @@ -1473,25 +1476,30 @@ def module_composition_factors(self, gp): sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]]) sage: C = LinearCode(G) sage: gp = C.permutation_automorphism_group() - - Now type "C.module_composition_factors(gp)" to get the record printed. + sage: C.module_composition_factors(gp) + [ rec( + IsIrreducible := true, + IsOverFiniteField := true, + ...) ] """ + from sage.libs.gap.libgap import libgap F = self.base_ring() - q = F.order() gens = gp.gens() G = self.generator_matrix() - n = len(G.columns()) - MS = MatrixSpace(F,n,n) - mats = [] # initializing list of mats by which the gens act on self + n = G.ncols() + MS = MatrixSpace(F, n, n) + mats = [] # initializing list of mats by which the gens act on self Fn = VectorSpace(F, n) - W = Fn.subspace_with_basis(G.rows()) # this is self + W = Fn.subspace_with_basis(G.rows()) # this is self for g in gens: p = MS(g.matrix()) - m = [W.coordinate_vector(r*p) for r in G.rows()] + m = [W.coordinate_vector(r * p) for r in G.rows()] mats.append(m) - mats_str = str(gap([[list(r) for r in m] for m in mats])) - gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))") - print(gap("MTX.CompositionFactors( M )")) + mats_gap = libgap(mats) + M_gap = mats_gap.GModuleByMats(F) + # our parser does not grok "foo.MTX.Bar" yet;so we cannot do + # M_gap.MTX.CompositionFactors() yet + return libgap.eval('MTX.CompositionFactors('+str(M_gap)+')') def permutation_automorphism_group(self, algorithm="partition"): r""" @@ -1596,15 +1604,16 @@ def permutation_automorphism_group(self, algorithm="partition"): G = self.generator_matrix() if 2*self.dimension() <= self.length() else self.dual_code().generator_matrix() n = len(G.columns()) if "gap" in algorithm: + from sage.libs.gap.libgap import libgap GapPackage("guava", spkg="gap_packages").require() - gap.load_package('guava') + libgap.LoadPackage('guava') wts = self.weight_distribution() # bottleneck 1 nonzerowts = [i for i in range(len(wts)) if wts[i]!=0] - Sn = SymmetricGroup(n) - Gp = gap("SymmetricGroup(%s)"%n) # initializing G in gap - Gstr = str(gap(G)) - gap.eval("C:=GeneratorMatCode("+Gstr+",GF("+str(q)+"))") - gap.eval("eltsC:=Elements(C)") + Sn = libgap.SymmetricGroup(n) + Sn_sage = SymmetricGroup(n) + Gp = Sn # initializing G in gap + C = libgap(G).GeneratorMatCode(libgap.GF(q)) + eltsC = C.Elements() if algorithm=="gap+verbose": print("\n Minimum distance: %s \n Weight distribution: \n %s" % (nonzerowts[1], wts)) stop = 0 # only stop if all gens are autos @@ -1615,22 +1624,20 @@ def permutation_automorphism_group(self, algorithm="partition"): if algorithm=="gap+verbose": size = Gp.Size() print("\n Using the %s codewords of weight %s \n Supergroup size: \n %s\n " % (wts[wt], wt, size)) - gap.eval("Cwt:=Filtered(eltsC,c->WeightCodeword(c)=%s)"%wt) # bottleneck 2 (repeated - gap.eval("matCwt:=List(Cwt,c->VectorCodeword(c))") # for each i until stop = 1) - if gap("Length(matCwt)") > 0: - A = gap("MatrixAutomorphisms(matCwt)") - G2 = gap("Intersection2(%s,%s)"%(str(A).replace("\n",""),str(Gp).replace("\n",""))) # bottleneck 3 - Gp = G2 + Cwt=filter(lambda c: c.WeightCodeword()==wt, eltsC) # bottleneck 2 (repeated + matCwt=list(map(lambda c: c.VectorCodeword(), Cwt)) # for each i until stop = 1) + if len(matCwt) > 0: + A = libgap(matCwt).MatrixAutomorphisms() + Gp = A.Intersection2(Gp) # bottleneck 3 if Gp.Size()==1: return PermutationGroup([()]) - autgp_gens = Gp.GeneratorsOfGroup() - gens = [Sn(str(x).replace("\n","")) for x in autgp_gens] + gens = Gp.GeneratorsOfGroup() stop = 1 # get ready to stop for x in gens: # if one of these gens is not an auto then don't stop - if not(self.is_permutation_automorphism(x)): + if not(self.is_permutation_automorphism(Sn_sage(x))): stop = 0 break - G = PermutationGroup(gens) + G = PermutationGroup(list(map(Sn_sage, gens))) return G if algorithm=="partition": if q == 2: @@ -1818,6 +1825,7 @@ def weight_distribution(self, algorithm=None): True """ + from sage.libs.gap.libgap import libgap if algorithm is None: if self.base_ring().order() == 2: algorithm = "binary" @@ -1826,12 +1834,11 @@ def weight_distribution(self, algorithm=None): F = self.base_ring() n = self.length() if algorithm=="gap": - Gmat = self.generator_matrix()._gap_init_() + Gmat = self.generator_matrix() q = self.base_ring().order() - z = 'Z(%s)*%s'%(q, [0]*self.length()) # GAP zero vector as a string - _ = gap.eval("w:=DistancesDistributionMatFFEVecFFE("+Gmat+", GF("+str(q)+"),"+z+")") - v = [eval(gap.eval("w["+str(i)+"]")) for i in range(1,self.length()+2)] # because GAP returns vectors in compressed form - return v + z = 0*libgap.Z(q)*([0]*self.length()) # GAP zero vector + w=libgap(Gmat).DistancesDistributionMatFFEVecFFE(libgap.GF(q),z) + return w.sage() elif algorithm=="binary": from sage.coding.binary_code import weight_dist return weight_dist(self.generator_matrix()) @@ -1840,8 +1847,7 @@ def weight_distribution(self, algorithm=None): raise NotImplementedError("The algorithm 'leon' is only implemented for q = 2,3,5,7.") # The GAP command DirectoriesPackageLibrary tells the location of the latest # version of the Guava libraries, so gives us the location of the Guava binaries too. - guava_bin_dir = gap.eval('DirectoriesPackagePrograms("guava")[1]') - guava_bin_dir = guava_bin_dir[guava_bin_dir.index('"') + 1:guava_bin_dir.rindex('"')] + guava_bin_dir = libgap.DirectoriesPackagePrograms("guava")[0].Filename("").sage() input = _dump_code_in_leon_format(self) + "::code" lines = subprocess.check_output([os.path.join(guava_bin_dir, 'wtdist'), input]) # to use the already present output parser @@ -2144,7 +2150,7 @@ def e(i): return G -############################ linear codes python class ######################## +# ########################### linear codes python class ######################## class LinearCode(AbstractLinearCode): r""" @@ -2773,7 +2779,7 @@ def _build_lookup_table(self): # distance 1 gracefully zero_syndrome = vector(F,[F.zero()]*(n-k)) zero_syndrome.set_immutable() - lookup = { zero_syndrome : vector(F,[F.zero()]*n) } + lookup = {zero_syndrome: vector(F,[F.zero()]*n)} error_position_tables = [cartesian_product([l]*i) for i in range(1, t+1)] first_collision = True #Filling the lookup table @@ -2814,7 +2820,7 @@ def _build_lookup_table(self): # Update decoder types depending on whether we are decoding beyond d/2 if self._code_minimum_distance: if self._maximum_error_weight == (self._code_minimum_distance-1)//2: - self._decoder_type.update({"minimum-distance","always-succeed"}) + self._decoder_type.update({"minimum-distance", "always-succeed"}) else: # then t > (d-1)/2 self._decoder_type.add("might-error") @@ -2822,7 +2828,6 @@ def _build_lookup_table(self): self._decoder_type.add("always-succeed") return lookup - def decode_to_code(self, r): r""" Corrects the errors in ``word`` and returns a codeword. diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 6ec0dc8e44b..4b3ae5f0a39 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -131,8 +131,8 @@ def __init__(self, C, positions): """ if not isinstance(positions, (Integer, int, set, list)): raise TypeError("positions must be either a Sage Integer, a Python int, a set or a list") - if isinstance(positions, (list, set)) and not all (isinstance(i, (int, Integer)) for i in positions): - raise TypeError("if positions is a list or a set, it has to contain only Python ints or Sage Integers") + if isinstance(positions, (list, set)) and not all(isinstance(i, (int, Integer)) for i in positions): + raise TypeError("if positions is a list or a set, it has to contain only Python ints or Sage Integers") if isinstance(positions, (Integer, int)): positions = {positions} if isinstance(positions, list): diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 3054ef00995..f5008f1a826 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -109,6 +109,7 @@ class AbstractTree(): sage: TestSuite(OrderedTree()).run() sage: TestSuite(BinaryTree()).run() """ + def pre_order_traversal_iter(self): r""" The depth-first pre-order traversal iterator. @@ -1828,6 +1829,7 @@ class or one of its subclasses generally, at least :class:`ClonableArray See also the assumptions in :class:`AbstractTree`. """ + def check(self): """ Check that ``self`` is a correct tree. @@ -2044,6 +2046,7 @@ class AbstractLabelledTree(AbstractTree): .. SEEALSO:: :class:`AbstractTree` """ + def __init__(self, parent, children, label=None, check=True): """ TESTS:: @@ -2321,6 +2324,7 @@ class AbstractLabelledClonableTree(AbstractLabelledTree, here from :class:`~sage.structure.list_clone.ClonableArray`, because it would prevent us to inherit later from :class:`~sage.structure.list_clone.ClonableList`. """ + def set_root_label(self, label): """ Set the label of the root of ``self``. diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 9ee4e166abe..9ae5c240be1 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -42,6 +42,7 @@ class AffinePermutation(ClonableArray): sage: p Type A affine permutation with window [3, -1, 0, 6, 5, 4, 10, 9] """ + def __init__(self, parent, lst, check=True): r""" Initialize ``self`` @@ -407,7 +408,6 @@ def grassmannian_quotient(self, i=0, side='right'): return (fin, gr) - class AffinePermutationTypeA(AffinePermutation): #---------------------- #Type-specific methods. @@ -448,7 +448,6 @@ def check(self): if l != list(range(k+1)): raise ValueError("entries must have distinct residues") - def value(self, i, base_window=False): r""" Return the image of the integer ``i`` under this permutation. @@ -730,7 +729,6 @@ def maximal_cyclic_factor(self, typ='decreasing', side='right', verbose=False): best_T.reverse() return best_T - def maximal_cyclic_decomposition(self, typ='decreasing', side='right', verbose=False): r""" Find the unique maximal decomposition of ``self`` into cyclically @@ -1065,6 +1063,8 @@ def tableau_of_word(self, w, typ='decreasing', side='right', alpha=None): return tab #------------------------------------------------------------------------------- + + class AffinePermutationTypeC(AffinePermutation): #---------------------- #Type-specific methods. @@ -1321,7 +1321,6 @@ def check(self): raise ValueError("type B affine permutations have an even number of " "entries less than 0 to the right of the 0th position") - def apply_simple_reflection_right(self, i): r""" Apply the simple reflection indexed by ``i`` on positions. @@ -1843,8 +1842,6 @@ def to_type_a(self): return A(self) - - #------------------------------------------------------------------------- # Class of all affine permutations. #------------------------------------------------------------------------- diff --git a/src/sage/combinat/algebraic_combinatorics.py b/src/sage/combinat/algebraic_combinatorics.py index b8f01700156..6a6a25b0907 100644 --- a/src/sage/combinat/algebraic_combinatorics.py +++ b/src/sage/combinat/algebraic_combinatorics.py @@ -41,6 +41,7 @@ - :class:`~sage.combinat.symmetric_group_representations.SymmetricGroupRepresentation` - :ref:`sage.combinat.yang_baxter_graph` - :ref:`sage.combinat.hall_polynomial` +- :ref:`sage.combinat.key_polynomial` Operads and their algebras -------------------------- diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index e0450a1fbe3..47cd721e0a5 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -87,6 +87,7 @@ from .debruijn_sequence import DeBruijnSequences from .schubert_polynomial import SchubertPolynomialRing +lazy_import('sage.combinat.key_polynomial', 'KeyPolynomialBasis', as_='KeyPolynomials') from .symmetric_group_algebra import SymmetricGroupAlgebra, HeckeAlgebraSymmetricGroupT from .symmetric_group_representations import SymmetricGroupRepresentation, SymmetricGroupRepresentations from .yang_baxter_graph import YangBaxterGraph diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index c7c7317811c..fba8f842fc8 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -1046,6 +1046,7 @@ class AlternatingSignMatrices(UniqueRepresentation, Parent): sage: L.category() Category of facade finite enumerated lattice posets """ + def __init__(self, n): r""" Initialize ``self``. @@ -1657,6 +1658,7 @@ class MonotoneTriangles(GelfandTsetlinPatternsTopRow): sage: all(A.from_monotone_triangle(m).to_monotone_triangle() == m for m in M) True """ + def __init__(self, n): r""" Initialize ``self``. diff --git a/src/sage/combinat/backtrack.py b/src/sage/combinat/backtrack.py index f257b9a3894..565b3efa973 100644 --- a/src/sage/combinat/backtrack.py +++ b/src/sage/combinat/backtrack.py @@ -44,6 +44,7 @@ class GenericBacktracker(): See also :class:`RecursivelyEnumeratedSet_forest` for handling simple special cases. """ + def __init__(self, initial_data, initial_state): r""" EXAMPLES:: @@ -127,6 +128,7 @@ class PositiveIntegerSemigroup(UniqueRepresentation, RecursivelyEnumeratedSet_fo sage: TestSuite(PP).run(skip='_test_enumerated_set_contains') sage: PP._test_enumerated_set_contains() # long time """ + def __init__(self): r""" TESTS:: diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 382e4abcdab..3fe87a12842 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -70,6 +70,7 @@ class BaxterPermutations_size(BaxterPermutations): sage: BaxterPermutations_size(5) Baxter permutations of size 5 """ + def __init__(self, n): """ EXAMPLES:: @@ -252,6 +253,7 @@ class BaxterPermutations_all(DisjointUnionEnumeratedSets, BaxterPermutations): sage: BaxterPermutations_all() Baxter permutations """ + def __init__(self, n=None): r""" EXAMPLES:: diff --git a/src/sage/combinat/binary_recurrence_sequences.py b/src/sage/combinat/binary_recurrence_sequences.py index 4dd28d897fe..a1f2d969c4c 100644 --- a/src/sage/combinat/binary_recurrence_sequences.py +++ b/src/sage/combinat/binary_recurrence_sequences.py @@ -105,7 +105,6 @@ class BinaryRecurrenceSequence(SageObject): """ def __init__(self, b, c, u0=0, u1=1): - """ See :class:`BinaryRecurrenceSequence` for full documentation. @@ -830,7 +829,6 @@ def _goodness(n, R, p): def _next_good_prime(p, R, qq, patience, qqold): - """ Find the next prime `\\ell` which is good by ``qq`` but not by ``qqold``, 1 mod ``p``, and for which ``b^2+4*c`` is a square mod `\\ell`, for the sequence ``R`` if it is possible in runtime patience. @@ -1033,7 +1031,6 @@ def _is_p_power_mod(a, p, N): def _estimated_time(M2, M1, length, p): - """ Find the estimated time to extend congruences mod M1 to consistent congruences mod M2. diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index a58d1b54f56..96ff98d14dd 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -4179,6 +4179,7 @@ class BinaryTrees_size(BinaryTrees): sage: from sage.combinat.binary_tree import BinaryTrees_size sage: for i in range(6): TestSuite(BinaryTrees_size(i)).run() """ + def __init__(self, size): """ TESTS:: @@ -4318,6 +4319,7 @@ class FullBinaryTrees_all(DisjointUnionEnumeratedSets, BinaryTrees): """ All full binary trees. """ + def __init__(self): """ TESTS:: @@ -4415,6 +4417,7 @@ class FullBinaryTrees_size(BinaryTrees): """ Full binary trees of a fixed size (number of nodes). """ + def __init__(self, size): r""" TESTS:: @@ -5157,6 +5160,7 @@ class LabelledBinaryTrees(LabelledOrderedTrees): This is a parent stub to serve as a factory class for trees with various labels constraints. """ + def _repr_(self): """ TESTS:: diff --git a/src/sage/combinat/blob_algebra.py b/src/sage/combinat/blob_algebra.py index 5da88de497f..c3d7037ae4c 100644 --- a/src/sage/combinat/blob_algebra.py +++ b/src/sage/combinat/blob_algebra.py @@ -33,6 +33,8 @@ from sage.combinat.dyck_word import DyckWords #@add_metaclass(InheritComparisonClasscallMetaclass) + + class BlobDiagram(Element): r""" A blob diagram. @@ -45,6 +47,7 @@ class BlobDiagram(Element): those without. The blobed pairs must either be either the leftmost propagating strand or to the left of it and not nested. """ + def __init__(self, parent, marked, unmarked): r""" Initialize ``self``. @@ -148,10 +151,12 @@ def temperley_lieb_diagram(self): """ return self.parent()._TL_diagrams(self.marked + self.unmarked) + class BlobDiagrams(Parent, UniqueRepresentation): r""" The set of all blob diagrams. """ + def __init__(self, n): r""" Initialize ``self``. @@ -370,6 +375,7 @@ def __iter__(self): Element = BlobDiagram + class BlobAlgebra(CombinatorialFreeModule): r""" The blob algebra. diff --git a/src/sage/combinat/cartesian_product.py b/src/sage/combinat/cartesian_product.py index a8199d6c95d..b03aed1987b 100644 --- a/src/sage/combinat/cartesian_product.py +++ b/src/sage/combinat/cartesian_product.py @@ -74,6 +74,7 @@ class for ``cartesian_product``; sage: elt.parent() is c True """ + def __init__(self, *iters): """ TESTS:: diff --git a/src/sage/combinat/chas/wqsym.py b/src/sage/combinat/chas/wqsym.py index 238d0281030..99659ab154a 100644 --- a/src/sage/combinat/chas/wqsym.py +++ b/src/sage/combinat/chas/wqsym.py @@ -69,9 +69,12 @@ def __init__(self, alg, graded=True): sage: M = algebras.WQSym(QQ).M() sage: TestSuite(M).run() # long time """ + def sorting_key(X): + return (sum(map(len, X)), X) CombinatorialFreeModule.__init__(self, alg.base_ring(), OrderedSetPartitions(), category=WQSymBases(alg, graded), + sorting_key=sorting_key, bracket="", prefix=self._prefix) def _repr_term(self, osp): @@ -574,7 +577,7 @@ class Monomial(WQSymBasis_abstract): sage: M = WQSym.M(); M Word Quasi-symmetric functions over Rational Field in the Monomial basis sage: sorted(M.basis(2)) - [M[{1, 2}], M[{2}, {1}], M[{1}, {2}]] + [M[{1}, {2}], M[{2}, {1}], M[{1, 2}]] """ _prefix = "M" _basis_name = "Monomial" @@ -625,6 +628,7 @@ def product_on_basis(self, x, y): def union(X, Y): return X.union(Y) + return self.sum_of_monomials(ShuffleProduct_overlapping(x, yshift, K, union)) @@ -975,12 +979,15 @@ def _C_to_X(self, P): temp = temp[:j] break + def union(X, Y): + return X.union(Y) + # Perform the quasi-shuffle product cur = {data[0]: 1} for B in data[1:]: ret = {} for A in cur: - for C in ShuffleProduct_overlapping(A, B, element_constructor=OSP): + for C in ShuffleProduct_overlapping(A, B, element_constructor=OSP, add=union): if C in ret: ret[C] += cur[A] else: diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e65d605fad1..283f4ab8985 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -130,6 +130,7 @@ class ClusterSeed(SageObject): sage: S = ClusterSeed(['D', 4],user_labels = [-1, 0, 1, 2]);S A seed for a cluster algebra of rank 4 of type ['D', 4] """ + def __init__(self, data, frozen=None, is_principal=False, user_labels=None, user_labels_prefix='x'): r""" @@ -1062,7 +1063,7 @@ def interact(self, fig_size=1, circular=True): sage: S = ClusterSeed(['A',4]) sage: S.interact() - VBox(children=... + ...VBox(children=... """ return cluster_interact(self, fig_size, circular, kind='seed') @@ -3443,7 +3444,6 @@ def mutation_class_iter(self, depth=infinity, show_depth=False, else: yield self - # instantiate the variables clusters = {} clusters[ cl ] = [ self, list(range(n)), [] ] @@ -4628,6 +4628,7 @@ def coeff_recurs(p, q, a1, a2, b, c): return sum((-1)**(k-1)*coeff_recurs(p, q-k, a1, a2, b, c)*_bino(a1-b*p+k-1, k) for k in range(1, q+1)) + def PathSubset(n, m): r""" Encodes a *maximal* Dyck path from (0,0) to (n,m) (for n >= m >= 0) as a subset of {0,1,2,..., 2n-1}. @@ -4687,6 +4688,7 @@ def SetToPath(T): ans.append(2*n-2) return ans + def is_LeeLiZel_allowable(T,n,m,b,c): """ Check if the subset T contributes to the computation of the greedy diff --git a/src/sage/combinat/cluster_algebra_quiver/interact.py b/src/sage/combinat/cluster_algebra_quiver/interact.py index 4e54c64e179..1c88e613b4c 100644 --- a/src/sage/combinat/cluster_algebra_quiver/interact.py +++ b/src/sage/combinat/cluster_algebra_quiver/interact.py @@ -27,7 +27,7 @@ def cluster_interact(self, fig_size=1, circular=True, kind='seed'): sage: S = ClusterSeed(['A',4]) sage: S.interact() # indirect doctest - VBox(children=... + ...VBox(children=... """ if kind not in ['seed', 'quiver']: raise ValueError('kind must be "seed" or "quiver"') @@ -107,7 +107,7 @@ def do_mutation(*args, **kwds): show_lastmutation.observe(refresh, 'value') which_plot.observe(refresh, 'value') - mut_buttons.on_displayed(refresh) + mut_buttons.on_widget_constructed(refresh) if kind == 'seed': top = widgets.HBox([show_seq, show_vars]) diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_class.py b/src/sage/combinat/cluster_algebra_quiver/mutation_class.py index bab04d652fe..78cf328747c 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_class.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_class.py @@ -377,6 +377,7 @@ def _mutation_class_iter( dg, n, m, depth=infinity, return_dig6=False, show_dept nr += ' ' * (10-len(nr)) print("Depth: %s found: %s Time: %.2f s" % (dc, nr, timer2 - timer)) + def _digraph_to_dig6( dg, hashable=False ): """ Return the dig6 and edge data of the digraph dg. diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 7a7f030bf8b..b5eddce4393 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -896,8 +896,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): dg_tmp.delete_vertices( c1 ) components = dg_tmp.connected_components() - #if not len( components ) == 2: - if len ( components ) != 2: + # if not len(components) == 2: + if len(components) != 2: return _false_return(4) else: dg_tmp1 = dg_tmp.subgraph( components[0] ) @@ -938,7 +938,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): c2.reverse() dg_tmp.delete_edge( tuple( c2 ) ) components = dg_tmp.connected_components() - if len ( components ) != 2: + if len(components) != 2: return _false_return(7) else: dg_tmp1 = dg_tmp.subgraph( components[0] ) @@ -1211,8 +1211,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): else: cycle.remove(edge) cycle.append( (edge[0],edge[1], 1 ) ) - r = sum ((x[2] for x in cycle)) - r = max ( r, n-r ) + r = sum(x[2] for x in cycle) + r = max(r, n - r) if ret_conn_vert: return [ QuiverMutationType( ['A',[r,n-r],1] ), connecting_vertices ] else: diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 4f6e16bf0fe..ae459dd8838 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -212,6 +212,7 @@ class ClusterQuiver(SageObject): ... ValueError: the input data was not recognized """ + def __init__(self, data, frozen=None, user_labels=None): """ TESTS:: @@ -236,7 +237,6 @@ def __init__(self, data, frozen=None, user_labels=None): ' additional parameter frozen is ignored.' ' Use set_frozen to freeze vertices.') - mutation_type = data self.__init__( mutation_type.standard_quiver() ) if user_labels: @@ -274,11 +274,11 @@ def __init__(self, data, frozen=None, user_labels=None): else: self.__init__( mutation_type.standard_quiver() ) elif len(data) == 3 and isinstance(data[0], str): - if (data[0] == 'F' and data[1] == 4 and data[2] == [2,1]) or (data[0] == 'G' and data[1] == 2 and data[2] == [3,1]): + if (data[0] == 'F' and data[1] == 4 and data[2] == [2,1]) or (data[0] == 'G' and data[1] == 2 and data[2] == [3,1]): quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], tuple(data[2]) )._digraph ) quiv._mutation_type = mutation_type self.__init__( quiv ) - elif (data[0] == 'F' and data[1] == 4 and data[2] == (2,1) ) or (data[0] == 'G' and data[1] == 2 and data[2] == (3,1) ): + elif (data[0] == 'F' and data[1] == 4 and data[2] == (2,1) ) or (data[0] == 'G' and data[1] == 2 and data[2] == (3,1) ): quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph ) quiv._mutation_type = mutation_type self.__init__( quiv ) @@ -699,7 +699,7 @@ def interact(self, fig_size=1, circular=True): sage: S = ClusterQuiver(['A',4]) sage: S.interact() - VBox(children=... + ...VBox(children=... """ return cluster_interact(self, fig_size, circular, kind="quiver") diff --git a/src/sage/combinat/cluster_complex.py b/src/sage/combinat/cluster_complex.py index 31562e6bc80..edc4f235aeb 100644 --- a/src/sage/combinat/cluster_complex.py +++ b/src/sage/combinat/cluster_complex.py @@ -50,13 +50,14 @@ from sage.categories.coxeter_groups import CoxeterGroups from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.subword_complex import SubwordComplex, SubwordComplexFacet -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN class ClusterComplexFacet(SubwordComplexFacet): r""" A cluster (i.e., a facet) of a cluster complex. """ + def cluster(self): """ Return this cluster as a set of almost positive roots. diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index ff01032147e..4ace496fbdb 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -28,6 +28,7 @@ class ColoredPermutation(MultiplicativeGroupElement): """ A colored permutation. """ + def __init__(self, parent, colors, perm): """ Initialize ``self``. @@ -400,6 +401,8 @@ def length(self): # TODO: Parts of this should be put in the category of complex # reflection groups + + class ColoredPermutations(Parent, UniqueRepresentation): r""" The group of `m`-colored permutations on `\{1, 2, \ldots, n\}`. @@ -454,6 +457,7 @@ class ColoredPermutations(Parent, UniqueRepresentation): - :wikipedia:`Generalized_symmetric_group` - :wikipedia:`Complex_reflection_group` """ + def __init__(self, m, n): """ Initialize ``self``. @@ -1278,6 +1282,7 @@ class SignedPermutations(ColoredPermutations): - :wikipedia:`Hyperoctahedral_group` """ + def __init__(self, n): """ Initialize ``self``. diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index e5ad98ea4ab..af2bc0da8c8 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -1530,6 +1530,7 @@ class CombinatorialElement(CombinatorialObject, Element, sage: Foo(17) 17 """ + def __init__(self, parent, *args, **kwds): """ Initialize this ``CombinatorialElement`` with a parent and a @@ -1591,6 +1592,7 @@ class CombinatorialClass(Parent, metaclass=ClasscallMetaclass): sage: InfiniteEnumeratedSets().example() An example of an infinite enumerated set: the non negative integers """ + def __init__(self, category=None): """ TESTS:: @@ -2416,6 +2418,7 @@ class Permutations_CC(CombinatorialClass): A testing class for :class:`CombinatorialClass` since :class:`Permutations` no longer inherits from :class:`CombinatorialClass` in :trac:`14772`. """ + def __init__(self, n): """ EXAMPLES:: @@ -2487,6 +2490,7 @@ class MapCombinatorialClass(ImageSubobject, CombinatorialClass): sage: next(i), next(i), next(i) ([], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1]) """ + def __init__(self, cc, f, name=None, *, is_injective=True): """ TESTS:: @@ -2514,6 +2518,7 @@ class InfiniteAbstractCombinatorialClass(CombinatorialClass): self._infinite_cclass_slice is supposed to accept any integer as an argument and return something which is iterable. """ + def cardinality(self) -> Integer | infinity: """ Count the elements of the combinatorial class. diff --git a/src/sage/combinat/combinatorial_map.py b/src/sage/combinat/combinatorial_map.py index 3befb0ce881..2f1055d8793 100644 --- a/src/sage/combinat/combinatorial_map.py +++ b/src/sage/combinat/combinatorial_map.py @@ -205,6 +205,7 @@ class CombinatorialMap(): :ref:`sage.combinat.combinatorial_map` and :func:`combinatorial_map_wrapper`. """ + def __init__(self, f, order=None, name=None): """ Constructor for combinatorial maps. diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index ee711e52f9f..0ef49a900b0 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -32,8 +32,8 @@ from itertools import accumulate from collections.abc import Sequence -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.enumerated_sets import EnumeratedSets +from sage.categories.additive_monoids import AdditiveMonoids from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.sets.finite_enumerated_set import FiniteEnumeratedSet @@ -1386,6 +1386,7 @@ def count(self, n): """ return sum(i == n for i in self) + Sequence.register(Composition) ############################################################## @@ -1547,14 +1548,14 @@ class Compositions(UniqueRepresentation, Parent): results. It is up to the user to ensure that the inner and outer compositions themselves satisfy the parts and slope constraints. - Note that if you specify ``min_part=0``, then the objects produced may - have parts equal to zero. This violates the internal assumptions - that the composition class makes. Use at your own risk, or - preferably consider using ``IntegerVectors`` instead:: + Note that setting ``min_part=0`` is not allowed:: - sage: Compositions(2, length=3, min_part=0).list() - doctest:...: RuntimeWarning: Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results! - [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] + sage: Compositions(2, length=3, min_part=0) + Traceback (most recent call last): + ... + ValueError: setting min_part=0 is not allowed for Compositions + + Instead you must use ``IntegerVectors``:: sage: list(IntegerVectors(2, 3)) [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] @@ -1651,8 +1652,7 @@ def __classcall_private__(self, n=None, **kwargs): if 'min_part' not in kwargs: kwargs['min_part'] = 1 elif kwargs['min_part'] == 0: - from warnings import warn - warn("Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results!", RuntimeWarning) + raise ValueError("setting min_part=0 is not allowed for Compositions") if 'outer' in kwargs: kwargs['ceiling'] = list(kwargs['outer']) @@ -1673,7 +1673,7 @@ def __classcall_private__(self, n=None, **kwargs): kwargs['min_length'] = len(inner) return IntegerListsLex(n, **kwargs) - def __init__(self, is_infinite=False): + def __init__(self, is_infinite=False, category=None): """ Initialize ``self``. @@ -1682,10 +1682,12 @@ def __init__(self, is_infinite=False): sage: C = Compositions() sage: TestSuite(C).run() """ + if category is None: + category = EnumeratedSets() if is_infinite: - Parent.__init__(self, category=InfiniteEnumeratedSets()) + Parent.__init__(self, category=category.Infinite()) else: - Parent.__init__(self, category=FiniteEnumeratedSets()) + Parent.__init__(self, category=category.Finite()) Element = Composition @@ -1869,6 +1871,7 @@ class Compositions_all(Compositions): """ Class of all compositions. """ + def __init__(self): """ Initialize ``self``. @@ -1878,7 +1881,8 @@ def __init__(self): sage: C = Compositions() sage: TestSuite(C).run() """ - Compositions.__init__(self, True) + cat = AdditiveMonoids() + Compositions.__init__(self, True, category=cat) def _repr_(self) -> str: """ @@ -1907,6 +1911,32 @@ def subset(self, size=None): return self return Compositions(size) + def zero(self): + """ + Return the zero of the additive monoid. + + This is the empty composition. + + EXAMPLES:: + + sage: C = Compositions() + sage: C.zero() + [] + """ + return Composition([]) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: C = Compositions() + sage: C.an_element() + [] + """ + return self.zero() + def __iter__(self): """ Iterate over all compositions. diff --git a/src/sage/combinat/composition_signed.py b/src/sage/combinat/composition_signed.py index 8d8b78bcd59..3ba592cd7ae 100644 --- a/src/sage/combinat/composition_signed.py +++ b/src/sage/combinat/composition_signed.py @@ -66,6 +66,7 @@ class SignedCompositions(Compositions_n): sage: SC = SignedCompositions(3) sage: TestSuite(SC).run() """ + def __repr__(self): """ TESTS:: diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index 0092a043e8a..5827c05461b 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -5,7 +5,6 @@ - Chris Berg, Jeff Ferreira (2012-9): Initial version """ - from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.non_negative_integers import NonNegativeIntegers from sage.sets.family import Family @@ -101,21 +100,21 @@ def __init__(self, parent, t): # Verify rows weakly decrease from left to right for row in t: - if any(row[i] < row[i+1] for i in range(len(row)-1)): + if any(row[i] < row[i + 1] for i in range(len(row) - 1)): raise ValueError("rows must weakly decrease from left to right") # Verify leftmost column strictly increases from top to bottom - first_col = [row[0] for row in t if t!=[[]]] - if any(first_col[i] >= first_col[i+1] for i in range(len(t)-1)): + first_col = [row[0] for row in t if t != [[]]] + if any(first_col[i] >= first_col[i + 1] for i in range(len(t) - 1)): raise ValueError("leftmost column must strictly increase from top to bottom") # Verify triple condition l = len(t) - m = max([len(r) for r in t] + [0]) + m = max((len(r) for r in t), default=0) TT = [row+[0]*(m-len(row)) for row in t] for i in range(l): - for j in range(i+1,l): - for k in range(1,m): + for j in range(i+1, l): + for k in range(1, m): if TT[j][k] and TT[i][k] <= TT[j][k] <= TT[i][k-1]: raise ValueError("triple condition must be satisfied") @@ -214,10 +213,9 @@ def descent_set(self): """ cols = {} for row in self: - for (col,i) in enumerate(row): + for col, i in enumerate(row): cols[i] = col - des_set = sorted([i for i in cols if i+1 in cols and cols[i+1] >= cols[i]]) - return des_set + return sorted(i for i in cols if i + 1 in cols and cols[i + 1] >= cols[i]) def descent_composition(self): r""" @@ -256,7 +254,7 @@ def shape_partition(self): sage: CompositionTableau([[2,1],[3],[4]]).shape_partition() [2, 1, 1] """ - return Partition(sorted([len(row) for row in self], reverse=True)) + return Partition(sorted((len(row) for row in self), reverse=True)) def is_standard(self): r""" @@ -270,9 +268,10 @@ def is_standard(self): sage: CompositionTableau([[2,1],[3],[4]]).is_standard() True """ - entries = sum(self,[]) + entries = sum(self, []) return sorted(entries) == list(range(1, self.size() + 1)) + class CompositionTableaux(UniqueRepresentation, Parent): r""" Composition tableaux. @@ -510,19 +509,21 @@ def __contains__(self, T): # for 1 <= i < j <= len(comp), for 2 <= k <= m, # T[j,k] \neq 0 and T[j,k] >= T[i,k] ==> T[j,k] > T[i,k-1] l = len(T) - m = max([len(r) for r in T] + [0]) + m = max((len(r) for r in T), default=0) TT = [row+[0]*(m-len(row)) for row in T] for i in range(l): - for j in range(i+1,l): - for k in range(1,m): + for j in range(i+1, l): + for k in range(1, m): if TT[j][k] != 0 and TT[j][k] >= TT[i][k] and TT[j][k] <= TT[i][k-1]: return False return True + class CompositionTableaux_all(CompositionTableaux, DisjointUnionEnumeratedSets): r""" All composition tableaux. """ + def __init__(self, max_entry=None): r""" Initialize ``self``. @@ -535,8 +536,8 @@ def __init__(self, max_entry=None): self.max_entry = max_entry CT_n = lambda n: CompositionTableaux_size(n, max_entry) DisjointUnionEnumeratedSets.__init__(self, - Family(NonNegativeIntegers(), CT_n), - facade=True, keepkey = False) + Family(NonNegativeIntegers(), CT_n), + facade=True, keepkey=False) def _repr_(self): r""" @@ -549,7 +550,7 @@ def _repr_(self): Composition Tableaux """ if self.max_entry is not None: - return "Composition tableaux with maximum entry %s"%str(self.max_entry) + return f"Composition tableaux with maximum entry {self.max_entry}" return "Composition Tableaux" def an_element(self): @@ -564,6 +565,7 @@ def an_element(self): """ return self.element_class(self, [[1, 1], [2]]) + class CompositionTableaux_size(CompositionTableaux): r""" Composition tableaux of a fixed size `n`. @@ -577,6 +579,7 @@ class CompositionTableaux_size(CompositionTableaux): - The class of composition tableaux of size ``n``. """ + def __init__(self, n, max_entry=None): r""" Initializes the class of composition tableaux of size ``n``. @@ -601,7 +604,7 @@ def __contains__(self, x): sage: [[1],[2,2]] in CompositionTableaux(4) False """ - return CompositionTableaux.__contains__(self, x) and sum(map(len,x)) == self.size + return CompositionTableaux.__contains__(self, x) and sum(map(len, x)) == self.size def __iter__(self): r""" @@ -632,7 +635,7 @@ def __iter__(self): True """ for comp in Compositions(self.size): - for T in CompositionTableaux_shape(comp,self.max_entry): + for T in CompositionTableaux_shape(comp, self.max_entry): yield self.element_class(self, T) def _repr_(self): @@ -661,9 +664,9 @@ def _an_element_(self): if self.size == 0: return self.element_class(self, []) if self.size == 1: - return self.element_class(self,[[1]]) + return self.element_class(self, [[1]]) + return self.element_class(self, [[1] * (self.size - 1), [2]]) - return self.element_class(self, [[1]*(self.size-1),[2]]) class CompositionTableaux_shape(CompositionTableaux): r""" @@ -675,7 +678,7 @@ class CompositionTableaux_shape(CompositionTableaux): - ``max_entry`` -- a nonnegative integer. This keyword argument defaults to the size of ``comp``. """ - def __init__(self, comp, max_entry=None): + def __init__(self, comp, max_entry=None): """ Initialize ``self``. @@ -753,7 +756,7 @@ def an_element(self): """ if self.shape == []: return self.element_class(self, []) - t = [[i]*len for (i,len) in enumerate(self.shape,start=1)] + t = [[i] * len for i, len in enumerate(self.shape, start=1)] return self.element_class(self, t) @@ -761,6 +764,7 @@ class CompositionTableauxBacktracker(GenericBacktracker): r""" A backtracker class for generating sets of composition tableaux. """ + def __init__(self, shape, max_entry=None): """ EXAMPLES:: @@ -774,14 +778,14 @@ def __init__(self, shape, max_entry=None): """ self._shape = shape self._n = sum(shape) - self._initial_data = [ [None]*s for s in shape ] + self._initial_data = [[None] * s for s in shape] if max_entry is None: - max_entry=sum(shape) - self.max_entry=max_entry + max_entry = sum(shape) + self.max_entry = max_entry # The ending position will be at the lowest box which is farthest right - ending_row = len(shape)-1 - ending_col = shape[-1]-1 + ending_row = len(shape) - 1 + ending_col = shape[-1] - 1 self._ending_position = (ending_row, ending_col) # Get the highest box that is farthest left @@ -819,26 +823,27 @@ def _rec(self, obj, state): new_state = self.get_next_pos(i, j) yld = bool(new_state is None) - for k in range(1,self.max_entry +1): - #We check to make sure that k does not violate the rule weak decrease in rows - if j!=0 and obj[i][j-1] < k: + for k in range(1, self.max_entry + 1): + # We check to make sure that k does not violate the rule weak decrease in rows + if j != 0 and obj[i][j - 1] < k: continue - #We check to make sure that k does not violate strict increase in first column - if j == 0 and i != 0 and k <= obj[i-1][j]: + # We check to make sure that k does not violate strict increase in first column + if j == 0 and i != 0 and k <= obj[i - 1][j]: continue - #We check to make sure that k does not violate the Triple Rule + # We check to make sure that k does not violate the Triple Rule if j != 0 and i != 0 and any(k == obj_copy[m][j] for m in range(i)): continue - if j != 0 and i != 0 and any(obj_copy[m][j] < k and k <= obj_copy[m][j-1] for m in range(i)): + if j != 0 and i != 0 and any(obj_copy[m][j] < k and k <= obj_copy[m][j - 1] + for m in range(i)): continue - #Fill in the in the i,j box with k + # Fill in the in the i,j box with k obj[i][j] = k obj_copy[i][j] = k - #Yield the object + # Yield the object yield copy.deepcopy(obj), new_state, yld def get_next_pos(self, ii, jj): @@ -854,8 +859,8 @@ def get_next_pos(self, ii, jj): if (ii, jj) == self._ending_position: return None - for j in range(jj+1, self._shape[ii]): + for j in range(jj + 1, self._shape[ii]): if self._shape[ii] >= j: return ii, j - return ii+1, 0 + return ii + 1, 0 diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py index 7feff91fe54..4bc07a2171b 100644 --- a/src/sage/combinat/constellation.py +++ b/src/sage/combinat/constellation.py @@ -183,6 +183,7 @@ class Constellation_class(Element): A constellation or a tuple of permutations `(g_0,g_1,...,g_k)` such that the product `g_0 g_1 ... g_k` is the identity. """ + def __init__(self, parent, g, connected, mutable, check): r""" TESTS:: @@ -1271,6 +1272,7 @@ class Constellations_p(UniqueRepresentation, Parent): sage: len(C.isomorphism_representatives()) 1 """ + def __init__(self, profile, domain=None, connected=True): r""" OPTIONS: diff --git a/src/sage/combinat/crystals/affine.py b/src/sage/combinat/crystals/affine.py index 813492aad80..c33fe853152 100644 --- a/src/sage/combinat/crystals/affine.py +++ b/src/sage/combinat/crystals/affine.py @@ -270,6 +270,7 @@ class AffineCrystalFromClassicalElement(ElementWrapper): sage: b._repr_() '[[1]]' """ + def classical_weight(self): """ Return the classical weight corresponding to ``self``. diff --git a/src/sage/combinat/crystals/affine_factorization.py b/src/sage/combinat/crystals/affine_factorization.py index 2499a05a5f9..8db1972f993 100644 --- a/src/sage/combinat/crystals/affine_factorization.py +++ b/src/sage/combinat/crystals/affine_factorization.py @@ -20,6 +20,7 @@ from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.rsk import RSK + class AffineFactorizationCrystal(UniqueRepresentation, Parent): r""" The crystal on affine factorizations with a cut-point, as introduced @@ -359,6 +360,7 @@ def to_tableau(self): """ return self.parent()._tableaux_isomorphism(self) + def affine_factorizations(w, l, weight=None): r""" Return all factorizations of ``w`` into ``l`` factors or of weight ``weight``. @@ -448,6 +450,7 @@ def affine_factorizations(w, l, weight=None): ##################################################################### ## Crystal isomorphisms + class FactorizationToTableaux(CrystalMorphism): def _call_(self, x): """ diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 49ee74d1df5..2a58d8ab609 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -25,6 +25,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.rings.infinity import Infinity + class AffinizationOfCrystal(UniqueRepresentation, Parent): r""" An affinization of a crystal. @@ -73,6 +74,7 @@ class AffinizationOfCrystal(UniqueRepresentation, Parent): - [HK2002]_ Chapter 10 """ + def __init__(self, B): """ Initialize ``self``. @@ -109,6 +111,7 @@ class Element(Element): """ An element in an affinization crystal. """ + def __init__(self, parent, b, m): """ Initialize ``self``. diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 64b727e9d51..b60717a13ee 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -316,7 +316,6 @@ def __init__(self, starting_weight, highest_weight_crystal): self._highest_weight_crystal = highest_weight_crystal self._cartan_type = cartan_type - if cartan_type.is_finite() and highest_weight_crystal: Parent.__init__(self, category=ClassicalCrystals()) self._R = RootsWithHeight(starting_weight) @@ -332,7 +331,6 @@ def __init__(self, starting_weight, highest_weight_crystal): self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = False - self.module_generators = ( self.element_class(self, ()), ) def _repr_(self): @@ -467,6 +465,7 @@ class CrystalOfAlcovePathsElement(ElementWrapper): sage: C([8,9]) ((alpha[1], 2), (alpha[1] + alpha[2], 4)) """ + def __iter__(self): r""" Initialize ``self``. @@ -560,7 +559,7 @@ def _latex_(self): sage: C([1,2])._latex_() [(\alpha_{1} + \alpha_{2}, 0), (\alpha_{1}, 0)] """ - return [ (latex(i.root),i.height) for i in self.value ] + return [(latex(i.root), i.height) for i in self.value] @cached_in_parent_method def integer_sequence(self): @@ -830,13 +829,12 @@ def _folding_data(self, i): else: # NOTE: we assume J is sorted by order on Element of RootsWithHeight - for k in range(max_height_Beta): + for k in range(max_height_Beta): x = R(Beta, k) if x <= J[0]: signs[x] = self._sign(Beta) - for j in range( len(J) ): - + for j in range(len(J)): Beta = Beta.reflection(J[j].root) sign_Beta = self._sign(Beta) max_height_Beta = weight.scalar( @@ -850,7 +848,7 @@ def _folding_data(self, i): if j == len(J) - 1: c2 = max_height_Beta else: - c2 = min (max_height_Beta, J[j+1]._cmp_v[0]*max_height_Beta + 1) + c2 = min(max_height_Beta, J[j+1]._cmp_v[0]*max_height_Beta + 1) for k in range(int(c1), int(c2)): @@ -914,12 +912,11 @@ def e(self, i): if ( (not finite_cartan_type or i!=0) and m_index < len(gi)-1 # alpha_i is a simple root ) or KR_test: - J.remove(positions[m_index]) if m_index+1 < len(positions): # if m_index+1 != 'infinity' # i.e. positions[m_index+1] makes sense - J.append(positions[m_index+1]) - return_value = Parent ( tuple( sorted(J) ) ) + J.append(positions[m_index + 1]) + return_value = Parent(tuple(sorted(J))) # we attach to each admissible sequence a list # which encodes a path (via root operators) from the () generator @@ -927,9 +924,9 @@ def e(self, i): # this is useful for investing the crystal try: - return_value.i_string = self.i_string + [['e',i]] + return_value.i_string = self.i_string + [['e', i]] except AttributeError: - return_value.i_string = [['e',i]] + return_value.i_string = [['e', i]] return return_value else: @@ -1047,15 +1044,15 @@ def f(self, i): if m_index < len(positions): # if m_index != 'infinity' # thus positions[m_index] makes sense J.remove(positions[m_index]) - return_value = Parent ( tuple( sorted(J) ) ) + return_value = Parent(tuple(sorted(J))) # we attach to each admissible sequence a list # which encodes a path (via root operators) from the generator () try: - return_value.i_string = self.i_string + [['f',i]] + return_value.i_string = self.i_string + [['f', i]] except AttributeError: - return_value.i_string = [['f',i]] + return_value.i_string = [['f', i]] return return_value else: @@ -1131,8 +1128,10 @@ def path(self): ret.append(ret[-1] * prod(s[j] for j in i.root.associated_reflection())) return ret + CrystalOfAlcovePaths.Element = CrystalOfAlcovePathsElement + class InfinityCrystalOfAlcovePaths(UniqueRepresentation, Parent): r""" `\mathcal{B}(\infty)` crystal of alcove paths. @@ -1611,6 +1610,7 @@ def _an_element_(self): """ return self( self._root_lattice.from_vector(vector([1])), 0 ) + class RootsWithHeightElement(Element): r""" Element of :class:`RootsWithHeight`. @@ -1632,6 +1632,7 @@ class RootsWithHeightElement(Element): sage: y = R(x, 1); y (alpha[1] + alpha[2], 1) """ + def __init__(self, parent, root, height): r""" Initialize ``self``. @@ -1744,12 +1745,14 @@ def _richcmp_(self, other, op): #assert self.parent() is other.parent(), "elements have different parents" return richcmp(self._cmp_v, other._cmp_v, op) + RootsWithHeight.Element = RootsWithHeightElement ##################################################################### # Test code, by comparing with existing crystal implementations. ##################################################################### + def _test_some_specific_examples(clss=CrystalOfAlcovePaths): r""" Test against some specific (finite type) examples. @@ -1888,6 +1891,7 @@ def _test_some_specific_examples(clss=CrystalOfAlcovePaths): return True + def compare_graphs(g1, g2, node1, node2): r""" Compare two edge-labeled :class:`graphs <DiGraph>` obtained from @@ -1928,6 +1932,7 @@ def compare_graphs(g1, g2, node1, node2): return False return True + def _test_against_tableaux(R, N, k, clss=CrystalOfAlcovePaths): r""" Test :class:`~sage.combinat.crystals.alcove_path.CrystalOfAlcovePaths` @@ -1974,6 +1979,7 @@ def _test_against_tableaux(R, N, k, clss=CrystalOfAlcovePaths): return print(" Compare graphs: ", compare_graphs(G, H, C(()), H.vertices(sort=True)[0])) + def _test_with_lspaths_crystal(cartan_type, weight, depth=10): r""" Test if the digraphs generated are isomorphic to the ones generated by diff --git a/src/sage/combinat/crystals/direct_sum.py b/src/sage/combinat/crystals/direct_sum.py index da2ce05bee2..e66ea6e9ef5 100644 --- a/src/sage/combinat/crystals/direct_sum.py +++ b/src/sage/combinat/crystals/direct_sum.py @@ -179,6 +179,7 @@ class Element(ElementWrapper): r""" A class for elements of direct sums of crystals. """ + def e(self, i): r""" Return the action of `e_i` on ``self``. diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index 607206c52f6..2aa68d0279e 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -88,6 +88,7 @@ class AbstractSingleCrystalElement(Element): r""" Abstract base class for elements in crystals with a single element. """ + def __lt__(self, other): r""" EXAMPLES:: @@ -350,6 +351,7 @@ class Element(AbstractSingleCrystalElement): r""" Element of a `T_{\lambda}` crystal. """ + def _repr_(self): r""" EXAMPLES:: @@ -612,6 +614,7 @@ class Element(AbstractSingleCrystalElement): r""" Element of a `R_{\lambda}` crystal. """ + def _repr_(self): r""" EXAMPLES:: @@ -854,6 +857,7 @@ class Element(Element): r""" Element of a `B_i` crystal. """ + def __init__(self, parent, m): r""" EXAMPLES:: @@ -1172,6 +1176,7 @@ class Element(AbstractSingleCrystalElement): r""" Element of a component crystal. """ + def _repr_(self): r""" EXAMPLES:: diff --git a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py index df9805e07b3..5861e84493c 100644 --- a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py +++ b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py @@ -36,6 +36,7 @@ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.lazy_attribute import lazy_attribute + class DecreasingHeckeFactorization(Element, metaclass=InheritComparisonClasscallMetaclass): """ Class of decreasing factorizations in the 0-Hecke monoid. @@ -777,6 +778,7 @@ def _check_decreasing_hecke_factorization(t): if factor[i] <= factor[i+1]: raise ValueError("each nonempty factor should be a strictly decreasing sequence") + def _check_containment(t, parent): """ Check if ``t`` is an element of ``parent``. @@ -828,6 +830,7 @@ def _check_containment(t, parent): if excess != parent.excess: raise ValueError("number of excess letters do not match") + def _generate_decreasing_hecke_factorizations(w, factors, ex, weight=None, parent=None): """ Generate all decreasing factorizations of word ``w`` in a 0-Hecke monoid @@ -884,6 +887,7 @@ def _generate_decreasing_hecke_factorizations(w, factors, ex, weight=None, paren Factors.append(parent.element_class(parent, t)) return sorted(Factors, reverse=True) + def _list_all_decreasing_runs(word, m): """ List all possible decreasing runs into ``m`` factors for ``word`` in a @@ -903,6 +907,7 @@ def _list_all_decreasing_runs(word, m): V = [[m+1-sum(elt[:i+1]) for i in range(len(elt))] for elt in P] return V + def _to_reduced_word(P): """ Return a reduced word associated to skew partition ``P``. @@ -937,6 +942,7 @@ def _to_reduced_word(P): L += [j-i+m] return L + def _lowest_weights(w, factors, ex, parent=None): """ Generate all decreasing factorizations in the 0-Hecke monoid that correspond @@ -1002,6 +1008,7 @@ def _lowest_weights(w, factors, ex, parent=None): M.append(parent.element_class(parent, t)) return sorted(M) + def _jumps(w): """ Detect all positions where letters weakly increase in ``w``. @@ -1015,6 +1022,7 @@ def _jumps(w): """ return [i+1 for i in range(len(w)-1) if w[i]<=w[i+1]] + def _is_valid_column_word(w, m=None): """ Determine if ``w`` is actually a valid column reading word of some @@ -1053,6 +1061,7 @@ def _is_valid_column_word(w, m=None): for j in range(len(L[i+1]))) return False + def _list_equivalent_words(w): """ List all words equivalent to ``w`` in a 0-Hecke monoid. @@ -1121,6 +1130,7 @@ def _applicable_relations(word): queue += [tuple(t)] return sorted(v for v in list(V)) + def _apply_relations(word, position, move): """ Apply a particular type of ``move`` on ``word`` at the specified diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index 0d30991e00e..9503199c28c 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -42,6 +42,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.root_system.root_system import RootSystem + class GeneralizedYoungWall(CombinatorialElement): r""" A generalized Young wall. @@ -59,6 +60,7 @@ class GeneralizedYoungWall(CombinatorialElement): 0|1| | """ + def __init__(self, parent, data): r""" EXAMPLES:: diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 09cd066c8bb..4b6d5bb38bb 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -31,6 +31,7 @@ from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations from sage.rings.integer_ring import ZZ + def HighestWeightCrystal(dominant_weight, model=None): r""" Return the highest weight crystal of highest weight ``dominant_weight`` @@ -257,6 +258,7 @@ def HighestWeightCrystal(dominant_weight, model=None): raise ValueError("invalid model") + class FiniteDimensionalHighestWeightCrystal_TypeE(TensorProductOfCrystals): """ Commonalities for all finite dimensional type `E` highest weight crystals. @@ -264,6 +266,7 @@ class FiniteDimensionalHighestWeightCrystal_TypeE(TensorProductOfCrystals): Subclasses should setup an attribute column_crystal in their ``__init__`` method before calling the ``__init__`` method of this class. """ + def __init__(self, dominant_weight): """ EXAMPLES:: @@ -330,6 +333,7 @@ def module_generator(self): tensor = sum(( [self.column_crystal[i]]*dominant_weight.coefficient(i) for i in dominant_weight.support()), []) return self._element_constructor_(*[B.module_generators[0] for B in tensor]) + class FiniteDimensionalHighestWeightCrystal_TypeE6(FiniteDimensionalHighestWeightCrystal_TypeE): r""" Class of finite dimensional highest weight crystals of type `E_6`. diff --git a/src/sage/combinat/crystals/induced_structure.py b/src/sage/combinat/crystals/induced_structure.py index a2265e8df71..2920fb67b7c 100644 --- a/src/sage/combinat/crystals/induced_structure.py +++ b/src/sage/combinat/crystals/induced_structure.py @@ -293,6 +293,7 @@ class Element(ElementWrapper): """ An element of an induced crystal. """ + def e(self, i): """ Return `e_i` of ``self``. @@ -397,6 +398,7 @@ def weight(self): """ return self.parent()._phi(self.value).weight() + class InducedFromCrystal(UniqueRepresentation, Parent): r""" A crystal induced from an injection. @@ -427,6 +429,7 @@ class InducedFromCrystal(UniqueRepresentation, Parent): sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) """ + def __init__(self, X, phi, inverse): """ Initialize ``self``. @@ -590,6 +593,7 @@ class Element(ElementWrapper): """ An element of an induced crystal. """ + def e(self, i): """ Return `e_i` of ``self``. diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index 7fa7e5f7a2d..32536d9de60 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -294,6 +294,7 @@ class Element(InfinityCrystalOfTableauxElement): r""" Elements in `\mathcal{B}(\infty)` crystal of tableaux. """ + def phi(self,i): r""" Return `\varphi_i` of ``self``. diff --git a/src/sage/combinat/crystals/kac_modules.py b/src/sage/combinat/crystals/kac_modules.py index 90edd27d93d..d9da3fd8bff 100644 --- a/src/sage/combinat/crystals/kac_modules.py +++ b/src/sage/combinat/crystals/kac_modules.py @@ -122,6 +122,7 @@ class Element(ElementWrapper): sage: 2^len(S.weight_lattice_realization().positive_odd_roots()) 4096 """ + def _repr_(self): r""" Return a string representation of ``self``. @@ -592,6 +593,7 @@ class Element(ElementWrapper): ....: y = x.f(i) ....: assert y is None or y.e(i) == x """ + def _repr_(self): """ Return a string representation of ``self``. @@ -773,6 +775,7 @@ def weight(self): ##################################################################### ## Helper functions + def to_dual_tableau(elt): r""" Return a type `A_n` crystal tableau ``elt`` as a tableau expressed @@ -816,6 +819,7 @@ def to_dual_tableau(elt): x.reverse() return Tableau(tab).conjugate() + def latex_dual(elt): r""" Return a latex representation of a type `A_n` crystal tableau ``elt`` diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index d2de08173f0..ee7175e8a03 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -46,6 +46,7 @@ from sage.combinat.partition import Partition, Partitions from sage.combinat.integer_vector import IntegerVectors + def KirillovReshetikhinCrystalFromLSPaths(cartan_type, r, s=1): r""" Single column Kirillov-Reshetikhin crystals. @@ -576,6 +577,7 @@ class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement """ Abstract class for all Kirillov-Reshetikhin crystal elements. """ + def _repr_diagram(self): """ Return a string representation of ``self`` as a diagram. @@ -666,6 +668,7 @@ def lusztig_involution(self): KirillovReshetikhinGenericCrystal.Element = KirillovReshetikhinGenericCrystalElement + class KirillovReshetikhinCrystalFromPromotion(KirillovReshetikhinGenericCrystal, AffineCrystalFromClassicalAndPromotion): r""" @@ -682,6 +685,7 @@ class KirillovReshetikhinCrystalFromPromotion(KirillovReshetikhinGenericCrystal, - ``promotion_inverse`` - ``dynkin_diagram_automorphism`` """ + def __init__(self, cartan_type, r, s): r""" TESTS:: @@ -1465,6 +1469,7 @@ def from_ambient_crystal(self): return AmbientRetractMap(self, self.ambient_crystal(), pdict_inv, index_set=ind, automorphism=lambda i: i-1) + class KR_type_CElement(KirillovReshetikhinGenericCrystalElement): r""" Class for the elements in the Kirillov-Reshetikhin crystals `B^{r,s}` @@ -1476,6 +1481,7 @@ class KR_type_CElement(KirillovReshetikhinGenericCrystalElement): sage: type(K.module_generators[0]) <class 'sage.combinat.crystals.kirillov_reshetikhin.KR_type_C_with_category.element_class'> """ + def e0(self): r""" Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, @@ -1580,6 +1586,7 @@ class KR_type_A2(KirillovReshetikhinGenericCrystal): sage: G.is_isomorphic(Gnew, edge_labels = True) True """ + def module_generator(self): r""" Return the unique module generator of classical weight @@ -1770,6 +1777,7 @@ def from_ambient_crystal(self): return AmbientRetractMap(self, self.ambient_crystal(), pdict_inv, index_set=ind, automorphism=lambda i: i-1) + class KR_type_A2Element(KirillovReshetikhinGenericCrystalElement): r""" Class for the elements in the Kirillov-Reshetikhin crystals `B^{r,s}` of @@ -1853,6 +1861,7 @@ def phi0(self): b = self.parent().to_ambient_crystal()(self) return b.phi(1) + KR_type_A2.Element = KR_type_A2Element @@ -1872,6 +1881,7 @@ class KR_type_box(KirillovReshetikhinGenericCrystal, AffineCrystalFromClassical) sage: b.e(0) [[-1]] """ + def __init__(self, cartan_type, r, s): r""" Initializes a Kirillov-Reshetikhin crystal ``self``. @@ -2115,6 +2125,7 @@ def phi0(self): b = self.parent().to_ambient_crystal()(self) return b.phi(0) + KR_type_box.Element = KR_type_boxElement @@ -2138,6 +2149,7 @@ class KR_type_Bn(KirillovReshetikhinGenericCrystal): sage: [b.weight() for b in K if b.is_highest_weight([0,2,3])] [Lambda[0] - Lambda[1], -2*Lambda[1] + 2*Lambda[3]] """ + def _element_constructor_(self, *args, **options): """ Construct an element of ``self``. @@ -2330,6 +2342,7 @@ class KR_type_BnElement(KirillovReshetikhinGenericCrystalElement): sage: type(K.module_generators[0]) <class 'sage.combinat.crystals.kirillov_reshetikhin.KR_type_Bn_with_category.element_class'> """ + def e0(self): r""" Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, @@ -2394,6 +2407,7 @@ def phi0(self): b = self.parent().to_ambient_crystal()(self) return b.phi(0) // 2 + KR_type_Bn.Element = KR_type_BnElement @@ -2486,6 +2500,7 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): u = u.f(i) return u + class KR_type_CnElement(KirillovReshetikhinGenericCrystalElement): r""" Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` @@ -2589,6 +2604,7 @@ def phi0(self): [l1,l2] = pm.pm_diagram[n-1] return l2 + KR_type_Cn.Element = KR_type_CnElement @@ -2774,6 +2790,7 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): u = u.f(i) return u + class KR_type_Dn_twistedElement(KirillovReshetikhinGenericCrystalElement): r""" Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` @@ -2923,8 +2940,10 @@ def phi0(self): l4 = pm.pm_diagram[n][0] return l2 + l4 // 2 + KR_type_Dn_twisted.Element = KR_type_Dn_twistedElement + class KR_type_spin(KirillovReshetikhinCrystalFromPromotion): r""" Class of Kirillov-Reshetikhin crystals `B^{n,s}` of type `D_n^{(1)}`. @@ -2977,6 +2996,7 @@ class KR_type_spin(KirillovReshetikhinCrystalFromPromotion): sage: all(b.f(0).e(0) == b for b in K if b.phi(0)>0) True """ + def _element_constructor_(self, *args, **options): """ Construct an element of ``self`` from the input. @@ -3203,6 +3223,7 @@ def promotion_inverse(self): ind.remove(1) return CrystalDiagramAutomorphism(T, self.promotion_on_highest_weight_vectors_inverse(), ind) + class KR_type_D_tri1(KirillovReshetikhinGenericCrystal): r""" Class of Kirillov-Reshetikhin crystals `B^{1,s}` of type `D_4^{(3)}`. @@ -3210,6 +3231,7 @@ class KR_type_D_tri1(KirillovReshetikhinGenericCrystal): The crystal structure was defined in Section 4 of [KMOY2007]_ using the coordinate representation. """ + def __init__(self, ct, s): r""" Initialize ``self``. @@ -3449,6 +3471,7 @@ class CrystalOfTableaux_E7(CrystalOfTableaux): This is a helper class for the corresponding:class:`KR crystal <sage.combinat.crystals.kirillov_reshetikhin.KR_type_E7>` `B^{7,s}`. """ + def module_generator(self, shape): r""" Return the module generator of ``self`` with shape ``shape``. @@ -3469,10 +3492,12 @@ def module_generator(self, shape): raise NotImplementedError("only implemented for single row shapes") return self(*[self.letters.highest_weight_vector()]*shape[0]) + class KR_type_E7(KirillovReshetikhinGenericCrystal): r""" The Kirillov-Reshetikhin crystal `B^{7,s}` of type `E_7^{(1)}`. """ + def __init__(self, ct, r, s): r""" Initialize ``self``. @@ -3707,6 +3732,7 @@ class PMDiagram(CombinatorialObject): sage: PMDiagram([pm.n, pm.width, pm.outer_shape(), pm.intermediate_shape(), pm.inner_shape()], from_shapes=True) == pm True """ + def __init__(self, pm_diagram, from_shapes = None): r""" Initialize ``self``. @@ -3940,6 +3966,7 @@ def partitions_in_box(r, s): """ return [x for n in range(r*s+1) for x in Partitions(n,max_part=s,max_length=r)] + def vertical_dominoes_removed(r, s): """ Returns all partitions obtained from a rectangle of width s and height r by removing @@ -3956,6 +3983,7 @@ def vertical_dominoes_removed(r, s): """ return [x.conjugate() for x in horizontal_dominoes_removed(s,r)] + def horizontal_dominoes_removed(r, s): """ Returns all partitions obtained from a rectangle of width s and height r by removing @@ -3975,6 +4003,7 @@ def horizontal_dominoes_removed(r, s): ##################################################################### ## Morphisms + class AmbientRetractMap(Map): r""" The retraction map from the ambient crystal. @@ -3984,6 +4013,7 @@ class AmbientRetractMap(Map): ambient retract is the partial map `\tilde{\phi} : Y \to X` such that `\tilde{\phi} \circ \phi` is the identity on `X`. """ + def __init__(self, base, ambient, pdict_inv, index_set, similarity_factor_domain=None, automorphism=None): """ @@ -4044,6 +4074,7 @@ def _call_(self, x): return d return self._pdict_inv[x] + class CrystalDiagramAutomorphism(CrystalMorphism): """ The crystal automorphism induced from the diagram automorphism. @@ -4061,6 +4092,7 @@ class CrystalDiagramAutomorphism(CrystalMorphism): - ``automorphism`` -- (default: the identity) the twisting automorphism - ``cache`` -- (default: True) cache the result """ + def __init__(self, C, on_hw, index_set=None, automorphism=None, cache=True): """ Construct the promotion operator. diff --git a/src/sage/combinat/crystals/kyoto_path_model.py b/src/sage/combinat/crystals/kyoto_path_model.py index 6ee4d0e58a4..ff4c69e9ff9 100644 --- a/src/sage/combinat/crystals/kyoto_path_model.py +++ b/src/sage/combinat/crystals/kyoto_path_model.py @@ -321,6 +321,7 @@ class Element(TensorProductOfRegularCrystalsElement): An element in the Kyoto path model. """ # For simplicity (and safety), we use the regular crystals implementation + def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``. diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 2ddd6f3c461..89fbf8e4253 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -9,7 +9,7 @@ - Travis Scrimshaw (2016): Implemented :class:`~sage.combinat.crystals.littelmann_path.InfinityCrystalOfLSPaths` """ -#**************************************************************************** +# *************************************************************************** # Copyright (C) 2012 Mark Shimozono # Anne Schilling # @@ -22,8 +22,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#**************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.misc.cachefunc import cached_in_parent_method, cached_method from sage.structure.unique_representation import UniqueRepresentation @@ -147,10 +147,7 @@ def __classcall_private__(cls, starting_weight, cartan_type = None, starting_wei """ if cartan_type is not None: cartan_type, starting_weight = CartanType(starting_weight), cartan_type - if cartan_type.is_affine(): - extended = True - else: - extended = False + extended = cartan_type.is_affine() R = RootSystem(cartan_type) P = R.weight_space(extended = extended) @@ -333,7 +330,7 @@ def split_step(self, which_step, r): sage: b.split_step(0,1/3) (1/3*Lambda[1] + 1/3*Lambda[2], 2/3*Lambda[1] + 2/3*Lambda[2]) """ - assert 0 <= which_step and which_step <= len(self.value) + assert 0 <= which_step <= len(self.value) v = self.value[which_step] return self.parent()(self.value[:which_step] + (r*v,(1-r)*v) + self.value[which_step+1:]) @@ -476,7 +473,7 @@ def e(self, i, power=1, to_string_end=False, length_only=False): ix = len(data)-1 while ix >= 0 and data[ix][1] < M + p: - # get the index of the current step to be processed + # get the index of the current step to be processed j = data[ix][0] # find the i-height where the current step might need to be split if ix == 0: @@ -621,7 +618,7 @@ def _latex_(self): ##################################################################### -## Projected level-zero +# Projected level-zero class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths): @@ -1144,7 +1141,9 @@ def energy_function(self): Wd = WeylGroup(cartan_dual, prefix='s', implementation="permutation") G = Wd.quantum_bruhat_graph(J) Qd = RootSystem(cartan_dual).root_lattice() - dualize = lambda x: Qv.from_vector(x.to_vector()) + + def dualize(x): + return Qv.from_vector(x.to_vector()) L = [Wd.from_reduced_word(x.reduced_word()) for x in L] def stretch_short_root(a): @@ -1169,15 +1168,15 @@ def stretch_short_root(a): else: return s else: - s = sum((1-scalars[i])*c_weight.scalar( dualize (Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels))) + s = sum((1-scalars[i])*c_weight.scalar( dualize(Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels))) if ct.dual().type() == 'BC': - return s/2 + return s / 2 else: return s ##################################################################### -## B(\infty) +# B(\infty) class InfinityCrystalOfLSPaths(UniqueRepresentation, Parent): @@ -1464,7 +1463,7 @@ def phi(self,i): ##################################################################### -## Helper functions +# Helper functions def positively_parallel_weights(v, w): diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 5179ffd1a76..44d0c1cb4bd 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -662,6 +662,7 @@ def f(self, i): del A[(i,kf)] return self.__class__(self.parent(), newdict, A) + class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): r""" Crystal `B(\infty)` in terms of (modified) Nakajima monomials. @@ -1015,6 +1016,7 @@ def get_variables(self): Element = NakajimaMonomial + class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): r""" Element class for @@ -1038,6 +1040,7 @@ class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): Y(0,0)^2 Y(0,1)^-1 Y(2,0) sage: TestSuite(m).run() """ + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -1090,6 +1093,7 @@ def weight(self): P = self.parent().weight_lattice_realization() return P(self.weight_in_root_lattice()) + P(self.parent().hw) + class CrystalOfNakajimaMonomials(InfinityCrystalOfNakajimaMonomials): r""" Let `\widetilde{\mathcal{M}}` be `\widehat{\mathcal{M}}` as a set, and with diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index 38367628485..a3d0d661015 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -22,6 +22,7 @@ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ + class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): r""" The type `A_n^{(1)}` crystal `B(\infty)` realized using @@ -132,6 +133,7 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): - [JL2009]_ - [LTV1999]_ """ + def __init__(self, n): """ Initialize ``self``. @@ -186,6 +188,7 @@ class Element(ElementWrapper): """ An element in a BZ multisegments crystal. """ + def __init__(self, parent, value): """ Initialize ``self``. diff --git a/src/sage/combinat/crystals/mv_polytopes.py b/src/sage/combinat/crystals/mv_polytopes.py index 4ff902a7b6b..ddcff43c6f4 100644 --- a/src/sage/combinat/crystals/mv_polytopes.py +++ b/src/sage/combinat/crystals/mv_polytopes.py @@ -45,6 +45,7 @@ class MVPolytope(PBWCrystalElement): ....: f.axes(False) sage: animate(frames).show(delay=60) # optional -- ImageMagick # long time """ + def _repr_(self): """ Return a string representation of ``self``. @@ -238,6 +239,7 @@ def plot(self, P=None, **options): P = self.parent().weight_lattice_realization() return P.plot_mv_polytope(self, **options) + class MVPolytopes(PBWCrystal): r""" The crystal of Mirković-Vilonen (MV) polytopes. @@ -355,6 +357,7 @@ class MVPolytopes(PBWCrystal): - [Kam2007]_ - [Kam2010]_ """ + def __init__(self, cartan_type): """ Initialize ``self``. diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py index 4a313c8fa78..b03ace896b4 100644 --- a/src/sage/combinat/crystals/pbw_crystal.py +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -32,10 +32,12 @@ from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + class PBWCrystalElement(Element): """ A crystal element in the PBW model. """ + def __init__(self, parent, lusztig_datum, long_word=None): """ Initialize ``self``. diff --git a/src/sage/combinat/crystals/polyhedral_realization.py b/src/sage/combinat/crystals/polyhedral_realization.py index a709284149f..f4f20fe84f7 100644 --- a/src/sage/combinat/crystals/polyhedral_realization.py +++ b/src/sage/combinat/crystals/polyhedral_realization.py @@ -215,6 +215,7 @@ class Element(TensorProductOfCrystalsElement): An element in the polyhedral realization of `B(\infty)`. """ # For simplicity (and safety), we use the regular crystals implementation + def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``. diff --git a/src/sage/combinat/crystals/star_crystal.py b/src/sage/combinat/crystals/star_crystal.py index 1434eac09eb..2b4ac9c90a8 100644 --- a/src/sage/combinat/crystals/star_crystal.py +++ b/src/sage/combinat/crystals/star_crystal.py @@ -85,6 +85,7 @@ class StarCrystal(UniqueRepresentation, Parent): sage: mg.f_string([1,2,1,2,2]) [[1, 1, 1, 1, 1, 2, 2], [2, 3, 3, 3]] """ + def __init__(self, Binf): r""" Initialize ``self``. diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index ed1c62f4cf5..b1221b51e88 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -319,6 +319,7 @@ class Element(ElementWrapper): """ An element of a subcrystal. Wraps an element in the ambient crystal. """ + def _richcmp_(self, other, op): """ EXAMPLES: diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 18d80008ccb..6748af04b77 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -51,7 +51,7 @@ TensorProductOfSuperCrystalsElement, TensorProductOfQueerSuperCrystalsElement) from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.arith.misc import integer_trunc as trunc @@ -68,6 +68,7 @@ class CrystalOfWords(UniqueRepresentation, Parent): column-reading (and hence different shapes) to be considered elements in the same crystal. """ + def _element_constructor_(self, *crystalElements): """ EXAMPLES:: @@ -87,6 +88,7 @@ def _element_constructor_(self, *crystalElements): class Element(TensorProductOfCrystalsElement): pass + class TensorProductOfCrystals(CrystalOfWords): r""" Tensor product of crystals. @@ -469,6 +471,7 @@ def _element_constructor_(self, *crystalElements): crystalElements = reversed(crystalElements) return self.element_class(self, list(crystalElements)) + class TensorProductOfCrystalsWithGenerators(TensorProductOfCrystals): """ Tensor product of crystals with a generating set. @@ -478,6 +481,7 @@ class TensorProductOfCrystalsWithGenerators(TensorProductOfCrystals): Deprecate this class in favor of using :meth:`~sage.categories.crystals.Crystals.ParentMethods.subcrystal`. """ + def __init__(self, crystals, generators, cartan_type): """ EXAMPLES:: @@ -510,6 +514,7 @@ def _repr_(self): st = repr(list(self.crystals)) return "The tensor product of the crystals {}".format(st) + class FullTensorProductOfCrystals(TensorProductOfCrystals): """ Full tensor product of crystals. @@ -518,6 +523,7 @@ class FullTensorProductOfCrystals(TensorProductOfCrystals): Merge this into :class:`TensorProductOfCrystals`. """ + def __init__(self, crystals, **options): """ TESTS:: @@ -616,6 +622,7 @@ def weight_lattice_realization(self): return cm.common_parent(*[crystal.weight_lattice_realization() for crystal in self.crystals]) + class FullTensorProductOfRegularCrystals(FullTensorProductOfCrystals): """ Full tensor product of regular crystals. @@ -623,6 +630,7 @@ class FullTensorProductOfRegularCrystals(FullTensorProductOfCrystals): class Element(TensorProductOfRegularCrystalsElement): pass + class TensorProductOfRegularCrystalsWithGenerators(TensorProductOfCrystalsWithGenerators): """ Tensor product of regular crystals with a generating set. @@ -630,6 +638,7 @@ class TensorProductOfRegularCrystalsWithGenerators(TensorProductOfCrystalsWithGe class Element(TensorProductOfRegularCrystalsElement): pass + class FullTensorProductOfSuperCrystals(FullTensorProductOfCrystals): r""" Tensor product of super crystals. @@ -644,6 +653,7 @@ class FullTensorProductOfSuperCrystals(FullTensorProductOfCrystals): class Element(TensorProductOfSuperCrystalsElement): pass + class QueerSuperCrystalsMixin(): """ Mixin class with methods for a finite queer supercrystal. @@ -682,6 +692,7 @@ def _long_element(self): n = self.cartan_type().n return tuple(Permutations(n+1).long_element().reduced_word()) + class FullTensorProductOfQueerSuperCrystals(FullTensorProductOfCrystals, QueerSuperCrystalsMixin): r""" Tensor product of queer super crystals. @@ -962,7 +973,6 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): T.shapes = spin_shapes return T - def __init__(self, cartan_type, shapes): """ Construct the crystal of all tableaux of the given shapes. @@ -1054,6 +1064,7 @@ def _element_constructor_(self, *args, **options): class Element(CrystalOfTableauxElement): pass + class CrystalOfQueerTableaux(CrystalOfWords, QueerSuperCrystalsMixin): """ A queer crystal of the semistandard decomposition tableaux of a given shape. @@ -1063,6 +1074,7 @@ class CrystalOfQueerTableaux(CrystalOfWords, QueerSuperCrystalsMixin): - ``cartan_type`` -- a Cartan type - ``shape`` -- a shape """ + def __init__(self, cartan_type, shape): """ Initialize ``self``. diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx index fad1ced578d..8b7bf544d1f 100644 --- a/src/sage/combinat/crystals/tensor_product_element.pyx +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -59,18 +59,6 @@ cdef class ImmutableListWithParent(ClonableArray): """ ClonableArray.__init__(self, parent, list, check=False) - cpdef long _hash_(self) except? -1: - """ - Return the hash of ``self``. - - TESTS:: - - sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] - sage: b._hash_() == hash(b) - True - """ - return hash(tuple(self._list)) - def __setstate__(self, state): """ For unpickling old pickles. diff --git a/src/sage/combinat/crystals/virtual_crystal.py b/src/sage/combinat/crystals/virtual_crystal.py index 0283b4a9543..f14bf59dd42 100644 --- a/src/sage/combinat/crystals/virtual_crystal.py +++ b/src/sage/combinat/crystals/virtual_crystal.py @@ -297,6 +297,7 @@ class Element(Subcrystal.Element): An element of a virtual (sub)crystal. Wraps an element in the ambient crystal. """ + def e(self, i): """ Return `e_i` of ``self``. diff --git a/src/sage/combinat/decorated_permutation.py b/src/sage/combinat/decorated_permutation.py index 03d3e2fb495..f8ca5c8b78b 100644 --- a/src/sage/combinat/decorated_permutation.py +++ b/src/sage/combinat/decorated_permutation.py @@ -53,6 +53,26 @@ def __classcall_private__(cls, pi): sage: DecoratedPermutation([2, 1, -3]) [2, 1, -3] + TESTS: + + Check that hashing and comparison works:: + + sage: S = DecoratedPermutations(3) + sage: elt1 = S([2, 1, -3]) + sage: elt2 = DecoratedPermutation([2, 1, -3]) + sage: elt1 == elt2 + True + + sage: elt1 == [2, 1, -3] + False + + sage: elt2 = DecoratedPermutation([2, 1, 3]) + sage: elt1 != elt2 + True + + sage: hash(elt1) # random + 915443076393556996 + """ pi = list(pi) return DecoratedPermutations(len(pi))(pi) @@ -86,57 +106,6 @@ def check(self): if self not in self.parent(): raise ValueError("{} is not a decorated permutation".format(self)) - def __hash__(self): - r""" - Return a hash of ``self``. - - TESTS:: - - sage: len(set(hash(pi) for pi in DecoratedPermutations(3))) - 16 - """ - return hash(tuple(self)) - - def __eq__(self, other): - """ - Check whether ``self`` is equal to ``other``. - - INPUT: - - - ``other`` -- the element that ``self`` is compared to - - OUTPUT: Boolean - - EXAMPLES:: - - sage: S = DecoratedPermutations(3) - sage: elt1 = S([2, 1, -3]) - sage: elt2 = DecoratedPermutation([2, 1, -3]) - sage: elt1 == elt2 - True - """ - return isinstance(other, DecoratedPermutation) and list(self) == list(other) - - def __ne__(self, other): - """ - Check whether ``self`` is not equal to ``other``. - - INPUT: - - - ``other`` -- the element that ``self`` is compared to - - OUTPUT: Boolean - - EXAMPLES:: - - sage: S = DecoratedPermutations(3) - sage: elt1 = S([2, 1, -3]) - sage: elt2 = DecoratedPermutation([2, 1, 3]) - sage: elt1 != elt2 - True - """ - return not (self == other) - def size(self): """ Return the size of the decorated permutation. @@ -182,6 +151,7 @@ class DecoratedPermutations(UniqueRepresentation, Parent): 16 """ + def __init__(self, n): r""" Initialize ``self``. diff --git a/src/sage/combinat/derangements.py b/src/sage/combinat/derangements.py index 00bdeca728c..eeeb73fb7ce 100644 --- a/src/sage/combinat/derangements.py +++ b/src/sage/combinat/derangements.py @@ -50,6 +50,7 @@ class Derangement(CombinatorialElement): sage: elt = D([4,3,2,1]) sage: TestSuite(elt).run() """ + def to_permutation(self): """ Return the permutation corresponding to ``self``. diff --git a/src/sage/combinat/descent_algebra.py b/src/sage/combinat/descent_algebra.py index 3707a258e47..1245a8d1d59 100644 --- a/src/sage/combinat/descent_algebra.py +++ b/src/sage/combinat/descent_algebra.py @@ -126,6 +126,7 @@ class DescentAlgebra(UniqueRepresentation, Parent): sage: all(I(B(b)) == b for b in I.basis()) True """ + def __init__(self, R, n): r""" EXAMPLES:: @@ -193,6 +194,7 @@ class D(CombinatorialFreeModule, BindableClass): sage: list(D.basis()) [D{}] """ + def __init__(self, alg, prefix="D"): r""" Initialize ``self``. @@ -410,6 +412,7 @@ class B(CombinatorialFreeModule, BindableClass): [B[1, 1, 1, 1], B[1, 1, 2], B[1, 2, 1], B[1, 3], B[2, 1, 1], B[2, 2], B[3, 1], B[4]] """ + def __init__(self, alg, prefix="B"): r""" Initialize ``self``. @@ -642,6 +645,7 @@ class I(CombinatorialFreeModule, BindableClass): sage: list(I.basis()) [I[1, 1, 1, 1], I[1, 1, 2], I[1, 2, 1], I[1, 3], I[2, 1, 1], I[2, 2], I[3, 1], I[4]] """ + def __init__(self, alg, prefix="I"): r""" Initialize ``self``. @@ -813,6 +817,7 @@ class DescentAlgebraBases(Category_realization_of_parent): r""" The category of bases of a descent algebra. """ + def __init__(self, base): r""" Initialize the bases of a descent algebra. diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index dc9b6c39f3f..72be454ec63 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1197,7 +1197,7 @@ def _get_r_s_t_u(v): s = r//150 x = r%150 - if x == 0: + if x == 0: t,u = 30*s-5, 25 elif x == 1: t,u = 30*s-5, 26 diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 3d324b65f0e..564f1ea141a 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2445,7 +2445,7 @@ def QDM_57_9_1_1_8(): M = [[B[x] for x in R] for R in M] # replacing [0,..,8] by the elements of B M.append([0]*9) - return G(57), M + return G(57), M # Quasi-difference matrices # diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 529d4908c15..ed9bb493616 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -48,13 +48,16 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.arith.misc import is_prime_power from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError import sage.arith.all as arith from sage.misc.unknown import Unknown +from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing def group_law(G): @@ -1263,6 +1266,867 @@ def turyn_1965_3x3xK(k=4): return G, [[G(v + k) for l, k in zip(L, K) for v in l]] +def _is_periodic_sequence(seq, period): + r"""Check if the sequence is periodic with correct period. + + The sequence should have length at least twice the period, so that + periodicity can be checked. + + INPUT: + + - ``seq`` -- the sequence to be tested (must have length at least twice the period). + + - ``period`` -- integer, the period that the sequence should have. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _is_periodic_sequence + sage: _is_periodic_sequence([0, 1, 2, 3, 0, 1, 2, 3], 4) + True + sage: _is_periodic_sequence([0, 1, 0, 1, 0, 1, 0, 1], 4) + False + sage: _is_periodic_sequence([0, 1, 1, 1, 0, 1, 2, 1], 4) + False + """ + assert len(seq) >= 2*period + + for per in range(1, period): + first = seq[:per] + periodic = True + for j in range(1, len(seq)//per): + if seq[j*per:(j+1)*per] != first: + periodic = False + break + if periodic: + return False + if seq[:period] != seq[period:2*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`. + + Given a prime power `q`, the m-sequence is created as described by [Zie1959]_ + from a primitive function over the finite field `GF(q)`. + + Given a primitive function `f=c_0+c_1x+...+c_nx^n` over `K=GF(q)` of degree `n`, + the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1}+...+c_na{i-n})`. + The first `n` elements will be `0,0,...,0,1` and these will give a maximal length recurrence sequence + as shown in [Mit2008]_. + + INPUT: + + - ``q`` -- a prime power. + + - ``n`` -- a nonnegative number. + + - ``check`` -- boolean (dafault True): if true, check that the result is a seqauence with correct period. + Setting it to false may speed up considerably the computation. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _create_m_sequence + sage: _create_m_sequence(3, 2) #random + [1, 0, 1, 2, 2, 0, 2, 1] + sage: _create_m_sequence(4, 2, check=False) #random + [1, 0, a, a + 1, a, a, 0, a + 1, 1, a + 1, a + 1, 0, 1, a, 1] + sage: _create_m_sequence(6, 2) + Traceback (most recent call last): + ... + ValueError: q must be a prime power + + """ + from sage.rings.finite_rings.finite_field_constructor import GF + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if n < 0: + raise ValueError('n cannot be negative') + + K = GF(q, 'a') + + T = PolynomialRing(K, 'x') + primitive = T.irreducible_element(n, algorithm='random') + while not primitive.is_primitive(): + primitive = T.irreducible_element(n, algorithm='random') + coeffs = primitive.coefficients() + exps = primitive.exponents() + + period = q**n - 1 + seq_len = period*2 if check else period + seq = [1]+[0]*(n-1) + + while len(seq) < seq_len: + nxt = 0 + for i, coeff in zip(exps[1:], coeffs[1:]): + nxt += coeff*seq[-i] + seq.append(-coeffs[0].inverse()*nxt) + + if check: + 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`. + + This method tries to construct submodules from various elements of `G` until + a submodule of the correct order is found. + + INPUT: + + - ``G`` -- an additive abelian group. + + - ``order`` -- integer, the order of the desired syubmodule. + + TESTS: + + sage: from sage.combinat.designs.difference_family import _get_submodule_of_order + sage: G = AdditiveAbelianGroup([48]) + sage: _get_submodule_of_order(G, 6).order() + 6 + sage: G = AdditiveAbelianGroup([13^2-1]) + sage: _get_submodule_of_order(G, 12).order() + 12 + """ + for el in G: + H = G.submodule([el]) + if H.order() == order: + return H + return None + +def relative_difference_set_from_m_sequence(q, N, check=True): + 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`. + + The relative difference set is constructed over the set of additive integers modulo `q^N-1`, + as described in Theorem 5.1 of [EB1966]_. Given an m-sequence `(a_i)` of period `q^N-1`, the + set is: `R=\{i | 0 \le i \le q^{N-1}, a_i=1\}`. + + INPUT: + + - ``q`` -- a prime power. + + - ``N`` -- a nonnegative number. + + - ``check`` -- boolean (default True). If true, check that the result is a relative difference + set before returning it. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence + sage: relative_difference_set_from_m_sequence(2, 4) #random + [(0), (4), (5), (6), (7), (9), (11), (12)] + sage: relative_difference_set_from_m_sequence(8, 2, check=False) #random + [(0), (6), (30), (40), (41), (44), (56), (61)] + sage: relative_difference_set_from_m_sequence(6, 2) + Traceback (most recent call last): + ... + ValueError: q must be a prime power + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order + sage: q, N = 5, 3 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: is_relative_difference_set(relative_difference_set_from_m_sequence(q, N), G, H, ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) + True + sage: q, N = 13, 2 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: is_relative_difference_set(relative_difference_set_from_m_sequence(q, N), G, H, ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) + True + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if N < 2: + raise ValueError('N must be at least 2') + + m_seq = _create_m_sequence(q, N, check=False) + period = q**N-1 + G = AdditiveAbelianGroup([period]) + + set1 = [i for i in G if m_seq[i[0]] == 1] + + if check: + H = _get_submodule_of_order(G, q-1) + assert is_relative_difference_set(set1, G, H, (period//(q-1), q-1, q**(N-1), q**(N-2))) + return set1 + +def relative_difference_set_from_homomorphism(q, N, d, check=True): + r"""Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}d)` where `nd = q-1`. + + Given a prime power `q`, a number `N \ge 2` and integers `d` such that `d | q-1` we create the + relative difference set using the construction from Corollary 5.1.1 of [EB1966]_. + + INPUT: + + - ``q`` -- a prime power. + + - ``N`` -- an integer greater than 1. + + - ``d`` -- an integer which divides `q-1`. + + - ``check`` -- boolean (default True). If true, check that the result is a relative difference + set before returning it. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_homomorphism + sage: relative_difference_set_from_homomorphism(7, 2, 3) #random + [(0), (3), (4), (2), (13), (7), (14)] + sage: relative_difference_set_from_homomorphism(9, 2, 4, check=False) #random + [(0), (4), (6), (13), (7), (12), (15), (8), (9)] + sage: relative_difference_set_from_homomorphism(9, 2, 5) + Traceback (most recent call last): + ... + ValueError: q-1 must be a multiple of d + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order + sage: q, N, d = 11, 2, 5 + sage: G = AdditiveAbelianGroup([(q^N-1)//d]) + sage: H = _get_submodule_of_order(G, (q-1)//d) + sage: is_relative_difference_set(relative_difference_set_from_homomorphism(q, N, d), G, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + True + sage: q, N, d = 9, 2, 4 + sage: G = AdditiveAbelianGroup([(q^N-1)//d]) + sage: H = _get_submodule_of_order(G, (q-1)//d) + sage: is_relative_difference_set(relative_difference_set_from_homomorphism(q, N, d), G, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + True + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if N < 2: + raise ValueError('N must be at least 2') + if (q-1)%d != 0: + raise ValueError('q-1 must be a multiple of d') + + G = AdditiveAbelianGroup([q**N-1]) + K = _get_submodule_of_order(G, d) + assert K is not None, 'Could not find kernel' + + G2 = G/K + + theta = G.hom([G2.gen(0)], G2) + diff_set = relative_difference_set_from_m_sequence(q, N, check=False) + second_diff_set = [theta(x) for x in diff_set] + + if check: + H = _get_submodule_of_order(G2, (q-1)//d) + assert is_relative_difference_set(second_diff_set, G2, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + return second_diff_set + +def is_relative_difference_set(R, G, H, params, verbose =False): + r"""Check if `R` is a difference set of `G` relative to `H`, with the given parameters. + + This function checks that `G`, `H` and `R` have the orders specified in the parameters, and + that `R` satisfies the definition of relative difference set (from [EB1966]_): the collection of + differences `r-s`, `r,s \in R`, `r \neq s` contains only elements of `G` which are not in `H`, and contains + every such element exactly `d` times. + + INPUT: + + - ``R`` -- list, the relative diffeence set of length `k`. + + - ``G`` -- an additive abelian group of order `mn`. + + - ``H`` -- a submodule of ``G`` of order `n`. + + - ``params`` -- a tuple in the form `(m, n, k, d)`. + + - ``verbose`` -- boolean (default False). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _get_submodule_of_order, relative_difference_set_from_m_sequence, is_relative_difference_set + sage: q, N = 5, 2 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: params = ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)) + sage: R = relative_difference_set_from_m_sequence(q, N) + sage: is_relative_difference_set(R, G, H, params) + True + + If we pass the ``verbose`` argument, the function will explain why it failed:: + + sage: R2 = [G[1], G[2], G[3], G[5], G[6]] + sage: is_relative_difference_set(R2, G, H, params, verbose=True) + There is a value in the difference set which is not repeated d times + False + """ + m, n, k, d = params + if G.order() != m*n: + if verbose: + print('Incorrect order of G:', G.order()) + return False + + if H.order() != n: + if verbose: + print('Incorect order of H:', H.order()) + + if len(R) != k: + if verbose: + print('Length of R not correct:', len(R)) + return False + + diff_set = {} + for el1 in R: + for el2 in R: + if el1 != el2: + idx = el1-el2 + if idx not in diff_set: + diff_set[idx] = 0 + diff_set[idx] += 1 + values = [diff_set[x] for x in diff_set] + if max(values) != d or min(values) != d: + if verbose: + print('There is a value in the difference set which is not repeated d times') + return False + + for el in G: + if el in H and el in diff_set: + if verbose: + print('An element of G is present in both the difference set and in H') + return False + if el not in H and el not in diff_set: + if verbose: + print('An element of G is not present in either one of H or the difference set') + return False + + return True + +def is_supplementary_difference_set(Ks, v, lmbda): + r"""Check that the sets in ``Ks`` are `n-\{v; k_1,...,k_n; \lambda \}` supplementary difference sets. + + From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be `n` subsets of an additive abelian group `G` of order `v` + such that `|S_i|= k_i`. If, for each `g\in G`, `g \neq 0`, the total number of solutions of `a_i-a'_i = g`, with + `a_i,a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1,...,k_n;\lambda\}` supplementary difference sets. + + INPUT: + + - ``Ks`` -- a list of sets to be checked. + + - ``v`` -- integer, the parameter `v` of the supplementary difference sets. + + - ``lmbda`` -- integer, the parameter `\lambda` of the supplementary difference sets. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import supplementary_difference_set, is_supplementary_difference_set + sage: S1, S2, S3, S4 = supplementary_difference_set(17) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 16, 16) + True + sage: is_supplementary_difference_set([S1, S2, S3, S4], 16, 14) + False + sage: is_supplementary_difference_set([S1, S2, S3, S4], 20, 16) + False + + .. SEEALSO:: + + :func:`supplementary_difference_set` + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + G = AdditiveAbelianGroup([v]) + differences_counter = {} + for K in Ks: + for el1 in K: + for el2 in K: + diff = G[el1]-G[el2] + + if diff not in differences_counter: + differences_counter[diff] = 0 + differences_counter[diff] += 1 + + for el in G: + if el == 0: + continue + elif el not in differences_counter or lmbda != differences_counter[el]: + return False + + return True + +def supplementary_difference_set(q, existence=False, check=True): + r"""Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`. + + The sets are created from relative difference sets as detailed in Theorem 3.3 of [Spe1975]_. this construction + requires that `q` is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is + an odd prime power. + + Note that the construction from [Spe1975]_ states that the resulting sets are `4-\{2v; v+1, v, v, v; 2v\}` + supplementary difference sets. However, the implementation of that construction returns + `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets. This is not important, since the supplementary + difference sets are not ordered. + + INPUT: + + - ``q`` -- an odd prime power. + + - ``existence`` -- boolean (dafault False). If true, only check whether the supplementary difference sets + can be constructed. + + - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets + before returning them. + + OUTPUT: + + If ``existence`` is false, the function returns the 4 sets (containing integers), or raises an + error if ``q`` does not satify the constraints. + If ``existence`` is true, the function returns a boolean representing whether supplementary difference + sets can be constructed. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import supplementary_difference_set + sage: supplementary_difference_set(17) #random + ([0, 2, 5, 6, 8, 10, 13, 14], + [0, 1, 2, 6, 7, 9, 10, 14, 15], + [0, 1, 2, 6, 11, 12, 13, 15], + [0, 2, 6, 9, 11, 12, 13, 15]) + + If existence is ``True``, the function returns a boolean:: + + sage: supplementary_difference_set(7, existence=True) + False + sage: supplementary_difference_set(17, existence=True) + True + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set + sage: is_supplementary_difference_set(supplementary_difference_set(17), 16, 16) + True + sage: is_supplementary_difference_set(supplementary_difference_set(9), 8, 8) + True + sage: supplementary_difference_set(7) + Traceback (most recent call last): + ... + ValueError: There is no s for which m-1 is an odd prime power + sage: supplementary_difference_set(8) + Traceback (most recent call last): + ... + ValueError: q must be an odd prime power + sage: supplementary_difference_set(8, existence=True) + False + sage: supplementary_difference_set(7, existence=True) + False + sage: supplementary_difference_set(1, existence=True) + False + + .. SEEALSO:: + + :func:`is_supplementary_difference_set` + + """ + s = 0 + m = -1 + + while q > 2**(s+1) and (q-1) % 2**(s+1) == 0: + prime_pow = (q-1)//2**(s+1)-1 + if is_prime_power(prime_pow) and prime_pow % 2 == 1: + m = (q - (2**(s+1) + 1)) // 2**(s+1) + 1 + break + s += 1 + + if existence: + return is_prime_power(q) and q % 2 == 1 and m != -1 + + if not is_prime_power(q) or q % 2 != 1: + raise ValueError('q must be an odd prime power') + if m == -1: + raise ValueError('There is no s for which m-1 is an odd prime power') + + set1 = relative_difference_set_from_homomorphism(m-1, 2, (m-2)//2, check=False) + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + P = PolynomialRing(ZZ, 'x') + + #Compute psi3, psi4 + hall = 0 + for d in set1: + hall += P.monomial(d[0]) + + T_2m = 0 + for i in range(2*m): + T_2m += P.monomial(i) + + modulo = P.monomial(2*m)-1 + + diff = T_2m - (1+P.monomial(m))*hall + diff = diff.mod(modulo) + exp1, exp2 = diff.exponents() + a = (exp1+exp2-m)//2 + + alfa3 = (P.monomial(a) + hall).mod(modulo) + alfa4 = (P.monomial(a+m) + hall).mod(modulo) + + psi3 = alfa3 + psi4 = alfa4 + for i in range(s): + psi3 = (alfa3(P.monomial(2))+P.monomial(1)*alfa4(P.monomial(2))).mod(P.monomial(4*m)-1) + psi4 = (alfa3(P.monomial(2)) + P.monomial(1)*(T_2m(P.monomial(2)) - alfa4(P.monomial(2)))).mod(P.monomial(4*m)-1) + + # Construction of psi1, psi2 + set2 = relative_difference_set_from_m_sequence(q, 2, check=False) + s3 = _get_fixed_relative_difference_set(set2) + + phi_exps = [] + for i in range(len(s3)): + for j in range(i+1, len(s3)): + diff = (s3[i]-s3[j]) + if diff%(q-1) == 0 and diff%(q**2-1) != 0: + phi_exps.append(s3[i]) + + exps1 = [(x+1)//2 for x in phi_exps if x%2 == 1] + exps2 = [x//2 for x in phi_exps if x%2 == 0] + + theta1 = 0 + for exp in exps1: + theta1 += P.monomial(exp) + theta1 = theta1.mod(P.monomial(q-1)-1) + + theta2 = 0 + for exp in exps2: + theta2 += P.monomial(exp) + theta2 = theta2.mod(P.monomial(q-1)-1) + + phi1 = 0 + phi2 = 0 + for exp in phi_exps: + if exp%2 == 0: + phi2 += P.monomial(exp) + else: + phi1 += P.monomial(exp) + + psi1 = ((1 + P.monomial((q-1)//2)) * theta1).mod(P.monomial(q-1) - 1) + psi2 = (1 + (1 + P.monomial((q-1)//2)) * theta2).mod(P.monomial(q-1) - 1) + + + K1 = list(map(Integer, psi1.exponents())) + K2 = list(map(Integer, psi2.exponents())) + K3 = list(map(Integer, psi3.exponents())) + K4 = list(map(Integer, psi4.exponents())) + if check: + assert is_supplementary_difference_set([K1, K2, K3, K4], q-1, q-1) + + return K1, K2, K3, K4 + +def _get_fixed_relative_difference_set(rel_diff_set, as_elements=False): + r"""Contruct an equivalent relative difference set fixed by the size of the set. + + Given a relative difference set `R(q+1, q-1, q, 1)`, it is possible to find a translation + of this set fixed by `q` (see Section 3 of [Spe1975]_). We say that a set is fixed by `t` if + `\{td | d\in R\}= R`. + + In addition, the set returned by this function will contain the element `0`. This is needed in the + construction of supplementary difference sets (see :func:`supplementary_difference_set`). + + INPUT: + + - ``rel_diff_set`` -- the relative difference set. + + - ``as_elements`` -- boolean (default False). If true, the list returned will contain elements of the + abelian group (this may slow down the computation considerably). + + OUTPUT: + + By default, this function returns the set as a list of integers. However, if ``as_elements`` + is set to true it will return the set as a list containing elements of the abelian group. + If no such set can be found, the function will raise an error. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, _get_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(5, 2) + sage: _get_fixed_relative_difference_set(s1) #random + [2, 10, 19, 23, 0] + + If ``as_elements`` is true, the reuslt will contain elements of the group:: + + sage: _get_fixed_relative_difference_set(s1, as_elements=True) #random + [(2), (10), (19), (23), (0)] + + TESTS:: + + sage: from sage.combinat.designs.difference_family import _is_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(5, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: s1 = relative_difference_set_from_m_sequence(9, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: type(s2[0]) + <class 'sage.groups.additive_abelian.additive_abelian_group.AdditiveAbelianGroup_fixed_gens_with_category.element_class'> + sage: s2 = _get_fixed_relative_difference_set(s1) + sage: type(s2[0]) + <class 'sage.rings.integer.Integer'> + """ + G = rel_diff_set[0].parent() + q = len(rel_diff_set) + + s2 = None + for el in G: + fixed_set = [el+x for x in rel_diff_set] + if _is_fixed_relative_difference_set(fixed_set, q): + s2 = fixed_set + break + assert s2 is not None, 'Cannot find fixed translation of the set' + + s3 = None + for i in range(G.order()): + temp = [((q+1)*i+x[0])%G.order() for x in s2] + if 0 in temp: + s3 = temp + break + assert s3 is not None, 'Cannot find fixed set containing 0' + + if as_elements: + return [G[x] for x in s3] + return s3 + +def _is_fixed_relative_difference_set(R, q): + r"""Check if the relative difference set `R` is fixed by `q`. + + A relative difference set `R` is fixed by `q` if `\{qd | d\in R\}= R` (see Section 3 of [Spe1975]_). + + INPUT: + + - ``R`` -- the relative difference sets, as a list containing elements of the abelian group. + + - ``q`` -- an integer. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, _get_fixed_relative_difference_set, _is_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(7, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: G = AdditiveAbelianGroup([15]) + sage: s3 = [G[1], G[2], G[3], G[4]] + sage: _is_fixed_relative_difference_set(s3, len(s3)) + False + + If the relative difference set does not contain elements of the group, the method returns false:: + + sage: s1 = relative_difference_set_from_m_sequence(7, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=False) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + False + + + """ + for el in R: + if q*el not in R: + return False + return True + + +def skew_supplementary_difference_set(n, existence=False, check=True): + r"""Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets where `S_1` is skew and `n_1+n_2+n_3+n_4= n+\lambda`. + + These sets are constructed from available data, as described in [Djo1994]_. The set `S_1 \subset G` is + always skew, i.e. `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus\{0\}`. + + The data for `n = 103, 151` is taken from [Djo1994]_ and the data for `n = 67, 113, 127, 157, 163, 181, 241` + is taken from [Djo1992]_. + + INPUT: + + - ``n`` -- integer, the parameter of the supplementary difference set. + + - ``existence`` -- boolean (dafault False). If true, only check whether the supplementary difference sets + can be constructed. + + - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets + with `S_1` skew before returning them. Setting this parameter to False may speed up the computation considerably. + + OUTPUT: + + If ``existence`` is false, the function returns the 4 sets (containing integers modulo `n`), or raises an + error if data for the given ``n`` is not available. + If ``existence`` is true, the function returns a boolean representing whether skew supplementary difference + sets can be constructed. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(103) + + If existence is ``True``, the function returns a boolean :: + + sage: skew_supplementary_difference_set(103, existence=True) + True + sage: skew_supplementary_difference_set(17, existence=True) + False + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(113, check=False) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 113, len(S1)+len(S2)+len(S3)+len(S4)-113) + True + sage: _is_skew_set(S1, 113) + True + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(67, check=False) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 67, len(S1)+len(S2)+len(S3)+len(S4)-67) + True + sage: _is_skew_set(S1, 67) + True + sage: skew_supplementary_difference_set(7) + Traceback (most recent call last): + ... + ValueError: Skew SDS of order 7 not yet implemented. + sage: skew_supplementary_difference_set(7, existence=True) + False + sage: skew_supplementary_difference_set(127, existence=True) + True + """ + + + indices = { + 67: [[0,3,5,6,9,10,13,14,17,18,20], + [0,2,4,9,11,12,13,16,19,21], + [1,3,6,10,11,13,14,16,20,21], + [2,4,6,8,9,11,14,17,19]], + 103: [[1,3,4,6,8,11,12,14,17,18,20,22,25,27,28,30,32], + [2,9,10,12,13,14,15,16,20,21,22,23,24,26,28,29,30], + [0,1,2,3,4,11,12,13,16,17,19,20,21,24,25,26,28,30,31], + [0,1,2,3,4,5,6,13,15,18,19,20,23,24,25,26,27,28,29,31]], + 113: [[0,3,4,6,8,10,13,14], + [1,3,8,9,10,11,12,13], + [0,2,3,5,6,7,12], + [1,2,3,5,8,9,15]], + 127: [[0,3,5,7,8,10,12,14,16], + [0,1,3,6,7,9,10,12,14,15], + [0,1,3,4,5,7,8,9,15,16], + [1,4,5,6,9,10,13,14,15,16]], + 151: [[0,3,5,6,8,11,13,14,16,19,21,23,25,27,28], + [2,3,6,13,16,17,20,23,25,26,27,28,29], + [0,1,2,3,4,6,7,8,9,10,11,12,23,24,27,28], + [1,4,5,10,11,12,13,14,16,18,19,22,25,26,27,28]], + 157:[[0,2,5,7,8,11], + [0,4,5,6,9,11], + [6,7,8,9,10,11], + [0,5,6,7,8,10,11]], + 163: [[0,2,5,6,9,10,13,14,17], + [0,1,7,10,12,15,16,17], + [0,1,3,5,8,13,15,16,17], + [3,6,7,8,11,12,13,14,16,17]], + 181: [[0,3,5,6,8,10,13,15,16,19], + [4,5,7,8,11,14,15,16,18,19], + [0,4,10,11,13,15,16,18,19], + [2,4,5,7,11,13,15,17,19]], + 241: [[0,2,4,6,8,11,12,14], + [1,3,4,6,7,13,14,15], + [6,8,9,10,12,13,14,15], + [3,4,5,9,10,13,14]], + } + + cosets_gens = { + 67: [1,2,3,4,5,6,8,10,12,15,17], + 103: [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 30], + 113: [1, 2, 3, 5, 6, 9, 10, 13], + 127: [1, 3, 5, 7, 9, 11, 13, 19, 21], + 151: [1, 2, 3 ,4, 5, 6, 9, 10, 11, 12, 15, 22, 27, 29, 30], + 157: [1, 2, 3, 5, 9, 15], + 163: [1, 2, 3, 5, 6, 9, 10, 15, 18], + 181: [1, 2, 3, 4, 6, 7, 8, 12, 13, 24], + 241: [1, 2, 4, 5, 7, 13, 19, 35], + } + + H_db = { + 67: [1, 29, 37], + 103: [1, 46, 56], + 113: [1,16,28,30,49,106,109], + 127: [1,2,4,8,16,32,64], + 151: [1, 8,19,59, 64], + 157: [1,14,16,39,46,67,75,93,99,101,108,130,153], + 163: [1,38,40,53,58,85,104,133,140], + 181: [1,39,43,48,62,65,73,80,132], + 241: [1,15,24,54,87,91,94,98,100,119,160,183,205,225,231], + } + + def generate_set(index_set, cosets): + S = [] + for idx in index_set: + S += cosets[idx] + return S + + + if existence: + return n in indices + + if n not in indices: + raise ValueError(f'Skew SDS of order {n} not yet implemented.') + + Z = Zmod(n) + H = list(map(Z, H_db[n])) + + cosets = [] + for el in cosets_gens[n]: + even_coset = [] + odd_coset = [] + for x in H: + even_coset.append(x*el) + odd_coset.append(-x*el) + cosets.append(even_coset) + cosets.append(odd_coset) + + S1 = generate_set(indices[n][0], cosets) + S2 = generate_set(indices[n][1], cosets) + S3 = generate_set(indices[n][2], cosets) + S4 = generate_set(indices[n][3], cosets) + + if check: + lmbda = len(S1)+len(S2)+len(S3)+len(S4) - n + assert is_supplementary_difference_set([S1, S2, S3, S4], n, lmbda) + assert _is_skew_set(S1, n) + + return S1, S2, S3, S4 + +def _is_skew_set(S, n): + r"""Check if `S` is a skew set over the set of integers modulo `n`. + + From [Djo1994]_, a set `S \subset G` (where `G` is a finite abelian group of order `n`) is of skew + type if `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus \{0\}`. + + INPUT: + + - ``S`` -- the set to be checked, containing integers modulo `n`. + + - ``n`` -- the order of `G`. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _is_skew_set + sage: Z5 = Zmod(5) + sage: _is_skew_set([Z5(1), Z5(2)], 5) + True + sage: _is_skew_set([Z5(1), Z5(2), Z5(3)], 5) + False + sage: _is_skew_set([Z5(1)], 5) + False + """ + G = Zmod(n) + for el in S: + if -el in S: + return False + for el in G: + if el == 0: + continue + if el not in S and -el not in S: + return False + return True + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 49b564ce8c3..dfb6c90f652 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -182,8 +182,7 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, assert blocks is None, "'blocks' cannot be defined when 'points' is a matrix" incidence_matrix = points points = blocks = None - elif (points is not None and - blocks is None): + elif points is not None and blocks is None: blocks = points points = set().union(*blocks) if points: diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 76afb73ffc3..526487adb97 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -162,14 +162,14 @@ def construction_3_4(k,n,m,r,s,explain_construction=False): matrix[i][B0[i]] = 0 # Last column - if orthogonal_array(k, m+r ,existence=True): + if orthogonal_array(k, m+r ,existence=True): last_group = [x for x in range(s+1) if x != B0[-1]][:s] elif orthogonal_array(k,m+r+1,existence=True): last_group = [x for x in range(s+1) if x != B0[-1]][:s-1] + [B0[-1]] else: raise RuntimeError - for i,x in enumerate(last_group): + for i, x in enumerate(last_group): matrix[-1][x] = i OA = OA_relabel(master_design,k+r+1,n, matrix=matrix) @@ -260,11 +260,11 @@ def construction_3_5(k,n,m,r,s,t,explain_construction=False): r1 = [None]*q r2 = [None]*q r3 = [None]*q - for i,x in enumerate(group_k_1): + for i, x in enumerate(group_k_1): r1[x] = i - for i,x in enumerate(group_k_2): + for i, x in enumerate(group_k_2): r2[x] = i - for i,x in enumerate(group_k_3): + for i, x in enumerate(group_k_3): r3[x] = i OA = OA_relabel(master_design, k+3,q, matrix=[list(range(q))]*k+[r1,r2,r3]) @@ -431,7 +431,8 @@ def OA_and_oval(q, *, solver=None, integrality_tolerance=1e-3): assert all(sum([xx == 0 for xx in b[1:]]) <= 2 for b in OA) return OA -def construction_q_x(k,q,x,check=True,explain_construction=False): + +def construction_q_x(k, q, x, check=True, explain_construction=False): r""" Return an `OA(k,(q-1)*(q-x)+x+2)` using the `q-x` construction. @@ -520,14 +521,14 @@ def construction_q_x(k,q,x,check=True,explain_construction=False): " Malcolm Greig,\n"+ " Designs from projective planes and PBD bases,\n"+ " vol. 7, num. 5, pp. 341--374,\n"+ - " Journal of Combinatorial Designs, 1999").format(q,x) + " Journal of Combinatorial Designs, 1999").format(q, x) n = (q-1)*(q-x)+x+2 # We obtain the qxq matrix from a OA(q,q)-q.OA(1,q). We will need to add # blocks corresponding to the rows/columns OA = incomplete_orthogonal_array(q,q,(1,)*q) - TD = [[i*q+xx for i,xx in enumerate(B)] for B in OA] + TD = [[i*q+xx for i, xx in enumerate(B)] for B in OA] # Add rows, extended with p1 and p2 p1 = q**2 @@ -563,7 +564,7 @@ def construction_q_x(k,q,x,check=True,explain_construction=False): for xx in B: OA.remove([xx]*k) - for BB in orthogonal_array(k,x+2): + for BB in orthogonal_array(k, x+2): OA.append([B[x] for x in BB]) if check: diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py new file mode 100644 index 00000000000..d2213120724 --- /dev/null +++ b/src/sage/combinat/diagram.py @@ -0,0 +1,1485 @@ +r""" +Combinatorial diagrams + +A combinatorial diagram is a collection of cells `(i,j)` indexed by pairs of +natural numbers. + +For arbitrary diagrams, see :class:`Diagram`. There are also two other specific +types of diagrams implemented here. They are northwest diagrams +(:class:`NorthwestDiagram`) and Rothe diagrams (:func:`RotheDiagram`, a special +kind of northwest diagram). + +AUTHORS: + +- Trevor K. Karn (2022-08-01): initial version +""" + +# **************************************************************************** +# Copyright (C) 2022 Trevor K. Karn <karnx018 (at) umn.edu> +# +# 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.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.combinat.composition import Composition +from sage.combinat.partition import Partition +from sage.combinat.permutation import Permutations +from sage.combinat.tableau import Tableau +from sage.combinat.tiling import Polyomino +from sage.combinat.skew_partition import SkewPartition +from sage.combinat.skew_tableau import SkewTableaux +from sage.matrix.matrix_dense import Matrix_dense +from sage.matrix.matrix_sparse import Matrix_sparse +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent + + +class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): + r""" + Combinatorial diagrams with positions indexed by rows in columns. + + The positions are indexed by rows and columns as in a matrix. For example, + a Ferrer's diagram is a diagram obtained from a partition + `\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_{\ell})`, where the + cells are in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` + consist of `(i,j)` for `0 \leq j < \lambda_i`. In English notation, the + indices are read from top left to bottom right as in a matrix. + + Indexing conventions are the same as + :class:`~sage.combinat.partition.Partition`. Printing the diagram of a + partition, however, will always be in English notation. + + EXAMPLES: + + To create an arbirtrary diagram, pass a list of all cells:: + + sage: from sage.combinat.diagram import Diagram + sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] + sage: D = Diagram(cells); D + [(0, 0), (0, 1), (1, 0), (1, 1), (4, 4), (4, 5), (4, 6), (5, 4), (7, 6)] + + We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are + present in the cells which are present in the diagram and a ``.`` represents + the absence of a cell in the diagram:: + + sage: D.pp() + O O . . . . . + O O . . . . . + . . . . . . . + . . . . . . . + . . . . O O O + . . . . O . . + . . . . . . . + . . . . . . O + + We can also check if certain cells are contained in a given diagram:: + + sage: (1, 0) in D + True + sage: (2, 2) in D + False + + If you know that there are entire empty rows or columns at the end of the + diagram, you can manually pass them with keyword arguments ``n_rows=`` or + ``n_cols=``:: + + sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() + O . . O . + . . . . . + . . O . O + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . + """ + @staticmethod + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): + r""" + Normalize the input so that it lives in the correct parent. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D.parent() + Combinatorial diagrams + """ + return Diagrams()(cells, n_rows, n_cols, check) + + def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + sage: D1.nrows() + 4 + sage: D1.ncols() + 4 + sage: TestSuite(D1).run() + + We can specify the number of rows and columns explicitly, + in case they are supposed to be empty:: + + sage: D2 = Diagram([(0,2),(0,3),(1,1),(3,2)], n_cols=5) + sage: D2.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + sage: D2.ncols() + 5 + sage: D2.pp() + . . O O . + . O . . . + . . . . . + . . O . . + sage: TestSuite(D2).run() + """ + self._cells = frozenset(cells) + + if self._cells: + # minimum possible number of rows/cols + N_rows = max(c[0] for c in self._cells) + N_cols = max(c[1] for c in self._cells) + else: # if there are no cells + N_rows = -1 + N_cols = -1 + + if n_rows is not None: + if n_rows <= N_rows: + raise ValueError('n_rows is too small') + self._n_rows = n_rows + else: + self._n_rows = N_rows + 1 + if n_cols is not None: + if n_cols <= N_cols: + raise ValueError('n_cols is too small') + self._n_cols = n_cols + else: + self._n_cols = N_cols + 1 + + self._n_nonempty_rows = len(set(i for i, j in self._cells)) + self._n_nonempty_cols = len(set(j for i, j in self._cells)) + + ClonableArray.__init__(self, parent, sorted(cells), check) + + def pp(self): + r""" + Return a visualization of the diagram. + + Cells which are present in the + diagram are filled with a ``O``. Cells which are not present in the + diagram are filled with a ``.``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() + O . . O . + . . . . . + . . O . O + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . + sage: Diagram([]).pp() + - + """ + if self._n_rows == 0 or self._n_cols == 0: + print('-') + return + print("\n".join(self._pretty_print())) + + def _ascii_art_(self): + r""" + Return a visualization of the diagram. + + Cells which are present in the + diagram are filled with a ``O``. Cells which are not present in the + diagram are filled with a ``.``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: ascii_art(Diagram([(0,0), (0,3), (2,2), (2,4)])) + O . . O . + . . . . . + . . O . O + sage: ascii_art(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)) + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . + sage: ascii_art(Diagram([])) + - + """ + from sage.typeset.ascii_art import ascii_art + if self._n_rows == 0 or self._n_cols == 0: + return ascii_art("-") + return ascii_art("\n".join(self._pretty_print())) + + def _unicode_art_(self): + r""" + Return a unicode visualization of the diagram. + + Cells which are present in the + diagram are filled with a crossed box. Cells which are not present in the + diagram are filled with an empty box. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)])) + ┌─┬─┬─┬─┬─┐ + │X│ │ │X│ │ + ├─┼─┼─┼─┼─┤ + │ │ │ │ │ │ + ├─┼─┼─┼─┼─┤ + │ │ │X│ │X│ + └─┴─┴─┴─┴─┘ + sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)) + ┌─┬─┬─┬─┬─┬─┐ + │X│ │ │X│ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │X│ │X│ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + └─┴─┴─┴─┴─┴─┘ + sage: unicode_art(Diagram([])) + ∅ + """ + from sage.typeset.unicode_art import unicode_art + if self._n_rows == 0 or self._n_cols == 0: + return unicode_art("∅") + + ndivs = self._n_cols - 1 + cell = "│X" + empty = "│ " + it = self._pretty_print(cell, empty) + ret = "┌─" + "┬─"*ndivs + "┐" + ret += "\n" + next(it) + "│" + for row in it: + ret += "\n├─" + "┼─"*ndivs + "┤" + ret += "\n" + row + "│" + ret += "\n└─" + "┴─"*ndivs + "┘" + return unicode_art(ret) + + def _pretty_print(self, cell='O ', empty='. '): + r""" + Return a visualization of the diagram. + + Cells which are present in the + diagram are filled with ``cell``. Cells which are not present in the + diagram are filled with ``empty``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: "\n".join(Diagram([(0,0), (0,3), (2,2), (2,4)])._pretty_print('x ','. ')) + 'x . . x . \n. . . . . \n. . x . x ' + sage: "\n".join(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)._pretty_print('x ','. ')) + 'x . . x . . \n. . . . . . \n. . x . x . \n. . . . . . \n. . . . . . \n. . . . . . ' + """ + for i in range(self._n_rows): + output_str = '' + for j in range(self._n_cols): + if (i, j) in self: + output_str += cell + else: + output_str += empty + yield output_str + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: latex(Diagram([])) + {\emptyset} + sage: latex(Diagram([(0,0), (0,3), (2,2), (2,4)])) + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{5}{p{0.6ex}}}\cline{1-1}\cline{4-4} + \lr{\phantom{x}}&&&\lr{\phantom{x}}&\\\cline{1-1}\cline{4-4} + &&&&\\\cline{3-3}\cline{5-5} + &&\lr{\phantom{x}}&&\lr{\phantom{x}}\\\cline{3-3}\cline{5-5} + \end{array}$} + } + """ + if self._n_rows == 0 or self._n_cols == 0: + return "{\\emptyset}" + + 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) + + def end_line(r): + # give the line ending to row ``r`` + if r == 0: + return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[0]) if j is not None) + elif r == len(array): + return r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j is not None) + else: + out = r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j is not None) + out += "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r]) if j is not None) + return out + + tex=r'\raisebox{-.6ex}{$\begin{array}[b]{*{%s}{p{0.6ex}}}'%(max(map(len,array))) + tex+=end_line(0)+'\n' + for r in range(len(array)): + tex+='&'.join('' if c is None else r'\lr{%s}'%(c,) for c in array[r]) + tex += end_line(r+1)+'\n' + return '{%s\n%s\n}' % (lr, tex+r'\end{array}$}') + + def number_of_rows(self): + r""" + Return the total number of rows of ``self``. + + EXAMPLES: + + The following example has three rows which are filled, but they + are contained in rows 0 to 3 (for a total of four):: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.number_of_rows() + 4 + sage: D1.nrows() + 4 + + The total number of rows includes including those which are empty. + We can also include empty rows at the end:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)], n_rows=6) + sage: D.number_of_rows() + 6 + sage: D.pp() + . . O O + . O . . + . . . . + . . O . + . . . . + . . . . + """ + return self._n_rows + + nrows = number_of_rows + + def number_of_cols(self): + r""" + Return the total number of rows of ``self``. + + EXAMPLES: + + The following example has three columns which are filled, but they + are contained in rows 0 to 3 (for a total of four):: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D.number_of_cols() + 4 + sage: D.ncols() + 4 + + We can also include empty columns at the end:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)], n_cols=6) + sage: D.number_of_cols() + 6 + sage: D.pp() + . . O O . . + . O . . . . + . . . . . . + . . O . . . + """ + return self._n_cols + + ncols = number_of_cols + + def cells(self): + r""" + Return a ``list`` of the cells contained in the diagram ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + """ + return sorted(self._cells) + + def number_of_cells(self): + r""" + Return the total number of cells contained in the diagram ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.number_of_cells() + 4 + sage: D1.n_cells() + 4 + """ + return len(self._cells) + + n_cells = number_of_cells + + size = number_of_cells + + def check(self): + r""" + Check that this is a valid diagram. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D.check() + + In the next two examples, a bad diagram is passed. + The first example fails because one cells is indexed by negative + integers:: + + sage: D = Diagram([(0,0), (0,-3), (2,2), (2,4)]) + Traceback (most recent call last): + ... + ValueError: Diagrams must be indexed by non-negative integers + + The next example fails because one cell is indexed by rational + numbers:: + + sage: D = Diagram([(0,0), (0,3), (2/3,2), (2,4)]) + Traceback (most recent call last): + ... + ValueError: Diagrams must be indexed by non-negative integers + """ + from sage.sets.non_negative_integers import NonNegativeIntegers + NN = NonNegativeIntegers() + if not all(all(list(i in NN for i in c)) for c in self._cells): + raise ValueError("Diagrams must be indexed by non-negative integers") + + +class Diagrams(UniqueRepresentation, Parent): + r""" + The class of combinatorial diagrams. + + A *combinatorial diagram* is a set of cells indexed by pairs of natural + numbers. Calling an instance of :class:`Diagrams` is one way to construct + diagrams. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: D = Dgms([(0,0), (0,3), (2,2), (2,4)]) + sage: D.parent() + Combinatorial diagrams + + """ + + def __init__(self, category=None): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams(); Dgms + Combinatorial diagrams + + TESTS:: + + sage: TestSuite(Dgms).run() + """ + Parent.__init__(self, category=InfiniteEnumeratedSets().or_subcategory(category)) + + def __iter__(self): + r""" + Iterate over ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: I = iter(Diagrams()) + sage: for i in range(10): + ....: print(next(I)) + [] + [(0, 0)] + [(1, 0)] + [(0, 0), (1, 0)] + [(0, 1)] + [(0, 0), (0, 1)] + [(0, 1), (1, 0)] + [(0, 0), (0, 1), (1, 0)] + [(2, 0)] + [(0, 0), (2, 0)] + sage: next(I).parent() + Combinatorial diagrams + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: I = iter(NorthwestDiagrams()) + sage: for i in range(20): + ....: print(next(I)) + [] + [(0, 0)] + [(1, 0)] + [(0, 0), (1, 0)] + [(0, 1)] + [(0, 0), (0, 1)] + [(0, 0), (0, 1), (1, 0)] + [(2, 0)] + [(0, 0), (2, 0)] + [(1, 0), (2, 0)] + [(0, 0), (1, 0), (2, 0)] + [(0, 0), (0, 1), (2, 0)] + [(0, 0), (0, 1), (1, 0), (2, 0)] + [(1, 1)] + [(0, 0), (1, 1)] + [(1, 0), (1, 1)] + [(0, 0), (1, 0), (1, 1)] + [(0, 1), (1, 1)] + [(0, 0), (0, 1), (1, 1)] + [(0, 0), (0, 1), (1, 0), (1, 1)] + """ + from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.categories.cartesian_product import cartesian_product + from sage.misc.misc import subsets + # the product of positive integers automatically implements an + # an enumeration which allows us to get out of the first column + N = NonNegativeIntegers() + NxN = cartesian_product([N, N]) + X = subsets(NxN) + while True: + cells = next(X) + try: + yield self.element_class(self, tuple((i, j) for i,j in cells)) + except ValueError: + # if cells causes the .check method of a + # subclass to fail, just go to the next one + pass + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams(); Dgms + Combinatorial diagrams + """ + return 'Combinatorial diagrams' + + def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): + r""" + Cosntruct an element of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: Dgms([(0,1),(2,2)]).pp() + . O . + . . . + . . O + + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: Dgms(p).pp() + O . . + O O O + + sage: from sage.combinat.composition import Composition + sage: a = Composition([4,2,0,2,4]) + sage: Dgms(a).pp() + O O O O + O O . . + . . . . + O O . . + O O O O + + sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) + sage: Dgms(M).pp() + O O O O + O O . . + . . . . + O O . . + O O O O + + TESTS:: + + sage: TestSuite(Dgms).run() + """ + if isinstance(cells, Polyomino): + return self.from_polyomino(cells) + if isinstance(cells, Composition): + return self.from_composition(cells) + if isinstance(cells, (Matrix_dense, Matrix_sparse)): + return self.from_zero_one_matrix(cells) + + return self.element_class(self, cells, n_rows, n_cols, check) + + def _an_element_(self): + r""" + Return an element of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: D = Dgms.an_element(); D + [(0, 2), (1, 1), (2, 3)] + sage: D.pp() + . . O . + . O . . + . . . O + """ + return self([(0, 2), (1, 1), (2, 3)]) + + def from_polyomino(self, p): + r""" + Create the diagram corresponding to a 2d + :class:`~sage.combinat.tiling.Polyomino` + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(p).pp() + O . . + O O O + + We can also call this method directly:: + + sage: Diagrams().from_polyomino(p).pp() + O . . + O O O + + This only works for a 2d :class:`~sage.combinat.tiling.Polyomino`:: + + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: Diagrams().from_polyomino(p) + Traceback (most recent call last): + ... + ValueError: the polyomino must be 2 dimensional + """ + if not p._dimension == 2: + raise ValueError("the polyomino must be 2 dimensional") + cells = list(map(tuple, p)) + return self.element_class(self, cells) + + def from_composition(self, alpha): + r""" + Create the diagram corresponding to a weak composition `\alpha \vDash n`. + + EXAMPLES:: + + sage: alpha = Composition([3,0,2,1,4,4]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(alpha).pp() + O O O . + . . . . + O O . . + O . . . + O O O O + O O O O + sage: Diagrams().from_composition(alpha).pp() + O O O . + . . . . + O O . . + O . . . + O O O O + O O O O + """ + cells = [] + for i, n in enumerate(alpha): + cells.extend((i, j) for j in range(n)) + return self.element_class(self, cells, check=False) + + def from_zero_one_matrix(self, M, check=True): + r""" + Get a diagram from a matrix with entries in `\{0, 1\}`, where + positions of cells are indicated by the `1`'s. + + EXAMPLES:: + + sage: M = matrix([[1,0,1,1],[0,1,1,0]]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(M).pp() + O . O O + . O O . + sage: Diagrams().from_zero_one_matrix(M).pp() + O . O O + . O O . + + sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) + sage: Diagrams()(M).pp() + O . . + O . . + . . . + """ + # check matrix is zero-one + n_rows, n_cols = M.dimensions() + + if check: + zero = M.base_ring().zero() + one = M.base_ring().one() + for i in range(n_rows): + for j in range(n_cols): + if not (M[i,j] == zero or M[i,j] == one): + raise ValueError("Matrix entries must be 0 or 1") + cells = [(i, j) for i in range(n_rows) for j in range(n_cols) if M[i,j]] + + return self.element_class(self, cells, n_rows, n_cols, check=False) + + Element = Diagram + + +#################### +# Northwest diagrams +#################### + +class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): + r""" + Diagrams with the northwest property. + + A diagram is a set of cells indexed by natural numbers. Such a diagram + has the *northwest property* if the presence of cells `(i1, j1)` and + `(i2, j2)` implies the presence of the cell + `(\min(i1, i2), \min(j1, j2))`. Diagrams with the northwest property are + called *northwest diagrams*. + + For general diagrams see :class:`Diagram`. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0, 2), (2,0)]) + + To visualize them, use the ``.pp()`` method:: + + sage: N.pp() + O . O + . . . + O . . + """ + @staticmethod + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): + """ + Normalize input to ensure a correct parent. This method also allows + one to specify whether or not to check the northwest property for the + provided cells. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram, NorthwestDiagrams + sage: N1 = NorthwestDiagram([(0,1), (0,2)]) + sage: N2 = NorthwestDiagram([(0,1), (0,3)]) + sage: N1.parent() is N2.parent() + True + sage: N3 = NorthwestDiagrams()([(0,1), (0,2)]) + sage: N3.parent() is NorthwestDiagrams() + True + sage: N1.parent() is NorthwestDiagrams() + True + """ + return NorthwestDiagrams()(cells, n_rows, n_cols, check) + + def check(self): + r""" + A diagram has the northwest property if the presence of cells + `(i1, j1)` and `(i2, j2)` implies the presence of the cell + `(min(i1, i2), min(j1, j2))`. This method checks if the northwest + property is satisfied for ``self`` + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0,3), (3,0)]) + sage: N.check() + + Here is a non-example:: + + sage: notN = NorthwestDiagram([(0,1), (1,0)]) #.check() is implicit + Traceback (most recent call last): + ... + ValueError: diagram is not northwest + + TESTS:: + + sage: NorthwestDiagram([(0,1/2)]) + Traceback (most recent call last): + ... + ValueError: Diagrams must be indexed by non-negative integers + """ + from itertools import combinations + Diagram.check(self) + if not all((min(i1, i2), min(j1, j2)) in self + for (i1, j1), (i2, j2) in combinations(self._cells, 2)): + raise ValueError("diagram is not northwest") + + def peelable_tableaux(self): + r""" + Return the set of peelable tableaux whose diagram is ``self``. + + For a fixed northwest diagram `D`, we say that a Young tableau `T` is + `D`-peelable if: + + 1. the row indices of the cells in the first column of `D` are + the entries in an initial segment in the first column of `T` and + 2. the tableau `Q` obtained by removing those cells from `T` and playing + jeu de taquin is `(D-C)`-peelable, where `D-C` is the diagram formed + by forgetting the first column of `D`. + + Reiner and Shimozono [RS1995]_ showed that the number + `\operatorname{red}(w)` of reduced words of a permutation `w` may be + computed using the peelable tableaux of the Rothe diagram `D(w)`. + Explicitly, + + .. MATH:: + + \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T}, + + where the sum runs over the `D(w)`-peelable tableaux `T` and `f_\lambda` + is the number of standard Young tableaux of shape `\lambda` (which may + be computed using the hook-length formula). + + EXAMPLES: + + We can compute the `D`-peelable diagrams for a northwest diagram `D`:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), + ....: (4,0), (4,2)] + sage: D = NorthwestDiagram(cells); D.pp() + O O O . . + O . . . . + O . O . O + . . . . . + O . O . . + sage: D.peelable_tableaux() + {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], + [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} + + EXAMPLES: + + If the diagram is only one column, there is only one peelable tableau:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: NWD = NorthwestDiagram([(0,0), (2,0)]) + sage: NWD.peelable_tableaux() + {[[1], [3]]} + + From [RS1995]_, we know that there is only one peelable tableau for the + Rothe diagram of the permutation (in one line notation) `251643`:: + + sage: D = NorthwestDiagram([(1, 2), (1, 3), (3, 2), (3, 3), (4, 2)]) + sage: D.pp() + . . . . + . . O O + . . . . + . . O O + . . O . + + sage: D.peelable_tableaux() + {[[2, 2], [4, 4], [5]]} + + Here are all the intermediate steps to compute the peelables for the + Rothe diagram of (in one-line notation) `64817235`. They are listed from + deepest in the recursion to the final step. The recursion has depth five + in this case so we will label the intermediate tableaux by `D_i` where + `i` is the step in the recursion at which they appear. + + Start with the one that has a single column:: + + sage: D5 = NorthwestDiagram([(2,0)]); D5.pp() + . + . + O + sage: D5.peelable_tableaux() + {[[3]]} + + Now we know all of the `D_5` peelables, so we can compute the `D_4` + peelables:: + + sage: D4 = NorthwestDiagram([(0, 0), (2,0), (4, 0), (2, 2)]) + sage: D4.pp() + O . . + . . . + O . O + . . . + O . . + + sage: D4.peelable_tableaux() + {[[1, 3], [3], [5]]} + + There is only one `D_4` peelable, so we can compute the `D_3` + peelables:: + + sage: D3 = NorthwestDiagram([(0,0), (0,1), (2, 1), (2, 3), (4,1)]) + sage: D3.pp() + O O . . + . . . . + . O . O + . . . . + . O . . + + sage: D3.peelable_tableaux() + {[[1, 1], [3, 3], [5]], [[1, 1, 3], [3], [5]]} + + Now compute the `D_2` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), + ....: (4,0), (4,2)] + sage: D2 = NorthwestDiagram(cells); D2.pp() + O O O . . + O . . . . + O . O . O + . . . . . + O . O . . + + sage: D2.peelable_tableaux() + {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], + [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} + + And the `D_1` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (0,3), (1,0), (1,1), (2,0), + ....: (2,1), (2,3), (2,5), (4,0), (4,1), (4,3)] + sage: D1 = NorthwestDiagram(cells); D1.pp() + O O O O . . + O O . . . . + O O . O . O + . . . . . . + O O . O . . + + sage: D1.peelable_tableaux() + {[[1, 1, 1, 1], [2, 2, 3, 3], [3, 3, 5], [5, 5]], + [[1, 1, 1, 1, 3], [2, 2, 3], [3, 3, 5], [5, 5]]} + + Which we can use to get the `D` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (0,3), (0,4), + ....: (1,0), (1,1), (1,2), + ....: (2,0), (2,1), (2,2), (2,4), (2,6), + ....: (4,1), (4,2), (4,4)] + sage: D = NorthwestDiagram(cells); D.pp() + O O O O O . . + O O O . . . . + O O O . O . O + . . . . . . . + . O O . O . . + sage: D.peelable_tableaux() + {[[1, 1, 1, 1, 1], [2, 2, 2, 3, 3], [3, 3, 3], [5, 5, 5]], + [[1, 1, 1, 1, 1], [2, 2, 2, 3, 3], [3, 3, 3, 5], [5, 5]], + [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3], [5, 5, 5]], + [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3, 5], [5, 5]]} + + ALGORITHM: + + This implementation uses the algorithm suggested in Remark 25 + of [RS1995]_. + + TESTS: + + Corner case:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: D = NorthwestDiagram([]) + sage: D.peelable_tableaux() + {[]} + """ + # TODO: There is a condition on the first column (if the rows in Dhat + # are a subset of the rows in the first column) which simplifies the + # description without performing JDT, so we should implement that + + # empty diagram case + if not self: + return set([Tableau([])]) + + # if there is a single column in the diagram then there is only + # one posslbe peelable tableau. + if self._n_nonempty_cols == 1: + return set([Tableau([[i+1] for i, j in self.cells()])]) + + first_col = min(j for i, j in self._cells) + + dhat_cells = [] + new_vals_cells = [] + for i, j in self._cells: + if j != first_col: + dhat_cells.append((i, j)) + else: + new_vals_cells.append(i + 1) + + new_vals = sorted(new_vals_cells) + + Dhat = NorthwestDiagram(dhat_cells) + k = self.n_cells() - Dhat.n_cells() + + peelables = set() + + for Q in Dhat.peelable_tableaux(): + # get the vertical strips + mu = Q.shape() + vertical_strip_cells = mu.vertical_border_strip_cells(k) + for s in vertical_strip_cells: + sQ = SkewTableaux()(Q) # sQ is skew - get it? + # perform the jeu de taquin slides + for c in s: + sQ = sQ.backward_slide(c) + # create the new tableau by filling the columns + sQ_new = sQ.to_list() + for n in range(k): + sQ_new[n][0] = new_vals[n] + + T = Tableau(sQ_new) + if T.is_column_strict(): + peelables.add(T) + + return peelables + + +class NorthwestDiagrams(Diagrams): + r""" + Diagrams satisfying the northwest property. + + A diagram `D` is a *northwest diagram* if for every two cells `(i_1, j_1)` + and `(i_2, j_2)` in `D` then there exists the cell + `(\min(i_1, i_2), \min(j_1, j_2)) \in D`. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0, 10), (5,0)]); N.pp() + O . . . . . . . . . O + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + + Note that checking whether or not the northwest property is satisfied is + automatically checked. The diagram found by adding the cell `(1,1)` to the + diagram above is *not* a northwest diagram. The cell `(1,0)` should be + present due to the presence of `(5,0)` and `(1,1)`:: + + sage: from sage.combinat.diagram import Diagram + sage: Diagram([(0, 0), (0, 10), (5, 0), (1, 1)]).pp() + O . . . . . . . . . O + . O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) + Traceback (most recent call last): + ... + ValueError: diagram is not northwest + + However, this behavior can be turned off if you are confident that + you are providing a northwest diagram:: + + sage: N = NorthwestDiagram([(0, 0), (0, 10), (5, 0), + ....: (1, 1), (0, 1), (1, 0)], + ....: check=False) + sage: N.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + + Note that arbitrary diagrams which happen to be northwest diagrams + only live in the parent of :class:`Diagrams`:: + + sage: D = Diagram([(0, 0), (0, 10), (5, 0), (1, 1), (0, 1), (1, 0)]) + sage: D.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D in NorthwestDiagrams() + False + + Here are some more examples:: + + sage: from sage.combinat.diagram import NorthwestDiagram, NorthwestDiagrams + sage: D = NorthwestDiagram([(0,1), (0,2), (1,1)]); D.pp() + . O O + . O . + sage: NWDgms = NorthwestDiagrams() + sage: D = NWDgms([(1,1), (1,2), (2,1)]); D.pp() + . . . + . O O + . O . + sage: D.parent() + Combinatorial northwest diagrams + + Additionally, there are natural constructions of a northwest diagram + given the data of a permutation (Rothe diagrams are the protypical example + of northwest diagrams), or the data of a partition of an integer, or a + skew partition. + + The Rothe diagram `D(\omega)` of a permutation `\omega` is specified by + the cells + + .. MATH:: + + D(\omega) = \{(\omega_j, i) : i<j,\, \omega_i > \omega_j \}. + + We can construct one by calling :meth:`rothe_diagram` method on the set + of all :class:`~sage.combinat.diagram.NorthwestDiagrams`:: + + sage: w = Permutations(4)([4,3,2,1]) + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O O O . + O O . . + O . . . + . . . . + + 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 + :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`:: + + sage: mu = Partition([7,3,1,1]) + sage: mu.pp() + ******* + *** + * + * + sage: NorthwestDiagrams().from_partition(mu).pp() + O O O O O O O + O O O . . . . + O . . . . . . + O . . . . . . + + It is also possible to turn a Ferrers diagram of a skew partition into a + northwest diagram, altough it is more subtle than just using the skew + diagram itself. One must first reflect the partition about a vertical axis + so that the skew partition looks "backwards":: + + sage: mu, nu = Partition([5,4,3,2,1]), Partition([3,2,1]) + sage: s = mu/nu; s.pp() + ** + ** + ** + ** + * + sage: NorthwestDiagrams().from_skew_partition(s).pp() + O O . . . + . O O . . + . . O O . + . . . O O + . . . . O + """ + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NWDgms = NorthwestDiagrams(); NWDgms + Combinatorial northwest diagrams + """ + return 'Combinatorial northwest diagrams' + + def _an_element_(self): + r""" + Return an element of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NWDgms = NorthwestDiagrams() + sage: NWD = NWDgms.an_element(); NWD + [(0, 1), (0, 2), (1, 1), (2, 3)] + sage: NWD.pp() + . O O . + . O . . + . . . O + sage: NWD.parent() is NWDgms + True + """ + return self([(0, 1), (0, 2), (1, 1), (2, 3)]) + + def rothe_diagram(self, w): + r""" + Return the Rothe diagram of ``w``. + + We construct a northwest diagram from a permutation by + constructing its Rothe diagram. Formally, if `\omega` is + a :class:`~sage.combinat.permutation.Permutation` + then the Rothe diagram `D(\omega)` is the diagram whose cells are + + .. MATH:: + + D(\omega) = \{(\omega_j, i) : i<j,\, \omega_i > \omega_j \}. + + Informally, one can construct the Rothe diagram by starting with all + `n^2` possible cells, and then deleting the cells `(i, \omega(i))` as + well as all cells to the right and below. (These are sometimes called + "death rays".) + + .. SEEALSO:: + + :func:`~sage.combinat.diagram.RotheDiagram` + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: w = Permutations(3)([2,1,3]) + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O . . + . . . + . . . + sage: NorthwestDiagrams().from_permutation(w).pp() + O . . + . . . + . . . + + sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O . . . . . . . + O . O O . . . . + O . O . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + """ + return RotheDiagram(w) + + from_permutation = rothe_diagram + + def from_partition(self, mu): + r""" + Return the Ferrer's diagram of ``mu`` as a northwest diagram. + + EXAMPLES:: + + sage: mu = Partition([5,2,1]); mu.pp() + ***** + ** + * + sage: mu.parent() + Partitions + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D = NorthwestDiagrams().from_partition(mu) + sage: D.pp() + O O O O O + O O . . . + O . . . . + sage: D.parent() + Combinatorial northwest diagrams + + This will print in English notation even if the notation is set to + French for the partition:: + + sage: Partitions.options.convention="french" + sage: mu.pp() + * + ** + ***** + sage: D.pp() + O O O O O + O O . . . + O . . . . + + TESTS:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: mu = [5, 2, 1] + sage: D = NorthwestDiagrams().from_partition(mu) + Traceback (most recent call last): + ... + ValueError: mu must be a Partition + """ + if not isinstance(mu, Partition): + raise ValueError("mu must be a Partition") + return self.element_class(self, mu.cells(), check=False) + + def from_skew_partition(self, s): + r""" + Get the northwest diagram found by reflecting a skew shape across + a vertical plane. + + EXAMPLES:: + + sage: mu, nu = Partition([3,2,1]), Partition([2,1]) + sage: s = mu/nu; s.pp() + * + * + * + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D = NorthwestDiagrams().from_skew_partition(s) + sage: D.pp() + O . . + . O . + . . O + + sage: mu, nu = Partition([3,3,2]), Partition([2,2,2]) + sage: s = mu/nu; s.pp() + * + * + sage: NorthwestDiagrams().from_skew_partition(s).pp() + O . . + O . . + . . . + + TESTS:: + + sage: mu = Partition([3,2,1]) + sage: NorthwestDiagrams().from_skew_partition(mu) + Traceback (most recent call last): + ... + ValueError: mu must be a SkewPartition + """ + if not isinstance(s, SkewPartition): + raise ValueError("mu must be a SkewPartition") + + n_cols = s.outer()[0] + n_rows = len(s.outer()) + + cells = [(i, n_cols - 1 - j) for i, j in s.cells()] + + return self.element_class(self, cells, n_rows, n_cols, check=False) + + def from_parallelogram_polyomino(self, p): + r""" + Create the diagram corresponding to a + :class:`~sage.combinat.parallelogram_polyomino.ParallelogramPolyomino`. + + EXAMPLES:: + + sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], + ....: [1, 1, 0, 1, 0, 0, 0, 0]]) + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() + O O . + O O O + . O O + . O O + . O O + """ + from sage.matrix.constructor import Matrix + M = Matrix(p.get_array()) + return self.from_zero_one_matrix(M) + + Element = NorthwestDiagram + + +def RotheDiagram(w): + r""" + The Rothe diagram of a permutation ``w``. + + EXAMPLES:: + + sage: w = Permutations(9)([1, 7, 4, 5, 9, 3, 2, 8, 6]) + sage: from sage.combinat.diagram import RotheDiagram + sage: D = RotheDiagram(w); D.pp() + . . . . . . . . . + . O O O O O . . . + . O O . . . . . . + . O O . . . . . . + . O O . . O . O . + . O . . . . . . . + . . . . . . . . . + . . . . . O . . . + . . . . . . . . . + + The Rothe diagram is a northwest diagram:: + + sage: D.parent() + Combinatorial northwest diagrams + + Some other examples:: + + sage: RotheDiagram([2, 1, 4, 3]).pp() + O . . . + . . . . + . . O . + . . . . + + sage: RotheDiagram([4, 1, 3, 2]).pp() + O O O . + . . . . + . O . . + . . . . + + Currently, only elements of the set of + :class:`sage.combinat.permutations.Permutations` are supported. In + particular, elements of permutation groups are not supported:: + + sage: w = SymmetricGroup(9).an_element() + sage: RotheDiagram(w) + Traceback (most recent call last): + ... + ValueError: w must be a permutation + + TESTS:: + + sage: w = Permutations(5)([1,2,3,4,5]) + sage: from sage.combinat.diagram import RotheDiagram + sage: RotheDiagram(w).pp() + . . . . . + . . . . . + . . . . . + . . . . . + . . . . . + """ + P = Permutations() + if w not in P: + raise ValueError('w must be a permutation') + w = P(w) + + N = w.size() + winv = w.inverse() + from sage.misc.mrange import cartesian_product_iterator + cells = [c for c in cartesian_product_iterator((range(N), range(N))) + if c[0] + 1 < winv(c[1] + 1) and c[1] + 1 < w(c[0] + 1)] + + return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index a59d6b0fb9b..bd281fdbb77 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -133,6 +133,7 @@ def brauer_diagrams(k): for p in perfect_matchings_iterator(k-1): yield [(s[a],s[b]) for a,b in p] + [[k, -k]] + def temperley_lieb_diagrams(k): r""" Return a generator of all Temperley--Lieb diagrams of order ``k``. @@ -156,6 +157,7 @@ def temperley_lieb_diagrams(k): if is_planar(i): yield i + def planar_diagrams(k): r""" Return a generator of all planar diagrams of order ``k``. @@ -166,11 +168,13 @@ def planar_diagrams(k): EXAMPLES:: sage: import sage.combinat.diagram_algebras as da - sage: all_diagrams = da.partition_diagrams(2) - sage: [SetPartition(p) for p in all_diagrams if p not in da.planar_diagrams(2)] + sage: all_diagrams = [SetPartition(p) for p in da.partition_diagrams(2)] + sage: da2 = [SetPartition(p) for p in da.planar_diagrams(2)] + sage: [p for p in all_diagrams if p not in da2] [{{-2, 1}, {-1, 2}}] - sage: all_diagrams = da.partition_diagrams(5/2) - sage: [SetPartition(p) for p in all_diagrams if p not in da.planar_diagrams(5/2)] + sage: all_diagrams = [SetPartition(p) for p in da.partition_diagrams(5/2)] + sage: da5o2 = [SetPartition(p) for p in da.planar_diagrams(5/2)] + sage: [p for p in all_diagrams if p not in da5o2] [{{-3, -1, 3}, {-2, 1, 2}}, {{-3, -2, 1, 3}, {-1, 2}}, {{-3, -1, 1, 3}, {-2, 2}}, @@ -182,9 +186,75 @@ def planar_diagrams(k): {{-3, -1, 3}, {-2, 1}, {2}}, {{-3, -1, 3}, {-2, 2}, {1}}] """ - for i in partition_diagrams(k): - if is_planar(i): - yield i + if k in ZZ: + X = list(range(1,k+1)) + list(range(-k,0)) + yield from planar_partitions_rec(X) + elif k + ZZ(1)/ZZ(2) in ZZ: # Else k in 1/2 ZZ + k = ZZ(k + ZZ(1) / ZZ(2)) + X = list(range(1,k+1)) + list(range(-k+1,0)) + for Y in planar_partitions_rec(X): + Y = list(Y) + for part in Y: + if k in part: + part.append(-k) + break + yield Y + else: + raise ValueError("argument %s must be a half-integer" % k) + + +def planar_partitions_rec(X): + r""" + Iterate over all planar set partitions of ``X`` by using a + recursive algorithm. + + ALGORITHM: + + To construct the set partition `\rho = \{\rho_1, \ldots, \rho_k\}` of + `[n]`, we remove the part of the set partition containing the last + element of ``X``, which, we consider to be `\rho_k = \{i_1, \ldots, i_m\}` + without loss of generality. The remaining parts come from the planar set + partitions of `\{1, \ldots, i_1-1\}, \{i_1+1, \ldots, i_2-1\}, \ldots, + \{i_m+1, \ldots, n\}`. + + EXAMPLES:: + + sage: import sage.combinat.diagram_algebras as da + sage: list(da.planar_partitions_rec([1,2,3])) + [([1, 2], [3]), ([1], [2], [3]), ([2], [1, 3]), ([1], [2, 3]), ([1, 2, 3],)] + """ + if not X: + return + if len(X) <= 2: + # Direct implementation of small cases + yield (X,) + if len(X) > 1: + yield ([X[0]], [X[1]]) + return + from sage.misc.misc import powerset + from itertools import product + for S in powerset(range(len(X)-1)): + if not S: + for Y in planar_partitions_rec(X[:-1]): + yield Y + ([X[-1]],) + continue + last = [X[i] for i in S] + last.append(X[-1]) + pt = [] + if S[0] != 0: + pt += [X[:S[0]]] + pt = [X[S[i]+1:S[i+1]] for i in range(len(S)-1) if S[i]+1 != S[i+1]] + if S[-1] + 1 != len(X) - 1: + pt += [X[S[-1]+1:-1]] + parts = [planar_partitions_rec(X[S[i]+1:S[i+1]]) for i in range(len(S)-1) + if S[i] + 1 != S[i+1]] + if S[0] != 0: + parts.append(planar_partitions_rec(X[:S[0]])) + if S[-1] + 1 != len(X) - 1: + parts.append(planar_partitions_rec(X[S[-1]+1:-1])) + for Y in product(*parts): + yield sum(Y, ()) + (last,) + def ideal_diagrams(k): r""" @@ -208,6 +278,7 @@ def ideal_diagrams(k): if propagating_number(i) < k: yield i + class AbstractPartitionDiagram(AbstractSetPartition): r""" Abstract base class for partition diagrams. @@ -246,6 +317,7 @@ class AbstractPartitionDiagram(AbstractSetPartition): ... ValueError: {{1, 2}, {3, 4}} does not represent two rows of vertices of order 2 """ + def __init__(self, parent, d, check=True): r""" Initialize ``self``. @@ -519,6 +591,7 @@ def dual(self): """ return self.parent([[-i for i in part] for part in self]) + class IdealDiagram(AbstractPartitionDiagram): r""" The element class for a ideal diagram. @@ -606,20 +679,20 @@ class PlanarDiagram(AbstractPartitionDiagram): sage: PlanarDiagrams(2) Planar diagrams of order 2 sage: PlanarDiagrams(2).list() - [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, - {{-2, -1, 1}, {2}}, + [{{-2}, {-1}, {1, 2}}, + {{-2}, {-1}, {1}, {2}}, {{-2, 1}, {-1}, {2}}, - {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, {{-2, 2}, {-1}, {1}}, + {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, {{-2}, {-1, 1}, {2}}, {{-2}, {-1, 2}, {1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2}, {-1}, {1}, {2}}] + {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1, 1, 2}}] """ @staticmethod def __classcall_private__(cls, diag): @@ -767,6 +840,7 @@ def __classcall_private__(cls, diag): PD = PartitionDiagrams(order) return PD(diag) + class BrauerDiagram(AbstractPartitionDiagram): r""" A Brauer diagram. @@ -1055,6 +1129,7 @@ def is_elementary_symmetric(self): D2 = sorted(sorted(abs(y) for y in x) for x in D2) return D1 == D2 and pi == list(range(1,len(pi)+1)) + class AbstractPartitionDiagrams(Parent, UniqueRepresentation): r""" This is an abstract base class for partition diagrams. @@ -1183,27 +1258,27 @@ def __iter__(self): [{{-2, -1}, {1, 2}}, {{-2, 2}, {-1, 1}}] sage: list(da.PlanarDiagrams(3/2)) - [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, + [{{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1}, {1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}] + {{-2, -1, 1, 2}}] sage: list(da.PlanarDiagrams(2)) - [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, - {{-2, -1, 1}, {2}}, + [{{-2}, {-1}, {1, 2}}, + {{-2}, {-1}, {1}, {2}}, {{-2, 1}, {-1}, {2}}, - {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, {{-2, 2}, {-1}, {1}}, + {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, {{-2}, {-1, 1}, {2}}, {{-2}, {-1, 2}, {1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2}, {-1}, {1}, {2}}] + {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1, 1, 2}}] sage: list(da.IdealDiagrams(3/2)) [{{-2, -1, 1, 2}}, @@ -1278,6 +1353,7 @@ def _element_constructor_(self, d): """ return self.element_class(self, d) + class PartitionDiagrams(AbstractPartitionDiagrams): r""" This class represents all partition diagrams of integer or integer @@ -1337,6 +1413,7 @@ def cardinality(self): """ return bell_number(ZZ(2 * self.order)) + class BrauerDiagrams(AbstractPartitionDiagrams): r""" This class represents all Brauer diagrams of integer or integer @@ -1555,6 +1632,7 @@ def from_involution_permutation_triple(self, D1_D2_pi): SP = SP0 + Perm return self(SP) # could pass 'SetPartition' ? + class TemperleyLiebDiagrams(AbstractPartitionDiagrams): r""" All Temperley-Lieb diagrams of integer or integer `+1/2` order. @@ -1639,6 +1717,7 @@ def __contains__(self, obj): return False return obj in BrauerDiagrams(self.order) and obj.is_planar() + class PlanarDiagrams(AbstractPartitionDiagrams): r""" All planar diagrams of integer or integer `+1/2` order. @@ -1654,11 +1733,11 @@ class PlanarDiagrams(AbstractPartitionDiagrams): sage: pld = da.PlanarDiagrams(3/2); pld Planar diagrams of order 3/2 sage: pld.list() - [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, + [{{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1}, {1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}] + {{-2, -1, 1, 2}}] TESTS:: @@ -1716,6 +1795,7 @@ def __contains__(self, obj): return False return super().__contains__(obj) + class IdealDiagrams(AbstractPartitionDiagrams): r""" All "ideal" diagrams of integer or integer `+1/2` order. @@ -1763,6 +1843,7 @@ def __contains__(self, obj): return False return super().__contains__(obj) and obj.propagating_number() < self.order + class DiagramAlgebra(CombinatorialFreeModule): r""" Abstract class for diagram algebras and is not designed to be used @@ -1790,6 +1871,7 @@ class DiagramAlgebra(CombinatorialFreeModule): P{{-2, -1}, {1}, {2}}, P{{-2}, {-1}, {1}, {2}}] """ + def __init__(self, k, q, base_ring, prefix, diagrams, category=None): r""" Initialize ``self``. @@ -2038,6 +2120,7 @@ class Element(CombinatorialFreeModule.Element): partition algebra elements. Most element methods are already implemented elsewhere. """ + def diagram(self): r""" Return the underlying diagram of ``self`` if ``self`` is a basis @@ -2073,6 +2156,7 @@ def diagrams(self): """ return self.support() + class UnitDiagramMixin(): """ Mixin class for diagram algebras that have the unit indexed by @@ -2095,10 +2179,12 @@ def one_basis(self): """ return self._base_diagrams(identity_set_partition(self._k)) + class DiagramBasis(DiagramAlgebra): """ Abstract base class for diagram algebras in the diagram basis. """ + def product_on_basis(self, d1, d2): r""" Return the product `D_{d_1} D_{d_2}` by two basis diagrams. @@ -2119,6 +2205,7 @@ def product_on_basis(self, d1, d2): (composite_diagram, loops_removed) = d1.compose(d2, check=False) return self.term(composite_diagram, self._q**loops_removed) + class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): r""" A partition algebra. @@ -3054,6 +3141,7 @@ def dual(self): return P._from_dict({D.dual(): c for D, c in self._monomial_coefficients.items()}, remove_zeros=False) + class OrbitBasis(DiagramAlgebra): r""" The orbit basis of the partition algebra. @@ -3477,11 +3565,13 @@ def to_diagram_basis(self): return self.parent()._alg(self) #return self._alg.coerce_map_from(self) + class SubPartitionAlgebra(DiagramBasis): """ A subalgebra of the partition algebra in the diagram basis indexed by a subset of the diagrams. """ + def __init__(self, k, q, base_ring, prefix, diagrams, category=None): """ Initialize ``self`` by adding a coercion to the ambient space. @@ -3930,6 +4020,7 @@ def _unicode_art_term(self, diagram): """ return TL_diagram_ascii_art(diagram, use_unicode=True) + class PlanarAlgebra(SubPartitionAlgebra, UnitDiagramMixin): r""" A planar algebra. @@ -3967,20 +4058,20 @@ class PlanarAlgebra(SubPartitionAlgebra, UnitDiagramMixin): sage: Pl.basis().keys()([[-1, 1], [2, -2]]) {{-2, 2}, {-1, 1}} sage: Pl.basis().list() - [Pl{{-2, -1, 1, 2}}, - Pl{{-2, 1, 2}, {-1}}, - Pl{{-2}, {-1, 1, 2}}, - Pl{{-2, -1}, {1, 2}}, - Pl{{-2}, {-1}, {1, 2}}, - Pl{{-2, -1, 1}, {2}}, + [Pl{{-2}, {-1}, {1, 2}}, + Pl{{-2}, {-1}, {1}, {2}}, Pl{{-2, 1}, {-1}, {2}}, - Pl{{-2, 2}, {-1, 1}}, - Pl{{-2, -1, 2}, {1}}, Pl{{-2, 2}, {-1}, {1}}, + Pl{{-2, 1, 2}, {-1}}, + Pl{{-2, 2}, {-1, 1}}, Pl{{-2}, {-1, 1}, {2}}, Pl{{-2}, {-1, 2}, {1}}, + Pl{{-2}, {-1, 1, 2}}, + Pl{{-2, -1}, {1, 2}}, Pl{{-2, -1}, {1}, {2}}, - Pl{{-2}, {-1}, {1}, {2}}] + Pl{{-2, -1, 1}, {2}}, + Pl{{-2, -1, 2}, {1}}, + Pl{{-2, -1, 1, 2}}] sage: E = Pl([[1,2],[-1,-2]]) sage: E^2 == x*E True @@ -4128,6 +4219,7 @@ class Element(SubPartitionAlgebra.Element): We need to take care of exponents since we are not unital. """ + def __pow__(self, n): """ Return ``self`` to the `n`-th power. @@ -4152,6 +4244,7 @@ def __pow__(self, n): raise ValueError("can only take positive integer powers") return generic_power(self, n) + def TL_diagram_ascii_art(diagram, use_unicode=False, blobs=[]): r""" Return ascii art for a Temperley-Lieb diagram ``diagram``. @@ -4348,6 +4441,7 @@ def signed(val, pos): ret.append(ret[0]) return char_art(ret, baseline=len(ret)//2) + def diagram_latex(diagram, fill=False, edge_options=None, edge_additions=None): r""" Return latex code for the diagram ``diagram`` using tikz. @@ -4445,6 +4539,7 @@ def sgn(x): # --> CHANGED 'identity' to 'identity_set_partition' for enhanced clarity. ######################################################################### + def is_planar(sp): r""" Return ``True`` if the diagram corresponding to the set partition ``sp`` @@ -4546,6 +4641,7 @@ def to_graph(sp): g.add_edge(part_list[i-1], part_list[i]) return g + def pair_to_graph(sp1, sp2): r""" Return a graph consisting of the disjoint union of the graphs of set @@ -4695,6 +4791,7 @@ def to_set_partition(l, k=None): return sp + def to_Brauer_partition(l, k=None): r""" Same as :func:`to_set_partition` but assumes omitted elements are @@ -4735,6 +4832,7 @@ def to_Brauer_partition(l, k=None): paired.append([i[0], -i[0]]) return to_set_partition(paired) + def identity_set_partition(k): r""" Return the identity set partition `\{\{1, -1\}, \ldots, \{k, -k\}\}`. diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 85fe966f70e..beb6b4404f6 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -895,6 +895,61 @@ def _latex_(self) -> str: res += "\\end{tikzpicture}$}}" return res + def _repr_svg_(self) -> str: + """ + Return the svg picture of ``self``. + + This can be displayed by Jupyter. + + EXAMPLES:: + + sage: PP = DyckWords(6).random_element() + sage: PP._repr_svg_() + '<?xml...</g></svg>' + """ + N = self.length() + width = 0.1 if N < 20 else N / 200 + resu = '<?xml version=\"1.0\" standalone=\"no\"?>' + resu += '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ' + resu += '\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">' + resu += '<svg xmlns=\"http://www.w3.org/2000/svg\" ' + resu += 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"500\" viewBox=' + + resu1 = '<g style=\"stroke-width:{};stroke-linejoin:bevel; '.format(width) + resu1 += 'stroke-linecap:butt; stroke:black; fill:snow\">' + + resu3 = '<g style=\"stroke-width:{};stroke-linejoin:bevel;stroke-dasharray:0.25; '.format(width / 2) + resu3 += 'stroke-linecap:butt; stroke:gray; fill:none\">' + + horizontal = "<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\"/>" + hori_lines = [] + path = ['<polyline points=\"0,0'] + x, y = 0, 0 + max_y = 0 + last_seen_level = [0] + for e in self: + x += 1 + if e == open_symbol: + y += 1 + last_seen_level.append(x - 1) + max_y = max(max_y, y) + else: + y -= 1 + old_x = last_seen_level.pop() + hori_lines.append(horizontal.format(old_x, -y, x, -y)) + path.append(f"{x},{-y}") + path.append('\"/>') + path.append('</g>') + resu1 += " ".join(path) + hori_lines.append('</g></svg>') + resu3 += "".join(hori_lines) + + margin = 2 * width + resu += '\"{} {} {} {} \">'.format(-margin, -max_y - margin, + N + 2 * margin, max_y + 2 * margin) + + return resu + resu1 + resu3 + def plot(self, **kwds): """ Plot a Dyck word as a continuous path. @@ -1838,6 +1893,7 @@ class DyckWord_complete(DyckWord): For further information on Dyck words, see :class:`DyckWords_class<sage.combinat.dyck_word.DyckWord>`. """ + def semilength(self) -> int: r""" Return the semilength of ``self``. @@ -3487,6 +3543,7 @@ class DyckWords_all(DyckWords): """ All Dyck words. """ + def __init__(self): """ Initialize ``self``. @@ -3561,6 +3618,7 @@ class DyckWordBacktracker(GenericBacktracker): - Dan Drake (2008-05-30) """ + def __init__(self, k1, k2): r""" TESTS:: @@ -3624,6 +3682,7 @@ class DyckWords_size(DyckWords): """ Dyck words with `k_1` openers and `k_2` closers. """ + def __init__(self, k1, k2): r""" TESTS: @@ -3900,6 +3959,7 @@ class CompleteDyckWords_all(CompleteDyckWords, DyckWords_all): """ All complete Dyck words. """ + def _repr_(self) -> str: r""" TESTS:: @@ -3943,6 +4003,7 @@ class height_poset(UniqueRepresentation, Parent): This is implemented by comparison of area sequences. """ + def __init__(self): r""" TESTS:: @@ -4009,6 +4070,7 @@ class CompleteDyckWords_size(CompleteDyckWords, DyckWords_size): """ All complete Dyck words of a given size. """ + def __init__(self, k): """ Initialize ``self``. diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index 6a1c2c607bc..31f24495c23 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -264,6 +264,7 @@ class Face(SageObject): sage: f.color() RGB color (0.5, 0.5, 0.5) """ + def __init__(self, v, t, color=None): r""" Face constructor. See class doc for more information. @@ -578,6 +579,7 @@ class Patch(SageObject): sage: Patch([Face((0,0,0),t) for t in [1,2,3]], face_contour=face_contour) Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] """ + def __init__(self, faces, face_contour=None): r""" Constructor of a patch (set of faces). @@ -1393,6 +1395,7 @@ class E1Star(SageObject): sage: E(P) Patch: [[(0, 0, 0, 0), 3]*, [(0, 0, 0, 0), 4]*, [(0, 0, 1, -1), 3]*, [(0, 1, 0, -1), 2]*, [(1, 0, 0, -1), 1]*] """ + def __init__(self, sigma, method='suffix'): r""" E1Star constructor. See class doc for more information. diff --git a/src/sage/combinat/expnums.pyx b/src/sage/combinat/expnums.pyx index 2163e6b9a96..e3cbbc6757e 100644 --- a/src/sage/combinat/expnums.pyx +++ b/src/sage/combinat/expnums.pyx @@ -87,17 +87,17 @@ def expnums(int n, int aa): mpz_init_set_si(bell[1], aa) cdef int i cdef int k - for i from 1 <= i < n: + for i in range(1, n): mpz_init(bell[i+1]) mpz_mul(bell[i+1], a, bell[1]) - for k from 0 <= k < i: + for k in range(i): mpz_add(bell[i-k], bell[i-k], bell[i-k+1]) z = Integer.__new__(Integer) mpz_set(z.value, bell[1]) r.append(z) - for i from 1 <= i <= n: + for i in range(1, n + 1): mpz_clear(bell[i]) sig_free(bell) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 85cc89c9d09..6be4e0e2683 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1424,7 +1424,6 @@ def __init__(self, label, word_out=None, self.color = color - def __lt__(self, other): """ Return ``True`` if label of ``self`` is less than label of ``other``. @@ -1484,7 +1483,6 @@ def final_word_out(self): """ return self._final_word_out_ - @final_word_out.setter def final_word_out(self, final_word_out): """ @@ -1542,7 +1540,6 @@ def final_word_out(self, final_word_out): else: self._final_word_out_ = [] - @property def is_final(self): """ @@ -1569,7 +1566,6 @@ def is_final(self): """ return (self.final_word_out is not None) - @is_final.setter def is_final(self, is_final): """ @@ -1872,7 +1868,6 @@ def __hash__(self): """ return hash(self.label()) - def _repr_(self): """ Return the string "label". @@ -2010,8 +2005,6 @@ def __bool__(self): """ return True # A state cannot be zero (see __init__) - - def _epsilon_successors_(self, fsm=None): """ Return the dictionary with states reachable from ``self`` @@ -2234,7 +2227,6 @@ class FSMTransition(SageObject): word_out = None """Output word of the transition. Read-only.""" - def __init__(self, from_state, to_state, word_in=None, word_out=None, hook=None): @@ -2276,7 +2268,6 @@ def __init__(self, from_state, to_state, else: raise TypeError('Wrong argument for hook.') - def __lt__(self, other): """ Return True if ``self`` is less than ``other`` with respect to the @@ -2490,8 +2481,6 @@ def __bool__(self): return True # A transition cannot be zero (see __init__) - - # **************************************************************************** @@ -3076,7 +3065,6 @@ class FiniteStateMachine(SageObject): # init # ************************************************************************ - def __init__(self, data=None, initial_states=None, final_states=None, @@ -3135,7 +3123,6 @@ def __init__(self, self._copy_from_other_(data) return - if initial_states is not None: if not isinstance(initial_states, Iterable): raise TypeError('Initial states must be iterable ' @@ -3220,7 +3207,6 @@ def __init__(self, if with_final_word_out is not None: self.construct_final_word_out(with_final_word_out) - # ************************************************************************ # copy and hash # ************************************************************************ @@ -3552,7 +3538,6 @@ def induced_sub_finite_state_machine(self, states): return new - def __hash__(self): """ Since finite state machines are mutable, they should not be @@ -3578,12 +3563,10 @@ def __hash__(self): raise TypeError("Finite state machines are mutable, " "and thus not hashable.") - # ************************************************************************ # operators # ************************************************************************ - def __or__(self, other): """ Return the disjoint union of this and another finite state machine. @@ -3616,10 +3599,8 @@ def __or__(self, other): else: raise TypeError("Can only add finite state machine") - __add__ = __or__ - def __iadd__(self, other): """ TESTS:: @@ -3658,7 +3639,6 @@ def __imul__(self, other): """ raise NotImplementedError - def __call__(self, *args, **kwargs): """ Call either method :meth:`.composition` or :meth:`.process` @@ -3930,8 +3910,6 @@ def __bool__(self): """ return bool(self._states_) - - def __eq__(self, other): """ Return ``True`` if the two finite state machines are equal, @@ -4074,7 +4052,6 @@ def __contains__(self, item): return self.has_transition(item) return False - def is_Markov_chain(self, is_zero=None): """ Checks whether ``self`` is a Markov chain where the transition @@ -4192,12 +4169,10 @@ def default_is_zero(expression): return all(is_zero_function(sum(t.word_in[0] for t in state.transitions) - 1) for state in self.iter_states()) - # ************************************************************************ # representations / LaTeX # ************************************************************************ - def _repr_(self): """ Represents the finite state machine as "Finite state machine @@ -4241,7 +4216,6 @@ def _repr_(self): default_format_letter = latex format_letter = default_format_letter - def format_letter_negative(self, letter): r""" Format negative numbers as overlined numbers, everything @@ -4273,7 +4247,6 @@ def format_letter_negative(self, letter): else: return latex(letter) - def format_transition_label_reversed(self, word): r""" Format words in transition labels in reversed order. @@ -4318,7 +4291,6 @@ def format_transition_label_reversed(self, word): """ return self.default_format_transition_label(reversed(word)) - def default_format_transition_label(self, word): r""" Default formatting of words in transition labels for LaTeX output. @@ -4405,10 +4377,8 @@ def default_format_transition_label(self, word): else: return EmptyWordLaTeX - format_transition_label = default_format_transition_label - def latex_options(self, coordinates=None, format_state_label=None, @@ -4994,7 +4964,6 @@ def _latex_transition_label_(self, transition, """ return ' ' - def set_coordinates(self, coordinates, default=True): """ Set coordinates of the states for the LaTeX representation by @@ -5188,7 +5157,6 @@ def default_function(transition): return matrix( len(relabeledFSM.states()), dictionary) - def determine_input_alphabet(self, reset=True): """ Determine the input alphabet according to the transitions @@ -5853,7 +5821,6 @@ def is_complete(self): return True - def is_connected(self): """ TESTS:: @@ -5865,7 +5832,6 @@ def is_connected(self): """ raise NotImplementedError - # ************************************************************************ # let the finite state machine work # ************************************************************************ @@ -6254,7 +6220,6 @@ def _process_convert_output_(self, output_data, **kwargs): accept_input, current_state, output = output_data return (accept_input, current_state, output) - def iter_process(self, input_tape=None, initial_state=None, process_iterator_class=None, iterator_type=None, @@ -6415,7 +6380,6 @@ def iter_process(self, input_tape=None, initial_state=None, else: raise ValueError('Iterator type %s unknown.' % (iterator_type,)) - def _iter_process_simple_(self, iterator): r""" Converts a :class:`process iterator <FSMProcessIterator>` to a simpler @@ -6550,7 +6514,6 @@ def add_state(self, state): pass return s - def add_states(self, states): """ Adds several states. See add_state for more information. @@ -6573,7 +6536,6 @@ def add_states(self, states): for state in states: self.add_state(state) - def add_transition(self, *args, **kwargs): """ Adds a transition to the finite state machine and returns the @@ -6663,7 +6625,6 @@ def add_transition(self, *args, **kwargs): return self._add_fsm_transition_(FSMTransition(**data)) - def _add_fsm_transition_(self, t): """ Adds a transition. @@ -6694,7 +6655,6 @@ def _add_fsm_transition_(self, t): from_state.transitions.append(t) return t - def add_from_transition_function(self, function, initial_states=None, explore_existing_states=True): """ @@ -6848,7 +6808,6 @@ def add_from_transition_function(self, function, initial_states=None, self.add_transition(s, st_label, word_in=letter, word_out=word) - def add_transitions_from_function(self, function, labels_as_input=True): """ Adds one or more transitions if ``function(state, state)`` @@ -6949,7 +6908,6 @@ def add_transitions_from_function(self, function, labels_as_input=True): label_out = None self.add_transition(s_from, s_to, label_in, label_out) - def delete_transition(self, t): """ Deletes a transition by removing it from the list of transitions of @@ -6973,7 +6931,6 @@ def delete_transition(self, t): transition = self.transition(t) transition.from_state.transitions.remove(transition) - def delete_state(self, s): """ Deletes a state and all transitions coming or going to this state. @@ -7015,7 +6972,6 @@ def delete_state(self, s): except AttributeError: pass - def remove_epsilon_transitions(self): """ TESTS:: @@ -7068,7 +7024,6 @@ def epsilon_successors(self, state): """ return self.state(state)._epsilon_successors_(self) - def accessible_components(self): """ Return a new finite state machine with the accessible states @@ -7179,7 +7134,6 @@ def coaccessible_components(self): # creating new finite state machines # ************************************************************************* - def disjoint_union(self, other): """ Return the disjoint union of this and another finite state @@ -7339,7 +7293,6 @@ def disjoint_union(self, other): return result - def concatenation(self, other): r""" Concatenate this finite state machine with another finite @@ -7538,10 +7491,8 @@ def concatenation(self, other): return result - __mul__ = concatenation - def kleene_star(self): r""" Compute the Kleene closure of this finite state machine. @@ -7635,7 +7586,6 @@ def kleene_star(self): return result - def intersection(self, other): """ TESTS:: @@ -7647,7 +7597,6 @@ def intersection(self, other): """ raise NotImplementedError - def product_FiniteStateMachine(self, other, function, new_input_alphabet=None, only_accessible_components=True, @@ -7917,7 +7866,6 @@ def default_final_function(*args): else: return result - def composition(self, other, algorithm=None, only_accessible_components=True): """ @@ -8280,7 +8228,6 @@ def function(transition1, transition2): return result - def _composition_explorative_(self, other): """ See :meth:`.composition` for details. @@ -8332,7 +8279,6 @@ def composition_transition(states, input): write_final_word_out=False, always_include_output=True)] - first = other if any(len(t.word_in) > 1 for t in first.iter_transitions()): @@ -8687,7 +8633,6 @@ def final_components(self): for component in condensation.vertices(sort=True) if condensation.out_degree(component) == 0] - def completion(self, sink=None): """ Return a completion of this finite state machine. @@ -8839,7 +8784,6 @@ def completion(self, sink=None): return result - # ************************************************************************* # simplifications # ************************************************************************* @@ -9230,7 +9174,6 @@ def quotient(self, classes): "final output words." % (c,) return new - def merged_transitions(self): """ Merges transitions which have the same ``from_state``, @@ -9302,7 +9245,6 @@ def key(transition): else: return self - def markov_chain_simplification(self): """ Consider ``self`` as Markov chain with probabilities as input labels @@ -9343,7 +9285,6 @@ def markov_chain_simplification(self): current = new number_states = new_number_states - def with_final_word_out(self, letters, allow_non_final=True): """ Constructs a new finite state machine with final output words @@ -9750,10 +9691,8 @@ def graph(self, edge_labels='words_in_out'): G.add_vertices(isolated_vertices) return G - digraph = graph - def plot(self): """ Plots a graph of the finite state machine with labeled @@ -9774,7 +9713,6 @@ def plot(self): """ return self.graph(edge_labels='words_in_out').plot() - def predecessors(self, state, valid_input=None): """ Lists all predecessors of a state. @@ -10442,7 +10380,6 @@ def substitute_one(g): 'variance': v_2*variable + SR(1).Order(), 'covariance': c*variable + SR(1).Order()} - def moments_waiting_time(self, test=bool, is_zero=None, expectation_only=False): r""" @@ -11590,7 +11527,6 @@ def is_equivalent(self, other): except KeyError: return False - def process(self, *args, **kwargs): """ Return whether the automaton accepts the input and the state @@ -11834,7 +11770,6 @@ class is created and is used during the processing. return any(result) return result - def _process_convert_output_(self, output_data, **kwargs): """ Helper function which converts the output of @@ -11881,7 +11816,6 @@ def _process_convert_output_(self, output_data, **kwargs): else: return accept_input - def shannon_parry_markov_chain(self): """ Compute a time homogeneous Markov chain such that all words of a @@ -12015,7 +11949,6 @@ def shannon_parry_markov_chain(self): P.state(s.label()).initial_probability = w[states[s]] * u[states[s]] return P - def with_output(self, word_out_function=None): r""" Construct a transducer out of this automaton. @@ -12111,7 +12044,6 @@ def with_output(self, word_out_function=None): t.word_out = word_out_function(t) return new - def language(self, max_length=None, **kwargs): r""" Return all words accepted by this automaton. @@ -13098,6 +13030,7 @@ class _FSMTapeCache_(SageObject): sage: TC2.tape_cache_manager [multi-tape at (0, 0)] """ + def __init__(self, tape_cache_manager, tape, tape_ended, position, is_multitape): """ @@ -13179,7 +13112,6 @@ def _repr_(self): else: return 'tape at %s' % (self.position[0][0],) - def __deepcopy__(self, memo): """ See :meth:`.deepcopy` for details. @@ -13354,7 +13286,6 @@ def finished(self, track_number=None): self.read(track_number) # to make sure tape_ended is correct return self.tape_ended[track_number] and not self.cache[track_number] - def preview_word(self, track_number=None, length=1, return_word=False): """ Reads a word from the input tape. @@ -13505,7 +13436,6 @@ def compare_to_tape(self, track_number, word): return False return True - def forward(self, transition): """ Forwards the tape according to the given transition. @@ -13584,7 +13514,6 @@ def length(word): for p, t in self.position] self.position = tuple(sorted(position)) - def transition_possible(self, transition): """ Tests whether the input word of ``transition`` can be read @@ -13629,7 +13558,6 @@ def transition_possible(self, transition): len(self.cache))) return self._transition_possible_test_(word_in) - def _transition_possible_epsilon_(self, word_in): """ This helper function tests whether ``word_in`` equals ``epsilon``, @@ -13662,7 +13590,6 @@ def _transition_possible_epsilon_(self, word_in): # functions. return all(letter is None for t in word_in for letter in t) - def _transition_possible_test_(self, word_in): """ This helper function tests whether ``word_in`` can be read @@ -13726,6 +13653,7 @@ class _FSMTapeCacheDetectEpsilon_(_FSMTapeCache_): This is a class is similar to :class:`_FSMTapeCache_` but accepts only epsilon transitions. """ + def __init__(self, *args, **kwargs): """ See :class:`_FSMTapeCache_` for more details. @@ -13740,7 +13668,6 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._visited_states_ = set() - def __deepcopy__(self, memo): """ See :meth:`_FSMTapeCache_.deepcopy` for details. @@ -13798,6 +13725,7 @@ class _FSMTapeCacheDetectAll_(_FSMTapeCache_): This is a class is similar to :class:`_FSMTapeCache_` but accepts each transition. """ + def compare_to_tape(self, track_number, word): """ Return whether it is possible to read a word of the same length @@ -14178,6 +14106,7 @@ class Current(dict): {} process (0 branches) """ + def __repr__(self): """ Return a nice representation of ``self``. @@ -14214,7 +14143,6 @@ def __repr__(self): result += "\n+-- %s, %s" % (tape_cache, outputs) return result - FinishedBranch = namedtuple('Branch', 'accept, state, output') r""" A :func:`named tuple <collections.namedtuple>` representing the @@ -14222,7 +14150,6 @@ def __repr__(self): it is fully processed. """ - def __init__(self, fsm, input_tape=None, initial_state=None, initial_states=[], @@ -14310,14 +14237,12 @@ def __init__(self, fsm, self._finished_ = [] # contains (accept, state, output) - _branch_ = namedtuple('Branch', 'tape_cache, outputs') r""" A :func:`named tuple <collections.namedtuple>` representing the attributes of a branch at a particular state during processing. """ - def _push_branch_(self, state, tape_cache, outputs): """ This helper function pushes a ``state`` together with @@ -14410,7 +14335,6 @@ def _push_branch_(self, state, tape_cache, outputs): else: states[state] = FSMProcessIterator._branch_(tape_cache, outputs) - def _push_branches_(self, state, tape_cache, outputs): """ This function pushes a branch (consisting of a ``state``, an @@ -15087,6 +15011,7 @@ class _FSMProcessIteratorEpsilon_(FSMProcessIterator): sage: it.visited_states {0: ['', 'bde', 'cde'], 1: ['a'], 2: ['b', 'c'], 3: ['bd', 'cd']} """ + def __init__(self, *args, **kwargs): """ See :class:`_FSMProcessIteratorEpsilon_` and @@ -15104,7 +15029,6 @@ def __init__(self, *args, **kwargs): kwargs['check_epsilon_transitions'] = False return super().__init__(*args, **kwargs) - def _push_branch_(self, state, tape_cache, outputs): """ This helper function does the actual adding of a ``state`` to @@ -15208,6 +15132,7 @@ class _FSMProcessIteratorAll_(FSMProcessIterator): Branch(accept=True, state='B', output='zzm'), Branch(accept=True, state='B', output='zzo')] """ + def __init__(self, *args, **kwargs): """ See :class:`_FSMProcessIteratorAll_` and diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 4b512a4a964..4a5a875796b 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -192,7 +192,6 @@ def AnyWord(self, input_alphabet): initial_states=[z], final_states=[z]) - def EmptyWord(self, input_alphabet=None): r""" Return an automaton recognizing the empty word. @@ -224,7 +223,6 @@ def EmptyWord(self, input_alphabet=None): final_states=[z], input_alphabet=input_alphabet) - def Word(self, word, input_alphabet=None): r""" Return an automaton recognizing the given word. @@ -284,7 +282,6 @@ def Word(self, word, input_alphabet=None): final_states=[ZZ(length)], input_alphabet=input_alphabet) - def ContainsWord(self, word, input_alphabet): r""" Return an automaton recognizing the words containing @@ -530,7 +527,6 @@ def transition_function(read, input): s.is_final = True return T - def Wait(self, input_alphabet, threshold=1): r""" Writes ``False`` until reading the ``threshold``-th occurrence @@ -574,7 +570,6 @@ def transition(state, input): return T - def map(self, f, input_alphabet): r""" Return a transducer which realizes a function @@ -620,7 +615,6 @@ def map(self, f, input_alphabet): initial_states=[0], final_states=[0]) - def operator(self, operator, input_alphabet, number_of_operands=2): r""" Returns a transducer which realizes an operation @@ -703,7 +697,6 @@ def transition_function(state, operands): initial_states=[0], final_states=[0]) - def all(self, input_alphabet, number_of_operands=2): r""" Returns a transducer which realizes logical ``and`` over the given @@ -755,7 +748,6 @@ def all(self, input_alphabet, number_of_operands=2): return self.operator(lambda *args: all(args), input_alphabet, number_of_operands) - def any(self, input_alphabet, number_of_operands=2): r""" Returns a transducer which realizes logical ``or`` over the given @@ -807,7 +799,6 @@ def any(self, input_alphabet, number_of_operands=2): return self.operator(lambda *args: any(args), input_alphabet, number_of_operands) - def add(self, input_alphabet, number_of_operands=2): r""" Returns a transducer which realizes addition on pairs over the @@ -862,7 +853,6 @@ def add(self, input_alphabet, number_of_operands=2): input_alphabet, number_of_operands=number_of_operands) - def sub(self, input_alphabet): r""" Returns a transducer which realizes subtraction on pairs over @@ -902,7 +892,6 @@ def sub(self, input_alphabet): """ return self.operator(operator.sub, input_alphabet) - def weight(self, input_alphabet, zero=0): r""" Returns a transducer which realizes the Hamming weight of the input @@ -969,7 +958,6 @@ def weight(state, input): initial_states=[0], final_states=[0]) - def abs(self, input_alphabet): r""" Returns a transducer which realizes the letter-wise @@ -1004,7 +992,6 @@ def abs(self, input_alphabet): """ return self.map(abs, input_alphabet) - def GrayCode(self): """ Returns a transducer converting the standard binary @@ -1057,10 +1044,8 @@ def GrayCode(self): final_states=[1], with_final_word_out=[0]) - RecursionRule = namedtuple('RecursionRule', ['K', 'r', 'k', 's', 't']) - def _parse_recursion_equation_(self, equation, base, function, var, word_function=None, output_rings=[ZZ, QQ]): """ @@ -1371,7 +1356,6 @@ def to_list(output): rule = self.RecursionRule(K=K,r=r, k=k, s=s, t=to_list(t)) return rule - def Recursion(self, recursions, base, function=None, var=None, input_alphabet=None, word_function=None, is_zero=None, output_rings=[ZZ, QQ]): diff --git a/src/sage/combinat/fqsym.py b/src/sage/combinat/fqsym.py index 28244d5b684..14d39076494 100644 --- a/src/sage/combinat/fqsym.py +++ b/src/sage/combinat/fqsym.py @@ -41,6 +41,7 @@ class FQSymBasis_abstract(CombinatorialFreeModule, BindableClass): - ``_basis_name`` -- the name of the basis and must match one of the names that the basis can be constructed from FQSym """ + def __init__(self, alg): r""" Initialize ``self``. @@ -1232,6 +1233,7 @@ class FQSymBases(Category_realization_of_parent): r""" The category of graded bases of `FQSym` indexed by permutations. """ + def __init__(self, base): r""" Initialize the bases of an `FQSym` diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index d6042d6facc..3432e18b27d 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1600,6 +1600,7 @@ class CartesianProductWithFlattening(): """ A class for Cartesian product constructor, with partial flattening """ + def __init__(self, flatten): """ INPUT: diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index b8f26d8ba8b..bb46692a8b7 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -214,7 +214,9 @@ def heap(self, **kargs): - ``display_labeling`` -- boolean (default: False). Setting the value to True will display the label `s_i` for each element `i` of the poset - OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` + OUTPUT: + + A labeled poset where the underlying set is `\{0,1,...,k-1\}` and where each element `i` carries `s_i` as its label. The partial order `\prec` on the poset is defined by declaring `i\prec j` if `i<j` and `m(s_i,s_j)\neq 2`. diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 81eb5131349..b24d6e19ce1 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -60,6 +60,7 @@ ({L: L, R: R}, {L: U, D: R}, {R: U, D: L}, {U: U, D: D}, {U: R, L: D}, {R: D, U: L}) # odd ) + def _make_color_list(n, colors=None, color_map=None, randomize=False): r""" TESTS:: @@ -735,7 +736,6 @@ def to_alternating_sign_matrix(self): """ return self._six_vertex_model.to_alternating_sign_matrix() - @options(link=True, loop=True, loop_fill=False) def plot(self, **options): r""" @@ -1220,6 +1220,7 @@ def six_vertex_model(self): """ return self._six_vertex_model + class FullyPackedLoops(Parent, UniqueRepresentation): r""" Class of all fully packed loops on an `n \times n` grid. @@ -1257,6 +1258,7 @@ class FullyPackedLoops(Parent, UniqueRepresentation): ....: == FullyPackedLoops(n).cardinality() for n in range(1, 7)) True """ + def __init__(self, n): r""" Initialize ``self``. diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index a210a4cc370..eda712ded06 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -1200,6 +1200,7 @@ class GelfandTsetlinPatternsTopRow(GelfandTsetlinPatterns): """ Gelfand-Tsetlin patterns with a fixed top row. """ + def __init__(self, top_row, strict): """ Initialize ``self``. diff --git a/src/sage/combinat/graph_path.py b/src/sage/combinat/graph_path.py index 7d0f1f591f1..2fb255579dd 100644 --- a/src/sage/combinat/graph_path.py +++ b/src/sage/combinat/graph_path.py @@ -268,6 +268,7 @@ class GraphPaths_all(Parent, GraphPaths_common): sage: p.cardinality() 37 """ + def __init__(self, g): """ TESTS:: @@ -412,6 +413,7 @@ class GraphPaths_st(Parent, GraphPaths_common): sage: GraphPaths(G,4,5).cardinality() 2 """ + def __init__(self, g, source, target): """ TESTS:: diff --git a/src/sage/combinat/gray_codes.py b/src/sage/combinat/gray_codes.py index 74fb00c0902..e2508e0e01c 100644 --- a/src/sage/combinat/gray_codes.py +++ b/src/sage/combinat/gray_codes.py @@ -102,6 +102,7 @@ def product(m): j = f[0] + def combinations(n,t): r""" Iterator through the switches of the revolving door algorithm. @@ -208,6 +209,7 @@ def combinations(n,t): else: return _revolving_door_even(n,t) + def _revolving_door_odd(n,t): r""" Revolving door switch for odd `t`. @@ -253,6 +255,7 @@ def _revolving_door_odd(n,t): else: # j == t break + def _revolving_door_even(n,t): r""" Revolving door algorithm for even `t`. diff --git a/src/sage/combinat/growth.py b/src/sage/combinat/growth.py index 62032b03019..763f8f7e482 100644 --- a/src/sage/combinat/growth.py +++ b/src/sage/combinat/growth.py @@ -644,6 +644,7 @@ class GrowthDiagram(SageObject): 0 0 0 1 1 0 """ + def __init__(self, rule, filling=None, shape=None, labels=None): r""" Initialize ``self``. @@ -1610,6 +1611,7 @@ def _shrink(self): # ABC for rules of growth diagrams ###################################################################### + class Rule(UniqueRepresentation): r""" Generic base class for a rule for a growth diagram. @@ -2293,6 +2295,7 @@ def backward_rule(self, y, g, z, h, x): return (0, t, 3, 0) raise ValueError("this should not happen") + class RuleLLMS(Rule): r""" A rule modelling the Schensted correspondence for affine @@ -2611,6 +2614,7 @@ def forward_rule(self, y, e, t, f, x, content): return g, z, h + class RuleBinaryWord(Rule): r""" A rule modelling a Schensted-like correspondence for binary words. @@ -2872,6 +2876,7 @@ def backward_rule(self, y, z, x): else: return (x[:-1], 0) + class RuleSylvester(Rule): r""" A rule modelling a Schensted-like correspondence for binary trees. @@ -3156,7 +3161,6 @@ def add_label(L, S, T, m): L = add_label(L, S, T, i) return L - @staticmethod def _delete_right_most_node(b): r""" @@ -3353,6 +3357,7 @@ def backward_rule(self, y, z, x): t = RuleSylvester._delete_right_most_node(y) return (t, 0) + class RuleYoungFibonacci(Rule): r""" A rule modelling a Schensted-like correspondence for @@ -3596,6 +3601,7 @@ def backward_rule(self, y, z, x): elif z[0] == 2: return (z[1:], 0) + class RulePartitions(Rule): r""" A rule for growth diagrams on Young's lattice on integer @@ -3680,6 +3686,7 @@ def Q_symbol(self, Q_chain): """ return SkewTableau(chain=Q_chain) + class RuleRSK(RulePartitions): r""" A rule modelling Robinson-Schensted-Knuth insertion. @@ -3743,6 +3750,7 @@ class RuleRSK(RulePartitions): sage: all([G.P_symbol(), G.Q_symbol()] == RSK(pi) for pi, G in l) True """ + def forward_rule(self, y, t, x, content): r""" Return the output shape given three shapes and the content. @@ -3894,6 +3902,7 @@ class RuleBurge(RulePartitions): sequences of cells with weakly decreasing row indices and weakly increasing column indices. """ + def forward_rule(self, y, t, x, content): r""" Return the output shape given three shapes and the content. @@ -3987,6 +3996,7 @@ def backward_rule(self, y, z, x): t.reverse() return (_make_partition(t), carry) + class RuleDomino(Rule): r""" A rule modelling domino insertion. @@ -4331,6 +4341,7 @@ def union(la, mu): ## Set the rules available from GrowthDiagram.rules.<tab> ##################################################################### + class Rules(): """ Catalog of rules for growth diagrams. @@ -4344,4 +4355,5 @@ class Rules(): Burge = RuleBurge Domino = RuleDomino + GrowthDiagram.rules = Rules diff --git a/src/sage/combinat/integer_lists/nn.py b/src/sage/combinat/integer_lists/nn.py index 6fe0cb0adb5..4329c6164d9 100644 --- a/src/sage/combinat/integer_lists/nn.py +++ b/src/sage/combinat/integer_lists/nn.py @@ -1,6 +1,6 @@ from sage.sets.family import Family from sage.combinat.integer_lists import IntegerListsLex -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets diff --git a/src/sage/combinat/integer_matrices.py b/src/sage/combinat/integer_matrices.py index 44d42ce39b1..75e8e896db8 100644 --- a/src/sage/combinat/integer_matrices.py +++ b/src/sage/combinat/integer_matrices.py @@ -22,6 +22,7 @@ from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ + class IntegerMatrices(UniqueRepresentation, Parent): r""" The class of non-negative integer matrices with @@ -293,6 +294,7 @@ def to_composition(self, x): from sage.combinat.composition import Composition return Composition([entry for row in x for entry in row if entry != 0]) + def integer_matrices_generator(row_sums, column_sums): r""" Recursively generate the integer matrices with the prescribed row sums and diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index e0cd57a3eec..82d4886f100 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -44,9 +44,10 @@ from sage.rings.infinity import PlusInfinity from sage.arith.all import binomial from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer + def is_gale_ryser(r,s): r""" Tests whether the given sequences satisfy the condition @@ -122,6 +123,7 @@ def is_gale_ryser(r,s): # same number of 1s domination return len(rstar) <= len(s2) and sum(r2) == sum(s2) and rstar.dominates(s) + def gale_ryser_theorem(p1, p2, algorithm="gale", *, solver=None, integrality_tolerance=1e-3): r""" @@ -444,6 +446,7 @@ class IntegerVector(ClonableArray): """ An integer vector. """ + def check(self): """ Check to make sure this is a valid integer vector by making sure @@ -478,6 +481,36 @@ def check(self): if self not in self.parent(): raise ValueError(f"{self} doesn't satisfy correct constraints") + def trim(self): + """ + Remove trailing zeros from the integer vector. + + EXAMPLES:: + + sage: IV = IntegerVectors() + sage: IV([5,3,5,1,0,0]).trim() + [5, 3, 5, 1] + sage: IV([5,0,5,1,0]).trim() + [5, 0, 5, 1] + sage: IV([4,3,3]).trim() + [4, 3, 3] + sage: IV([0,0,0]).trim() + [] + + sage: IV = IntegerVectors(k=4) + sage: v = IV([4,3,2,0]).trim(); v + [4, 3, 2] + sage: v.parent() + Integer vectors + """ + P = IntegerVectors() + v = list(self) + if all(i == 0 for i in v): + return P.element_class(P, [], check=False) + while not v[-1]: + v = v[:-1] + return P.element_class(P, v, check=False) + class IntegerVectors(Parent, metaclass=ClasscallMetaclass): """ @@ -712,6 +745,7 @@ class IntegerVectors_all(UniqueRepresentation, IntegerVectors): """ Class of all integer vectors. """ + def __init__(self): """ Initialize ``self``. @@ -756,6 +790,7 @@ class IntegerVectors_n(UniqueRepresentation, IntegerVectors): """ Integer vectors that sum to `n`. """ + def __init__(self, n): """ TESTS:: @@ -828,6 +863,7 @@ class IntegerVectors_k(UniqueRepresentation, IntegerVectors): """ Integer vectors of length `k`. """ + def __init__(self, k): """ TESTS:: @@ -902,6 +938,7 @@ class IntegerVectors_nk(UniqueRepresentation, IntegerVectors): - Martin Albrecht - Mike Hansen """ + def __init__(self, n, k): """ TESTS:: @@ -1212,6 +1249,7 @@ class IntegerVectorsConstraints(IntegerVectors): """ Class of integer vectors subject to various constraints. """ + def __init__(self, n=None, k=None, **constraints): """ Initialize ``self``. diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index 1c2c109217c..a03314b6bf7 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -271,6 +271,7 @@ class WeightedIntegerVectors_all(DisjointUnionEnumeratedSets): [0, 11, 1, 0, 0] [0, 12, 0, 0, 0] """ + def __init__(self, weight): """ TESTS:: diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py index 26559396b39..539fc59b9c0 100644 --- a/src/sage/combinat/integer_vectors_mod_permgroup.py +++ b/src/sage/combinat/integer_vectors_mod_permgroup.py @@ -11,7 +11,7 @@ # **************************************************************************** from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -229,6 +229,7 @@ def __classcall__(cls, G, sum=None, max_part=None, sgs=None): assert (max_part == NN(max_part)) return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs) + class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, RecursivelyEnumeratedSet_forest): r""" A class for integer vectors enumerated up to the action of a @@ -265,6 +266,7 @@ class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, RecursivelyEnu sage: TestSuite(I).run() """ + def __init__(self, G, sgs=None): """ TESTS:: @@ -537,6 +539,7 @@ class Element(ClonableIntArray): ... AssertionError """ + def check(self): r""" Checks that ``self`` verify the invariants needed for @@ -592,6 +595,7 @@ class IntegerVectorsModPermutationGroup_with_constraints(UniqueRepresentation, R sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4) sage: TestSuite(I).run() """ + def __init__(self, G, d, max_part, sgs=None): r""" TESTS:: @@ -944,6 +948,7 @@ class Element(ClonableIntArray): ... AssertionError: [3, 2, 0, 0] should be a integer vector of sum 4 """ + def check(self): r""" Checks that ``self`` meets the constraints of being an element of ``self.parent()``. diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index cd0846e3d6e..e8168802800 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -48,7 +48,7 @@ from sage.misc.latex import latex from sage.misc.lazy_attribute import lazy_attribute from sage.rings.integer import Integer -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.non_negative_integers import NonNegativeIntegers from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family @@ -3568,6 +3568,7 @@ class TamariIntervalPosets_all(DisjointUnionEnumeratedSets, TamariIntervalPosets r""" The enumerated set of all Tamari interval-posets. """ + def __init__(self): r""" TESTS:: @@ -3655,6 +3656,7 @@ class TamariIntervalPosets_size(TamariIntervalPosets): r""" The enumerated set of interval-posets of a given size. """ + def __init__(self, size): r""" TESTS:: diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 2046814a92e..95a9283f7f0 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -85,7 +85,7 @@ Classes and Methods =================== """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2016 Daniel Krenn <dev@danielkrenn.at> # 2021 Gabriel F. Lipnik <dev@gabriellipnik.at> # @@ -93,9 +93,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from .recognizable_series import RecognizableSeries from .recognizable_series import RecognizableSeriesSpace from .recognizable_series import minimize_result @@ -477,7 +476,7 @@ def subsequence(self, a, b): # = v(kan+ar+c) [kan+ar+c >= 0] # = v(k(an+d)+f) [an+d >= 0] # = mu[f] v(an+d) [an+d >= 0]. - d, f = (a*r + c).quo_rem(k) + d, f = (a * r + c).quo_rem(k) if d not in kernel: kernel.append(d) rule[r, c] = (d, f) @@ -492,7 +491,7 @@ def matrix_row(r, c): {r: Matrix.block([matrix_row(r, c) for c in kernel]) for r in A}, vector(chain.from_iterable( - b.get(c, 0)*self.left + b.get(c, 0) * self.left for c in kernel)), vector(chain.from_iterable( (self.__getitem__(c, multiply_left=False) if c >= 0 else zero_R) @@ -760,9 +759,9 @@ def partial_sums(self, include_n=False): result = P.element_class( P, - {r: Matrix.block([[B[0], -B[r+1]], [Z, self.mu[r]]]) for r in A}, + {r: Matrix.block([[B[0], -B[r + 1]], [Z, self.mu[r]]]) for r in A}, vector(chain(self.left, - (dim*(0,) if include_n else -self.left))), + (dim * (0,) if include_n else -self.left))), vector(chain(self.right, self.right))) return result @@ -1276,6 +1275,7 @@ class RecurrenceParser(): This is used by :meth:`kRegularSequenceSpace.from_recurrence` to construct a :class:`kRegularSequence`. """ + def __init__(self, k, coefficient_ring): r""" See :class:`RecurrenceParser`. @@ -1776,7 +1776,7 @@ def parse_one_summand(summand, eq): if left_side.operator() != function: raise ValueError("Term %s in the equation %s is not an evaluation of %s." % (left_side, eq, function)) - if len(left_side.operands()) != 1: + if len(left_side.operands()) != 1: raise ValueError("Term %s in the equation %s does not have " "one argument." % (left_side, eq)) @@ -1788,7 +1788,7 @@ def parse_one_summand(summand, eq): "integer coefficients." % (left_side, eq, left_side.operands()[0], var)) from None - if polynomial_left.degree() > 1: + if polynomial_left.degree() > 1: raise ValueError("Term %s in the equation %s: " "%s is not a polynomial in %s of degree smaller than 2." % (left_side, eq, polynomial_left, var)) @@ -1872,7 +1872,7 @@ def parse_one_summand(summand, eq): if not M: raise ValueError("No recurrence relations are given.") - elif M and m is None: # for the zero sequence + elif M and m is None: # for the zero sequence m = M - 1 missing_remainders = [rem for rem in srange(k**M) @@ -2189,7 +2189,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) keys_coeffs = coeffs.keys() indices_right = [key[1] for key in keys_coeffs if coeffs[key]] - if not indices_right: # the sequence is the zero sequence + if not indices_right: # the sequence is the zero sequence l = 0 u = 0 else: @@ -2239,7 +2239,7 @@ def converted_value(n, v): % (values_not_in_ring, coefficient_ring)) last_value_needed = max( - k**(M-1) - k**m + uu + (n1 > 0)*k**(M-1)*(k*(n1 - 1) + k - 1), # for matrix W + k**(M-1) - k**m + uu + (n1 > 0)*k**(M-1)*(k*(n1 - 1) + k - 1), # for matrix W k**m*offset + u, max(keys_initial)) initial_values = self.values( @@ -2872,7 +2872,7 @@ def entry(i, kk): if j < M - 1: return int(kk == ind[(j + 1, k**j*rem + d)]) else: - rem_d = k**(M-1)*rem + (d%k**M) + rem_d = k**(M-1)*rem + (d % k**M) dd = d // k**M if rem_d < k**M: lambd = l - ind[(m, (k**m)*dd + l)] @@ -2892,7 +2892,7 @@ def wanted_inhomogeneity(row): j, d = ind[row] if j != M - 1: return (None, None) - rem_d = k**(M-1)*rem + (d%k**M) + rem_d = k**(M-1)*rem + (d % k**M) dd = d // k**M if rem_d < k**M: return (rem_d, dd) diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 65b925d9ce0..24f00e12926 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -192,6 +192,7 @@ def WeakTableau(t, k, inner_shape = [], representation = "core"): else: raise NotImplementedError("The representation option needs to be 'core', 'bounded', or 'factorized_permutation'") + def WeakTableaux(k, shape , weight, representation = "core"): r""" This is the dispatcher method for the parent class of weak `k`-tableaux. @@ -257,11 +258,14 @@ def WeakTableaux(k, shape , weight, representation = "core"): raise NotImplementedError("The representation option needs to be 'core', 'bounded', or 'factorized_permutation'") #Abstract class for the elements of weak tableau + + class WeakTableau_abstract(ClonableList, metaclass=InheritComparisonClasscallMetaclass): r""" Abstract class for the various element classes of WeakTableau. """ + def shape(self): r""" Return the shape of ``self``. @@ -520,10 +524,13 @@ def representation(self, representation = 'core'): raise ValueError("The representation must be one of 'core', 'bounded', or 'factorized_permutation'") #Abstract class for the parents of weak tableaux + + class WeakTableaux_abstract(UniqueRepresentation, Parent): r""" Abstract class for the various parent classes of WeakTableaux. """ + def shape(self): r""" Return the shape of the tableaux of ``self``. @@ -1205,7 +1212,9 @@ def _height_of_restricted_subword(self, sw, r): """ R = [v for v in self.shape().to_partition().cells() if self[v[0]][v[1]] < r] L = [v for v in sw if self[v[0]][v[1]] <= r] - return max(v[0] for v in L+R) + return max(v[0] for v in L + R) + + class WeakTableaux_core(WeakTableaux_abstract): r""" @@ -1784,6 +1793,8 @@ def __iter__(self): Element = WeakTableau_bounded #Weak tableaux in terms of factorized permutations + + class WeakTableau_factorized_permutation(WeakTableau_abstract): r""" A weak (skew) `k`-tableau represented in terms of factorizations of affine @@ -3913,6 +3924,7 @@ def to_transposition_sequence( self ): """ return StrongTableaux.marked_CST_to_transposition_sequence( self.to_standard_list(), self.k ) + class StrongTableaux(UniqueRepresentation, Parent): def __init__( self, k, shape, weight ): @@ -4341,7 +4353,8 @@ def follows_tableau_unsigned_standard( cls, Tlist, k ): sage: StrongTableaux.follows_tableau_unsigned_standard([], 4) [[[1]]] """ - v = max([0]+[abs(v) for rows in Tlist for v in rows if v is not None])+1 + v = 1 + max((abs(v) for rows in Tlist for v in rows if v is not None), + default=0) out = [] sh = Core([len(r) for r in Tlist], k + 1) for ga in sh.strong_covers(): @@ -4506,8 +4519,8 @@ def marked_CST_to_transposition_sequence(self, T, k): m = -min(marks) # the largest marked cell transeq = [] # start with the empty list and append on the right sh = Core([len(r) for r in T], k + 1) - j = max([ c-r for r,row in enumerate(LL) for c,val in enumerate(row) - if val == -m ]) + j = max(c - r for r, row in enumerate(LL) for c, val in enumerate(row) + if val == -m) P = sh.to_partition() for l in range(k): msh = sh.affine_symmetric_group_action([j-l,j+1], transposition=True) @@ -4585,6 +4598,7 @@ def transpositions_to_standard_strong( self, transeq, k, emptyTableau=[] ): #### common or global functions related to weak/strong tableaux + def nabs(v): r""" Return the absolute value of ``v`` or ``None``. @@ -4610,6 +4624,7 @@ def nabs(v): else: return abs(v) + def intermediate_shapes(t): r""" Return the intermediate shapes of tableau ``t``. diff --git a/src/sage/combinat/kazhdan_lusztig.py b/src/sage/combinat/kazhdan_lusztig.py index a8561253b28..e5a1456b7de 100644 --- a/src/sage/combinat/kazhdan_lusztig.py +++ b/src/sage/combinat/kazhdan_lusztig.py @@ -59,6 +59,7 @@ class KazhdanLusztigPolynomial(UniqueRepresentation, SageObject): sage: W.kazhdan_lusztig_polynomial([2], [3,2,3,1,2]) # optional - coxeter3 q + 1 """ + def __init__(self, W, q, trace=False): """ Initialize ``self``. diff --git a/src/sage/combinat/key_polynomial.py b/src/sage/combinat/key_polynomial.py new file mode 100644 index 00000000000..be4d5aa8c04 --- /dev/null +++ b/src/sage/combinat/key_polynomial.py @@ -0,0 +1,865 @@ +r""" +Key polynomials + +Key polynomials (also known as type A Demazure characters) are defined by +applying the divided difference operator `\pi_\sigma`, where `\sigma` is +a permutation, to a monomial corresponding to an integer partition +`\mu \vdash n`. + +.. SEEALSO:: + + For Demazure characters in other types, see + + - :meth:`sage.combinat.root_system.weyl_characters.WeylCharacterRing.demazure_character` + - :meth:`sage.categories.classical_crystals.ClassicalCrystals.ParentMethods.demazure_character` + +AUTHORS: + +- Trevor K. Karn (2022-08-17): initial version +""" + +# **************************************************************************** +# Copyright (C) 2022 Trevor K. Karn <karnx018 (at) umn.edu> +# +# 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.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis +from sage.combinat.integer_vector import IntegerVectors +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.permutation import Permutation +from sage.structure.element import parent +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing, InfinitePolynomialRing_sparse +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.polynomial_ring import PolynomialRing_commutative +from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base + +from collections.abc import Collection + + +class KeyPolynomial(CombinatorialFreeModule.Element): + r""" + A key polynomial. + + Key polynomials are polynomials that form a basis for a polynomial ring + and are indexed by weak compositions. + + Elements should be created by first creating the basis + :class:`KeyPolynomialBasis` and passing a list representing the indexing + composition. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: f = k([4,3,2,1]) + k([1,2,3,4]); f + k[1, 2, 3, 4] + k[4, 3, 2, 1] + sage: f in k + True + """ + def _mul_(self, other): + r""" + Multiply the elements ``self`` and ``other``. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k([4,3,2]) * k([1,1,1]) + k[5, 4, 3] + + sage: k = KeyPolynomials(QQ, 4) + sage: k([4,3,2,0]) * k([1,1,1,0]) + k[5, 4, 3, 0] + """ + return self.parent().from_polynomial(self.expand() * other.expand()) + + def expand(self): + r""" + Return ``self`` written in the monomial basis (i.e., as an element + in the corresponding polynomial ring). + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: f = k([4,3,2,1]) + sage: f.expand() + z_3*z_2^2*z_1^3*z_0^4 + + sage: f = k([1,2,3]) + sage: f.expand() + z_2^3*z_1^2*z_0 + z_2^3*z_1*z_0^2 + z_2^2*z_1^3*z_0 + + 2*z_2^2*z_1^2*z_0^2 + z_2^2*z_1*z_0^3 + z_2*z_1^3*z_0^2 + + z_2*z_1^2*z_0^3 + """ + P = self.parent() + R = P._polynomial_ring + out = R.zero() + z = P.poly_gens() + + for m, c in self.monomial_coefficients().items(): + # find the permutation sorting mu into m + w, mu = sorting_word(m) + + # create the monomial to apply + monom = R.prod(z[i] ** mi for i, mi in enumerate(mu) if mi) + + out += c * isobaric_divided_difference(monom, w) + + return out + + to_polynomial = expand + + def pi(self, w): + r""" + Apply the operator `\pi_w` to ``self``. + + ``w`` may be either a ``Permutation`` or a list of indices of simple + transpositions (1-based). + + The convention is to apply from left to right so if + ``w = [w1, w2, ..., wm]`` then we apply + `\pi_{w_2 \cdots w_m} \circ \pi_{w_1}` + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k([3,2,1]).pi(2) + k[3, 1, 2] + sage: k([3,2,1]).pi([2,1]) + k[1, 3, 2] + sage: k([3,2,1]).pi(Permutation([3,2,1])) + k[1, 2, 3] + sage: f = k([3,2,1]) + k([3,2,1,1]) + sage: f.pi(2) + k[3, 1, 2] + k[3, 1, 2, 1] + sage: k.one().pi(1) + k[] + + sage: k([3,2,1,0]).pi(2).pi(2) + k[3, 1, 2] + sage: (-k([3,2,1,0]) + 4*k([3,1,2,0])).pi(2) + 3*k[3, 1, 2] + + sage: k = KeyPolynomials(QQ, 4) + sage: k([3,2,1,0]).pi(2) + k[3, 1, 2, 0] + sage: k([3,2,1,0]).pi([2,1]) + k[1, 3, 2, 0] + sage: k([3,2,1,0]).pi(Permutation([3,2,1,4])) + k[1, 2, 3, 0] + sage: f = k([3,2,1,0]) + k([3,2,1,1]) + sage: f.pi(2) + k[3, 1, 2, 0] + k[3, 1, 2, 1] + sage: k.one().pi(1) + k[0, 0, 0, 0] + + TESTS: + + We check that this is consistent with the definition via the + isobaric divided difference oerators:: + + sage: from sage.combinat.key_polynomial import isobaric_divided_difference as idd + sage: k = KeyPolynomials(QQ, 4) + sage: S4 = Permutations(4) + sage: f = k([4,2,2,0]) + sage: all(idd(f.expand(), w.reduced_word()) == f.pi(w).expand() for w in S4) + True + + sage: f = k([4,2,0,1]) - 3 * k([2,0,1,2]) + sage: all(idd(f.expand(), w.reduced_word()) == f.pi(w).expand() for w in S4) + True + """ + P = self.parent() + if isinstance(w, Permutation): + w = w.reduced_word() + if not isinstance(w, Collection): + w = [w] + + if not w or not self: + return self + + N = max(w) + 1 + + if P._k is not None and N > P._k: + raise ValueError(f"pi_{N-1} does not exist for this polynomial ring") + + ret = P.element_class(P, {}) + for m, c in self._monomial_coefficients.items(): + m = list(m) + n = len(m) + for i in w: + if i > n: + continue + if i == n: + m += [0] + n += 1 + if m[i-1] <= m[i]: + continue + m[i-1], m[i] = m[i], m[i-1] + m = P._indices(m) + if P._k is None: + m = m.trim() + if m in ret._monomial_coefficients: + ret._monomial_coefficients[m] += c + else: + ret._monomial_coefficients[m] = c + if not ret._monomial_coefficients[m]: + del ret._monomial_coefficients + return ret + + isobaric_divided_difference = pi + + def divided_difference(self, w): + r""" + Apply the divided difference operator `\partial_w` to ``self``. + + The convention is to apply from left to right so if + ``w = [w1, w2, ..., wm]`` then we apply + `\partial_{w_2 \cdots w_m} \circ \partial_{w_1}` + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k([3,2,1]).divided_difference(2) + k[3, 1, 1] + sage: k([3,2,1]).divided_difference([2,3]) + k[3, 1] + + sage: k = KeyPolynomials(QQ, 4) + sage: k([3,2,1,0]).divided_difference(2) + k[3, 1, 1, 0] + """ + if not isinstance(w, Collection): + w = [w] + f = self.expand() + for wi in w: + f = divided_difference(f, wi) + return self.parent().from_polynomial(f) + + +class KeyPolynomialBasis(CombinatorialFreeModule): + r""" + The key polynomial basis for a polynomial ring. + + For a full definition, see + `SymmetricFunctions.com <https://www.symmetricfunctions.com/key.htm>`_. + Key polynomials are indexed by weak compositions with no trailing zeros, + and `\sigma` is the permutation of shortest length which sorts the + indexing composition into a partition. + + EXAMPLES: + + Key polynomials are a basis, indexed by (weak) compositions, + for polynomial rings:: + + sage: k = KeyPolynomials(QQ) + sage: k([3,0,1,2]) + k[3, 0, 1, 2] + sage: k([3,0,1,2])/2 + 1/2*k[3, 0, 1, 2] + sage: R = k.polynomial_ring(); R + Infinite polynomial ring in z over Rational Field + + sage: K = KeyPolynomials(GF(5)); K + Key polynomial basis over Finite Field of size 5 + sage: 2*K([3,0,1,2]) + 2*k[3, 0, 1, 2] + sage: 5*(K([3,0,1,2]) + K([3,1,1])) + 0 + + We can expand them in the standard monomial basis:: + + sage: k([3,0,1,2]).expand() + z_3^2*z_2*z_0^3 + z_3^2*z_1*z_0^3 + z_3*z_2^2*z_0^3 + + 2*z_3*z_2*z_1*z_0^3 + z_3*z_1^2*z_0^3 + z_2^2*z_1*z_0^3 + + z_2*z_1^2*z_0^3 + + sage: k([0,0,2]).expand() + z_2^2 + z_2*z_1 + z_2*z_0 + z_1^2 + z_1*z_0 + z_0^2 + + If we have a polynomial, we can express it in the key basis:: + + sage: z = R.gen() + sage: k.from_polynomial(z[2]^2*z[1]*z[0]) + k[1, 1, 2] - k[1, 2, 1] + + sage: f = z[3]^2*z[2]*z[0]^3 + z[3]^2*z[1]*z[0]^3 + z[3]*z[2]^2*z[0]^3 + \ + ....: 2*z[3]*z[2]*z[1]*z[0]^3 + z[3]*z[1]^2*z[0]^3 + z[2]^2*z[1]*z[0]^3 + \ + ....: z[2]*z[1]^2*z[0]^3 + sage: k.from_polynomial(f) + k[3, 0, 1, 2] + + Since the ring of key polynomials may be regarded as a different choice of + basis for a polynomial ring, it forms an algebra, so we have + multiplication:: + + sage: k([10,5,2])*k([1,1,1]) + k[11, 6, 3] + + We can also multiply by polynomials in the monomial basis:: + + sage: k([10,9,1])*z[0] + k[11, 9, 1] + sage: z[0] * k([10,9,1]) + k[11, 9, 1] + sage: k([10,9,1])*(z[0] + z[3]) + k[10, 9, 1, 1] + k[11, 9, 1] + + When the sorting permutation is the longest element, the key polynomial + agrees with the Schur polynomial:: + + sage: s = SymmetricFunctions(QQ).schur() + sage: k([1,2,3]).expand() + z_2^3*z_1^2*z_0 + z_2^3*z_1*z_0^2 + z_2^2*z_1^3*z_0 + + 2*z_2^2*z_1^2*z_0^2 + z_2^2*z_1*z_0^3 + z_2*z_1^3*z_0^2 + + z_2*z_1^2*z_0^3 + sage: s[3,2,1].expand(3) + x0^3*x1^2*x2 + x0^2*x1^3*x2 + x0^3*x1*x2^2 + 2*x0^2*x1^2*x2^2 + + x0*x1^3*x2^2 + x0^2*x1*x2^3 + x0*x1^2*x2^3 + + The polynomial expansions can be computed using crystals and expressed in + terms of the key basis:: + + sage: T = crystals.Tableaux(['A',3],shape=[2,1]) + sage: f = T.demazure_character([3,2,1]) + sage: k.from_polynomial(f) + k[1, 0, 0, 2] + + The default behavior is to work in a polynomial ring with infinitely many + variables. One can work in a specicfied number of variables:: + + sage: k = KeyPolynomials(QQ, 4) + sage: k([3,0,1,2]).expand() + z_0^3*z_1^2*z_2 + z_0^3*z_1*z_2^2 + z_0^3*z_1^2*z_3 + + 2*z_0^3*z_1*z_2*z_3 + z_0^3*z_2^2*z_3 + z_0^3*z_1*z_3^2 + z_0^3*z_2*z_3^2 + + sage: k([0,0,2,0]).expand() + z_0^2 + z_0*z_1 + z_1^2 + z_0*z_2 + z_1*z_2 + z_2^2 + + sage: k([0,0,2,0]).expand().parent() + Multivariate Polynomial Ring in z_0, z_1, z_2, z_3 over Rational Field + + If working in a specified number of variables, the length of the indexing + composition must be the same as the number of variables:: + + sage: k([0,0,2]) + Traceback (most recent call last): + ... + TypeError: do not know how to make x (= [0, 0, 2]) an element of self + (=Key polynomial basis over Rational Field) + + One can also work in a specified polynomial ring:: + + sage: k = KeyPolynomials(QQ['x0', 'x1', 'x2', 'x3']) + sage: k([0,2,0,0]) + k[0, 2, 0, 0] + sage: k([4,0,0,0]).expand() + x0^4 + + If one wishes to use a polynomial ring as coefficients for the key + polynomials, pass the keyword argument ``poly_coeffs=True``:: + + sage: k = KeyPolynomials(QQ['q'], poly_coeffs=True) + sage: R = k.base_ring(); R + Univariate Polynomial Ring in q over Rational Field + sage: R.inject_variables() + Defining q + sage: (q^2 + q + 1)*k([0,2,2,0,3,2]) + (q^2+q+1)*k[0, 2, 2, 0, 3, 2] + """ + Element = KeyPolynomial + + @staticmethod + def __classcall_private__(cls, R=None, k=None, poly_ring=None, poly_coeffs=False): + r""" + Normalize input. + + EXAMPLES:: + + sage: KeyPolynomials(InfinitePolynomialRing(QQ, ['x', 'y'])) + Traceback (most recent call last): + ... + ValueError: polynomial ring has too many generators + + sage: KeyPolynomials(QQ['t0','t1','t2','t3']) + Key polynomial basis over Rational Field + + sage: KeyPolynomials(QQ['t']) + Key polynomial basis over Rational Field + + sage: KeyPolynomials(InfinitePolynomialRing(QQ['t'], 'z')) + Key polynomial basis over Univariate Polynomial Ring in t over Rational Field + + sage: KeyPolynomials(QQ) + Key polynomial basis over Rational Field + + sage: KeyPolynomials(QQ, 3) + Key polynomial basis over Rational Field + """ + poly_type = (PolynomialRing_commutative, + MPolynomialRing_base, + InfinitePolynomialRing_sparse) + + if isinstance(R, poly_type): + # if a polynomial ring is provided, we need to determine + # if it is meant to be self.polynomial_ring() or self.base_ring() + if isinstance(R, poly_type[0:2]): + k = R.ngens() + if isinstance(R, InfinitePolynomialRing_sparse) and R.ngens() > 1: + raise ValueError("polynomial ring has too many generators") + if isinstance(R.base_ring(), poly_type[0:2]): + # if R is of the form K[t_1, ..., t_n][z_*] + # or K[t_1, ..., t_n][z_1, ..., z_k] + return cls.__classcall__(cls, k=k, poly_ring=R) + if poly_coeffs: + # if R is a polynomial ring, but its base ring is not + # and poly_coeffs is true, then we should interpret + # R as the base ring + return cls.__classcall__(cls, R=R) + return cls.__classcall__(cls, k=k, poly_ring=R) + else: + # if R is not a polynomial ring, we know it is self.base_ring() + return cls.__classcall__(cls, R=R, k=k) + + def __init__(self, R=None, k=None, poly_ring=None): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: R = GF(3)['t'].fraction_field() + sage: k = KeyPolynomials(QQ) + sage: TestSuite(k).run() + sage: k = KeyPolynomials(R) + sage: TestSuite(k).run() + + sage: k = KeyPolynomials(QQ, 4) + sage: TestSuite(k).run() + sage: k = KeyPolynomials(R, 4) + sage: TestSuite(k).run() + """ + self._k = k + + if self._k is not None: + def build_index(m): + return self._indices(m) + else: + def build_index(m): + return self._indices(reversed(m)).trim() + + self._build_index = build_index + + if R is not None: + if poly_ring: + raise ValueError("specify only one of base_ring or poly_ring (not both)") + if k: + self._polynomial_ring = PolynomialRing(R, 'z_', k) + else: + self._polynomial_ring = InfinitePolynomialRing(R, 'z') + if poly_ring is not None: + if R is not None: + raise ValueError("specify only one of base_ring or poly_ring (not both)") + R = poly_ring.base_ring() + self._polynomial_ring = poly_ring + + self._name = "Key polynomial basis" + + CombinatorialFreeModule.__init__(self, R, IntegerVectors(k=k), + category=GradedAlgebrasWithBasis(R), + prefix='k', bracket=False) + + def _coerce_map_from_(self, R): + r""" + Return the coercion map from ``R`` if it exists. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: m1 = k([3, 2, 4, 0]); m1 + k[3, 2, 4] + sage: m2 = k(Composition([3, 2, 4])); m2 + k[3, 2, 4] + sage: m1 == m2 + True + + sage: R = k.polynomial_ring() + sage: z = R.gen() + sage: z[0] * k([4, 3, 3, 2]) + k[5, 3, 3, 2] + + sage: X = SchubertPolynomialRing(QQ) + sage: k(X([4, 3, 2, 1])) + k[3, 2, 1] + """ + P = self._polynomial_ring + if R is P: + return self.from_polynomial + + from sage.combinat.schubert_polynomial import SchubertPolynomialRing_xbasis + if isinstance(R, SchubertPolynomialRing_xbasis): + return self.from_schubert_polynomial + + phi = P.coerce_map_from(R) + if phi is not None: + return self.coerce_map_from(P) * phi + return None + + def _monomial(self, x): + r""" + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k([3, 2, 3, 4, 0]) + k[3, 2, 3, 4] + sage: k = KeyPolynomials(QQ, 5) + sage: k([3, 2, 3, 4, 0]) + k[3, 2, 3, 4, 0] + """ + if self._k: + return self._from_dict({x: self.base_ring().one()}, remove_zeros=False) + return self._from_dict({x.trim(): self.base_ring().one()}, remove_zeros=False) + + def __getitem__(self, c): + """ + This method implements the abuses of notations ``k[2,1]``, + ``k[[2,1]]``, etc. + + INPUT: + + - ``c`` -- anything that can represent an index of a basis element + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k[3] + k[3] + sage: k[3, 0, 2] + k[3, 0, 2] + sage: k[3, 1, 2, 0, 0] + k[3, 1, 2] + + sage: k = KeyPolynomials(QQ, 4) + sage: k[3, 0, 1, 0] + k[3, 0, 1, 0] + sage: k[3] + Traceback (most recent call last): + ... + ValueError: [3] doesn't satisfy correct constraints + """ + C = self._indices + if not isinstance(c, C.element_class): + if c in ZZ: + c = C([c]) + else: + c = C(c) + return self._monomial(c) + + def one_basis(self): + r""" + Return the basis element indexing the identity. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k.one_basis() + [] + + sage: k = KeyPolynomials(QQ, 4) + sage: k.one_basis() + [0, 0, 0, 0] + """ + if self._k: + return self._indices([0] * self._k) + return self._indices([]) + + def polynomial_ring(self): + r""" + Return the polynomial ring associated to ``self``. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k.polynomial_ring() + Infinite polynomial ring in z over Rational Field + + sage: k = KeyPolynomials(QQ, 4) + sage: k.polynomial_ring() + Multivariate Polynomial Ring in z_0, z_1, z_2, z_3 over Rational Field + """ + return self._polynomial_ring + + def poly_gens(self): + r""" + Return the polynomial generators for the polynomial ring + associated to ``self``. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: k.poly_gens() + z_* + + sage: k = KeyPolynomials(QQ, 4) + sage: k.poly_gens() + (z_0, z_1, z_2, z_3) + """ + if self._k: + return self._polynomial_ring.gens() + return self._polynomial_ring.gen() + + def from_polynomial(self, f): + r""" + Expand a polynomial in terms of the key basis. + + EXAMPLES:: + + sage: k = KeyPolynomials(QQ) + sage: z = k.poly_gens(); z + z_* + sage: p = z[0]^4*z[1]^2*z[2]*z[3] + z[0]^4*z[1]*z[2]^2*z[3] + sage: k.from_polynomial(p) + k[4, 1, 2, 1] + + sage: all(k(c) == k.from_polynomial(k(c).expand()) for c in IntegerVectors(n=5, k=4)) + True + + sage: T = crystals.Tableaux(['A', 4], shape=[4,2,1,1]) + sage: k.from_polynomial(T.demazure_character([2])) + k[4, 1, 2, 1] + + """ + if f not in self._polynomial_ring: + try: # to accept elements of SymbolicRing + from sage.calculus.var import var + f = f.substitute(list(d == var(f'z_{i}') + for i, d in enumerate(f.variables()))) + f = self._polynomial_ring(f) + except AttributeError: + raise ValueError(f"f must be an element of {self._polynomial_ring}") + + out = self.zero() + + while f: + M = f.monomials()[0] + c = f.monomial_coefficient(M) + + new_term = self._from_dict({self._build_index(*M.exponents()): c}) + + f -= new_term.expand() + out += new_term + + return out + + def from_schubert_polynomial(self, x): + r""" + Expand a Schubert polynomial in the key basis. + + EXAMPLES:: + + sage: k = KeyPolynomials(ZZ) + sage: X = SchubertPolynomialRing(ZZ) + sage: f = X([2,1,5,4,3]) + sage: k.from_schubert_polynomial(f) + k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1] + sage: k.from_schubert_polynomial(2) + 2*k[] + sage: k(f) + k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1] + + sage: k = KeyPolynomials(GF(7), 4) + sage: k.from_schubert_polynomial(f) + k[1, 0, 2, 1] + k[2, 0, 2, 0] + k[3, 0, 0, 1] + + TESTS:: + + sage: k = KeyPolynomials(ZZ) + sage: k.from_schubert_polynomial(k([3,2])) + Traceback (most recent call last): + ... + ValueError: not a Schubert polynomial + + sage: k = KeyPolynomials(ZZ) + sage: X = SchubertPolynomialRing(ZZ) + sage: it = iter(Compositions()) + sage: for _ in range(50): + ....: C = next(it) + ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C + + sage: k = KeyPolynomials(ZZ, 4) + sage: X = SchubertPolynomialRing(ZZ) + sage: it = iter(k.basis().keys()) + sage: for _ in range(50): + ....: C = next(it) + ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C + """ + if x in self.base_ring(): + return self(x) + + from sage.combinat.schubert_polynomial import SchubertPolynomial_class + if not isinstance(x, SchubertPolynomial_class): + raise ValueError('not a Schubert polynomial') + + from sage.combinat.diagram import RotheDiagram + out = self.zero() + if self._k is not None: + def build_elt(wt): + wt = list(wt) + wt += [0] * (self._k - len(wt)) + return self[wt] + else: + def build_elt(wt): + return self[wt] + + for m, c in x.monomial_coefficients().items(): + D = RotheDiagram(m) + a = self.zero() + for d in D.peelable_tableaux(): + a += build_elt(d.left_key_tableau().weight()) + out += c * a + + return out + + +def divided_difference(f, i): + r""" + Apply the ``i``-th divided difference operator to the polynomial ``f``. + + EXAMPLES:: + + sage: from sage.combinat.key_polynomial import divided_difference + sage: k = KeyPolynomials(QQ) + sage: z = k.poly_gens() + sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3] + sage: divided_difference(f, 3) + z_3^2*z_1 + z_3*z_2*z_1 + z_2^2*z_1 + + sage: k = KeyPolynomials(QQ, 4) + sage: z = k.poly_gens() + sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3] + sage: divided_difference(f, 3) + z_1*z_2^2 + z_1*z_2*z_3 + z_1*z_3^2 + + sage: k = KeyPolynomials(QQ) + sage: R = k.polynomial_ring(); R + Infinite polynomial ring in z over Rational Field + sage: z = R.gen() + sage: divided_difference(z[1]*z[2]^3, 2) + -z_2^2*z_1 - z_2*z_1^2 + sage: divided_difference(z[1]*z[2]*z[3], 3) + 0 + sage: divided_difference(z[1]*z[2]*z[3], 4) + z_2*z_1 + sage: divided_difference(z[1]*z[2]*z[4], 4) + -z_2*z_1 + + sage: k = KeyPolynomials(QQ, 5) + sage: z = k.polynomial_ring().gens() + sage: divided_difference(z[1]*z[2]^3, 2) + -z_1^2*z_2 - z_1*z_2^2 + sage: divided_difference(z[1]*z[2]*z[3], 3) + 0 + sage: divided_difference(z[1]*z[2]*z[3], 4) + z_1*z_2 + sage: divided_difference(z[1]*z[2]*z[4], 4) + -z_1*z_2 + """ + P = parent(f) + if isinstance(P, InfinitePolynomialRing_sparse): + z = P.gen() + else: + z = P.gens() + + si_f = f.subs({z[i]: z[i-1], z[i-1]: z[i]}) + return (si_f - f) // (z[i] - z[i-1]) + +def isobaric_divided_difference(f, w): + r""" + Apply the isobaric divided difference operator `\pi_w` to the + polynomial `f`. + + ``w`` may be either a single index or a list of + indices of simple transpositions. + + .. WARNING:: + + The simple transpositions should be applied from left to right. + + EXAMPLES:: + + sage: from sage.combinat.key_polynomial import isobaric_divided_difference as idd + sage: R.<z> = InfinitePolynomialRing(GF(3)) + sage: idd(z[1]^4*z[2]^2*z[4], 4) + 0 + + sage: idd(z[1]^4*z[2]^2*z[3]*z[4], 3) + z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4 + + sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [3, 4]) + z_4^2*z_3*z_2*z_1^4 + z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4 + + sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [4, 3]) + z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4 + + sage: idd(z[1]^2*z[2], [3, 2]) + z_3*z_2^2 + z_3*z_2*z_1 + z_3*z_1^2 + z_2^2*z_1 + z_2*z_1^2 + """ + P = parent(f) + if isinstance(P, InfinitePolynomialRing_sparse): + z = P.gen() + else: + z = P.gens() + + if not hasattr(w, "__iter__"): # this allows us to pass i instead of a word + w = [w] + for i in w: + fp = z[i-1] * f + si_fp = fp.subs({z[i]: z[i-1], z[i-1]: z[i]}) + f = (si_fp - fp) // (z[i] - z[i-1]) + return f + +def sorting_word(alpha): + r""" + Get a reduced word for the permutation which sorts ``alpha`` + into a partition. + + The result is a list ``l = [i0, i1, i2, ...]`` where each ``ij`` + is a positive integer such that it applies the simple + transposition `(i_j, i_j+1)`. The transpositions are applied + starting with ``i0``, then ``i1`` is applied, followed by ``i2``, + and so on. See :meth:`sage.combinat.permutation.Permutation.reduced_words` + for the convention used. + + EXAMPLES:: + + sage: IV = IntegerVectors() + sage: from sage.combinat.key_polynomial import sorting_word + sage: list(sorting_word(IV([2,3,2]))[0]) + [1] + sage: sorting_word(IV([2,3,2]))[1] + [3, 2, 2] + sage: list(sorting_word(IV([5,6,7]))[0]) + [1, 2, 1] + sage: list(sorting_word(IV([0,3,2]))[0]) + [2, 1] + sage: list(sorting_word(IV([0,3,0,2]))[0]) + [2, 3, 1] + sage: list(sorting_word(IV([3,2,1]))[0]) + [] + sage: list(sorting_word(IV([2,3,3]))[0]) + [2, 1] + """ + w = [] + L = list(alpha) + n = len(L) + + # bubble sort to get the shortest sorting word + for i in range(n-1): + for j in range(n-i-1): + if L[j] < L[j + 1]: + w.append(j+1) + L[j], L[j + 1] = L[j + 1], L[j] + return reversed(w), L diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index 53249483d6e..13df595dce4 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -494,6 +494,7 @@ class RhombusPiece(PuzzlePiece): sage: RhombusPiece(delta,nabla) 2/\3 6\/5 """ + def __init__(self, north_piece, south_piece): r""" EXAMPLES:: @@ -650,6 +651,7 @@ class PuzzlePieces(): [0/\0 0\/0, 0/\0 1\/10, 0/\10 10\/0, 0/\10 1\/1, 1/\0 0\/1, 1/\1 10\/0, 1/\1 1\/1, 10/\1 0\/0, 10/\1 1\/10] """ + def __init__(self, forbidden_border_labels=None): r""" INPUT: @@ -1070,6 +1072,7 @@ class PuzzleFilling(): r""" Create partial puzzles and provides methods to build puzzles from them. """ + def __init__(self, north_west_labels, north_east_labels): r""" TESTS:: @@ -1925,6 +1928,7 @@ class KnutsonTaoPuzzleSolver(UniqueRepresentation): (4, 5): 1/\1 2\/2(1), (5, 5): 2(1)/1\2}] """ + def __init__(self, puzzle_pieces): r""" Knutson-Tao puzzle solver. diff --git a/src/sage/combinat/lr_tableau.py b/src/sage/combinat/lr_tableau.py index 8e8fefaa1c5..c19effc2173 100644 --- a/src/sage/combinat/lr_tableau.py +++ b/src/sage/combinat/lr_tableau.py @@ -288,6 +288,7 @@ def is_littlewood_richardson(t, heights): return False return True + def _tableau_join(t1, t2, shift=0): """ Join semistandard tableau ``t1`` with semistandard tableau ``t2`` diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index 953b5c4e680..9b5ee82aa81 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -193,8 +193,8 @@ cdef class dancing_linksWrapper: r""" Reinitialization of the search algorithm - This recreates an empty `dancing_links` object and adds the rows to - the instance of dancing_links. + This recreates an empty ``dancing_links`` object and adds the rows to + the instance of ``dancing_links.`` EXAMPLES:: @@ -805,7 +805,7 @@ cdef class dancing_linksWrapper: INPUT: - ``ncpus`` -- integer (default: ``None``), maximal number of - subprocesses to use at the same time. If `ncpus>1` the dancing + subprocesses to use at the same time. If ``ncpus>1`` the dancing links problem is split into independent subproblems to allow parallel computation. If ``None``, it detects the number of effective CPUs in the system using @@ -968,8 +968,8 @@ cdef class dancing_linksWrapper: .. NOTE:: - When comparing the time taken by method `one_solution`, - have in mind that `one_solution_using_sat_solver` first + When comparing the time taken by method ``one_solution``, + have in mind that ``one_solution_using_sat_solver`` first creates the SAT solver instance from the dancing links solver. This copy of data may take many seconds depending on the size of the problem. @@ -1096,8 +1096,8 @@ cdef class dancing_linksWrapper: .. NOTE:: - When comparing the time taken by method `one_solution`, have in - mind that `one_solution_using_milp_solver` first creates (and + When comparing the time taken by method ``one_solution``, have in + mind that ``one_solution_using_milp_solver`` first creates (and caches) the MILP solver instance from the dancing links solver. This copy of data may take many seconds depending on the size of the problem. diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 9ca538ac482..91f73d29101 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -31,7 +31,7 @@ The module below implements the Paley constructions (see for example [Hora]_) and the Sylvester construction. It also allows you to pull a -Hadamard matrix from the database at [HadaSloa]_. +Hadamard matrix from the database at [SloaHada]_. AUTHORS: @@ -39,11 +39,11 @@ REFERENCES: -.. [HadaSloa] \N.J.A. Sloane's Library of Hadamard Matrices, at - http://neilsloane.com/hadamard/ -.. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` -.. [Hora] \K. J. Horadam, Hadamard Matrices and Their Applications, - Princeton University Press, 2006. +- [SloaHada]_ + +- [HadaWiki]_ + +- [Hora]_ """ #***************************************************************************** @@ -51,23 +51,27 @@ # 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 urllib.request import urlopen +from sage.combinat.designs.difference_family import skew_supplementary_difference_set from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix, block_matrix, block_diagonal_matrix, diagonal_matrix from sage.arith.all import is_square, is_prime_power, divisors from math import sqrt from sage.matrix.constructor import identity_matrix as I -from sage.matrix.constructor import ones_matrix as J +from sage.matrix.constructor import ones_matrix as J +from sage.matrix.constructor import zero_matrix from sage.misc.unknown import Unknown from sage.cpython.string import bytes_to_str +from sage.modules.free_module_element import vector +from sage.combinat.t_sequences import T_sequences_smallcases def normalise_hadamard(H): - """ + r""" Return the normalised Hadamard matrix corresponding to ``H``. The normalised Hadamard matrix corresponding to a Hadamard matrix `H` is a @@ -93,8 +97,8 @@ def hadamard_matrix_paleyI(n, normalize=True): r""" Implement the Paley type I construction. - The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a - prime `p` (see [Hora]_). + The Paley type I case corresponds to the case `p=n-1 \cong 3 \mod{4}` for a + prime power `p` (see [Hora]_). INPUT: @@ -160,8 +164,8 @@ def hadamard_matrix_paleyII(n): r""" Implement the Paley type II construction. - The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a - prime `p` (see [Hora]_). + The Paley type II case corresponds to the case `p=n/2-1 \cong 1 \mod{4}` for a + prime power `p` (see [Hora]_). EXAMPLES:: @@ -223,9 +227,967 @@ def hadamard_matrix_paleyII(n): return normalise_hadamard(H) +def hadamard_matrix_williamson_type(a, b, c, d, check=True): + r""" + Construction of Williamson type Hadamard matrix. + + Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, + and satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, + one can construct a Hadamard matrix of order `4n`, cf. [Ha83]_. + + INPUT: + + - ``a`` -- (1,-1) list specifying the 1st row of `A`. + + - ``b`` -- (1,-1) list specifying the 1st row of `B`. + + - ``d`` -- (1,-1) list specifying the 1st row of `C`. + + - ``c`` -- (1,-1) list specifying the 1st row of `D`. + + - ``check`` (boolean) -- Whether to check that the output is an Hadamard matrix before returning it. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type + sage: a = [ 1, 1, 1] + sage: b = [ 1, -1, -1] + sage: c = [ 1, -1, -1] + sage: d = [ 1, -1, -1] + sage: M = hadamard_matrix_williamson_type(a,b,c,d,check=True) + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix + sage: a = [ 1, 1, 1] + sage: b = [ 1, -1, -1] + sage: c = [ 1, -1, -1] + sage: d = [ 1, -1, -1] + sage: is_hadamard_matrix(hadamard_matrix_williamson_type(a,b,c,d)) + True + sage: e = [1, 1, 1] + sage: hadamard_matrix_williamson_type(a,b,c,e, check=True) + Traceback (most recent call last): + ... + AssertionError + sage: f = [1, -1, 1, -1] + sage: hadamard_matrix_williamson_type(a,b,c,f, check=True) + Traceback (most recent call last): + ... + AssertionError + """ + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + n = len(a) + assert len(a) == len(b) == len(c) == len(d) + assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) + + M = block_matrix([[ A, B, C, D], + [-B, A, -D, C], + [-C, D, A, -B], + [-D, -C, B, A]]) + if check: + assert is_hadamard_matrix(M, normalized=False, skew=False) + return M + +def williamson_type_quadruples_smallcases(n, existence=False): + r""" + Quadruples of matrices that can be used to construct Williamson type Hadamard matrices. + + This function contains for some values of n, four `n\times n` matrices used in the + Williamson construction of Hadamard matrices. Namely, the function returns the first row of + 4 `n\times n` circulant matrices with the properties described in + :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. + The matrices for n=29 and n=43 are given in [Ha83]_. + + INPUT: + + - ``n`` -- the order of the matrices to be returned + + - ``existence`` -- if true, only check that we have the quadruple (default false). + + OUTPUT: + + If ``existence`` is false, returns a tuple containing four vectors, each being the first line + of one of the four matrices. It raises an error if no such matrices are available. + If ``existence`` is true, returns a boolean representing whether the matrices are available or not. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import williamson_type_quadruples_smallcases + sage: williamson_type_quadruples_smallcases(29) + ((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, -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, 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, -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)) + sage: williamson_type_quadruples_smallcases(43, existence=True) + True + + TESTS:: + + sage: williamson_type_quadruples_smallcases(123, existence=True) + False + sage: williamson_type_quadruples_smallcases(123) + Traceback (most recent call last): + ... + ValueError: The Williamson type quadruple of order 123 is not yet implemented. + """ + db = { + 1: ([1], [1], [1], [1]), + 7: ([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]), + 9: ([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, -1, -1, -1, -1, -1, 1]), + 29: ([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,-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, 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,-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]), + 43: ([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, 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, 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, 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, -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, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), + } + + if existence: + return n in db + + if n not in db: + raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n) + a, b, c, d = map(vector, db[n]) + return a, b, c, d + +def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): + r""" + Construct Williamson type Hadamard matrices for some small values of n. + + This function uses the data contained in + :func:`sage.combinat.matrices.hadamard_matrix.williamson_type_quadruples_smallcases` + to create Hadamard matrices of the Williamson type, using the construction from + :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. + + INPUT: + + - ``n`` -- the order of the matrix. + + - ``existence`` -- if true, only check that we can do the construction (default false). + + - ``check`` -- if true (default), check the result. + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import williamson_hadamard_matrix_smallcases + sage: williamson_hadamard_matrix_smallcases(116) + 116 x 116 dense matrix over Integer Ring... + sage: williamson_hadamard_matrix_smallcases(172) + 172 x 172 dense matrix over Integer Ring... + sage: williamson_hadamard_matrix_smallcases(100) + Traceback (most recent call last): + ... + ValueError: The Williamson type Hadamard matrix of order 100 is not yet implemented. + """ + assert n%4 == 0 + + if not williamson_type_quadruples_smallcases(n//4, existence=True): + if existence: + return False + raise ValueError("The Williamson type Hadamard matrix of order %s is not yet implemented." % n) + + if existence: + return True + + a, b, c, d = williamson_type_quadruples_smallcases(n//4) + return hadamard_matrix_williamson_type(a, b, c, d, check=check) + + +def hadamard_matrix_156(): + r""" + Construct an Hadamard matrix of order 156. + + The matrix is created using the construction detailed in [BH1965]_. + This uses four circulant matrices of size `13\times 13`, + which are composed into a `156\times 156` block matrix. + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix, hadamard_matrix_156 + sage: is_hadamard_matrix(hadamard_matrix_156()) + True + sage: hadamard_matrix_156() + 156 x 156 dense matrix over Integer Ring... + """ + a = [1, 1,-1,-1, 1,-1, 1, 1,-1, 1,-1,-1, 1] + b = [1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1] + c = [1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, 1, 1] + d = [1, 1,-1, 1,-1, 1, 1, 1, 1,-1, 1,-1, 1] + + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + return block_matrix([[ A, A, A, B,-B, C,-C,-D, B, C,-D,-D], + [ A,-A, B,-A,-B,-D, D,-C,-B,-D,-C,-C], + [ A,-B,-A, A,-D, D,-B, B,-C,-D, C,-C], + [ B, A,-A,-A, D, D, D, C, C,-B,-B,-C], + [ B,-D, D, D, A, A, A, C,-C, B,-C, B], + [ B, C,-D, D, A,-A, C,-A,-D, C, B,-B], + [ D,-C, B,-B, A,-C,-A, A, B, C, D,-D], + [-C,-D,-C,-D, C, A,-A,-A,-D, B,-B,-B], + [ D,-C,-B,-B,-B, C, C,-D, A, A, A, D], + [-D,-B, C, C, C, B, B,-D, A,-A, D,-A], + [ C,-B,-C, C, D,-B,-D,-B, A,-D,-A, A], + [-C,-D,-D, C,-C,-B, B, B, D, A,-A,-A]]) + +def construction_four_symbol_delta_code_I(X, Y, Z, W): + r""" + Construct 4-symbol `\delta` code of length `2n+1`. + + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: + + .. MATH:: + + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + + where `N_A(s)` is the nonperiodic correlation function: + + .. MATH:: + + N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} + + The construction (detailed in [Tur1974]_) is as follows: + + .. MATH:: + + \begin{aligned} + T_1 &= X;Z \\ + T_2 &= X;-Z \\ + T_3 &= Y;W \\ + T_4 &= Y;-W + \end{aligned} + + INPUT: + + - ``X`` -- a list, representing the first sequence (length `n+1`). + + - ``Y`` -- a list, representing the second sequence (length `n+1`). + + - ``Z`` -- a list, representing the third sequence (length `n`). + + - ``W`` -- a list, representing the fourth sequence (length `n`). + + OUTPUT: + A tuple containing the 4-symbol `\delta` code of length `2n+1`. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_I + sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1], [1]) + ([1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]) + + TESTS:: + + sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1, 1], [1]) + Traceback (most recent call last): + ... + AssertionError + sage: construction_four_symbol_delta_code_I([1, 1], [1, 1], [-1], [1]) + Traceback (most recent call last): + ... + AssertionError + """ + n = len(X) + assert len(Y) == n and len(Z) == n-1 and len(W) == n-1 + + autocorrelation = lambda seq, j: sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) + for j in range(1, n): + assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + + T1 = X + Z + T2 = X + [-z for z in Z] + T3 = Y + W + T4 = Y + [-w for w in W] + return T1, T2, T3, T4 + + +def construction_four_symbol_delta_code_II(X, Y, Z, W): + r""" + Construct 4-symbol `\delta` code of length `4n+3`. + + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: + + .. MATH:: + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + + where `N_A(s)` is the nonperiodic correlation function: + + .. MATH:: + + N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} + + The construction (detailed in [Tur1974]_) is as follows (writing + `A/B` to mean `A` alternated with `B`): + + .. MATH:: + + \begin{aligned} + T_1 &= X/Z;Y/W;1 \\ + T_2 &= X/Z;Y/-W;-1 \\ + T_3 &= X/Z;-Y/-W;1 \\ + T_4 &= X/Z;-Y/W;-1 + \end{aligned} + + INPUT: + + - ``X`` -- a list, representing the first sequence (length `n+1`). + + - ``Y`` -- a list, representing the second sequence (length `n+1`). + + - ``Z`` -- a list, representing the third sequence (length `n`). + + - ``W`` -- a list, representing the fourth sequence (length `n`). + + OUTPUT: + A tuple containing the four 4-symbol `\delta` code of length `4n+3`. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_II + sage: construction_four_symbol_delta_code_II([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, -1, 1, 1, -1]) + + TESTS:: + + sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1, 1], [1]) + Traceback (most recent call last): + ... + AssertionError + sage: construction_four_symbol_delta_code_II([1, 1], [1, 1], [-1], [1, 1]) + Traceback (most recent call last): + ... + AssertionError + + """ + + n = len(Z) + assert len(X) == n+1 and len(Y) == n+1 and len(W) == n + + autocorrelation = lambda seq, j: sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) + for j in range(1, n): + assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + + def alternate(seq1, seq2): + return [seq1[i//2] if i%2 == 0 else seq2[(i-1)//2] for i in range(len(seq1)+len(seq2))] + + XaltZ = alternate(X, Z) + Wneg = [-w for w in W] + Yneg = [-y for y in Y] + + T1 = XaltZ + alternate(Y, W) + [1] + T2 = XaltZ + alternate(Y, Wneg) + [-1] + T3 = XaltZ + alternate(Yneg, Wneg) + [1] + T4 = XaltZ + alternate(Yneg, W) + [-1] + return T1, T2, T3, T4 + +def four_symbol_delta_code_smallcases(n, existence=False): + r""" + Return the 4-symobl `\delta` code of length `n` if available. + + The 4-symbol `\delta` codes are constructed using :func:`construction_four_symbol_delta_code_I` + or :func:`construction_four_symbol_delta_code_II`. + The base sequences used are taken from [Tur1974]_. + + INPUT: + + - ``n`` -- integer, the length of the desired 4-symbol `\delta` code. + + - ``existence`` -- boolean, if true only check if the sequences are available. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases + sage: four_symbol_delta_code_smallcases(3) + ([1, -1, 1], [1, -1, -1], [1, 1, 1], [1, 1, -1]) + sage: four_symbol_delta_code_smallcases(3, existence=True) + True + + TESTS:: + + sage: four_symbol_delta_code_smallcases(17) + Traceback (most recent call last): + ... + ValueError: The four-symbol delta code of length 17 have not yet been implemented + sage: four_symbol_delta_code_smallcases(17, existence=True) + False + """ + db = { + 1: ([1, -1], [1, 1], [1], [1]), + 14: ([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], + [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]) + } + + T1, T2, T3, T4 = None, None, None, None + if n%2 == 1 and (n-1)//2 in db: + if existence: + return True + X, Y, Z, W = db[(n-1)//2] + T1, T2, T3, T4 = construction_four_symbol_delta_code_I(X, Y, Z, W) + elif n%4 == 3 and (n-3)//4 in db: + if existence: + return True + X, Y, Z, W = db[(n-3)//4] + T1, T2, T3, T4 = construction_four_symbol_delta_code_II(X, Y, Z, W) + + if existence: + return False + + if T1 is None: + raise ValueError("The four-symbol delta code of length %s have not yet been implemented" % n) + + return T1, T2, T3, T4 + +def _construction_goethals_seidel_matrix(A, B ,C, D): + r""" + Construct the Goethals Seidel matrix. + + The matrix is described in [GS70s]_. Given matrices `A`, `B`, `C`, `D` + the construction is: + + .. MATH:: + + \left(\begin{array}{rrrr} + A & BR & CR & DR \\ + -BR & A & -D\top R & C\top R \\ + -CR & D\top R & A & -B\top R \\ + -DR & -C\top R & B\top R & A + \end{array}\right) + + Where `R` is the anti-diagonal matrix with all nonzero entries + equal to one. + + INPUT: + + - ``A`` -- The first matrix used in the construction. + + - ``B`` -- The second matrix used in the construction. + + - ``C`` -- The third matrix used in the construction. + + - ``D`` -- The fourth matrix used in the construction. + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import _construction_goethals_seidel_matrix + sage: A = matrix([[1, -1], [1, 1]]) + sage: B = matrix([[-1, -1], [-1, 1]]) + sage: C = matrix([[1, 1], [1, -1]]) + sage: D = matrix([[-1, 1], [-1, 1]]) + sage: _construction_goethals_seidel_matrix(A, B, C, D) + [ 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|-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| 1 -1| 1 1] + """ + n = len(A[0]) + R = matrix(ZZ, n, n, lambda i,j: 1 if i+j==n-1 else 0) + return block_matrix([[ A, B*R, C*R, D*R], + [-B*R, A, -D.T*R, C.T*R], + [-C*R, D.T*R, A, -B.T*R], + [-D*R, -C.T*R, B.T*R, A]]) + +def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): + r""" + Create an Hadamard matrix using the contruction detailed in [CW1972]_. + + Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) + such that the entrywise product of two distinct matrices is always equal to `0` and that + `\sum_{i=1}^{4}X_iX_i^\top = nI_n` holds, and four matrices `A, B, C, D` of order `m` with + elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` and + `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n` holds, we construct an Hadamard matrix + of order `4nm`. + + INPUT: + + - ``x1`` -- a list or vector, representing the first row of the circulant matrix `X_1`. + + - ``x2`` -- a list or vector, representing the first row of the circulant matrix `X_2`. + + - ``x3`` -- a list or vector, representing the first row of the circulant matrix `X_3`. + + - ``x4`` -- a list or vector, representing the first row of the circulant matrix `X_4`. + + - ``A`` -- the matrix described above. + + - ``B`` -- the matrix described above. + + - ``C`` -- the matrix described above. + + - ``D`` -- the matrix described above. + + - ``check`` -- a boolean, if true (default) check that the resulting matrix is Hadamard + before returing it. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction + sage: from sage.combinat.t_sequences import T_sequences_smallcases + sage: seqs = T_sequences_smallcases(19) + sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + 76 x 76 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction, is_hadamard_matrix + sage: seqs = T_sequences_smallcases(13) + sage: H = hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + sage: is_hadamard_matrix(H) + True + sage: len(H[0]) == 13*4*1 + True + sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1, -1]), matrix([1]), matrix([1])) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_cooper_wallis_construction([1,-1], [1, 1], [1,1], [1,1], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + Traceback (most recent call last): + ... + AssertionError + """ + + n = len(x1) + assert n == len(x2) == len(x3) == len(x4) + + X1, X2, X3, X4 = map(matrix.circulant, [x1, x2, x3, x4]) + + matrices = [X1, X2, X3, X4] + for i in range(4): + for j in range(i+1, 4): + assert matrices[i].elementwise_product(matrices[j]) == zero_matrix(n) + assert X1*X1.T + X2*X2.T + X3*X3.T + X4*X4.T == n*I(n) + + m = len(A[0]) + assert m == len(B[0]) == len(C[0]) == len(D[0]) + will_matrices = [A, B, C, D] + for i in range(4): + for j in range(i+1, 4): + assert will_matrices[i]*will_matrices[j].T == will_matrices[j]*will_matrices[i].T + assert A*A.T + B*B.T + C*C.T + D*D.T == 4*m*I(m) + + e1 = _construction_goethals_seidel_matrix(X1, X2, X3, X4) + e2 = _construction_goethals_seidel_matrix(X2, -X1, X4, -X3) + e3 = _construction_goethals_seidel_matrix(X3, -X4, -X1, X2) + e4 = _construction_goethals_seidel_matrix(X4, X3, -X2, -X1) + + H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D) + if check: + assert is_hadamard_matrix(H) + return H + +def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): + r""" + Construct Hadamard matrices using the Cooper-Wallis construction for some small values of `n`. + + This function calls the function :func:`hadamard_matrix_cooper_wallis_construction` + with the appropriate arguments. + It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either + T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.T_sequences_smallcases`. + The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`. + + Data for T-matrices of order 67 is taken from [Saw1985]_. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed. + + - ``check`` -- boolean: if True (default), check the the matrix is an Hadamard matrix before returning. + + - ``existence`` -- boolean (default False): if True, only check if matrix exists. + + OUTPUT: + + If ``existence`` is 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. + If ``existence`` is true, returns a boolean representing whether the matrix can be constructed or not. + + .. SEEALSO:: + + :func:`hadamard_matrix_cooper_wallis_construction` + + EXAMPLES: + + By default The function returns the Hadamard matrix :: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases + sage: hadamard_matrix_cooper_wallis_smallcases(28) + 28 x 28 dense matrix over Integer Ring... + + If ``existence`` is set to True, the function returns a boolean :: + + sage: hadamard_matrix_cooper_wallis_smallcases(20, existence=True) + True + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases, is_hadamard_matrix + sage: is_hadamard_matrix(hadamard_matrix_cooper_wallis_smallcases(188)) + True + sage: hadamard_matrix_cooper_wallis_smallcases(64, existence=True) + False + sage: hadamard_matrix_cooper_wallis_smallcases(64) + Traceback (most recent call last): + ... + ValueError: The Cooper-Wallis construction for Hadamard matrices of order 64 is not yet implemented. + sage: hadamard_matrix_cooper_wallis_smallcases(14) + Traceback (most recent call last): + ... + AssertionError + """ + assert n%4 == 0 and n > 0 + + db = { + 67: ( + [1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, -1, 0, 1, -1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 1, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -1, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, -1, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, -1, 1, 1, 0, 0, 0] + ) + } + + for T_seq_len in divisors(n//4): + will_size = n//(4* T_seq_len) + if (T_seq_len in db or T_sequences_smallcases(T_seq_len, existence=True)) and williamson_type_quadruples_smallcases(will_size, existence=True): + if existence: + return True + + e1, e2, e3, e4 = None, None, None, None + if T_seq_len in db: + e1, e2, e3, e4 = db[T_seq_len] + else: + e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False) + + will_matrices = williamson_type_quadruples_smallcases(will_size) + A, B, C, D = map(matrix.circulant, will_matrices) + M = hadamard_matrix_cooper_wallis_construction(e1, e2, e3, e4, A, B, C, D, check=False) + + if check: + assert is_hadamard_matrix(M) + return M + + if existence: + return False + raise ValueError("The Cooper-Wallis construction for Hadamard matrices of order %s is not yet implemented." % n) + +def _get_baumert_hall_units(n, existence=False): + r""" + Construct Baumert-Hall units of size `n` from available 4-symbol `\delta` codes. + + The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the + Goethals-Seidel construction of Hadamard matrices. + We need a 4-symbol `\delta` code to detail the first row of circulant matrices M1, M2, M3, M4 + used in the construction. + + INPUT: + + - ``n`` -- integer, the size of the Baumert-Hall units. + + - ``existence`` -- boolean (default False): if true only check whether the units can be contructed. + + OUTPUT: + + If ``existence`` is true, return a boolean representing whether the Baumert-Hall units can + be constructed. Otherwise, return a tuple containing the four Baumert-Hall units. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import _get_baumert_hall_units + sage: _get_baumert_hall_units(28) + (28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring) + + TESTS:: + + sage: _get_baumert_hall_units(116, existence=True) + True + sage: _get_baumert_hall_units(200, existence=True) + False + sage: _get_baumert_hall_units(15) + Traceback (most recent call last): + ... + AssertionError + sage: _get_baumert_hall_units(200) + Traceback (most recent call last): + ... + ValueError: The Baumert-Hall units of size 200 have not yet been implemented + """ + assert n%4 == 0 and n > 0 + + + delta_codes_len = n//4 + if not four_symbol_delta_code_smallcases(delta_codes_len, existence=True): + if existence: + return False + raise ValueError("The Baumert-Hall units of size %s have not yet been implemented" % n) + + if existence: + return True + + T1, T2, T3, T4 = four_symbol_delta_code_smallcases(delta_codes_len) + M1 = matrix.circulant(T1) + M2 = matrix.circulant(T2) + M3 = matrix.circulant(T3) + M4 = matrix.circulant(T4) + + M1hat = matrix(ZZ, 0.25*(M1+M2+M3+M4)) + M2hat = matrix(ZZ, 0.25*(M1-M2-M3+M4)) + M3hat = matrix(ZZ, 0.25*(M1+M2-M3-M4)) + M4hat = matrix(ZZ, 0.25*(M1-M2+M3-M4)) + + e1 = _construction_goethals_seidel_matrix(M1hat, -M2hat, -M3hat, -M4hat) + e2 = _construction_goethals_seidel_matrix(M2hat, M1hat, M4hat, -M3hat) + e3 = _construction_goethals_seidel_matrix(M3hat, -M4hat, M1hat, M2hat) + e4 = _construction_goethals_seidel_matrix(M4hat, M3hat, -M2hat, M1hat) + return e1, e2, e3, e4 + +def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): + r""" + Construction of Turyn type Hadamard matrix. + + Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, + satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of + Baumert-Hall units of order `4t`, one can construct a Hadamard matrix of order + `4tn` as detailed by Turyn in [Tur1974]_. + + INPUT: + + - ``a`` -- 1,-1 list specifying the 1st row of `A`. + + - ``b`` -- 1,-1 list specifying the 1st row of `B`. + + - ``d`` -- 1,-1 list specifying the 1st row of `C`. + + - ``c`` -- 1,-1 list specifying the 1st row of `D`. + + - ``e1`` -- Matrix representing the first Baumert-Hall unit. + + - ``e2`` -- Matrix representing the second Baumert-Hall unit. + + - ``e3`` -- Matrix representing the third Baumert-Hall unit. + + - ``e4`` -- Matrix representing the fourth Baumert-Hall unit. + + - ``check`` -- Whether to check that the output is an Hadamard matrix before returning it. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units + sage: A, B, C, D = _get_baumert_hall_units(28) + sage: hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D) + 28 x 28 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units, is_hadamard_matrix + sage: A, B, C, D = _get_baumert_hall_units(12) + sage: is_hadamard_matrix(hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D)) + True + sage: hadamard_matrix_turyn_type([1, -1], [1], [1], [1], A, B, C, D) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_turyn_type([1, -1], [1, 1], [1, 1], [1, 1], A, B, C, D) + Traceback (most recent call last): + ... + AssertionError + """ + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + n = len(a) + assert len(a) == len(b) == len(c) == len(d) + assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) + + t4 = len(e1[0]) + assert t4 %4 == 0 + t = t4//4 + + # Check that e1, e2, e3, e4 are valid Baumert-Hall units + for i in range(t4): + for j in range(t4): + assert abs(e1[i, j]) + abs(e2[i, j]) + abs(e3[i, j]) + abs(e4[i, j]) == 1 + + assert e1*e1.T == t*I(t4) and e2*e2.T == t*I(t4) and e3*e3.T == t*I(t4) and e4*e4.T == t*I(t4) + + units = [e1, e2, e3, e4] + for i in range(len(units)): + for j in range(i+1, len(units)): + assert units[i]*units[j].T + units[j]*units[i].T == 0*I(t4) + + + H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D) + if check: + assert is_hadamard_matrix(H) + return H + +def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): + r""" + Construct an Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples. + + The function looks for Baumert-Hall units and Williamson type matrices from + :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases` + and use them to construct an Hadamard matrix with the Turyn construction + defined in :func:`hadamard_matrix_turyn_type`. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed. + + - ``existence`` -- boolean (default False): if True, only check if matrix exists. + + - ``check`` -- bolean: if True (default), check the the matrix is an Hadamard matrix before returning. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases + sage: turyn_type_hadamard_matrix_smallcases(28, existence=True) + True + sage: turyn_type_hadamard_matrix_smallcases(28) + 28 x 28 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases, is_hadamard_matrix + sage: is_hadamard_matrix(turyn_type_hadamard_matrix_smallcases(236)) # long time + True + sage: turyn_type_hadamard_matrix_smallcases(64, existence=True) + False + sage: turyn_type_hadamard_matrix_smallcases(64) + Traceback (most recent call last): + ... + ValueError: The Turyn type construction for Hadamard matrices of order 64 is not yet implemented. + """ + assert n%4 == 0 and n > 0 + + for delta_code_len in divisors(n//4): + units_size = delta_code_len*4 + will_size = n//units_size + if _get_baumert_hall_units(units_size, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True): + if existence: + return True + + e1, e2, e3, e4 = _get_baumert_hall_units(units_size) + a, b, c, d = williamson_type_quadruples_smallcases(will_size) + return hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=check) + + if existence: + return False + raise ValueError("The Turyn type construction for Hadamard matrices of order %s is not yet implemented." % n) + +def hadamard_matrix_spence_construction(n, existence=False, check=True): + r"""Create an Hadamard matrix of order `n` using Spence construction. + + This construction (detailed in [Spe1975]_), uses supplementary difference sets implemented in + :func:`sage.combinat.designs.difference_family.supplementary_difference_set` to create the + desired matrix. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed. + + - ``existence`` -- boolean (default False): if True, only check if matrix exists. + + - ``check`` -- bolean: if True (default), check the the matrix is an Hadamard matrix before returning. + + OUTPUT: + + If ``existence`` is true, returns a boolean representing whether the Hadamard matrix can + be constructed. Otherwise, returns the Hadamard matrix, or raises an error if it cannot be constructed. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_spence_construction + sage: hadamard_matrix_spence_construction(36) + 36 x 36 dense matrix over Integer Ring... + + If ``existence`` is ``True``, the function returns a boolean :: + + sage: hadamard_matrix_spence_construction(52, existence=True) + True + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix + sage: is_hadamard_matrix(hadamard_matrix_spence_construction(100)) + True + sage: hadamard_matrix_spence_construction(48, existence=True) + False + sage: hadamard_matrix_spence_construction(48) + Traceback (most recent call last): + ... + ValueError: The order 48 is not covered by Spence construction. + sage: hadamard_matrix_spence_construction(5) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_spence_construction(0) + Traceback (most recent call last): + ... + AssertionError + """ + from sage.combinat.designs.difference_family import supplementary_difference_set + + assert n%4 == 0 and n > 0 + + q = n//4 + + if existence: + return supplementary_difference_set(q, existence=True) + + if not supplementary_difference_set(q, existence=True): + raise ValueError(f'The order {n} is not covered by Spence construction.') + + S1, S2, S3, S4 = supplementary_difference_set(q, check=False) + + A1 = matrix.circulant([1 if j in S1 else -1 for j in range(q-1)]) + A2 = matrix.circulant([1 if j in S4 else -1 for j in range(q-1)]) + A3 = matrix.circulant([1 if j in S3 else -1 for j in range(q-1)]) + A4 = matrix.circulant([1 if j in S2 else -1 for j in range(q-1)]) + + P = matrix(ZZ, [[1 if (i + j)%(q-1) == 0 else 0 for i in range(1, q)] for j in range(1, q)]) + + e = matrix([1]*(q-1)) + m1 = matrix([-1]) + p1 = matrix([1]) + H = block_matrix([[ p1, m1, p1, p1, e, e, e, e], + [ p1, p1, m1, p1, -e, e, -e, e], + [ m1, p1, p1, p1, -e, e, e, -e], + [ m1, m1, m1, p1, -e, -e, e, e], + [-e.T, e.T, e.T, -e.T, A1, A2*P, A3*P, A4*P], + [-e.T, -e.T, e.T, e.T, -A2*P, A1, -A4.T*P, A3.T*P], + [-e.T, -e.T, -e.T, -e.T, -A3*P, A4.T*P, A1, -A2.T*P], + [ e.T, -e.T, e.T, -e.T, -A4*P, -A3.T*P, A2.T*P, A1]]) + if check: + assert is_hadamard_matrix(H, verbose=True) + + return H + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" - Test if `M` is a hadamard matrix. + Test if `M` is a Hadamard matrix. INPUT: @@ -331,8 +1293,7 @@ def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): @matrix_method def hadamard_matrix(n,existence=False, check=True): r""" - Tries to construct a Hadamard matrix using a combination of Paley - and Sylvester constructions. + Tries to construct a Hadamard matrix using the available methods. INPUT: @@ -405,12 +1366,18 @@ def hadamard_matrix(n,existence=False, check=True): False sage: matrix.hadamard(12,existence=True) True - sage: matrix.hadamard(92,existence=True) + sage: matrix.hadamard(668,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): ... ValueError: The Hadamard matrix of order 10 does not exist + sage: matrix.hadamard(312, existence=True) + True + sage: matrix.hadamard(1904, existence=True) + True + sage: matrix.hadamard(324, existence=True) + True """ if not(n % 4 == 0) and (n > 2): if existence: @@ -428,9 +1395,9 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_paleyII(n) - elif n == 4 or n % 8 == 0: + elif n == 4 or n % 8 == 0 and hadamard_matrix(n//2,existence=True) is True: if existence: - return hadamard_matrix(n//2,existence=True) + return True had = hadamard_matrix(n//2,check=False) chad1 = matrix([list(r) + list(r) for r in had.rows()]) mhad = (-1) * had @@ -442,19 +1409,47 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_paleyI(n) + elif williamson_hadamard_matrix_smallcases(n, existence=True): + if existence: + return True + M = williamson_hadamard_matrix_smallcases(n, check=False) + elif n == 156: + if existence: + return True + M = hadamard_matrix_156() + elif hadamard_matrix_cooper_wallis_smallcases(n, existence=True): + if existence: + return True + M = hadamard_matrix_cooper_wallis_smallcases(n, check=False) + elif turyn_type_hadamard_matrix_smallcases(n, existence=True): + if existence: + return True + M = turyn_type_hadamard_matrix_smallcases(n, check=False) + elif hadamard_matrix_spence_construction(n ,existence=True): + if existence: + return True + M = hadamard_matrix_spence_construction(n, check=False) + elif skew_hadamard_matrix(n, existence=True) is True: + if existence: + return True + M = skew_hadamard_matrix(n, check=False) + elif regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1, existence=True) is True: + if existence: + return True + M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1) else: if existence: return Unknown raise ValueError("The Hadamard matrix of order %s is not yet implemented." % n) if check: - assert is_hadamard_matrix(M, normalized=True) + assert is_hadamard_matrix(M) return M def hadamard_matrix_www(url_file, comments=False): - """ + r""" Pull file from Sloane's database and return the corresponding Hadamard matrix as a Sage matrix. @@ -631,21 +1626,21 @@ def true(): if existence: return true() M = RSHCD_324(e) - elif ( e == 1 and - n%16 == 0 and - sqn is not None and - is_prime_power(sqn-1) and - is_prime_power(sqn+1)): + elif (e == 1 and + n % 16 == 0 and + sqn is not None and + is_prime_power(sqn - 1) and + is_prime_power(sqn + 1)): if existence: return true() M = -rshcd_from_close_prime_powers(sqn) - elif ( e == 1 and - sqn is not None and - sqn%4 == 2 and + elif (e == 1 and + sqn is not None and + sqn % 4 == 2 and strongly_regular_graph(sqn-1,(sqn-2)//2,(sqn-6)//4, existence=True) is True and - is_prime_power(ZZ(sqn+1))): + is_prime_power(ZZ(sqn + 1))): if existence: return true() M = rshcd_from_prime_power_and_conference_matrix(sqn+1) @@ -907,29 +1902,19 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): REFERENCES: - .. [GS70s] \J.M. Goethals and J. J. Seidel, - *A skew Hadamard matrix of order 36*, - J. Aust. Math. Soc. 11(1970), 343-344 + - [GS70s]_ - .. [Wall71] \J. Wallis, - *A skew-Hadamard matrix of order 92*, - Bull. Aust. Math. Soc. 5(1971), 203-204 + - [Wall71]_ - .. [KoSt08] \C. Koukouvinos, S. Stylianou - *On skew-Hadamard matrices*, - Discrete Math. 308(2008) 2723-2731 + - [KoSt08]_ """ n = len(a) - R = matrix(ZZ, n, n, lambda i,j: 1 if i+j==n-1 else 0) A,B,C,D=map(matrix.circulant, [a,b,c,d]) if check: assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) assert A+A.T==2*I(n) - M = block_matrix([[ A, B*R, C*R, D*R], - [-B*R, A, -D.T*R, C.T*R], - [-C*R, D.T*R, A, -B.T*R], - [-D*R, -C.T*R, B.T*R, A]]) + M = _construction_goethals_seidel_matrix(A, B, C, D) if check: assert is_hadamard_matrix(M, normalized=False, skew=True) return M @@ -944,6 +1929,10 @@ def GS_skew_hadamard_smallcases(n, existence=False, check=True): Matrices for `n=36` and `52` are given in [GS70s]_. Matrices for `n=92` are given in [Wall71]_. + Additional data is obtained from skew supplementary difference sets contained in + :func:`sage.combinat.designs.difference_family.skew_supplementary_difference_set`, using the + construction described in [Djo1992]_. + INPUT: - ``n`` -- the order of the matrix @@ -969,7 +1958,7 @@ def pmtoZ(s): return [1 if x == '+' else -1 for x in s] if existence: - return n in [36, 52, 92] + return n in [36, 52, 92] or skew_supplementary_difference_set(n//4, existence=True) if n == 36: a = [ 1, 1, 1, -1, 1, -1, 1, -1, -1] @@ -990,6 +1979,16 @@ def pmtoZ(s): c = [1, 1,-1,-1,-1, 1,-1, 1,-1, 1,-1, 1, 1,-1, 1,-1, 1,-1, 1,-1,-1,-1, 1] d = [1,-1,-1,-1,-1, 1,-1,-1, 1,-1,-1, 1, 1,-1,-1, 1,-1,-1, 1,-1,-1,-1,-1] return WGS(a, b, c, d, check=check) + + if skew_supplementary_difference_set(n//4, existence=True): + t = n//4 + S1, S2, S3, S4 = skew_supplementary_difference_set(t, check=False) + a = [-1 if i in S1 else 1 for i in range(t)] + b = [-1 if i in S2 else 1 for i in range(t)] + c = [-1 if i in S3 else 1 for i in range(t)] + d = [-1 if i in S4 else 1 for i in range(t)] + return WGS(a, b, c, d, check=check) + return None _skew_had_cache={} @@ -1076,10 +2075,7 @@ def skew_hadamard_matrix(n,existence=False, skew_normalize=True, check=True): REFERENCES: - .. [Ha83] \M. Hall, - Combinatorial Theory, - 2nd edition, - Wiley, 1983 + - [Ha83]_ """ if n < 1: raise ValueError("parameter n must be strictly positive") @@ -1146,7 +2142,6 @@ def true(): if check: assert is_hadamard_matrix(M, normalized=False, skew=True) if skew_normalize: - from sage.modules.free_module_element import vector assert M[0]==vector([1]*n) _skew_had_cache[n]=True return M diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 3274ed077e2..82e6ff65311 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -554,16 +554,18 @@ def apply_isotopism(self, row_perm, col_perm, sym_perm): def filled_cells_map(self): """ - Number the filled cells of self with integers from {1, 2, 3, ...} + Number the filled cells of self with integers from {1, 2, 3, ...}. INPUT: - - ``self`` - Partial latin square self (empty cells - have negative values) + - ``self`` -- partial latin square self (empty cells + have negative values) - OUTPUT: A dictionary cells_map where cells_map[(i,j)] = m means - that (i,j) is the m-th filled cell in P, while cells_map[m] = - (i,j). + OUTPUT: + + A dictionary ``cells_map`` where ``cells_map[(i,j)] = m`` means that + ``(i,j)`` is the ``m``-th filled cell in ``P``, + while ``cells_map[m] = (i,j)``. EXAMPLES:: diff --git a/src/sage/combinat/misc.py b/src/sage/combinat/misc.py index d29b31a101f..8034531ed19 100644 --- a/src/sage/combinat/misc.py +++ b/src/sage/combinat/misc.py @@ -18,6 +18,7 @@ from sage.misc.misc_c import prod + class DoublyLinkedList(): """ A doubly linked list class that provides constant time hiding and @@ -38,6 +39,7 @@ class DoublyLinkedList(): sage: dll.unhide(2); dll Doubly linked list of [1, 2, 3]: [1, 2, 3] """ + def __init__(self, l): """ TESTS:: @@ -177,7 +179,6 @@ def prev(self, j): return self.prev_value[j] - def _monomial_exponent_to_lower_factorial(me, x): r""" Converts a tuple of exponents to the monomial obtained by replacing @@ -207,6 +208,7 @@ def _monomial_exponent_to_lower_factorial(me, x): terms.append( x[i]-j ) return prod(terms) + def umbral_operation(poly): r""" Returns the umbral operation `\downarrow` applied to poly. @@ -272,6 +274,7 @@ class IterableFunctionCall: bbb foo """ + def __init__(self, f, *args, **kwargs): """ EXAMPLES:: @@ -384,10 +387,10 @@ def check_integer_list_constraints(l, **kwargs): filters['max_part'] = lambda x: max(x) <= max_part filters['min_length'] = lambda x: len(x) >= min_length filters['max_length'] = lambda x: len(x) <= max_length - filters['min_slope'] = lambda x: min([x[i+1]-x[i] for i in range(len(x)-1)]+[min_slope+1]) >= min_slope - filters['max_slope'] = lambda x: max([x[i+1]-x[i] for i in range(len(x)-1)]+[max_slope-1]) <= max_slope - filters['outer'] = lambda x: len(outer) >= len(x) and min([outer[i]-x[i] for i in range(len(x))]) >= 0 - filters['inner'] = lambda x: len(x) >= len(inner) and max([inner[i]-x[i] for i in range(len(inner))]) <= 0 + filters['min_slope'] = lambda x: min((x[i + 1] - x[i] for i in range(len(x) - 1)), default=min_slope + 1) >= min_slope + filters['max_slope'] = lambda x: max((x[i + 1] - x[i] for i in range(len(x) - 1)), default=max_slope - 1) <= max_slope + filters['outer'] = lambda x: len(outer) >= len(x) and min(outer[i] - x[i] for i in range(len(x))) >= 0 + filters['inner'] = lambda x: len(x) >= len(inner) and max(inner[i] - x[i] for i in range(len(inner))) <= 0 for key in kwargs: result = [x for x in result if filters[key](x)] diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 8963ab00f0d..6f5495f36e9 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -1098,6 +1098,7 @@ def shuffle_product(self, other, overlap=False): ############################################################## + class OrderedMultisetPartitionsIntoSets(UniqueRepresentation, Parent): r""" Ordered Multiset Partitions into Sets. @@ -1961,6 +1962,7 @@ def subset(self, size): ############### + class OrderedMultisetPartitionsIntoSets_all_constraints(OrderedMultisetPartitionsIntoSets): r""" All ordered multiset partitions into sets (with or without constraints). @@ -1996,6 +1998,7 @@ class OrderedMultisetPartitionsIntoSets_all_constraints(OrderedMultisetPartition sage: Set(C) == Set(E) False """ + def _repr_(self): """ Return a string representation of ``self``. @@ -2012,10 +2015,12 @@ def _repr_(self): ############### + class OrderedMultisetPartitionsIntoSets_n(OrderedMultisetPartitionsIntoSets): """ Ordered multiset partitions into sets of a fixed integer `n`. """ + def __init__(self, n): """ Initialize ``self``. @@ -2144,11 +2149,13 @@ def __iter__(self): for co in _iterator_size(self._n): yield self.element_class(self, co) + class OrderedMultisetPartitionsIntoSets_n_constraints(OrderedMultisetPartitionsIntoSets): """ Class of ordered multiset partitions into sets of a fixed integer `n` satisfying constraints. """ + def __init__(self, n, **constraints): """ Mimic class ``OrderedMultisetPartitionsIntoSets_n`` to initialize. @@ -2182,10 +2189,12 @@ def _repr_(self): ############### + class OrderedMultisetPartitionsIntoSets_X(OrderedMultisetPartitionsIntoSets): """ Class of ordered multiset partitions into sets of a fixed multiset `X`. """ + def __init__(self, X): """ Initialize ``self``. @@ -2356,6 +2365,7 @@ class OrderedMultisetPartitionsIntoSets_X_constraints(OrderedMultisetPartitionsI Class of ordered multiset partitions into sets of a fixed multiset `X` satisfying constraints. """ + def __init__(self, X, **constraints): """ Mimic class ``OrderedMultisetPartitionsIntoSets_X`` to initialize. @@ -2391,11 +2401,13 @@ def _repr_(self): ############### + class OrderedMultisetPartitionsIntoSets_alph_d(OrderedMultisetPartitionsIntoSets): """ Class of ordered multiset partitions into sets of specified order `d` over a fixed alphabet `A`. """ + def __init__(self, A, d): """ Initialize ``self``. @@ -2521,11 +2533,13 @@ def cardinality(self): deg += prod(binomial(len(self._alphabet), a) for a in alpha) return ZZ(deg) + class OrderedMultisetPartitionsIntoSets_alph_d_constraints(OrderedMultisetPartitionsIntoSets): """ Class of ordered multiset partitions into sets of specified order `d` over a fixed alphabet `A` satisfying constraints. """ + def __init__(self, A, d, **constraints): """ Mimic class ``OrderedMultisetPartitionsIntoSets_alph_d`` to initialize. @@ -2574,6 +2588,7 @@ def _repr_(self): ############### + def _get_multiset(co): """ Construct the multiset (as a sorted tuple) suggested by the lists @@ -2588,6 +2603,7 @@ def _get_multiset(co): """ return tuple(sorted(_concatenate(co), key=str)) + def _get_weight(lst): """ Construct the multiset (as a dictionary) suggested by the @@ -2605,6 +2621,7 @@ def _get_weight(lst): out[k] = out.get(k,0) + 1 return out + def _has_nonempty_sets(x): """ Blocks should be nonempty sets/lists/tuples of distinct elements. @@ -2652,6 +2669,7 @@ def _concatenate(list_of_iters): """ return tuple([val for block in list_of_iters for val in block]) + def _is_finite(constraints): """ Return ``True`` if the dictionary ``constraints`` corresponds to @@ -2682,6 +2700,7 @@ def _is_finite(constraints): Bounds = set(["length", "max_length", "order", "max_order"]) return Bounds.intersection(set(constraints)) != set() + def _base_iterator(constraints): """ Return a base iterator for ordered multiset partitions into sets or ``None``. @@ -2951,6 +2970,7 @@ def _iterator_order(A, d, lengths=None): for co in cartesian_product([Subsets_sk(A, a) for a in alpha]): yield tuple(frozenset(X) for X in co) + def _descents(w): r""" Return descent positions in the word ``w``. @@ -2965,6 +2985,7 @@ def _descents(w): """ return [j for j in range(len(w)-1) if w[j] > w[j+1]] + def _break_at_descents(alpha, weak=True): r""" Return the deconcatenation of the composition ``alpha`` at its @@ -3006,6 +3027,7 @@ def _break_at_descents(alpha, weak=True): Blocks.append(block) return Blocks + def _refine_block(S, strong=False): r""" Return the list of all possible refinements of a set `S`. @@ -3065,6 +3087,7 @@ def _refine_block(S, strong=False): out.append(tuple(a)) return out + def _is_initial_segment(lst): r""" Return True if ``lst`` is an interval in `\ZZ` of the form `[0, 1, \ldots, n]`. @@ -3081,6 +3104,7 @@ def _is_initial_segment(lst): """ return list(range(max(lst)+1)) == lst + def _split_block(S, k=2): """ Return the list of all possible splittings of a set `S` into `k` parts. @@ -3117,6 +3141,7 @@ def _split_block(S, k=2): out.append(tuple(a)) return out + def _to_minimaj_blocks(T): r""" Return a tuple of tuples, representing an ordered multiset partition into sets @@ -3184,6 +3209,7 @@ class MinimajCrystal(UniqueRepresentation, Parent): ((2, 3, 1), (1, 3)) sage: b.e(2) """ + def __init__(self, n, ell, k): """ Initialize ``self``. @@ -3395,6 +3421,7 @@ class Element(ElementWrapper): The pair ``(w, breaks)`` may be recovered via ``b.value``. """ + def _repr_(self): """ Return the string representation of ``self``. diff --git a/src/sage/combinat/ncsf_qsym/combinatorics.py b/src/sage/combinat/ncsf_qsym/combinatorics.py index a0f8825ae3e..cddfdc1ef2c 100644 --- a/src/sage/combinat/ncsf_qsym/combinatorics.py +++ b/src/sage/combinat/ncsf_qsym/combinatorics.py @@ -52,6 +52,7 @@ def coeff_pi(J, I): """ return prod(prod(K.partial_sums()) for K in J.refinement_splitting(I)) + def coeff_lp(J,I): r""" Returns the coefficient `lp_{J,I}` as defined in [NCSF]_. @@ -75,6 +76,7 @@ def coeff_lp(J,I): """ return prod(K[-1] for K in J.refinement_splitting(I)) + def coeff_ell(J,I): r""" Returns the coefficient `\ell_{J,I}` as defined in [NCSF]_. @@ -122,6 +124,7 @@ def coeff_sp(J, I): """ return prod(factorial(len(K))*prod(K) for K in J.refinement_splitting(I)) + def coeff_dab(I, J): r""" Return the number of standard composition tableaux of shape `I` with @@ -149,6 +152,7 @@ def coeff_dab(I, J): d += 1 return d + def compositions_order(n): r""" Return the compositions of `n` ordered as defined in [QSCHUR]_. @@ -180,6 +184,7 @@ def _keyfunction(I): return sorted(I, reverse=True), list(I) return sorted(Compositions(n), key=_keyfunction, reverse=True) + def m_to_s_stat(R, I, K): r""" Return the coefficient of the complete non-commutative symmetric @@ -211,12 +216,13 @@ def m_to_s_stat(R, I, K): """ stat = 0 for J in Compositions(I.size()): - if (I.is_finer(J) and K.is_finer(J)): + if I.is_finer(J) and K.is_finer(J): pvec = [0] + Composition(I).refinement_splitting_lengths(J).partial_sums() pp = prod( R( len(I) - pvec[i] ) for i in range( len(pvec)-1 ) ) - stat += R((-1)**(len(I)-len(K)) / pp * coeff_lp(K,J)) + stat += R((-1)**(len(I)-len(K)) / pp * coeff_lp(K, J)) return stat + @cached_function def number_of_fCT(content_comp, shape_comp): r""" @@ -256,6 +262,7 @@ def number_of_fCT(content_comp, shape_comp): s += number_of_fCT(Composition(content_comp[:-1]),x) return s + @cached_function def number_of_SSRCT(content_comp, shape_comp): r""" diff --git a/src/sage/combinat/ncsf_qsym/generic_basis_code.py b/src/sage/combinat/ncsf_qsym/generic_basis_code.py index 87364ac04c6..8763a8e1bee 100644 --- a/src/sage/combinat/ncsf_qsym/generic_basis_code.py +++ b/src/sage/combinat/ncsf_qsym/generic_basis_code.py @@ -40,6 +40,7 @@ from sage.categories.category_types import Category_over_base_ring from sage.categories.realizations import RealizationsCategory + class BasesOfQSymOrNCSF(Category_realization_of_parent): def _repr_object_names(self): @@ -993,6 +994,7 @@ class AlgebraMorphism(ModuleMorphismByLinearity): # Find a better name """ A class for algebra morphism defined on a free algebra from the image of the generators """ + def __init__(self, domain, on_generators, position = 0, codomain = None, category = None, anti = False): """ Given a map on the multiplicative basis of a free algebra, this method @@ -1076,12 +1078,15 @@ def __init__(self, domain, on_generators, position = 0, codomain = None, categor assert codomain is not None if category is None: if anti: - category = ModulesWithBasis (domain.base_ring()) + category = ModulesWithBasis(domain.base_ring()) else: category = AlgebrasWithBasis(domain.base_ring()) self._anti = anti self._on_generators = on_generators - ModuleMorphismByLinearity.__init__(self, domain = domain, codomain = codomain, position = position, category = category) + ModuleMorphismByLinearity.__init__(self, domain=domain, + codomain=codomain, + position=position, + category=category) def __eq__(self, other): """ @@ -1151,6 +1156,7 @@ def _on_basis(self, c): c = reversed(c) return self.codomain().prod(self._on_generators(i) for i in c) + class GradedModulesWithInternalProduct(Category_over_base_ring): r""" Constructs the class of modules with internal product. This is used to give an internal diff --git a/src/sage/combinat/ncsf_qsym/ncsf.py b/src/sage/combinat/ncsf_qsym/ncsf.py index 7a53a902b94..f1fd9622bae 100644 --- a/src/sage/combinat/ncsf_qsym/ncsf.py +++ b/src/sage/combinat/ncsf_qsym/ncsf.py @@ -400,6 +400,7 @@ class NonCommutativeSymmetricFunctions(UniqueRepresentation, Parent): sage: TestSuite(complete).run() """ + def __init__(self, R): r""" TESTS:: @@ -502,6 +503,7 @@ class Bases(Category_realization_of_parent): sage: R in N.Bases() True """ + def super_categories(self): r""" Return the super categories of the category of bases of the @@ -2017,6 +2019,7 @@ class MultiplicativeBases(Category_realization_of_parent): sage: N.Ribbon() in N.MultiplicativeBases() False """ + def super_categories(self): r""" Return the super categories of the category of multiplicative @@ -2290,6 +2293,7 @@ class MultiplicativeBasesOnGroupLikeElements(Category_realization_of_parent): sage: N.Ribbon() in N.MultiplicativeBasesOnGroupLikeElements() False """ + def super_categories(self): r""" Return the super categories of the category of multiplicative @@ -2535,6 +2539,7 @@ class Ribbon(CombinatorialFreeModule, BindableClass): sage: NCSF.R() Non-Commutative Symmetric Functions over the Rational Field in the Ribbon basis """ + def __init__(self, NCSF): r""" EXAMPLES:: @@ -2960,6 +2965,7 @@ class Complete(CombinatorialFreeModule, BindableClass): sage: NCSF.S() Non-Commutative Symmetric Functions over the Rational Field in the Complete basis """ + def __init__(self, NCSF): r""" EXAMPLES:: @@ -3119,6 +3125,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the Complete basis. """ + def psi_involution(self): r""" Return the image of the noncommutative symmetric function @@ -3227,6 +3234,7 @@ class Elementary(CombinatorialFreeModule, BindableClass): sage: NCSF.L() Non-Commutative Symmetric Functions over the Rational Field in the Elementary basis """ + def __init__(self, NCSF): r""" EXAMPLES:: @@ -3642,6 +3650,7 @@ class Psi(CombinatorialFreeModule, BindableClass): sage: test_psi(4) True """ + def __init__(self, NCSF): r""" TESTS: @@ -4087,6 +4096,7 @@ class Phi(CombinatorialFreeModule, BindableClass): sage: Phi.an_element() 2*Phi[] + 2*Phi[1] + 3*Phi[1, 1] """ + def __init__(self, NCSF): r""" TESTS: @@ -4811,6 +4821,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the Immaculate basis. """ + def bernstein_creation_operator(self, n): r""" Return the image of ``self`` under the `n`-th Bernstein @@ -5351,6 +5362,7 @@ class Zassenhaus_left(CombinatorialFreeModule, BindableClass): for all `n \geq 0`. """ + def __init__(self, NCSF): r""" EXAMPLES:: @@ -5526,6 +5538,7 @@ class Zassenhaus_right(CombinatorialFreeModule, BindableClass): \sigma_1 = exp(Z_1) exp(Z_2/2) exp(Z_3/3) \cdots exp(Z_n/n) \cdots. """ + def __init__(self, NCSF): r""" EXAMPLES:: diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 086b68c6175..08633d12c4e 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -542,6 +542,7 @@ class QuasiSymmetricFunctions(UniqueRepresentation, Parent): sage: algebras.QSym(QQ) is QSym True """ + def __init__(self, R): """ The Hopf algebra of quasi-symmetric functions. @@ -720,6 +721,7 @@ class Bases(Category_realization_of_parent): sage: QSym.Bases() Category of bases of Quasisymmetric functions over the Rational Field """ + def super_categories(self): r""" Return the super categories of bases of the Quasi-symmetric functions. @@ -1698,6 +1700,7 @@ class Monomial(CombinatorialFreeModule, BindableClass): sage: M(m([])) M[] """ + def __init__(self, QSym): """ EXAMPLES:: @@ -2184,6 +2187,7 @@ class Fundamental(CombinatorialFreeModule, BindableClass): sage: F(s(0)) 0 """ + def __init__(self, QSym): """ EXAMPLES:: @@ -2196,7 +2200,6 @@ def __init__(self, QSym): prefix='F', bracket=False, category=QSym.Bases()) - def _from_schur_on_basis(self, la): r""" Maps the Schur symmetric function indexed by ``la`` to the @@ -2666,6 +2669,7 @@ class Essential(CombinatorialFreeModule, BindableClass): sage: E(s(0)) 0 """ + def __init__(self, QSym): """ EXAMPLES:: @@ -2832,6 +2836,7 @@ class Quasisymmetric_Schur(CombinatorialFreeModule, BindableClass): sage: QS(s[2,1,1]) QS[1, 1, 2] + QS[1, 2, 1] + QS[2, 1, 1] """ + def __init__(self, QSym): r""" EXAMPLES:: @@ -3017,6 +3022,7 @@ class Young_Quasisymmetric_Schur(CombinatorialFreeModule, BindableClass): sage: YQS(s[2,1,1]) YQS[1, 1, 2] + YQS[1, 2, 1] + YQS[2, 1, 1] """ + def __init__(self, QSym): r""" Initialize ``self``. @@ -3047,7 +3053,6 @@ def __init__(self, QSym): self._M.module_morphism(self._from_monomial_on_basis, codomain=self, category=QSym.Bases()).register_as_coercion() - def _realization_name(self): r""" Return a nicer name for ``self`` than what is inherited @@ -3854,6 +3859,7 @@ class psi(CombinatorialFreeModule, BindableClass): sage: all(test_psi(k) for k in range(1,5)) True """ + def __init__(self, QSym): r""" Initialize ``self``. @@ -3996,6 +4002,7 @@ class phi(CombinatorialFreeModule, BindableClass): sage: all(test_phi(k) for k in range(1,5)) True """ + def __init__(self, QSym): r""" Initialize ``self``. diff --git a/src/sage/combinat/ncsym/bases.py b/src/sage/combinat/ncsym/bases.py index 6d8b1b86185..25f3bece38e 100644 --- a/src/sage/combinat/ncsym/bases.py +++ b/src/sage/combinat/ncsym/bases.py @@ -27,6 +27,7 @@ class NCSymBasis_abstract(CombinatorialFreeModule, BindableClass): """ Abstract base class for a basis of `NCSym` or its dual. """ + def _element_constructor_(self, x): """ Construct an element of ``self``. @@ -53,6 +54,7 @@ class NCSymOrNCSymDualBases(Category_realization_of_parent): Base category for the category of bases of symmetric functions in non-commuting variables or its Hopf dual for the common code. """ + def super_categories(self): r""" Return the super categories of bases of (the Hopf dual of) the @@ -340,6 +342,7 @@ class NCSymBases(Category_realization_of_parent): sage: NCSymBases(NCSym) Category of bases of symmetric functions in non-commuting variables over the Rational Field """ + def super_categories(self): r""" Return the super categories of bases of the Hopf dual of the @@ -741,6 +744,7 @@ class MultiplicativeNCSymBases(Category_realization_of_parent): sage: MultiplicativeNCSymBases(NCSym) Category of multiplicative bases of symmetric functions in non-commuting variables over the Rational Field """ + def super_categories(self): r""" Return the super categories of bases of the Hopf dual of the @@ -845,6 +849,7 @@ class NCSymDualBases(Category_realization_of_parent): sage: NCSymDualBases(DNCSym) Category of bases of dual symmetric functions in non-commuting variables over the Rational Field """ + def super_categories(self): r""" Return the super categories of bases of the Hopf dual of the diff --git a/src/sage/combinat/ncsym/dual.py b/src/sage/combinat/ncsym/dual.py index 965068ef0d8..5acd9c8f176 100644 --- a/src/sage/combinat/ncsym/dual.py +++ b/src/sage/combinat/ncsym/dual.py @@ -36,6 +36,7 @@ class SymmetricFunctionsNonCommutingVariablesDual(UniqueRepresentation, Parent): See Section 2.3 of [BZ05]_ for a study. """ + def __init__(self, R): """ Initialize ``self``. @@ -123,6 +124,7 @@ class w(NCSymBasis_abstract): sage: h(elt) 3*h[2, 1] """ + def __init__(self, NCSymD): """ EXAMPLES:: @@ -425,6 +427,7 @@ class Element(CombinatorialFreeModule.Element): r""" An element in the `\mathbf{w}` basis. """ + def expand(self, n, letter='x'): r""" Expand ``self`` written in the `\mathbf{w}` basis in `n^2` diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 4aa258feaad..551ca834b1d 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -32,6 +32,7 @@ from sage.rings.integer_ring import ZZ from functools import reduce + def matchings(A, B): """ Iterate through all matchings of the sets `A` and `B`. @@ -77,6 +78,7 @@ def matchings(A, B): for m in matchings(rem_A, rem_B): yield [[a, b]] + m + def nesting(la, nu): r""" Return the nesting number of ``la`` inside of ``nu``. @@ -140,6 +142,7 @@ def nesting(la, nu): nst += 1 return nst + class SymmetricFunctionsNonCommutingVariables(UniqueRepresentation, Parent): r""" Symmetric functions in non-commutative variables. @@ -279,6 +282,7 @@ class SymmetricFunctionsNonCommutingVariables(UniqueRepresentation, Parent): sage: Sp(e(eltp).to_symmetric_function()) -4*p[] + 2*p[1] + p[2, 2] """ + def __init__(self, R): """ Initialize ``self``. @@ -349,6 +353,7 @@ class monomial(NCSymBasis_abstract): m{} # m{{1, 3}, {2}} + m{{1}} # m{{1, 2}} + m{{1, 2}} # m{{1}} + m{{1, 3}, {2}} # m{} """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -762,6 +767,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the monomial basis of `NCSym`. """ + def expand(self, n, alphabet='x'): r""" Expand ``self`` written in the monomial basis in `n` @@ -854,6 +860,7 @@ class elementary(NCSymBasis_abstract): sage: NCSym = SymmetricFunctionsNonCommutingVariables(QQ) sage: e = NCSym.e() """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -971,6 +978,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the elementary basis of `NCSym`. """ + def omega(self): r""" Return the involution `\omega` applied to ``self``. @@ -1049,6 +1057,7 @@ class homogeneous(NCSymBasis_abstract): sage: h[[1,2]].coproduct() h{} # h{{1, 2}} + 2*h{{1}} # h{{1}} + h{{1, 2}} # h{} """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -1154,6 +1163,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the homogeneous basis of `NCSym`. """ + def omega(self): r""" Return the involution `\omega` applied to ``self``. @@ -1245,6 +1255,7 @@ class powersum(NCSymBasis_abstract): sage: x.to_symmetric_function() 4*p[] + 8*p[1] + 4*p[1, 1] + 12*p[2] + 12*p[2, 1] + 9*p[2, 2] """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -1578,6 +1589,7 @@ class Element(CombinatorialFreeModule.Element): """ An element in the powersum basis of `NCSym`. """ + def to_symmetric_function(self): r""" The projection of ``self`` to the symmetric functions. @@ -1656,6 +1668,7 @@ class coarse_powersum(NCSymBasis_abstract): sage: ps(cp[[1,2],[3]].to_symmetric_function()) p[2, 1] """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -1730,6 +1743,7 @@ class x_basis(NCSymBasis_abstract): x{{1}, {2}, {3}} # x{{1, 2}, {3}} + x{{1, 2}, {3}} # x{{1}, {2}, {3}} + x{{1, 2}, {3}} # x{{1, 2}, {3}} """ + def __init__(self, NCSym): """ EXAMPLES:: @@ -1829,6 +1843,7 @@ class deformed_coarse_powersum(NCSymBasis_abstract): [ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1/q] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1] """ + def __init__(self, NCSym, q=2): """ EXAMPLES:: @@ -1974,6 +1989,7 @@ class supercharacter(NCSymBasis_abstract): of Univariate Polynomial Ring in q over Rational Field in the supercharacter basis with parameter q=2 """ + def __init__(self, NCSym, q=2): """ EXAMPLES:: diff --git a/src/sage/combinat/non_decreasing_parking_function.py b/src/sage/combinat/non_decreasing_parking_function.py index b5ecf2bb8c0..9b04286372c 100644 --- a/src/sage/combinat/non_decreasing_parking_function.py +++ b/src/sage/combinat/non_decreasing_parking_function.py @@ -38,7 +38,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from sage.categories.monoids import Monoids -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer from sage.structure.element import Element from sage.structure.parent import Parent @@ -173,6 +173,7 @@ class NonDecreasingParkingFunction(Element): ... ValueError: [1, 1, 4] is not a non-decreasing parking function """ + def __init__(self, lst): """ TESTS:: @@ -466,6 +467,7 @@ class NonDecreasingParkingFunctions_n(UniqueRepresentation, Parent): - Florent Hivert """ + def __init__(self, n): """ TESTS:: diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index 8157b4a95a3..fce1668bd22 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -1013,6 +1013,7 @@ class OrderedTrees_size(OrderedTrees): sage: S.list() [[[], []], [[[]]]] """ + def __init__(self, size): """ TESTS:: @@ -1348,6 +1349,7 @@ class LabelledOrderedTrees(UniqueRepresentation, Parent): sage: y.parent() is LOT True """ + def __init__(self, category=None): """ TESTS:: diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index d153149c48b..2e2a8aa5f39 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -22,6 +22,7 @@ # When using bar should be replaced by '|' or ''. lr_macro = Template(r'\def\lr#1{\multicolumn{1}{$bar@{\hspace{.6ex}}c@{\hspace{.6ex}}$bar}{\raisebox{-.3ex}{$$#1$$}}}') + def tex_from_array(array, with_lines=True): r""" Return a latex string for a two dimensional array of partition, composition or skew composition shape diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index 89420bce186..40ab068f205 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -104,6 +104,7 @@ class LocalOptions: sage.structure.global_options.py. We should split global_option in two classes LocalOptions and GlobalOptions. """ + def __init__(self, name='', **options): r""" Construct a new LocalOptions. @@ -455,6 +456,7 @@ def _dispatch(self, obj, dispatch_to, option, *get_values, **set_values): f = getattr(obj, dispatch_to + "_" + str(self._options[option])) return f(*get_values, **set_values) + default_tikz_options = dict( scale=1, line_size=1, point_size=3.5, color_line='black', color_point='black', color_bounce_0='red', color_bounce_1='blue', @@ -595,6 +597,7 @@ class _drawing_tool: (1.000000, -1.000000);' """ + def __init__(self, options, XY=lambda v: v): r""" Construct a drawing tools to produce some TIKZ drawing. @@ -2308,6 +2311,7 @@ class _polyomino_row: sage: row [0, 1, 1] """ + def __init__(self, polyomino, row): r""" The constructor of the class @@ -3997,6 +4001,7 @@ class ParallelogramPolyominoesFactory(SetFactory): sage: PPS.cardinality() +Infinity """ + def __call__(self, size=None, policy=None): r""" Return a family of parallelogram polyominoes enumerated with the @@ -4097,6 +4102,7 @@ class ParallelogramPolyominoes_size( [[0, 1, 0, 1], [1, 1, 0, 0]], [[0, 1, 1, 1], [1, 1, 1, 0]]] """ + def __init__(self, size, policy): r""" Construct a set of Parallelogram Polyominoes of a given size. @@ -4265,6 +4271,7 @@ class ParallelogramPolyominoes_all( sage: PPS Parallelogram polyominoes """ + def __init__(self, policy): r""" Construct the set of all parallelogram polyominoes. diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 8decd58e656..bd67b573238 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -1466,6 +1466,7 @@ class ParkingFunctions_n(ParkingFunctions): sage: PF3(PF3([1,1,1])) [1, 1, 1] """ + def __init__(self, n): """ TESTS:: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 49c93865c11..cf1b0c2aee9 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -307,7 +307,7 @@ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.arith.all import factorial, gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer @@ -542,7 +542,6 @@ def __init__(self, parent, mu): Traceback (most recent call last): ... ValueError: [3, 1, 7] is not an element of Partitions - """ if isinstance(mu, Partition): # since we are (suppose to be) immutable, we can share the underlying data @@ -737,9 +736,9 @@ def _repr_exp_high(self): if not self._list: return '-' exp = self.to_exp()[::-1] # reversed list of exponents - M=max(self) - return '%s' % ', '.join('%s%s' % (M-m, '' if e==1 else '^%s'%e) - for (m,e) in enumerate(exp) if e>0) + M = max(self) + return ', '.join('%s%s' % (M - m, '' if e == 1 else '^%s' % e) + for m, e in enumerate(exp) if e > 0) def _repr_compact_low(self): """ @@ -1050,13 +1049,26 @@ def __truediv__(self, p): sage: p/[2,2,2] Traceback (most recent call last): ... - ValueError: To form a skew partition p/q, q must be contained in p. + ValueError: to form a skew partition p/q, q must be contained in p """ if not self.contains(p): - raise ValueError("To form a skew partition p/q, q must be contained in p.") + raise ValueError("to form a skew partition p/q, q must be contained in p") return SkewPartition([self[:], p]) + def stretch(self, k): + """ + Return the partition obtained by multiplying each part with the + given number. + + EXAMPLES:: + + sage: p = Partition([4,2,2,1,1]) + sage: p.stretch(3) + [12, 6, 6, 3, 3] + """ + return _Partitions([k * p for p in self]) + def power(self, k): r""" Return the cycle type of the `k`-th power of any permutation @@ -2250,7 +2262,7 @@ def t_completion(self, t): ValueError: 5-completion is not defined """ if self._list and t < self.size() + self._list[0]: - raise ValueError("{}-completion is not defined".format(t)) + raise ValueError(f"{t}-completion is not defined") return Partition([t - self.size()] + self._list) def larger_lex(self, rhs): @@ -3025,14 +3037,15 @@ def arm_length(self, i, j): p = self if i < len(p) and j < p[i]: return p[i]-(j+1) - else: - raise ValueError("The cell is not in the diagram") + raise ValueError("the cell is not in the diagram") def arm_lengths(self, flat=False): """ Return a tableau of shape ``self`` where each cell is filled with - its arm length. The optional boolean parameter ``flat`` provides - the option of returning a flat list. + its arm length. + + The optional boolean parameter ``flat`` provides the option of + returning a flat list. EXAMPLES:: @@ -3076,15 +3089,12 @@ def arm_cells(self, i, j): sage: Partition([]).arm_cells(0,0) Traceback (most recent call last): ... - ValueError: The cell is not in the diagram - + ValueError: the cell is not in the diagram """ p = self if i < len(p) and j < p[i]: - return [ (i, x) for x in range(j+1, p[i]) ] - else: - raise ValueError("The cell is not in the diagram") - + return [(i, x) for x in range(j + 1, p[i])] + raise ValueError("the cell is not in the diagram") def leg_length(self, i, j): """ @@ -3118,12 +3128,10 @@ def leg_length(self, i, j): sage: cell = [0,0]; Partition([3,3]).leg_length(*cell) 1 """ - conj = self.conjugate() if j < len(conj) and i < conj[j]: - return conj[j]-(i+1) - else: - raise ValueError("The cell is not in the diagram") + return conj[j] - (i + 1) + raise ValueError("the cell is not in the diagram") def leg_lengths(self, flat=False): """ @@ -3178,10 +3186,10 @@ def leg_cells(self, i, j): sage: Partition([]).leg_cells(0,0) Traceback (most recent call last): ... - ValueError: The cell is not in the diagram + ValueError: the cell is not in the diagram """ l = self.leg_length(i, j) - return [(x, j) for x in range(i+1, i+l+1)] + return [(x, j) for x in range(i + 1, i + l + 1)] def attacking_pairs(self): """ @@ -3500,8 +3508,8 @@ def lower_hook_lengths(self, alpha): """ p = self conj = p.conjugate() - return [[conj[j] - i + alpha*(p[i]-(j+1)) for j in range(p[i])] for i in range(len(p))] - + return [[conj[j] - i + alpha*(p[i] - (j + 1)) for j in range(p[i])] + for i in range(len(p))] def weighted_size(self): r""" @@ -3944,6 +3952,10 @@ def corners(self): where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`. + .. NOTE:: + + This is referred to as an "inner corner" in [Sag2001]_. + EXAMPLES:: sage: Partition([3,2,1]).corners() @@ -4016,6 +4028,10 @@ def outside_corners(self): where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`. + .. NOTE:: + + These are called "outer corners" in [Sag2001]_. + EXAMPLES:: sage: Partition([2,2,1]).outside_corners() @@ -4417,10 +4433,9 @@ def add_cell(self, i, j = None): pl[i] += 1 return Partition(pl) - raise ValueError("[%s, %s] is not an addable cell"%(i,j)) + raise ValueError(f"[{i}, {j}] is not an addable cell") - - def remove_cell(self, i, j = None): + def remove_cell(self, i, j=None): """ Return the partition obtained by removing a cell at the end of row ``i`` of ``self``. @@ -4513,7 +4528,7 @@ def k_skew(self, k): return SkewPartition([[],[]]) if self[0] > k: - raise ValueError("the partition must be %d-bounded" % k) + raise ValueError(f"the partition must be {k}-bounded") #Find the k-skew diagram of the partition formed #by removing the first row @@ -4764,7 +4779,7 @@ def vertical_border_strip_cells(self, k): sage: list(Partition([]).vertical_border_strip_cells(2)) [[(0, 0), (1, 0)]] sage: list(Partition([2,2]).vertical_border_strip_cells(2)) - [[(0, 2), (1, 2)], + [[(0, 2), (1, 2)], [(0, 2), (2, 0)], [(2, 0), (3, 0)]] sage: list(Partition([3,2,2]).vertical_border_strip_cells(2)) @@ -4778,7 +4793,6 @@ def vertical_border_strip_cells(self, k): return [] shelf = [] - res = [] i = 0 ell = len(self._list) while i < ell: @@ -4805,7 +4819,7 @@ def vertical_border_strip_cells(self, k): for _ in range(iv[t]): current_strip.append((j, tmp[j])) j += 1 - j = sum(shelf[:t+1]) + j = sum(shelf[:t+1]) yield current_strip def horizontal_border_strip_cells(self, k): @@ -4835,7 +4849,6 @@ def horizontal_border_strip_cells(self, k): return list() L = self._list - res = [] shelf = [k] # the number of boxes which will fit in a row mapping = [0] # a record of the rows for i in range(len(L)-1): @@ -4899,13 +4912,13 @@ def remove_horizontal_border_strip(self, k): sage: Partition([]).remove_horizontal_border_strip(6).list() [] """ - return Partitions_with_constraints(n = self.size()-k, - min_length = len(self)-1, - max_length = len(self), - floor = self[1:]+[0], - ceiling = self[:], - max_slope = 0, - name = "The subpartitions of {} obtained by removing an horizontal border strip of length {}".format(self,k)) + return Partitions_with_constraints(n=self.size() - k, + min_length=len(self) - 1, + max_length=len(self), + floor=self[1:] + [0], + ceiling=self[:], + max_slope=0, + name=f"The subpartitions of {self} obtained by removing an horizontal border strip of length {k}") def k_conjugate(self, k): r""" @@ -5072,11 +5085,9 @@ def jacobi_trudi(self): """ return SkewPartition([ self, [] ]).jacobi_trudi() - def character_polynomial(self): r""" - Return the character polynomial associated to the partition - ``self``. + Return the character polynomial associated to the partition ``self``. The character polynomial `q_\mu` associated to a partition `\mu` is defined by @@ -5107,8 +5118,7 @@ def character_polynomial(self): sage: Partition([2,1]).character_polynomial() 1/3*x0^3 - 2*x0^2 + 8/3*x0 - x2 """ - - #Create the polynomial ring we will use + # Create the polynomial ring we will use k = self.size() P = PolynomialRing(QQ, k, 'x') x = P.gens() @@ -5200,7 +5210,7 @@ def dimension(self, smaller=None, k=1): Checks that the dimension satisfies the obvious recursion relation:: sage: test = lambda larger, smaller: larger.dimension(smaller) == sum(mu.dimension(smaller) for mu in larger.down()) - sage: all(test(larger,smaller) for l in range(1,10) for s in range(10) + sage: all(test(larger,smaller) for l in range(1,8) for s in range(8) ....: for larger in Partitions(l) for smaller in Partitions(s) if smaller != larger) True @@ -5476,6 +5486,7 @@ def coloring(i): immutable=True, multiedges=True) return self.dual_equivalence_graph(directed, coloring) + ############## # Partitions # ############## @@ -5733,15 +5744,15 @@ class Partitions(UniqueRepresentation, Parent): sage: Partitions(5,parts_in=[1,2,3,4], length=4) Traceback (most recent call last): ... - ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. + ValueError: the parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else sage: Partitions(5,starting=[3,2], length=2) Traceback (most recent call last): ... - ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. + ValueError: the parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else sage: Partitions(5,ending=[3,2], length=2) Traceback (most recent call last): ... - ValueError: The parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else. + ValueError: the parameters 'parts_in', 'starting' and 'ending' cannot be combined with anything else sage: Partitions(NN, length=2) Traceback (most recent call last): ... @@ -5801,26 +5812,7 @@ def __classcall_private__(cls, n=None, **kwargs): """ if n == infinity: raise ValueError("n cannot be infinite") - if n is None or n is NN or n is NonNegativeIntegers(): - if len(kwargs) > 0: - if len(kwargs) == 1: - if 'max_part' in kwargs: - return Partitions_all_bounded(kwargs['max_part']) - if 'regular' in kwargs: - return RegularPartitions_all(kwargs['regular']) - if 'restricted' in kwargs: - return RestrictedPartitions_all(kwargs['restricted']) - elif len(kwargs) == 2: - if 'regular' in kwargs: - if kwargs['regular'] < 1 or kwargs['regular'] not in ZZ: - raise ValueError("the regularity must be a positive integer") - if 'max_part' in kwargs: - return RegularPartitions_bounded(kwargs['regular'], kwargs['max_part']) - if 'max_length' in kwargs: - return RegularPartitions_truncated(kwargs['regular'], kwargs['max_length']) - raise ValueError("the size must be specified with any keyword argument") - return Partitions_all() - elif isinstance(n, (int,Integer)): + if isinstance(n, (int,Integer)): if len(kwargs) == 0: return Partitions_n(n) @@ -5834,8 +5826,8 @@ def __classcall_private__(cls, n=None, **kwargs): ('parts_in' in kwargs or 'starting' in kwargs or 'ending' in kwargs)): - raise ValueError("The parameters 'parts_in', 'starting' and "+ - "'ending' cannot be combined with anything else.") + raise ValueError("the parameters 'parts_in', 'starting' and "+ + "'ending' cannot be combined with anything else") if 'parts_in' in kwargs: return Partitions_parts_in(n, kwargs['parts_in']) @@ -5852,10 +5844,10 @@ def __classcall_private__(cls, n=None, **kwargs): kwargs['name'] = "Partitions of the integer %s satisfying constraints %s"%(n, ", ".join( ["%s=%s"%(key, kwargs[key]) for key in sorted(kwargs)] )) # min_part is at least 1, and it is 1 by default - kwargs['min_part'] = max(1,kwargs.get('min_part',1)) + kwargs['min_part'] = max(1, kwargs.get('min_part', 1)) # max_slope is at most 0, and it is 0 by default - kwargs['max_slope'] = min(0,kwargs.get('max_slope',0)) + kwargs['max_slope'] = min(0, kwargs.get('max_slope', 0)) if kwargs.get('min_slope', -float('inf')) > 0: raise ValueError("the minimum slope must be non-negative") @@ -5874,6 +5866,25 @@ def __classcall_private__(cls, n=None, **kwargs): kwargs.get('min_length',0)) del kwargs['inner'] return Partitions_with_constraints(n, **kwargs) + elif n is None or n is NN or n is NonNegativeIntegers(): + if len(kwargs) > 0: + if len(kwargs) == 1: + if 'max_part' in kwargs: + return Partitions_all_bounded(kwargs['max_part']) + if 'regular' in kwargs: + return RegularPartitions_all(kwargs['regular']) + if 'restricted' in kwargs: + return RestrictedPartitions_all(kwargs['restricted']) + elif len(kwargs) == 2: + if 'regular' in kwargs: + if kwargs['regular'] < 1 or kwargs['regular'] not in ZZ: + raise ValueError("the regularity must be a positive integer") + if 'max_part' in kwargs: + return RegularPartitions_bounded(kwargs['regular'], kwargs['max_part']) + if 'max_length' in kwargs: + return RegularPartitions_truncated(kwargs['regular'], kwargs['max_length']) + raise ValueError("the size must be specified with any keyword argument") + return Partitions_all() raise ValueError("n must be an integer or be equal to one of " "None, NN, NonNegativeIntegers()") @@ -6040,7 +6051,7 @@ def _element_constructor_(self, lst): """ if isinstance(lst, PartitionTuple): if lst.level() != 1: - raise ValueError('%s is not an element of %s' % (lst, self)) + raise ValueError(f'{lst} is not an element of {self}') lst = lst[0] if lst.parent() is self: return lst @@ -6114,12 +6125,13 @@ def subset(self, *args, **kwargs): sage: P.subset(ending=[3,1]) Traceback (most recent call last): ... - ValueError: Invalid combination of arguments + ValueError: invalid combination of arguments """ if len(args) != 0 or len(kwargs) != 0: - raise ValueError("Invalid combination of arguments") + raise ValueError("invalid combination of arguments") return self + class Partitions_all(Partitions): """ Class of all partitions. @@ -6223,12 +6235,12 @@ def from_frobenius_coordinates(self, frobenius_coordinates): [7, 5, 5, 1, 1] """ if len(frobenius_coordinates) != 2: - raise ValueError('%s is not a valid partition, two sequences of coordinates are needed'%str(frobenius_coordinates)) + raise ValueError('%s is not a valid partition, two sequences of coordinates are needed' % str(frobenius_coordinates)) else: a = frobenius_coordinates[0] b = frobenius_coordinates[1] if len(a) != len(b): - raise ValueError('%s is not a valid partition, the sequences of coordinates need to be the same length'%str(frobenius_coordinates)) + raise ValueError('%s is not a valid partition, the sequences of coordinates need to be the same length' % str(frobenius_coordinates)) # should add tests to see if a and b are sorted down, nonnegative and strictly decreasing r = len(a) if r == 0: @@ -6236,16 +6248,16 @@ def from_frobenius_coordinates(self, frobenius_coordinates): tmp = [a[i]+i+1 for i in range(r)] # should check that a is strictly decreasing if a[-1] < 0: - raise ValueError('%s is not a partition, no coordinate can be negative'%str(frobenius_coordinates)) + raise ValueError('%s is not a partition, no coordinate can be negative' % str(frobenius_coordinates)) if b[-1] >= 0: tmp.extend([r]*b[r-1]) else: - raise ValueError('%s is not a partition, no coordinate can be negative'%str(frobenius_coordinates)) - for i in range(r-1,0,-1): + raise ValueError('%s is not a partition, no coordinate can be negative' % str(frobenius_coordinates)) + for i in range(r - 1, 0, -1): if b[i-1]-b[i] > 0: tmp.extend([i]*(b[i-1]-b[i]-1)) else: - raise ValueError('%s is not a partition, the coordinates need to be strictly decreasing'%str(frobenius_coordinates)) + raise ValueError('%s is not a partition, the coordinates need to be strictly decreasing' % str(frobenius_coordinates)) return self.element_class(self, tmp) def from_beta_numbers(self, beta): @@ -6390,6 +6402,7 @@ def from_core_and_quotient(self, core, quotient): new_w.sort(reverse=True) return self.element_class(self, [new_w[i]+i for i in range(len(new_w))]) + class Partitions_all_bounded(Partitions): def __init__(self, k): """ @@ -6449,6 +6462,7 @@ def __iter__(self): yield self.element_class(self, p) n += 1 + class Partitions_n(Partitions): """ Partitions of the integer `n`. @@ -6829,6 +6843,7 @@ def subset(self, **kwargs): """ return Partitions(self.n, **kwargs) + class Partitions_nk(Partitions): """ Partitions of the integer `n` of length equal to `k`. @@ -7043,6 +7058,7 @@ def subset(self, **kwargs): """ return Partitions(self.n, length=self.k, **kwargs) + class Partitions_parts_in(Partitions): """ Partitions of `n` with parts in a given set `S`. @@ -7069,7 +7085,7 @@ def __classcall_private__(cls, n, parts): True """ parts = tuple(sorted(parts)) - return super(Partitions_parts_in, cls).__classcall__(cls, Integer(n), parts) + return super().__classcall__(cls, Integer(n), parts) def __init__(self, n, parts): """ @@ -7378,8 +7394,8 @@ def __classcall_private__(cls, n, starting_partition): True """ starting_partition = Partition(starting_partition) - return super(Partitions_starting, cls).__classcall__(cls, Integer(n), - starting_partition) + return super().__classcall__(cls, Integer(n), + starting_partition) def __init__(self, n, starting_partition): """ @@ -7452,6 +7468,7 @@ def next(self, part): """ return next(part) + class Partitions_ending(Partitions): """ All partitions with a given ending. @@ -7470,8 +7487,8 @@ def __classcall_private__(cls, n, ending_partition): True """ ending_partition = Partition(ending_partition) - return super(Partitions_ending, cls).__classcall__(cls, Integer(n), - ending_partition) + return super().__classcall__(cls, Integer(n), + ending_partition) def __init__(self, n, ending_partition): """ @@ -7546,8 +7563,8 @@ def next(self, part): """ if part == self._ending: return None - else: - return next(part) + return next(part) + class PartitionsInBox(Partitions): r""" @@ -7668,11 +7685,13 @@ def cardinality(self): """ return binomial(self.h + self.w, self.w) + class Partitions_constraints(IntegerListsLex): """ For unpickling old constrained ``Partitions_constraints`` objects created with sage <= 3.4.1. See :class:`Partitions`. """ + def __setstate__(self, data): r""" TESTS:: @@ -7720,6 +7739,7 @@ class Partitions_with_constraints(IntegerListsLex): Element = Partition options = Partitions.options + ###################### # Regular Partitions # ###################### @@ -7744,6 +7764,7 @@ class RegularPartitions(Partitions): - ``is_infinite`` -- boolean; if the subset of `\ell`-regular partitions is infinite """ + def __init__(self, ell, is_infinite=False): """ Initialize ``self``. @@ -7818,11 +7839,12 @@ def _fast_iterator(self, n, max_part): max_part = n bdry = self._ell - 1 - for i in reversed(range(1, max_part+1)): - for p in self._fast_iterator(n-i, i): + for i in reversed(range(1, max_part + 1)): + for p in self._fast_iterator(n - i, i): if p.count(i) < bdry: yield [i] + p + class RegularPartitions_all(RegularPartitions): r""" The class of all `\ell`-regular partitions. @@ -7835,6 +7857,7 @@ class RegularPartitions_all(RegularPartitions): :class:`~sage.combinat.partition.RegularPartitions` """ + def __init__(self, ell): """ Initialize ``self``. @@ -7890,6 +7913,7 @@ def __iter__(self): yield self.element_class(self, p) n += 1 + class RegularPartitions_truncated(RegularPartitions): r""" The class of `\ell`-regular partitions with max length `k`. @@ -7903,6 +7927,7 @@ class RegularPartitions_truncated(RegularPartitions): :class:`~sage.combinat.partition.RegularPartitions` """ + def __init__(self, ell, max_len): """ Initialize ``self``. @@ -8006,11 +8031,12 @@ def _fast_iterator(self, n, max_part, depth=0): max_part = n bdry = self._ell - 1 - for i in reversed(range(1, max_part+1)): - for p in self._fast_iterator(n-i, i, depth+1): + for i in reversed(range(1, max_part + 1)): + for p in self._fast_iterator(n - i, i, depth + 1): if p.count(i) < bdry: yield [i] + p + class RegularPartitions_bounded(RegularPartitions): r""" The class of `\ell`-regular `k`-bounded partitions. @@ -8024,6 +8050,7 @@ class RegularPartitions_bounded(RegularPartitions): :class:`~sage.combinat.partition.RegularPartitions` """ + def __init__(self, ell, k): """ Initialize ``self``. @@ -8088,6 +8115,7 @@ def __iter__(self): for p in self._fast_iterator(n, k): yield self.element_class(self, p) + class RegularPartitions_n(RegularPartitions, Partitions_n): r""" The class of `\ell`-regular partitions of `n`. @@ -8101,6 +8129,7 @@ class RegularPartitions_n(RegularPartitions, Partitions_n): :class:`~sage.combinat.partition.RegularPartitions` """ + def __init__(self, n, ell): """ Initialize ``self``. @@ -8215,6 +8244,7 @@ def _an_element_(self): raise EmptySetError return Partitions_n._an_element_(self) + ###################### # Ordered Partitions # ###################### @@ -8265,7 +8295,7 @@ def __classcall_private__(cls, n, k=None): """ if k is not None: k = Integer(k) - return super(OrderedPartitions, cls).__classcall__(cls, Integer(n), k) + return super().__classcall__(cls, Integer(n), k) def __init__(self, n, k): """ @@ -8362,6 +8392,7 @@ def cardinality(self): ans = libgap.NrOrderedPartitions(n, k) return ZZ(ans) + ########################## # Partitions Greatest LE # ########################## @@ -8439,6 +8470,7 @@ def cardinality(self): Element = Partition options = Partitions.options + ########################## # Partitions Greatest EQ # ########################## @@ -8530,6 +8562,7 @@ def cardinality(self): Element = Partition options = Partitions.options + ######################### # Restricted Partitions # ######################### @@ -8553,6 +8586,7 @@ class RestrictedPartitions_generic(Partitions): - ``is_infinite`` -- boolean; if the subset of `\ell`-restricted partitions is infinite """ + def __init__(self, ell, is_infinite=False): """ Initialize ``self``. @@ -8651,6 +8685,7 @@ def _fast_iterator(self, n, max_part): break yield [i] + p + class RestrictedPartitions_all(RestrictedPartitions_generic): r""" The class of all `\ell`-restricted partitions. @@ -8663,6 +8698,7 @@ class RestrictedPartitions_all(RestrictedPartitions_generic): :class:`~sage.combinat.partition.RestrictedPartitions_generic` """ + def __init__(self, ell): """ Initialize ``self``. @@ -8702,6 +8738,7 @@ def __iter__(self): yield self.element_class(self, p) n += 1 + class RestrictedPartitions_n(RestrictedPartitions_generic, Partitions_n): r""" The class of `\ell`-restricted partitions of `n`. @@ -8715,6 +8752,7 @@ class RestrictedPartitions_n(RestrictedPartitions_generic, Partitions_n): :class:`~sage.combinat.partition.RestrictedPartitions_generic` """ + def __init__(self, n, ell): """ Initialize ``self``. @@ -8910,7 +8948,7 @@ def number_of_partitions(n, algorithm='default'): """ n = ZZ(n) if n < 0: - raise ValueError("n (=%s) must be a nonnegative integer"%n) + raise ValueError("n (=%s) must be a nonnegative integer" % n) elif n == 0: return ZZ.one() @@ -8920,7 +8958,7 @@ def number_of_partitions(n, algorithm='default'): if algorithm == 'flint': return cached_number_of_partitions(n) - raise ValueError("unknown algorithm '%s'"%algorithm) + raise ValueError("unknown algorithm '%s'" % algorithm) def number_of_partitions_length(n, k, algorithm='hybrid'): @@ -8988,7 +9026,7 @@ def number_of_partitions_length(n, k, algorithm='hybrid'): # Rather than caching an under-used function I have cached the default # number_of_partitions functions which is currently using FLINT. # AM trac #13072 -cached_number_of_partitions = cached_function( flint_number_of_partitions ) +cached_number_of_partitions = cached_function(flint_number_of_partitions) # October 2012: fixing outdated pickles which use classes being deprecated from sage.misc.persist import register_unpickle_override diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 740482395b4..fcece47fbf8 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -75,6 +75,7 @@ class SetPartitionsXkElement(SetPartition): An element for the classes of ``SetPartitionXk`` where ``X`` is some letter. """ + def check(self): """ Check to make sure this is a set partition. diff --git a/src/sage/combinat/partition_kleshchev.py b/src/sage/combinat/partition_kleshchev.py index 8fd46bc76fe..bc4963aa6cf 100644 --- a/src/sage/combinat/partition_kleshchev.py +++ b/src/sage/combinat/partition_kleshchev.py @@ -85,7 +85,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.cpython.getattr import getattr_from_other_class from collections import defaultdict @@ -93,6 +93,8 @@ #-------------------------------------------------- # Kleshchev partition - element classes #-------------------------------------------------- + + class KleshchevPartition(Partition): r""" Abstract base class for Kleshchev partitions. See @@ -857,10 +859,12 @@ def is_restricted(self): KP = self.parent() return _is_restricted(self.to_list(), KP._multicharge, KP._convention) + class KleshchevCrystalMixin(): """ Mixin class for the crystal structure of a Kleshchev partition. """ + def epsilon(self, i): r""" Return the Kashiwara crystal operator `\varepsilon_i` applied to ``self``. @@ -964,10 +968,12 @@ def weight(self): return wt - WLR.sum(alpha[self.content(*c, multicharge=r)] for c in self.cells()) + class KleshchevPartitionCrystal(KleshchevPartition, KleshchevCrystalMixin): """ Kleshchev partition with the crystal structure. """ + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -1023,10 +1029,12 @@ def f(self, i): mu[r] += 1 return type(self)(P, mu) + class KleshchevPartitionTupleCrystal(KleshchevPartitionTuple, KleshchevCrystalMixin): """ Kleshchev partition tuple with the crystal structure. """ + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -1414,6 +1422,7 @@ class KleshchevPartitions_all(KleshchevPartitions): - [TingleyLN]_ - [Vazirani2002]_ """ + def __init__(self, e, multicharge, convention): r""" Initializes ``self``. @@ -1628,6 +1637,7 @@ class KleshchevPartitions_size(KleshchevPartitions): """ Kleshchev partitions of a fixed size. """ + def __init__(self, e, multicharge=(0,), size=0, convention='RS'): r""" Initialize ``self``. @@ -1853,6 +1863,7 @@ def _an_element_(self): # helper functions #-------------------------------------------------- + def _a_good_cell(kpt, multicharge, convention): """ Return a good cell from ``kpt`` considered as a Kleshchev partition @@ -1904,6 +1915,7 @@ def _a_good_cell(kpt, multicharge, convention): # finally return the result return ret + def _is_regular(kpt, multicharge, convention): """ Return ``True`` if ``kpt`` is a ``multicharge``-regular @@ -1934,6 +1946,7 @@ def _is_regular(kpt, multicharge, convention): cell = _a_good_cell(kpt, multicharge, convention) return all(part == [] for part in kpt) + def _is_restricted(kpt, multicharge, convention): """ Return ``True`` if ``kpt`` is an ``multicharge``-restricted diff --git a/src/sage/combinat/partition_shifting_algebras.py b/src/sage/combinat/partition_shifting_algebras.py index f632e769ef8..7dfee4fa460 100644 --- a/src/sage/combinat/partition_shifting_algebras.py +++ b/src/sage/combinat/partition_shifting_algebras.py @@ -51,6 +51,7 @@ class ShiftingSequenceSpace(Singleton, Parent): sage: (0.5, 1) in S False """ + def __init__(self): r""" Initialize ``self``. @@ -233,6 +234,7 @@ class ShiftingOperatorAlgebra(CombinatorialFreeModule): sage: s(op(h[3,2,1])) s[3, 2, 1] """ + def __init__(self, base_ring=QQ['t'], prefix='S'): r""" Initialize ``self``. @@ -533,6 +535,7 @@ class Element(CombinatorialFreeModule.Element): r""" An element of a :class:`ShiftingOperatorAlgebra`. """ + def __call__(self, operand): r""" Call method for shifting sequence operators to act on objects. diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 800e2a61c31..4fbf6b91723 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -267,7 +267,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.misc.cachefunc import cached_method from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer from sage.sets.positive_integers import PositiveIntegers from sage.structure.parent import Parent @@ -2140,6 +2140,7 @@ class PartitionTuples_level(PartitionTuples): Class of partition tuples of a fixed level, but summing to an arbitrary integer. """ + def __init__(self, level, category=None): r""" Initializes this class. @@ -2244,6 +2245,7 @@ class PartitionTuples_size(PartitionTuples): """ Class of partition tuples of a fixed size, but arbitrary level. """ + def __init__(self, size): r""" Initialize this class. @@ -2513,6 +2515,7 @@ class RegularPartitionTuples(PartitionTuples): r""" Abstract base class for `\ell`-regular partition tuples. """ + def __init__(self, regular, **kwds): """ Initialize ``self``. @@ -2588,6 +2591,7 @@ class RegularPartitionTuples_all(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples. """ + def __init__(self, regular): r""" Initialize ``self``. @@ -2696,6 +2700,7 @@ class RegularPartitionTuples_level(PartitionTuples_level): sage: [[3,1],[3],[5,5,5]] in RPT False """ + def __init__(self, level, regular): r""" Initialize ``self``. @@ -2865,6 +2870,7 @@ class RegularPartitionTuples_size(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples with a fixed size. """ + def __init__(self, size, regular): r""" Initialize ``self``. @@ -2994,6 +3000,7 @@ class RegularPartitionTuples_level_size(PartitionTuples_level_size): ([3], [], [2, 1, 1]), ([2, 1], [], [4])] """ + def __init__(self, level, size, regular): r""" Initialize ``self``. diff --git a/src/sage/combinat/path_tableaux/catalog.py b/src/sage/combinat/path_tableaux/catalog.py index 8cb7f2d322d..0605c6da583 100644 --- a/src/sage/combinat/path_tableaux/catalog.py +++ b/src/sage/combinat/path_tableaux/catalog.py @@ -6,7 +6,7 @@ object is an easy way to discover and quickly create the path tableaux that are available (as listed here). -Let ``<tab>`` indicate pressing the tab key. So begin by typing +Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing ``path_tableaux.<tab>`` to the see the currently implemented path tableaux. - :class:`~sage.combinat.path_tableaux.path_tableau.CylindricalDiagram` diff --git a/src/sage/combinat/path_tableaux/dyck_path.py b/src/sage/combinat/path_tableaux/dyck_path.py index 26501ec6cc7..c3c24929e99 100644 --- a/src/sage/combinat/path_tableaux/dyck_path.py +++ b/src/sage/combinat/path_tableaux/dyck_path.py @@ -37,6 +37,7 @@ ############################################################################### + class DyckPath(PathTableau): r""" An instance is the sequence of nonnegative @@ -358,6 +359,7 @@ def to_tableau(self): else: return StandardTableau([top, bot]) + class DyckPaths(PathTableaux): """ The parent class for DyckPath. diff --git a/src/sage/combinat/path_tableaux/frieze.py b/src/sage/combinat/path_tableaux/frieze.py index da1d4205b00..1706a5cd4e1 100644 --- a/src/sage/combinat/path_tableaux/frieze.py +++ b/src/sage/combinat/path_tableaux/frieze.py @@ -440,6 +440,7 @@ class FriezePatterns(PathTableaux): [ , 1, 2, 1] [ , , 1, 1, 1] """ + def __init__(self, field): r""" Initialize ``self``. diff --git a/src/sage/combinat/path_tableaux/path_tableau.py b/src/sage/combinat/path_tableaux/path_tableau.py index 10af14b7a40..e0d612b8f87 100644 --- a/src/sage/combinat/path_tableaux/path_tableau.py +++ b/src/sage/combinat/path_tableaux/path_tableau.py @@ -429,10 +429,12 @@ def dual_equivalence_graph(self): G.add_edge(a,b,"%d,%d" % (i,j)) return G + class PathTableaux(UniqueRepresentation,Parent): """ The abstract parent class for PathTableau. """ + def __init__(self): """ Initialize ``self``. @@ -458,6 +460,7 @@ def _element_constructor_(self, *args, **kwds): """ return self.element_class(self, *args, **kwds) + class CylindricalDiagram(SageObject): r""" Cylindrical growth diagrams. @@ -474,6 +477,7 @@ class CylindricalDiagram(SageObject): [ , , , , , 0, 1, 0, 1, 2, 1, 0] [ , , , , , , 0, 1, 2, 3, 2, 1, 0] """ + def __init__(self, T): """ Initialize ``self`` from the diff --git a/src/sage/combinat/path_tableaux/semistandard.py b/src/sage/combinat/path_tableaux/semistandard.py index 55236fe9ef8..5fefebfe51a 100644 --- a/src/sage/combinat/path_tableaux/semistandard.py +++ b/src/sage/combinat/path_tableaux/semistandard.py @@ -85,7 +85,7 @@ from sage.combinat.tableau import Tableau from sage.combinat.gelfand_tsetlin_patterns import GelfandTsetlinPattern from sage.combinat.partition import _Partitions -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN ############################################################################### @@ -469,6 +469,7 @@ def _test_jdt_promotion(self, **options): RHS = self.to_tableau().promotion_inverse(len(self)-2) tester.assertEqual(LHS,RHS) + class SemistandardPathTableaux(PathTableaux): """ The parent class for :class:`SemistandardPathTableau`. diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index f6d802e2beb..a237f2398a9 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -200,11 +200,8 @@ AUTHORS: - Mike Hansen - - Dan Drake (2008-04-07): allow Permutation() to take lists of tuples - - Sébastien Labbé (2009-03-17): added robinson_schensted_inverse - - Travis Scrimshaw: * (2012-08-16): ``to_standard()`` no longer modifies input @@ -216,11 +213,10 @@ - Darij Grinberg (2013-09-07): added methods; ameliorated :trac:`14885` by exposing and documenting methods for global-independent multiplication. - - Travis Scrimshaw (2014-02-05): Made :class:`StandardPermutations_n` a finite Weyl group to make it more uniform with :class:`SymmetricGroup`. Added ability to compute the conjugacy classes. - +- Trevor K. Karn (2022-08-05): Add :meth:`Permutation.n_reduced_words` - Amrutha P, Shriya M, Divya Aggarwal (2022-08-16): Added Multimajor Index. Classes and methods @@ -229,6 +225,7 @@ # **************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com> +# 2022 Trevor K. Karn <karnx018 at umn.edu> # 2022 Amrutha P <amruthap1916@gmail.com> # 2022 Shriya M <25shriya@gmail.com> # 2022 Divya Aggarwal <divyaa@iiitd.ac.in> @@ -1128,7 +1125,6 @@ def _to_cycles_list(self, singletons=True): return cycles - def to_permutation_group_element(self): """ Return a PermutationGroupElement equal to ``self``. @@ -1745,7 +1741,6 @@ def show(self, representation="cycles", orientation="landscape", **args): raise ValueError("The value of 'representation' must be equal to "+ "'cycles', 'chord-diagram' or 'braid'") - def number_of_inversions(self) -> Integer: r""" Return the number of inversions in ``self``. @@ -2216,8 +2211,8 @@ def longest_increasing_subsequence_length(self) -> Integer: def longest_increasing_subsequences(self): r""" - Return the list of the longest increasing subsequences of ``self`` - + Return the list of the longest increasing subsequences of ``self``. + A theorem of Schensted ([Sch1961]_) states that an increasing subsequence of length `i` ends with the value entered in the `i`-th column of the p-tableau. The algorithm records which column of the @@ -2247,9 +2242,9 @@ def longest_increasing_subsequences(self): # getting the column in which each element is inserted first_row_p_tableau = [] columns = [] - D = DiGraph(n+2) + D = DiGraph(n + 2) for x in self._list: - j = bisect(first_row_p_tableau, x) + j = bisect(first_row_p_tableau, x) if j == len(first_row_p_tableau): if columns: for k in columns[-1]: @@ -2993,6 +2988,38 @@ def reduced_word_lexmin(self): return rw + def rothe_diagram(self): + r""" + Return the Rothe diagram of ``self``. + + EXAMPLES:: + + sage: p = Permutation([4,2,1,3]) + sage: D = p.rothe_diagram(); D + [(0, 0), (0, 1), (0, 2), (1, 0)] + sage: D.pp() + O O O . + O . . . + . . . . + . . . . + """ + from sage.combinat.diagram import RotheDiagram + return RotheDiagram(self) + + def number_of_reduced_words(self): + r""" + Return the number of reduced words of ``self`` without explicitly + computing them all. + + EXAMPLES:: + + sage: p = Permutation([6,4,2,5,1,8,3,7]) + sage: len(p.reduced_words()) == p.number_of_reduced_words() + True + """ + Tx = self.rothe_diagram().peelable_tableaux() + + return sum(map(_tableau_contribution, Tx)) ################ # Fixed Points # @@ -5242,11 +5269,28 @@ def shifted_shuffle(self, other): return self.shifted_concatenation(other, "right").\ right_permutohedron_interval(self.shifted_concatenation(other, "left")) + +def _tableau_contribution(T): + r""" + Get the number of SYT of shape(``T``). + + EXAMPLES:: + + sage: T = Tableau([[1,1,1],[2]]) + sage: from sage.combinat.permutation import _tableau_contribution + sage: _tableau_contribution(T) + 3 + """ + from sage.combinat.tableau import StandardTableaux + return(StandardTableaux(T.shape()).cardinality()) + ################################################################ # Parent classes ################################################################ # Base class for permutations + + class Permutations(UniqueRepresentation, Parent): r""" Permutations. @@ -5617,6 +5661,7 @@ class Permutations_nk(Permutations): r""" Length-`k` partial permutations of `\{1, 2, \ldots, n\}`. """ + def __init__(self, n, k): """ TESTS:: @@ -5632,6 +5677,7 @@ class Element(ClonableArray): """ A length-`k` partial permutation of `[n]`. """ + def check(self): """ Verify that ``self`` is a valid length-`k` partial @@ -5820,6 +5866,7 @@ class Element(ClonableArray): """ A permutation of an arbitrary multiset. """ + def check(self): """ Verify that ``self`` is a valid permutation of the underlying @@ -6187,6 +6234,7 @@ class Element(ClonableArray): """ A permutation of an arbitrary set. """ + def check(self): """ Verify that ``self`` is a valid permutation of the underlying @@ -6417,6 +6465,7 @@ def random_element(self): ################################## # Arrangements + class Arrangements(Permutations): r""" An arrangement of a multiset ``mset`` is an ordered selection @@ -6499,6 +6548,7 @@ class Arrangements_msetk(Arrangements, Permutations_msetk): r""" Arrangements of length `k` of a multiset `M`. """ + def _repr_(self): """ TESTS:: @@ -6513,6 +6563,7 @@ class Arrangements_setk(Arrangements, Permutations_setk): r""" Arrangements of length `k` of a set `S`. """ + def _repr_(self): """ TESTS:: @@ -6526,10 +6577,12 @@ def _repr_(self): ############################################################### # Standard permutations + class StandardPermutations_all(Permutations): """ All standard permutations. """ + def __init__(self): """ TESTS:: @@ -6619,6 +6672,7 @@ class StandardPermutations_n_abstract(Permutations): Anything inheriting from this class should override the ``__contains__`` method. """ + def __init__(self, n, category=None): """ TESTS: @@ -6684,6 +6738,7 @@ def __contains__(self, x): """ return Permutations.__contains__(self, x) and len(x) == self.n + class StandardPermutations_n(StandardPermutations_n_abstract): r""" Permutations of the set `\{1, 2, \ldots, n\}`. @@ -6696,6 +6751,7 @@ class StandardPermutations_n(StandardPermutations_n_abstract): Have a :meth:`reduced_word` which works in both multiplication conventions. """ + def __init__(self, n): """ Initialize ``self``. @@ -6761,6 +6817,11 @@ def _coerce_map_from_(self, G): True sage: P.has_coerce_map_from(Permutations(7)) False + + sage: P.has_coerce_map_from(groups.misc.Cactus(5)) + True + sage: P.has_coerce_map_from(groups.misc.Cactus(7)) + False """ if isinstance(G, SymmetricGroup): D = G.domain() @@ -6769,6 +6830,9 @@ def _coerce_map_from_(self, G): return self._from_permutation_group_element if isinstance(G, StandardPermutations_n) and G.n <= self.n: return True + from sage.groups.cactus_group import CactusGroup + if isinstance(G, CactusGroup) and G.n() <= self.n: + return self._from_cactus_group_element return super()._coerce_map_from_(G) def _from_permutation_group_element(self, x): @@ -6785,6 +6849,21 @@ def _from_permutation_group_element(self, x): """ return self(x.domain()) + def _from_cactus_group_element(self, x): + """ + Return an element of ``self`` from a cactus group element. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: elt = s12 * s23 * s13 + sage: P5 = Permutations(5) + sage: P5._from_cactus_group_element(elt) + [1, 3, 2, 4, 5] + """ + return self(x.to_permutation()) + def as_permutation_group(self): """ Return ``self`` as a permutation group. @@ -7357,6 +7436,8 @@ def apply_simple_reflection_right(self, i): ############################# # TODO: Make this a coercion + + def from_permutation_group_element(pge, parent=None): """ Return a :class:`Permutation` given a :class:`PermutationGroupElement` @@ -7377,6 +7458,7 @@ def from_permutation_group_element(pge, parent=None): return parent(pge.domain()) + def from_rank(n, rank): r""" Return the permutation of the set `\{1,...,n\}` with lexicographic @@ -7414,6 +7496,7 @@ def from_rank(n, rank): return from_lehmer_code(factoradic, Permutations(n)) + def from_inversion_vector(iv, parent=None): r""" Return the permutation corresponding to inversion vector ``iv``. @@ -7442,6 +7525,7 @@ def from_inversion_vector(iv, parent=None): parent = Permutations() return parent(p) + def from_cycles(n, cycles, parent=None): r""" Return the permutation in the `n`-th symmetric group whose decomposition @@ -7478,59 +7562,55 @@ def from_cycles(n, cycles, parent=None): sage: Permutation("(-12,2)(3,4)") Traceback (most recent call last): ... - ValueError: All elements should be strictly positive integers, and I just found a non-positive one. + ValueError: all elements should be strictly positive integers, but I found -12 sage: Permutation("(1,2)(2,4)") Traceback (most recent call last): ... - ValueError: an element appears twice in the input + ValueError: the element 2 appears more than once in the input sage: permutation.from_cycles(4, [[1,18]]) Traceback (most recent call last): ... - ValueError: You claimed that this was a permutation on 1...4 but it contains 18 + ValueError: you claimed that this is a permutation on 1...4, but it contains 18 + + TESTS: + + Verify that :trac:`34662` has been fixed:: + + sage: permutation.from_cycles(6, (c for c in [[1,2,3], [4,5,6]])) + [2, 3, 1, 5, 6, 4] """ if parent is None: parent = Permutations(n) - p = list(range(1, n+1)) - - # Is it really a permutation on 1...n ? - flattened_and_sorted = [] - for c in cycles: - flattened_and_sorted.extend(c) - flattened_and_sorted.sort() - - # Empty input - if not flattened_and_sorted: - return parent(p, check_input=False) - - # Only positive elements - if int(flattened_and_sorted[0]) < 1: - raise ValueError("All elements should be strictly positive " - "integers, and I just found a non-positive one.") - - # Really smaller or equal to n ? - if flattened_and_sorted[-1] > n: - raise ValueError("You claimed that this was a permutation on 1..."+ - str(n)+" but it contains "+str(flattened_and_sorted[-1])) - - # Disjoint cycles ? - previous = flattened_and_sorted[0] - 1 - for i in flattened_and_sorted: - if i == previous: - raise ValueError("an element appears twice in the input") - else: - previous = i + # None represents a value of the permutation that has not been specified yet + p = n * [None] for cycle in cycles: - if not cycle: - continue - first = cycle[0] - for i in range(len(cycle)-1): - p[cycle[i]-1] = cycle[i+1] - p[cycle[-1]-1] = first - + cycle_length = len(cycle) + for i in range(cycle_length): + # two consecutive terms in the cycle represent k and p(k) + k = ZZ(cycle[i]) + pk = ZZ(cycle[(i + 1) % cycle_length]) + + # 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)}") + 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)}") + if p[k - 1] is not None: + raise ValueError(f"the element {k} appears more than once" + " in the input") + + p[k - 1] = pk + # values that are not in any cycle are fixed points of the permutation + for i in range(n): + if p[i] is None: + p[i] = ZZ(i + 1) return parent(p, check_input=False) + def from_lehmer_code(lehmer, parent=None): r""" Return the permutation with Lehmer code ``lehmer``. @@ -7552,6 +7632,34 @@ def from_lehmer_code(lehmer, parent=None): parent = Permutations() return parent(p) + +def from_lehmer_cocode(lehmer, parent=Permutations()): + r""" + Return the permutation with Lehmer cocode ``lehmer``. + + The Lehmer cocode of a permutation `p` is defined as the + list `(c_1, c_2, \ldots, c_n)`, where `c_i` is the number + of `j < i` such that `p(j) > p(i)`. + + EXAMPLES:: + + sage: import sage.combinat.permutation as permutation + sage: lcc = Permutation([2,1,5,4,3]).to_lehmer_cocode(); lcc + [0, 1, 0, 1, 2] + sage: permutation.from_lehmer_cocode(lcc) + [2, 1, 5, 4, 3] + """ + p = [] + ell = len(lehmer) + i = ell-1 + open_spots = list(range(1, ell+1)) + for ivi in reversed(lehmer): + p.append(open_spots.pop(i-ivi)) + i -= 1 + p.reverse() + return parent(p) + + def from_reduced_word(rw, parent=None): r""" Return the permutation corresponding to the reduced word ``rw``. @@ -7582,6 +7690,7 @@ def from_reduced_word(rw, parent=None): return parent(p) + def bistochastic_as_sum_of_permutations(M, check = True): r""" Return the positive sum of permutations corresponding to @@ -7919,6 +8028,7 @@ def __iter__(self): """ return iter(descents_composition_list(Composition(descents=(self._d, self.n)))) + def descents_composition_list(dc): """ Return a list of all the permutations that have the descent @@ -7947,6 +8057,7 @@ def descents_composition_list(dc): """ return [p.inverse() for p in StandardPermutations_recoils(dc)] + def descents_composition_first(dc): r""" Compute the smallest element of a descent class having a descent @@ -7973,6 +8084,7 @@ def descents_composition_first(dc): return Permutations()(res) + def descents_composition_last(dc): r""" Return the largest element of a descent class having a descent @@ -7997,6 +8109,7 @@ def descents_composition_last(dc): return Permutations()(res) + class StandardPermutations_recoilsfiner(Permutations): @staticmethod def __classcall_private__(cls, recoils): @@ -8064,6 +8177,7 @@ def __iter__(self): for le in dag.topological_sort_generator(): yield self.element_class(self, le) + class StandardPermutations_recoilsfatter(Permutations): @staticmethod def __classcall_private__(cls, recoils): @@ -8135,6 +8249,7 @@ def __iter__(self): for le in dag.topological_sort_generator(): yield self.element_class(self, le) + class StandardPermutations_recoils(Permutations): r""" Permutations of `\{1, \ldots, n\}` with a fixed recoils composition. @@ -8206,6 +8321,7 @@ def __iter__(self): for le in dag.topological_sort_generator(): yield self.element_class(self, le) + def from_major_code(mc, final_descent=False): r""" Return the permutation with major code ``mc``. @@ -8398,6 +8514,7 @@ def __iter__(self): """ return iter(transitive_ideal(lambda x: x.bruhat_succ(), self.p)) + def bruhat_lequal(p1, p2): r""" Return ``True`` if ``p1`` is less than ``p2`` in the Bruhat order. @@ -8471,6 +8588,7 @@ def permutohedron_lequal(p1, p2, side="right"): ############ from sage.combinat.words.finite_word import evaluation_dict + def to_standard(p, key=None): r""" Return a standard permutation corresponding to the iterable ``p``. @@ -8634,6 +8752,7 @@ def list(self, distinct=False): ################################################# + class CyclicPermutationsOfPartition(Permutations): """ Combinations of cyclic permutations of each cell of a given partition. @@ -8710,6 +8829,7 @@ class Element(ClonableArray): """ A cyclic permutation of a partition. """ + def check(self): """ Check that ``self`` is a valid element. @@ -8901,6 +9021,7 @@ def __iter__(self): yield x n += 1 + class StandardPermutations_avoiding_generic(StandardPermutations_n_abstract): """ Generic class for subset of permutations avoiding a set of patterns. @@ -9015,6 +9136,7 @@ def cardinality(self): one = ZZ.one() return sum(one for p in self) + class StandardPermutations_avoiding_12(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9046,6 +9168,7 @@ def cardinality(self): """ return ZZ.one() + class StandardPermutations_avoiding_21(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9077,6 +9200,7 @@ def cardinality(self): """ return ZZ.one() + class StandardPermutations_avoiding_132(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9144,11 +9268,11 @@ def __iter__(self): for right in StandardPermutations_avoiding_132(self.n-i-1): yield self.element_class(self, [x+(self.n-i-1) for x in left] + [self.n] + list(right) ) - #Yield all the 132 avoiding permutations to the left for left in StandardPermutations_avoiding_132(self.n - 1): yield self.element_class(self, list(left) + [self.n]) + class StandardPermutations_avoiding_123(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9220,6 +9344,7 @@ def __iter__(self): yield self.element_class(self, new_p) + class StandardPermutations_avoiding_321(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9251,6 +9376,7 @@ def __iter__(self): for p in StandardPermutations_avoiding_123(self.n): yield self.element_class(self, p.reverse()) + class StandardPermutations_avoiding_231(StandardPermutations_avoiding_generic): def __init__(self, n): """ @@ -9392,6 +9518,7 @@ class PermutationsNK(Permutations_setk): This exists solely for unpickling ``PermutationsNK`` objects created with Sage <= 6.3. """ + def __setstate__(self, state): r""" For unpickling old ``PermutationsNK`` objects. @@ -9418,6 +9545,7 @@ def __setstate__(self, state): self.__class__ = Permutations_setk self.__init__(tuple(range(state['_n'])), state['_k']) + from sage.misc.persist import register_unpickle_override register_unpickle_override("sage.combinat.permutation", "Permutation_class", Permutation) register_unpickle_override("sage.combinat.permutation", "CyclicPermutationsOfPartition_partition", CyclicPermutationsOfPartition) diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index 49edf8abdea..c8b29e06294 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -25,21 +25,22 @@ from __future__ import annotations from typing import Iterator -from sage.structure.list_clone import ClonableArray -from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.posets.posets import Poset -from sage.rings.integer import Integer -from sage.misc.misc_c import prod from sage.combinat.tableau import Tableau +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.lazy_import import lazy_import +from sage.misc.misc_c import prod +from sage.modules.free_module_element import vector +from sage.rings.integer import Integer +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation lazy_import("sage.plot.plot3d.platonic", "cube") class PlanePartition(ClonableArray, - metaclass=InheritComparisonClasscallMetaclass): + metaclass=InheritComparisonClasscallMetaclass): r""" A plane partition. @@ -396,6 +397,70 @@ def pp(self, show_box=False): """ print(self._repr_diagram(show_box)) + def _repr_svg_(self) -> str: + """ + Return the svg picture of a plane partition. + + This can be displayed by Jupyter. + + EXAMPLES:: + + sage: PP = PlanePartition([[2, 1, 1], [1, 1]]) + sage: PP._repr_svg_() + '<?xml...</g></svg>' + """ + colors = ["snow", "tomato", "steelblue"] + + resu = '<?xml version=\"1.0\" standalone=\"no\"?>' + resu += '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ' + resu += '\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">' + resu += '<svg xmlns=\"http://www.w3.org/2000/svg\" ' + resu += 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"300\" viewBox=' + + resu1 = '<defs><polygon points=\"0, 0 -0.866, 0.5 0, 1 0.866, 0.5\" ' + resu1 += f'id=\"cz\" style=\"fill:{colors[0]}\"/>' + resu1 += '<polygon points=\"0, 0 0.866, 0.5 0.866, -0.5 0, -1\" ' + resu1 += f'id=\"cx\" style=\"fill:{colors[1]}\"/>' + resu1 += '<polygon points=\"0, 0 0, -1 -0.866, -0.5 -0.866, 0.5\" ' + resu1 += f'id=\"cy\" style=\"fill:{colors[2]}\"/></defs>' + resu1 += '<g style=\"stroke-width:0.01;stroke-linejoin:bevel; ' + resu1 += 'stroke-linecap:butt; stroke:black; fill:red\">' + + vx = -vector([0.866, -0.5]) + vy = -vector([-0.866, -0.5]) + vz = -vector([0, 1]) + Nx, Ny, Nz = self.parent().box() + + resu += '\"%.3f %.3f %.3f %.3f \">' % (-0.866 * Nx, -Nz, + 0.866 * Nx + 0.866 * Ny, + Nz + 0.5 * (Nx + Ny)) + resu += resu1 + + mat = self.z_tableau() + for i in range(Nx): + for j in range(Ny): + if mat[i][j]: + v = i * vx + j * vy + mat[i][j] * vz + resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1]) + resu += '\" xlink:href=\"#cz\" />' + + mat = self.y_tableau() + for j in range(Nz): + for k in range(Nx): + if mat[j][k]: + v = j * vz + k * vx + mat[j][k] * vy + resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1]) + resu += '\" xlink:href=\"#cy\" />' + + mat = self.x_tableau() + for k in range(Ny): + for i in range(Nz): + if mat[k][i]: + v = k * vy + i * vz + mat[k][i] * vx + resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1]) + resu += '\" xlink:href=\"#cx\" />' + return resu + '</g></svg>' + def _latex_(self, show_box=False, colors=["white", "lightgray", "darkgray"]) -> str: r""" @@ -622,8 +687,8 @@ def is_SPP(self) -> bool: for j in range(c2): T[i][j] = Z[i][j] return all(T[r][c] == T[c][r] - for r in range(size) - for c in range(r, size)) + for r in range(size) + for c in range(r, size)) def is_CSPP(self) -> bool: r""" diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 515b762278c..d857092f9c7 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -3489,15 +3489,15 @@ def _glue_spectra(a_spec, b_spec, orientation): p = len(a_spec) q = len(b_spec) - for r in range(1, p+q+1): + for r in range(1, p + q + 1): new_a_spec.append(0) - for i in range(max(1, r-q), min(p, r) + 1): - k_val = binomial(r-1, i-1) * binomial(p+q-r, p-i) + for i in range(max(1, r - q), min(p, r) + 1): + k_val = binomial(r - 1, i - 1) * binomial(p + q - r, p - i) if orientation: - inner_sum = sum(b_spec[j-1] for j in range(r-i + 1, len(b_spec) + 1)) + inner_sum = sum(b_spec[j - 1] for j in range(r - i + 1, len(b_spec) + 1)) else: - inner_sum = sum(b_spec[j-1] for j in range(1, r-i + 1)) - new_a_spec[-1] = new_a_spec[-1] + (a_spec[i-1] * k_val * inner_sum) + inner_sum = sum(b_spec[j - 1] for j in range(1, r - i + 1)) + new_a_spec[-1] = new_a_spec[-1] + (a_spec[i - 1] * k_val * inner_sum) return new_a_spec diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 00f50e68f19..43bd9f83fdc 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1307,9 +1307,9 @@ def is_semidistributive(self): """ H = self._hasse_diagram # See trac #21528 for explanation. - return ( (H.in_degree_sequence().count(1) == + return ((H.in_degree_sequence().count(1) == H.out_degree_sequence().count(1)) and - self.is_meet_semidistributive() ) + self.is_meet_semidistributive()) def is_meet_semidistributive(self, certificate=False): r""" @@ -1693,11 +1693,11 @@ def is_cosectionally_complemented(self, certificate=False): H = self._hasse_diagram jn = H.join_matrix() n = H.order() - for e in range(n-2, -1, -1): + for e in range(n - 2, -1, -1): t = 0 for uc in H.neighbors_out(e): t = jn[t, uc] - if t == n-1: + if t == n - 1: break else: if certificate: @@ -1882,8 +1882,8 @@ def is_sectionally_complemented(self, certificate=False): H = self._hasse_diagram mt = H.meet_matrix() - n = H.order()-1 - for e in range(2, n+1): + n = H.order() - 1 + for e in range(2, n + 1): t = n for lc in H.neighbors_in(e): t = mt[t, lc] @@ -1903,8 +1903,8 @@ def breadth(self, certificate=False): any join of elements `x_1, x_2, \ldots, x_{n+1}` is join of a proper subset of `x_i`. - This can be also characterized by sublattices: a lattice - of breadth at least `n` contains a sublattice isomorphic to the + This can be also characterized by subposets: a lattice + of breadth at least `n` contains a subposet isomorphic to the Boolean lattice of `2^n` elements. INPUT: @@ -3085,9 +3085,9 @@ def vertical_decomposition(self, elements_only=False): if elements_only: return [self[e] for e in self._hasse_diagram.vertical_decomposition(return_list=True)] - elms = ( [0] + - self._hasse_diagram.vertical_decomposition(return_list=True) + - [self.cardinality() - 1] ) + elms = ([0] + + self._hasse_diagram.vertical_decomposition(return_list=True) + + [self.cardinality() - 1]) n = len(elms) result = [] for i in range(n - 1): @@ -4300,7 +4300,7 @@ def is_constructible_by_doublings(self, type): return True if (type == 'interval' and len(self.join_irreducibles()) != - len(self.meet_irreducibles())): + len(self.meet_irreducibles())): return False if type == 'upper' or type == 'interval': @@ -4975,6 +4975,7 @@ def _log_2(n): return bits return bits + 1 + ############################################################################ FiniteMeetSemilattice._dual_class = FiniteJoinSemilattice diff --git a/src/sage/combinat/posets/moebius_algebra.py b/src/sage/combinat/posets/moebius_algebra.py index 9ed121e66c2..aada2725078 100644 --- a/src/sage/combinat/posets/moebius_algebra.py +++ b/src/sage/combinat/posets/moebius_algebra.py @@ -102,7 +102,7 @@ def __init__(self, R, L): TESTS:: - sage: L = posets.BooleanLattice(4) + sage: L = posets.BooleanLattice(3) sage: M = L.moebius_algebra(QQ) sage: TestSuite(M).run() """ @@ -632,7 +632,7 @@ def __init__(self, M, prefix='KL'): TESTS:: - sage: L = posets.BooleanLattice(4) + sage: L = posets.BooleanLattice(3) sage: M = L.quantum_moebius_algebra() sage: TestSuite(M.KL()).run() # long time """ diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index e415dc300f2..7f1fcf1e633 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -219,7 +219,7 @@ def BooleanLattice(n, facade=None, use_subsets=False): from sage.sets.set import Set V = [Set(), Set([1])] return LatticePoset((V, [V]), facade=facade) - return LatticePoset(([0,1], [[0,1]]), facade=facade) + return LatticePoset(([0, 1], [[0, 1]]), facade=facade) if use_subsets: from sage.sets.set import Set @@ -288,7 +288,7 @@ def ChainPoset(n, facade=None): raise TypeError("number of elements must be an integer, not {0}".format(n)) if n < 0: raise ValueError("number of elements must be non-negative, not {0}".format(n)) - D = DiGraph([range(n), [[x,x+1] for x in range(n-1)]], + D = DiGraph([range(n), [[x, x + 1] for x in range(n - 1)]], format='vertices_and_edges') return FiniteLatticePoset(hasse_diagram=D, category=FiniteLatticePosets(), @@ -377,7 +377,7 @@ def PentagonPoset(facade=None): sage: posets.DiamondPoset(5).is_distributive() False """ - return LatticePoset([[1,2],[4],[3],[4],[]], facade=facade) + return LatticePoset([[1, 2], [4], [3], [4], []], facade=facade) @staticmethod def DiamondPoset(n, facade=None): @@ -404,10 +404,10 @@ def DiamondPoset(n, facade=None): raise TypeError("number of elements must be an integer, not {0}".format(n)) if n <= 2: raise ValueError("n must be an integer at least 3") - c = [[n-1] for x in range(n)] - c[0] = [x for x in range(1,n-1)] - c[n-1] = [] - D = DiGraph({v:c[v] for v in range(n)}, format='dict_of_lists') + c = [[n - 1] for x in range(n)] + c[0] = [x for x in range(1, n - 1)] + c[n - 1] = [] + D = DiGraph({v: c[v] for v in range(n)}, format='dict_of_lists') return FiniteLatticePoset(hasse_diagram=D, category=FiniteLatticePosets(), facade=facade) @@ -441,8 +441,8 @@ def Crown(n, facade=None): raise TypeError("number of elements must be an integer, not {0}".format(n)) if n < 2: raise ValueError("n must be an integer at least 2") - D = {i: [i+n, i+n+1] for i in range(n-1)} - D[n-1] = [n, n+n-1] + D = {i: [i + n, i + n + 1] for i in range(n - 1)} + D[n - 1] = [n, n + n - 1] return FinitePoset(hasse_diagram=DiGraph(D), category=FinitePosets(), facade=facade) @@ -485,7 +485,7 @@ def DivisorLattice(n, facade=None): if n <= 0: raise ValueError("n must be a positive integer") Div_n = divisors(n) - hasse = DiGraph([Div_n, lambda a, b: b%a==0 and is_prime(b//a)]) + hasse = DiGraph([Div_n, lambda a, b: b % a == 0 and is_prime(b // a)]) return FiniteLatticePoset(hasse, elements=Div_n, facade=facade, category=FiniteLatticePosets()) @@ -509,7 +509,8 @@ def IntegerCompositions(n): """ from sage.combinat.composition import Compositions C = Compositions(n) - return Poset((C, [[c,d] for c in C for d in C if d.is_finer(c)]), cover_relations=False) + return Poset((C, [[c, d] for c in C for d in C if d.is_finer(c)]), + cover_relations=False) @staticmethod def IntegerPartitions(n): @@ -535,19 +536,19 @@ def lower_covers(partition): of elements in the poset of integer partitions. """ lc = [] - for i in range(len(partition)-1): - for j in range(i+1,len(partition)): + for i in range(len(partition) - 1): + for j in range(i + 1, len(partition)): new_partition = partition[:] del new_partition[j] del new_partition[i] - new_partition.append(partition[i]+partition[j]) + new_partition.append(partition[i] + partition[j]) new_partition.sort(reverse=True) tup = tuple(new_partition) if tup not in lc: lc.append(tup) return lc from sage.combinat.partition import Partitions - H = DiGraph(dict([[tuple(p),lower_covers(p)] for p in Partitions(n)])) + H = DiGraph(dict([[tuple(p), lower_covers(p)] for p in Partitions(n)])) return Poset(H.reverse()) @staticmethod @@ -574,20 +575,20 @@ def lower_covers(partition): restricted poset of integer partitions. """ lc = [] - for i in range(len(partition)-1): - for j in range(i+1,len(partition)): + for i in range(len(partition) - 1): + for j in range(i + 1, len(partition)): if partition[i] != partition[j]: new_partition = partition[:] del new_partition[j] del new_partition[i] - new_partition.append(partition[i]+partition[j]) + new_partition.append(partition[i] + partition[j]) new_partition.sort(reverse=True) tup = tuple(new_partition) if tup not in lc: lc.append(tup) return lc from sage.combinat.partition import Partitions - H = DiGraph(dict([[tuple(p),lower_covers(p)] for p in Partitions(n)])) + H = DiGraph(dict([[tuple(p), lower_covers(p)] for p in Partitions(n)])) return Poset(H.reverse()) @staticmethod @@ -622,7 +623,7 @@ def IntegerPartitionsDominanceOrder(n): [[4, 2], [5, 1]], [[5, 1], [6]]] """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.partition import Partitions, Partition @@ -677,9 +678,8 @@ def PowerPoset(n): all_pos_n.add(P.relabel(list(r))) return MeetSemilattice((all_pos_n, - lambda A, B: all(B.is_lequal(x, y) for x,y in A.cover_relations_iterator()) - )) - + lambda A, B: all(B.is_lequal(x, y) + for x, y in A.cover_relations_iterator()))) @staticmethod def ProductOfChains(chain_lengths, facade=None): @@ -794,7 +794,7 @@ def RandomPoset(n, p): p = float(p) except Exception: raise TypeError("probability must be a real number, not {0}".format(p)) - if p < 0 or p> 1: + if p < 0 or p > 1: raise ValueError("probability must be between 0 and 1, not {0}".format(p)) D = DiGraph(loops=False, multiedges=False) @@ -904,7 +904,7 @@ def RandomLattice(n, p, properties=None): if n <= 3: return posets.ChainPoset(n) covers = _random_lattice(n, p) - covers_dict = {i:covers[i] for i in range(n)} + covers_dict = {i: covers[i] for i in range(n)} D = DiGraph(covers_dict) D.relabel([i-1 for i in Permutations(n).random_element()]) return LatticePoset(D, cover_relations=True) @@ -973,7 +973,7 @@ def SetPartitions(n): sage: posets.SetPartitions(4) Finite lattice containing 15 elements """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.set_partition import SetPartitions @@ -1173,9 +1173,11 @@ def SymmetricGroupWeakOrderPoset(n, labels="permutations", side="right"): Finite poset containing 24 elements """ if n < 10 and labels == "permutations": - element_labels = dict([[s,"".join(map(str,s))] for s in Permutations(n)]) + element_labels = dict([[s, "".join(map(str, s))] + for s in Permutations(n)]) if n < 10 and labels == "reduced_words": - element_labels = dict([[s,"".join(map(str,s.reduced_word_lexmin()))] for s in Permutations(n)]) + element_labels = dict([[s, "".join(map(str, s.reduced_word_lexmin()))] + for s in Permutations(n)]) if side == "left": def weak_covers(s): @@ -1193,7 +1195,8 @@ def weak_covers(s): """ return [v for v in s.bruhat_succ() if s.length() + (s.inverse().left_action_product(v)).length() == v.length()] - return Poset(dict([[s, weak_covers(s)] for s in Permutations(n)]),element_labels) + return Poset(dict([[s, weak_covers(s)] for s in Permutations(n)]), + element_labels) @staticmethod def TetrahedralPoset(n, *colors, **labels): @@ -1253,33 +1256,33 @@ def TetrahedralPoset(n, *colors, **labels): if c not in ('green', 'red', 'yellow', 'orange', 'silver', 'blue'): raise ValueError("color input must be from the following: 'green', 'red', 'yellow', 'orange', 'silver', and 'blue'") elem = [(i, j, k) for i in range(n) - for j in range(n-i) for k in range(n-i-j)] + for j in range(n - i) for k in range(n - i - j)] rels = [] elem_labels = {} if 'labels' in labels: if labels['labels'] == 'integers': labelcount = 0 - for (i,j,k) in elem: - elem_labels[(i,j,k)] = labelcount + for (i, j, k) in elem: + elem_labels[(i, j, k)] = labelcount labelcount += 1 for c in colors: - for (i,j,k) in elem: - if i+j+k < n-1: + for (i, j, k) in elem: + if i + j + k < n - 1: if c == 'green': - rels.append([(i,j,k),(i+1,j,k)]) + rels.append([(i, j, k), (i + 1, j, k)]) if c == 'red': - rels.append([(i,j,k),(i,j,k+1)]) + rels.append([(i, j, k), (i, j, k + 1)]) if c == 'yellow': - rels.append([(i,j,k),(i,j+1,k)]) - if j < n-1 and k > 0: + rels.append([(i, j, k), (i, j + 1, k)]) + if j < n - 1 and k > 0: if c == 'orange': - rels.append([(i,j,k),(i,j+1,k-1)]) - if i < n-1 and j > 0: + rels.append([(i, j, k), (i, j + 1, k - 1)]) + if i < n - 1 and j > 0: if c == 'silver': - rels.append([(i,j,k),(i+1,j-1,k)]) - if i < n-1 and k > 0: + rels.append([(i, j, k), (i + 1, j - 1, k)]) + if i < n - 1 and k > 0: if c == 'blue': - rels.append([(i,j,k),(i+1,j,k-1)]) + rels.append([(i, j, k), (i + 1, j, k - 1)]) return Poset([elem, rels], elem_labels) # shard intersection order @@ -1684,9 +1687,9 @@ def PermutationPattern(n): if n <= 0: raise ValueError("number of elements must be nonnegative, not {}".format(n)) elem = [] - for i in range(1, n+1): + for i in range(1, n + 1): elem += Permutations(i) - return Poset((elem, lambda a,b: b.has_pattern(a))) + return Poset((elem, lambda a, b: b.has_pattern(a))) @staticmethod def PermutationPatternInterval(bottom, top): @@ -1737,7 +1740,7 @@ def PermutationPatternInterval(bottom, top): level = 0 # Consider the top element to be level 0, and then go down from there. rel = [] # List of covering relations to be fed into poset constructor. while len(top) - len(bottom) >= level + 1: - elem.append([]) # Add a new empty level + elem.append([]) # Add a new empty level for upper in elem[level]: # Run through all permutations on current level # and find relations for which it is upper cover @@ -1746,17 +1749,17 @@ def PermutationPatternInterval(bottom, top): # Try and remove the ith element from the permutation lower = list(upper) j = lower.pop(i) - for k in range(len(top)-level-1): # Standardize result + for k in range(len(top)-level-1): # Standardize result if lower[k] > j: lower[k] = lower[k] - 1 lower_perm = P(lower) - if lower_perm.has_pattern(bottom): # Check to see if result is in interval + if lower_perm.has_pattern(bottom): # Check to see if result is in interval rel += [[lower_perm, upper_perm]] - if lower not in elem[level+1]: - elem[level+1].append(lower_perm) + if lower not in elem[level + 1]: + elem[level + 1].append(lower_perm) level += 1 elem = [item for sublist in elem for item in sublist] - return Poset((elem,rel)) + return Poset((elem, rel)) @staticmethod def PermutationPatternOccurrenceInterval(bottom, top, pos): @@ -1793,13 +1796,13 @@ def PermutationPatternOccurrenceInterval(bottom, top, pos): P = Permutations() top = P(top) bottom = P(bottom) - if not to_standard([top[z] for z in pos]) == list(bottom): # check input + if not to_standard([top[z] for z in pos]) == list(bottom): # check input raise ValueError("cannot find 'bottom' in 'top' given by 'pos'") elem = [[(top, pos)]] level = 0 rel = [] while len(top) - len(bottom) >= level + 1: - elem.append([]) # Add a new empty level + elem.append([]) # Add a new empty level for upper in elem[level]: for i in range(len(top)-level): # Try and remove the ith element from the permutation @@ -1816,11 +1819,11 @@ def PermutationPatternOccurrenceInterval(bottom, top, pos): lower_pos[f] = upper[1][f] - 1 rel += [[(P(lower_perm), tuple(lower_pos)), (P(upper[0]), upper[1])]] - if (P(lower_perm), tuple(lower_pos)) not in elem[level+1]: - elem[level+1].append((P(lower_perm), tuple(lower_pos))) + if (P(lower_perm), tuple(lower_pos)) not in elem[level + 1]: + elem[level + 1].append((P(lower_perm), tuple(lower_pos))) level += 1 elem = [item for sublist in elem for item in sublist] - return Poset([elem,rel]) + return Poset([elem, rel]) @staticmethod def RibbonPoset(n, descents): @@ -1838,7 +1841,9 @@ def RibbonPoset(n, descents): sage: sorted(R.cover_relations()) [[0, 1], [2, 1], [3, 2], [3, 4]] """ - return Mobile(DiGraph([list(range(n)), [(i+1, i) if i in descents else (i, i+1) for i in range(n-1) ]])) + return Mobile(DiGraph([list(range(n)), + [(i + 1, i) if i in descents else (i, i + 1) + for i in range(n - 1)]])) @staticmethod def MobilePoset(ribbon, hangers, anchor=None): @@ -1889,15 +1894,15 @@ def MobilePoset(ribbon, hangers, anchor=None): for r, hangs in hangers.items(): for i, h in enumerate(hangs): for v in h._elements: - elements.append((r,i,v)) + elements.append((r, i, v)) for cr in h.cover_relations(): cover_relations.append(((r, i, cr[0]), (r, i, cr[1]))) - cover_relations.append(((r,i,h.top()), r)) + cover_relations.append(((r, i, h.top()), r)) return Mobile(DiGraph([elements, cover_relations])) -## RANDOM LATTICES +# RANDOM LATTICES # Following are helper functions for random lattice generation. # There is no parameter checking, 0, 1, ..., n may or may not be a @@ -1939,8 +1944,8 @@ def _random_lattice(n, p): from sage.misc.functional import sqrt from sage.misc.prandom import random - n = n-1 - meets = [[None]*n for _ in range(n)] + n = n - 1 + meets = [[None] * n for _ in range(n)] meets[0][0] = 0 maxs = set([0]) lc_all = [[]] # No lower covers for the bottom element. @@ -2008,11 +2013,11 @@ def _random_dismantlable_lattice(n): """ from sage.misc.prandom import randint - D = DiGraph({0: [n-1]}) - for i in range(1, n-1): - a = randint(0, i//2) + D = DiGraph({0: [n - 1]}) + for i in range(1, n - 1): + a = randint(0, i // 2) b_ = list(D.depth_first_search(a)) - b = b_[randint(1, len(b_)-1)] + b = b_[randint(1, len(b_) - 1)] D.add_vertex(i) D.add_edge(a, i) D.add_edge(i, b) @@ -2053,19 +2058,19 @@ def _random_planar_lattice(n): """ from sage.misc.prandom import randint - G = DiGraph({0: [n-1]}) + G = DiGraph({0: [n - 1]}) while G.order() < n: - i = G.order()-1 - a = randint(0, i//2) + i = G.order() - 1 + a = randint(0, i // 2) b_ = list(G.depth_first_search(a)) - b = b_[randint(1, len(b_)-1)] + b = b_[randint(1, len(b_) - 1)] G1 = G.copy() G.add_vertex(i) G.add_edge(a, i) G.add_edge(i, b) G.delete_edge(a, b) G2 = G.copy() - G2.add_edge(n-1, 0) + G2.add_edge(n - 1, 0) if not G2.is_planar(): G = G1.copy() return G @@ -2102,7 +2107,7 @@ def _random_distributive_lattice(n): from sage.graphs.digraph_generators import digraphs if n < 4: - return digraphs.Path(n-1) + return digraphs.Path(n - 1) H = HasseDiagram({0: []}) while sum(1 for _ in H.antichains_iterator()) < n: @@ -2123,7 +2128,7 @@ def _random_distributive_lattice(n): for b in D.neighbors_out(to_delete): D.add_edge(a, b) D.delete_vertex(to_delete) - D.relabel({z:z-1 for z in range(to_delete + 1, D.order() + 1)}) + D.relabel({z: z - 1 for z in range(to_delete + 1, D.order() + 1)}) H = HasseDiagram(D) return D @@ -2177,4 +2182,5 @@ def _random_stone_lattice(n): return result + posets = Posets diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 2c236e4a4ae..2836d59d960 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -184,6 +184,7 @@ :meth:`~FinitePoset.flag_h_polynomial` | Return the flag h-polynomial of the poset. :meth:`~FinitePoset.order_polynomial` | Return the order polynomial of the poset. :meth:`~FinitePoset.zeta_polynomial` | Return the zeta polynomial of the poset. + :meth:`~FinitePoset.M_triangle` | Return the M-triangle of the poset. :meth:`~FinitePoset.kazhdan_lusztig_polynomial` | Return the Kazhdan-Lusztig polynomial of the poset. :meth:`~FinitePoset.coxeter_polynomial` | Return the characteristic polynomial of the Coxeter transformation. :meth:`~FinitePoset.degree_polynomial` | Return the generating polynomial of degrees of vertices in the Hasse diagram. @@ -286,6 +287,7 @@ from __future__ import annotations from collections import defaultdict from copy import copy, deepcopy +from typing import List from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -765,7 +767,8 @@ def Poset(data=None, element_labels=None, cover_relations=False, linear_extensio # Check for duplicate elements elif len(elements) != len(set(elements)): raise ValueError("the provided list of elements is not a linear " - "extension for the poset as it contains duplicate elements") + "extension for the poset as it contains " + "duplicate elements") else: elements = None return FinitePoset(D, elements=elements, category=category, facade=facade, key=key) @@ -2652,7 +2655,7 @@ def relations_number(self): # Maybe this should also be deprecated. intervals_number = relations_number - def linear_intervals_count(self) -> list[int]: + def linear_intervals_count(self) -> List[int]: """ Return the enumeration of linear intervals w.r.t. their cardinality. @@ -7257,6 +7260,47 @@ def zeta_polynomial(self): f = g[n] + f / n return f + def M_triangle(self): + r""" + Return the M-triangle of the poset. + + The poset is expected to be graded. + + OUTPUT: + + an :class:`~sage.combinat.triangles_FHM.M_triangle` + + The M-triangle is the generating polynomial of the Möbius numbers + + .. MATH:: + + M(x, y)=\sum_{a \leq b} \mu(a,b) x^{|a|}y^{|b|} . + + EXAMPLES:: + + sage: P = posets.DiamondPoset(5) + sage: P.M_triangle() + M: x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + + TESTS:: + + sage: P = posets.PentagonPoset() + sage: P.M_triangle() + Traceback (most recent call last): + ... + ValueError: the poset is not graded + """ + from sage.combinat.triangles_FHM import M_triangle + hasse = self._hasse_diagram + rk = hasse.rank_function() + if rk is None: + raise ValueError('the poset is not graded') + x, y = polygen(ZZ, 'x,y') + p = sum(hasse.moebius_function(a, b) * x**rk(a) * y**rk(b) + for a in hasse + for b in hasse.principal_order_filter(a)) + return M_triangle(p) + def f_polynomial(self): r""" Return the `f`-polynomial of the poset. @@ -7294,12 +7338,17 @@ def f_polynomial(self): sage: P = Poset({2: []}) sage: P.f_polynomial() 1 + + sage: P = Poset({2:[1,3]}) + sage: P.f_polynomial() + Traceback (most recent call last): + ... + ValueError: the poset is not bounded """ q = polygen(ZZ, 'q') - one = q.parent().one() hasse = self._hasse_diagram if len(hasse) == 1: - return one + return q.parent().one() maxi = hasse.top() mini = hasse.bottom() if mini is None or maxi is None: @@ -8106,7 +8155,7 @@ def is_eulerian(self, k=None, certificate=False): for rank_diff in range(2, k + 1, 2): for level in range(height - rank_diff): for i in levels[level]: - for j in levels[level+rank_diff]: + for j in levels[level + rank_diff]: if H.is_lequal(i, j) and M[i, j] != 1: if certificate: return (False, (self._vertex_to_element(i), @@ -8160,13 +8209,13 @@ def is_greedy(self, certificate=False): True """ H = self._hasse_diagram - N1 = H.order()-1 + N1 = H.order() - 1 it = H.greedy_linear_extensions_iterator() A = next(it) - A_jumps = sum(1 for i in range(N1) if H.has_edge(A[i], A[i+1])) + A_jumps = sum(1 for i in range(N1) if H.has_edge(A[i], A[i + 1])) for B in it: - B_jumps = sum(1 for i in range(N1) if H.has_edge(B[i], B[i+1])) + B_jumps = sum(1 for i in range(N1) if H.has_edge(B[i], B[i + 1])) if A_jumps != B_jumps: if certificate: if A_jumps > B_jumps: @@ -8452,13 +8501,15 @@ def p_partition_enumerator(self, tup, R, weights=None, check=False): # The simple case: ``weights == None``. F = QR.Fundamental() for lin in self.linear_extensions(facade=True): - descents = [i + 1 for i in range(n-1) if tupdict[lin[i]] > tupdict[lin[i+1]]] + descents = [i + 1 for i in range(n - 1) + if tupdict[lin[i]] > tupdict[lin[i + 1]]] res += F(Composition(from_subset=(descents, n))) return res for lin in self.linear_extensions(facade=True): M = QR.Monomial() lin_weights = Composition([weights.get(lin[i], 1) for i in range(n)]) - descents = [i + 1 for i in range(n-1) if tupdict[lin[i]] > tupdict[lin[i+1]]] + descents = [i + 1 for i in range(n - 1) + if tupdict[lin[i]] > tupdict[lin[i + 1]]] d_c = Composition(from_subset=(descents, n)) for comp in d_c.finer(): res += M[lin_weights.fatten(comp)] diff --git a/src/sage/combinat/ranker.py b/src/sage/combinat/ranker.py index 4c387d9a61e..f1e8928f73e 100644 --- a/src/sage/combinat/ranker.py +++ b/src/sage/combinat/ranker.py @@ -20,6 +20,7 @@ from sage.structure.parent import Parent from sage.categories.enumerated_sets import EnumeratedSets + def from_list(l): """ Returns a ranker from the list l. @@ -106,6 +107,7 @@ def rank_from_list(l): """ return CallableDict((x,i) for i,x in enumerate(l)) + def unrank_from_list(l): """ Returns an unrank function from a list. @@ -123,6 +125,7 @@ def unrank_from_list(l): unrank = lambda j: l[j] return unrank + def on_fly(): """ Returns a pair of enumeration functions rank / unrank. @@ -173,6 +176,7 @@ def unrank(i): return [rank, unrank] + def unrank(L, i): r""" Return the `i`-th element of `L`. diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 6652872cb6c..ed98f1dc243 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -901,8 +901,6 @@ def __bool__(self): return False return True - - def __hash__(self): r""" A hash value of this recognizable series. @@ -1762,7 +1760,6 @@ def __reduce__(self): return _pickle_RecognizableSeriesSpace, \ (self.coefficient_ring(), self.indices(), self.category()) - def alphabet(self): r""" Return the alphabet of this recognizable series space. diff --git a/src/sage/combinat/ribbon_shaped_tableau.py b/src/sage/combinat/ribbon_shaped_tableau.py index 736ee907f54..ef860b9703d 100644 --- a/src/sage/combinat/ribbon_shaped_tableau.py +++ b/src/sage/combinat/ribbon_shaped_tableau.py @@ -222,6 +222,7 @@ def from_shape_and_word(self, shape, word): pos += l return self.element_class(self, r) + class StandardRibbonShapedTableaux(StandardSkewTableaux): """ The set of all standard ribbon shaped tableaux. @@ -349,6 +350,7 @@ def from_permutation(self, p): r.reverse() return self.element_class(self, r) + class StandardRibbonShapedTableaux_shape(StandardRibbonShapedTableaux): """ Class of standard ribbon shaped tableaux of ribbon shape ``shape``. @@ -444,10 +446,12 @@ def __iter__(self): for p in descents_composition_list(self.shape): yield self.from_permutation(p) + class Ribbon_class(RibbonShapedTableau): """ This exists solely for unpickling ``Ribbon_class`` objects. """ + def __setstate__(self, state): r""" Unpickle old ``Ribbon_class`` objects. @@ -462,6 +466,7 @@ def __setstate__(self, state): self.__class__ = RibbonShapedTableau self.__init__(RibbonShapedTableaux(), state['_list']) + from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.ribbon', 'Ribbon_class', Ribbon_class) register_unpickle_override('sage.combinat.ribbon', 'StandardRibbons_shape', StandardRibbonShapedTableaux) diff --git a/src/sage/combinat/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 8b703ee721b..a2955884c7d 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -15,6 +15,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +import functools from sage.structure.parent import Parent from sage.structure.element import parent @@ -22,17 +23,15 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.sets_cat import Sets from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ from sage.rings.integer import Integer from sage.combinat.combinat import CombinatorialElement from sage.combinat.skew_partition import SkewPartition, SkewPartitions from sage.combinat.skew_tableau import SkewTableau, SkewTableaux, SemistandardSkewTableaux from sage.combinat.tableau import Tableaux from sage.combinat.partition import Partition, _Partitions -from . import permutation -import functools - from sage.combinat.permutation import to_standard +from sage.misc.persist import register_unpickle_override +from . import permutation class RibbonTableau(SkewTableau): @@ -76,8 +75,8 @@ class RibbonTableau(SkewTableau): sage: RibbonTableau([[0, 0, 3, 0], [1, 1, 0], [2, 0, 4]]).evaluation() [2, 1, 1, 1] """ - #The following method is private and will only get called - #when calling RibbonTableau() directly, and not via element_class + # The following method is private and will only get called + # when calling RibbonTableau() directly, and not via element_class @staticmethod def __classcall_private__(cls, rt=None, expr=None): """ @@ -98,7 +97,7 @@ def __classcall_private__(cls, rt=None, expr=None): raise TypeError("each element of the ribbon tableau must be an iterable") if not all(row for row in rt): raise TypeError("a ribbon tableau cannot have empty rows") - #calls the inherited __init__ method (of SkewTableau ) + # calls the inherited __init__ method (of SkewTableau ) return RibbonTableaux()(rt) def length(self): @@ -146,9 +145,9 @@ def to_word(self): from sage.combinat.words.word import Word return Word([letter for row in reversed(self) for letter in row]) -##################### -# Ribbon Tableaux # -##################### +# =================== +# Ribbon Tableaux +# =================== class RibbonTableaux(UniqueRepresentation, Parent): @@ -213,7 +212,7 @@ def __classcall_private__(cls, shape=None, weight=None, length=None): True """ if shape is None and weight is None and length is None: - return super(RibbonTableaux, cls).__classcall__(cls) + return super().__classcall__(cls) return RibbonTableaux_shape_weight_length(shape, weight, length) @@ -293,10 +292,10 @@ def __classcall_private__(cls, shape, weight, length): else: shape = SkewPartition(shape) - if shape.size() != length*sum(weight): - raise ValueError("Incompatible shape and weight") + if shape.size() != length * sum(weight): + raise ValueError("incompatible shape and weight") - return super(RibbonTableaux, cls).__classcall__(cls, shape, tuple(weight), length) + return super().__classcall__(cls, shape, tuple(weight), length) def __init__(self, shape, weight, length): """ @@ -305,7 +304,7 @@ def __init__(self, shape, weight, length): sage: R = RibbonTableaux([[2,1],[]],[1,1,1],1) sage: TestSuite(R).run() """ - self._shape = shape + self._shape = shape self._weight = weight self._length = length Parent.__init__(self, category=FiniteEnumeratedSets()) @@ -355,8 +354,8 @@ def __contains__(self, x): return False return x in self.list() - #return x.is_ribbon() and x.shape() == self._shape \ - #and tuple(x.weight()) == self._weight and x in list(self) + # return x.is_ribbon() and x.shape() == self._shape \ + # and tuple(x.weight()) == self._weight and x in list(self) def cardinality(self): """ @@ -449,14 +448,14 @@ def insertion_tableau(skp, perm, evaluation, tableau, length): [[1], [[1, 0], [0]]] """ psave = Partition(skp[1]) - partc = skp[1] + [0]*(len(skp[0])-len(skp[1])) + partc = skp[1] + [0] * (len(skp[0]) - len(skp[1])) tableau = SkewTableau(expr=tableau).to_expr()[1] for k in range(len(tableau)): - tableau[-(k+1)] += [0] * ( skp[0][k] - partc[k] - len(tableau[-(k+1)])) + tableau[-(k + 1)] += [0] * (skp[0][k] - partc[k] - len(tableau[-(k + 1)])) - ## We construct a tableau from the southwest corner to the northeast one + # We construct a tableau from the southwest corner to the northeast one tableau = [[0] * (skp[0][k] - partc[k]) for k in reversed(range(len(tableau), len(skp[0])))] + tableau @@ -464,16 +463,17 @@ def insertion_tableau(skp, perm, evaluation, tableau, length): tableau = tableau.to_expr()[1] skp = SkewPartition(skp).conjugate().to_list() - skp[1].extend( [0]*(len(skp[0])-len(skp[1])) ) + skp[1].extend([0] * (len(skp[0]) - len(skp[1]))) if len(perm) > len(skp[0]): return None - for k in range(len(perm)): - if perm[ -(k+1) ] !=0: - tableau[len(tableau)-len(perm)+k][ skp[0][len(perm)-(k+1)] - skp[1][ len(perm)-(k+1) ] - 1 ] = evaluation + lp = len(perm) + for k in range(lp): + if perm[-(k + 1)] != 0: + tableau[len(tableau) - lp + k][skp[0][lp - (k + 1)] - skp[1][lp - (k + 1)] - 1] = evaluation - return SkewTableau(expr=[psave.conjugate(),tableau]).conjugate().to_expr() + return SkewTableau(expr=[psave.conjugate(), tableau]).conjugate().to_expr() def count_rec(nexts, current, part, weight, length): @@ -547,30 +547,28 @@ def list_rec(nexts, current, part, weight, length): [[[], [[2, 2], [1, 1]]]] """ if current == [] and nexts == [] and weight == []: - return [[part[1],[]]] + return [[part[1], []]] - ## Test if the current nodes is not an empty node + # Test if the current nodes is not an empty node if not current: return [] - ## Test if the current nodes drive us to new solutions + # Test if the current nodes drive us to new solutions if nexts: - res = [] - for i in range(len(current)): - for j in range(len(nexts[i])): - res.append( insertion_tableau(part, current[i][1], len(weight), nexts[i][j], length) ) - return res - else: - ## The current nodes are at the bottom of the tree - res = [] - for i in range(len(current)): - res.append( insertion_tableau(part, current[i][1], len(weight), [[],[]], length) ) - return res + return [insertion_tableau(part, curr_i[1], len(weight), + nexts_ij, length) + for nexts_i, curr_i in zip(nexts, current) + for nexts_ij in nexts_i] + + # The current nodes are at the bottom of the tree + return [insertion_tableau(part, curr_i[1], + len(weight), [[], []], length) + for curr_i in current] -############################# -#Spin and Cospin Polynomials# -############################# +# =============================== +# Spin and Cospin Polynomials +# =============================== def spin_rec(t, nexts, current, part, weight, length): """ Routine used for constructing the spin polynomial. @@ -606,19 +604,18 @@ def spin_rec(t, nexts, current, part, weight, length): partp = part[0].conjugate() ell = len(partp) - #compute the contribution of the ribbons added at - #the current node + # compute the contribution of the ribbons added at + # the current node for val in current: perms = val[1] perm = [partp[i] + ell - (i + 1) - perms[i] for i in reversed(range(ell))] perm = to_standard(perm) - tmp.append( weight[-1]*(length-1) - perm.number_of_inversions() ) + tmp.append(weight[-1] * (length - 1) - perm.number_of_inversions()) if nexts: - return [ sum(sum(t**tval * nval for nval in nexts[i]) - for i, tval in enumerate(tmp)) ] - else: - return [ sum(t**val for val in tmp) ] + return [sum(sum(t**tval * nval for nval in nexts[i]) + for i, tval in enumerate(tmp))] + return [sum(t**val for val in tmp)] def spin_polynomial_square(part, weight, length): @@ -647,15 +644,16 @@ def spin_polynomial_square(part, weight, length): R = ZZ['t'] if part in _Partitions: - part = SkewPartition([part,_Partitions([])]) + part = SkewPartition([part, _Partitions([])]) elif part in SkewPartitions(): part = SkewPartition(part) - if part == [[],[]] and weight == []: + if part == [[], []] and not weight: return R.one() t = R.gen() - return R(graph_implementation_rec(part, weight, length, functools.partial(spin_rec,t))[0]) + return R(graph_implementation_rec(part, weight, length, + functools.partial(spin_rec, t))[0]) def spin_polynomial(part, weight, length): @@ -685,7 +683,7 @@ def spin_polynomial(part, weight, length): sp = spin_polynomial_square(part, weight, length) t = SR.var('t') coeffs = sp.list() - return sum(c * t**(QQ(i)/2) for i,c in enumerate(coeffs)) + return sum(c * t**(ZZ(i) / 2) for i, c in enumerate(coeffs)) def cospin_polynomial(part, weight, length): @@ -720,22 +718,22 @@ def cospin_polynomial(part, weight, length): if sp == 0: return R.zero() - coeffs = [c for c in sp.list() if c != 0] + coeffs = [c for c in sp.list() if c] d = len(coeffs) - 1 t = R.gen() - return R( sum(c * t**(d-i) for i,c in enumerate(coeffs)) ) + return R(sum(c * t**(d - i) for i, c in enumerate(coeffs))) -## ////////////////////////////////////////////////////////////////////////////////////////// -## // Generic function for driving into the graph of partitions coding all ribbons -## // tableaux of a given shape and weight -## ////////////////////////////////////////////////////////////////////////////////////////// -## //This function construct the graph of the set of k-ribbon tableaux -## //of a given skew shape and a given weight. -## //The first argument is always a skew partition. -## //In the case where the inner partition is empty there is no branch without solutions -## //In the other cases there is in average a lot of branches without solutions -## ///////////////////////////////////////////////////////////////////////////////////////// +# ////////////////////////////////////////////////////////////////////////////////////////// +# // Generic function for driving into the graph of partitions coding all ribbons +# // tableaux of a given shape and weight +# ////////////////////////////////////////////////////////////////////////////////////////// +# // This function constructs the graph of the set of k-ribbon tableaux +# // of a given skew shape and a given weight. +# // The first argument is always a skew partition. +# // In the case where the inner partition is empty, there is no branch without solutions. +# // In the other cases, there is in average a lot of branches without solutions. +# ///////////////////////////////////////////////////////////////////////////////////////// def graph_implementation_rec(skp, weight, length, function): @@ -760,16 +758,16 @@ def graph_implementation_rec(skp, weight, length, function): # Some tests in order to know if the shape and the weight are compatible. if weight and weight[-1] <= len(partp): - perms = permutation.Permutations([0]*(len(partp)-weight[-1]) + [length]*(weight[-1])).list() + perms = permutation.Permutations([0] * (len(partp) - weight[-1]) + [length] * (weight[-1])).list() else: return function([], [], skp, weight, length) selection = [] for j in range(len(perms)): - retire = [(val + ell - (i+1) - perms[j][i]) for i,val in enumerate(partp)] + retire = [(val + ell - (i + 1) - perms[j][i]) for i, val in enumerate(partp)] retire.sort(reverse=True) - retire = [val - ell + (i+1) for i,val in enumerate(retire)] + retire = [val - ell + (i + 1) for i, val in enumerate(retire)] if retire[-1] >= 0 and retire == sorted(retire, reverse=True): retire = Partition(retire).conjugate() @@ -784,19 +782,17 @@ def graph_implementation_rec(skp, weight, length, function): if append: selection.append([retire, perms[j]]) - #selection contains the list of current nodes + # selection contains the list of current nodes if len(weight) == 1: return function([], selection, skp, weight, length) else: - #The recursive calls permit us to construct the list of the sons - #of all current nodes in selection + # The recursive calls permit us to construct the list of the sons + # of all current nodes in selection a = [graph_implementation_rec([p[0], outer], weight[:-1], length, function) for p in selection] return function(a, selection, skp, weight, length) -############################################################## - class MultiSkewTableau(CombinatorialElement): """ @@ -828,12 +824,13 @@ def __classcall_private__(cls, x): """ if isinstance(x, MultiSkewTableau): return x - - return MultiSkewTableaux()([SkewTableau(i) for i in x] ) + return MultiSkewTableaux()([SkewTableau(i) for i in x]) def size(self): """ - Return the size of ``self``, which is the sum of the sizes of the skew + Return the size of ``self``. + + This is the sum of the sizes of the skew tableaux in ``self``. EXAMPLES:: @@ -858,7 +855,7 @@ def weight(self): """ weights = [x.weight() for x in self] m = max([len(x) for x in weights]) - weight = [0]*m + weight = [0] * m for w in weights: for i in range(len(w)): weight[i] += w[i] @@ -896,7 +893,7 @@ def inversion_pairs(self): inv = [] for k in range(len(self)): for b in self[k].cells(): - inv += self._inversion_pairs_from_position(k,b) + inv += self._inversion_pairs_from_position(k, b) return inv def inversions(self): @@ -937,25 +934,25 @@ def _inversion_pairs_from_position(self, k, ij): [((1, (0, 1)), (2, (0, 0)))] """ pk = k - pi,pj = ij + pi, pj = ij c = pi - pj value = self[pk][pi][pj] pk_cells = self[pk].cells_by_content(c) - same_diagonal = [ t.cells_by_content(c) for t in self[pk+1:] ] - above_diagonal = [ t.cells_by_content(c+1) for t in self[pk+1:] ] + same_diagonal = [t.cells_by_content(c) for t in self[pk + 1:]] + above_diagonal = [t.cells_by_content(c + 1) for t in self[pk + 1:]] res = [] - for i,j in pk_cells: + for i, j in pk_cells: if pi < i and value > self[pk][i][j]: - res.append( ((pk,(pi,pj)), (pk,(i,j))) ) + res.append(((pk, (pi, pj)), (pk, (i, j)))) for k in range(len(same_diagonal)): - for i,j in same_diagonal[k]: - if value > self[pk+k+1][i][j]: - res.append( ((pk,(pi,pj)), (pk+k+1,(i,j))) ) + for i, j in same_diagonal[k]: + if value > self[pk + k + 1][i][j]: + res.append(((pk, (pi, pj)), (pk + k + 1, (i, j)))) for k in range(len(above_diagonal)): - for i,j in above_diagonal[k]: - if value < self[pk+k+1][i][j]: - res.append( ((pk,(pi,pj)), (pk+k+1,(i,j))) ) + for i, j in above_diagonal[k]: + if value < self[pk + k + 1][i][j]: + res.append(((pk, (pi, pj)), (pk + k + 1, (i, j)))) return res @@ -963,6 +960,7 @@ class MultiSkewTableaux(UniqueRepresentation, Parent): r""" Multiskew tableaux. """ + def __init__(self, category=None): """ EXAMPLES:: @@ -1042,7 +1040,7 @@ def __classcall_private__(cls, shape, weight): if sum(weight) != sum(s.size() for s in shape): raise ValueError("the sum of weight must be the sum of the sizes of shape") - return super(SemistandardMultiSkewTableaux, cls).__classcall__(cls, shape, weight) + return super().__classcall__(cls, shape, weight) def __init__(self, shape, weight): """ @@ -1051,7 +1049,7 @@ def __init__(self, shape, weight): sage: S = SemistandardMultiSkewTableaux([ [[2,1],[]], [[2,2],[1]] ], [2,2,2]) sage: TestSuite(S).run() """ - self._shape = shape + self._shape = shape self._weight = weight MultiSkewTableaux.__init__(self, category=FiniteEnumeratedSets()) @@ -1080,14 +1078,9 @@ def __contains__(self, x): return False if x.weight() != list(self._weight): return False - if x.shape() != list(self._shape): return False - - if not all( x[i].is_semistandard() for i in range(len(x)) ): - return False - - return True + return all(xi.is_semistandard() for xi in x) def __iter__(self): """ @@ -1097,8 +1090,6 @@ def __iter__(self): sage: SemistandardMultiSkewTableaux([SkewPartition([[1, 1, 1], []]), SkewPartition([[3], []])],[2,2,2]).list() [[[[1], [2], [3]], [[1, 2, 3]]]] - :: - sage: a = SkewPartition([[8,7,6,5,1,1],[2,1,1]]) sage: weight = [3,3,2] sage: k = 3 @@ -1120,21 +1111,21 @@ def __iter__(self): for i in range(1, len(parts)): trans = parttmp[0][0] current_part = parts[i] - current_part[1] += [0]*(len(current_part[0])-len(current_part[1])) - inner_current = [ trans + j for j in current_part[1] ] - outer_current = [ trans + j for j in current_part[0] ] - parttmp = [ outer_current + parttmp[0], inner_current + parttmp[1] ] + current_part[1] += [0] * (len(current_part[0]) - len(current_part[1])) + inner_current = [trans + j for j in current_part[1]] + outer_current = [trans + j for j in current_part[0]] + parttmp = [outer_current + parttmp[0], inner_current + parttmp[1]] # List the corresponding skew tableaux - l = [ st.to_word() for st in SemistandardSkewTableaux(parttmp, mu) ] + l = (st.to_word() for st in SemistandardSkewTableaux(parttmp, mu)) S = SkewTableaux() - for k in range(len(l)): - pos = 0 #Double check this - restmp = [ S.from_shape_and_word(parts[0], [l[k][j] for j in range(s[0])]) ] + for lk in l: + pos = 0 # Double check this + restmp = [S.from_shape_and_word(parts[0], [lk[j] for j in range(s[0])])] for i in range(1, len(parts)): - w = [l[k][j] for j in range(pos+s[i-1], pos+s[i-1]+s[i])] - restmp.append( S.from_shape_and_word(parts[i], w) ) + w = [lk[j] for j in range(pos + s[i - 1], pos + s[i - 1] + s[i])] + restmp.append(S.from_shape_and_word(parts[i], w)) yield self.element_class(self, restmp) @@ -1142,6 +1133,7 @@ class RibbonTableau_class(RibbonTableau): """ This exists solely for unpickling ``RibbonTableau_class`` objects. """ + def __setstate__(self, state): r""" Unpickle old ``RibbonTableau_class`` objects. @@ -1156,7 +1148,7 @@ def __setstate__(self, state): self.__class__ = RibbonTableau self.__init__(RibbonTableaux(), state['_list']) -from sage.misc.persist import register_unpickle_override + register_unpickle_override('sage.combinat.ribbon_tableau', 'RibbonTableau_class', RibbonTableau_class) register_unpickle_override('sage.combinat.ribbon_tableau', 'RibbonTableaux_shapeweightlength', RibbonTableaux) register_unpickle_override('sage.combinat.ribbon_tableau', 'SemistandardMultiSkewTtableaux_shapeweight', SemistandardMultiSkewTableaux) diff --git a/src/sage/combinat/rigged_configurations/bij_abstract_class.py b/src/sage/combinat/rigged_configurations/bij_abstract_class.py index bbc3a4e5d04..c06680c4cf2 100644 --- a/src/sage/combinat/rigged_configurations/bij_abstract_class.py +++ b/src/sage/combinat/rigged_configurations/bij_abstract_class.py @@ -35,6 +35,7 @@ from copy import deepcopy from sage.misc.abstract_method import abstract_method + class KRTToRCBijectionAbstract: """ Root abstract class for the bijection from KR tableaux to rigged configurations. @@ -290,6 +291,7 @@ def _next_index(self, r, target): """ return r + 1 + class RCToKRTBijectionAbstract: """ Root abstract class for the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_infinity.py b/src/sage/combinat/rigged_configurations/bij_infinity.py index 4c675135019..fa1067ccd93 100644 --- a/src/sage/combinat/rigged_configurations/bij_infinity.py +++ b/src/sage/combinat/rigged_configurations/bij_infinity.py @@ -49,6 +49,7 @@ class FromTableauIsomorphism(Morphism): Crystal isomorphism of `B(\infty)` in the tableau model to the rigged configuration model. """ + def _repr_type(self): r""" Return the type of morphism of ``self``. @@ -114,11 +115,13 @@ def _call_(self, x): raise NotImplementedError("bijection of type {} not yet implemented".format(ct)) return self.codomain()(bij.run()) + class FromRCIsomorphism(Morphism): r""" Crystal isomorphism of `B(\infty)` in the rigged configuration model to the tableau model. """ + def _repr_type(self): r""" Return the type of morphism of ``self``. @@ -206,6 +209,7 @@ def _call_(self, x): c -= 1 return self.codomain()(*flatten(y)) + class MLTToRCBijectionTypeB(KRTToRCBijectionTypeB): def run(self): r""" @@ -244,6 +248,7 @@ def run(self): self.ret_rig_con.set_immutable() # Return it to immutable return self.ret_rig_con + class RCToMLTBijectionTypeB(RCToKRTBijectionTypeB): def run(self): r""" @@ -283,6 +288,7 @@ def run(self): return ret_crystal_path + class MLTToRCBijectionTypeD(KRTToRCBijectionTypeD): def run(self): r""" @@ -322,6 +328,7 @@ def run(self): self.ret_rig_con.set_immutable() # Return it to immutable return self.ret_rig_con + class RCToMLTBijectionTypeD(RCToKRTBijectionTypeD): def run(self): r""" diff --git a/src/sage/combinat/rigged_configurations/bij_type_A.py b/src/sage/combinat/rigged_configurations/bij_type_A.py index 0a7070297db..a700b0b4ba7 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A.py @@ -39,6 +39,7 @@ from sage.combinat.rigged_configurations.bij_abstract_class import KRTToRCBijectionAbstract from sage.combinat.rigged_configurations.bij_abstract_class import RCToKRTBijectionAbstract + class KRTToRCBijectionTypeA(KRTToRCBijectionAbstract): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -102,6 +103,7 @@ def next_state(self, val): if tableau_height > 0: self._update_vacancy_nums(tableau_height - 1) + class RCToKRTBijectionTypeA(RCToKRTBijectionAbstract): r""" Specific implementation of the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py index 8f41efa6474..934a92851a5 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py @@ -41,6 +41,7 @@ from sage.rings.rational_field import QQ + class KRTToRCBijectionTypeA2Dual(KRTToRCBijectionTypeC): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -199,6 +200,7 @@ def next_state(self, val): partition.rigging[i] = partition.rigging[i] - QQ(1)/QQ(2) break + class RCToKRTBijectionTypeA2Dual(RCToKRTBijectionTypeC): r""" Specific implementation of the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_even.py b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py index 7253b9b6579..76dcca00f1f 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_even.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py @@ -39,6 +39,7 @@ from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + class KRTToRCBijectionTypeA2Even(KRTToRCBijectionTypeC): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -119,6 +120,7 @@ def next_state(self, val): self._update_vacancy_nums(tableau_height - 1) self._update_partition_values(tableau_height - 1) + class RCToKRTBijectionTypeA2Even(RCToKRTBijectionTypeC): r""" Specific implementation of the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py b/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py index 753c2a99c74..477cbf6f9ab 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py @@ -38,6 +38,7 @@ from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + class KRTToRCBijectionTypeA2Odd(KRTToRCBijectionTypeA): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -106,6 +107,7 @@ def next_state(self, val): self._update_vacancy_nums(tableau_height - 1) self._update_partition_values(tableau_height - 1) + class RCToKRTBijectionTypeA2Odd(RCToKRTBijectionTypeA): r""" Specific implementation of the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index c73d2a41483..0f672e8fc2b 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -39,11 +39,13 @@ from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + class KRTToRCBijectionTypeB(KRTToRCBijectionTypeC): r""" Specific implementation of the bijection from KR tableaux to rigged configurations for type `B_n^{(1)}`. """ + def run(self, verbose=False): """ Run the bijection from a tensor product of KR tableaux to a rigged @@ -536,11 +538,13 @@ def other_outcome(self, rc, pos_val, width_n): self._update_vacancy_nums(tableau_height - 1) self._update_partition_values(tableau_height - 1) + class RCToKRTBijectionTypeB(RCToKRTBijectionTypeC): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `B_n^{(1)}`. """ + def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR diff --git a/src/sage/combinat/rigged_configurations/bij_type_C.py b/src/sage/combinat/rigged_configurations/bij_type_C.py index c571978c353..0d18e575043 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_C.py +++ b/src/sage/combinat/rigged_configurations/bij_type_C.py @@ -38,6 +38,7 @@ from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + class KRTToRCBijectionTypeC(KRTToRCBijectionTypeA): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -156,6 +157,7 @@ def _insert_cell_case_S(self, partition): partition.rigging[j+1] = None return + class RCToKRTBijectionTypeC(RCToKRTBijectionTypeA): r""" Specific implementation of the bijection from rigged configurations to diff --git a/src/sage/combinat/rigged_configurations/bij_type_D.py b/src/sage/combinat/rigged_configurations/bij_type_D.py index b45ec097ed0..a09c9383f1c 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D.py @@ -38,6 +38,7 @@ from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + class KRTToRCBijectionTypeD(KRTToRCBijectionTypeA): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -410,10 +411,12 @@ def halving_map(self): self.ret_rig_con[i].rigging[j] //= 2 self.ret_rig_con[i].vacancy_numbers[j] //= 2 + class RCToKRTBijectionTypeD(RCToKRTBijectionTypeA): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `D_n^{(1)}`. """ + def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR diff --git a/src/sage/combinat/rigged_configurations/bij_type_D_tri.py b/src/sage/combinat/rigged_configurations/bij_type_D_tri.py index 55159ab4f48..e0796bf880d 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D_tri.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D_tri.py @@ -38,6 +38,7 @@ from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + class KRTToRCBijectionTypeDTri(KRTToRCBijectionTypeA): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -46,6 +47,7 @@ class KRTToRCBijectionTypeDTri(KRTToRCBijectionTypeA): This inherits from type `A_n^{(1)}` because we use the same methods in some places. """ + def next_state(self, val): r""" Build the next state for type `D_4^{(3)}`. @@ -233,11 +235,13 @@ def next_state(self, val): P.rigging[j-1] -= 1 break + class RCToKRTBijectionTypeDTri(RCToKRTBijectionTypeA): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `D_4^{(3)}`. """ + def next_state(self, height): r""" Build the next state for type `D_4^{(3)}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py index 78ba3d4a071..e9c13e4b54d 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py @@ -41,6 +41,7 @@ from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD + class KRTToRCBijectionTypeDTwisted(KRTToRCBijectionTypeD, KRTToRCBijectionTypeA2Even): r""" Specific implementation of the bijection from KR tableaux to rigged @@ -49,6 +50,7 @@ class KRTToRCBijectionTypeDTwisted(KRTToRCBijectionTypeD, KRTToRCBijectionTypeA2 This inherits from type `C_n^{(1)}` and `D_n^{(1)}` because we use the same methods in some places. """ + def run(self, verbose=False): """ Run the bijection from a tensor product of KR tableaux to a rigged @@ -305,11 +307,13 @@ def next_state(self, val): partition.rigging[j-1] -= 1 break + class RCToKRTBijectionTypeDTwisted(RCToKRTBijectionTypeD, RCToKRTBijectionTypeA2Even): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `D_{n+1}^{(2)}`. """ + def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR diff --git a/src/sage/combinat/rigged_configurations/bij_type_E67.py b/src/sage/combinat/rigged_configurations/bij_type_E67.py index c0ebd5735cb..6fedd39c5a0 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_E67.py +++ b/src/sage/combinat/rigged_configurations/bij_type_E67.py @@ -41,11 +41,13 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method + class KRTToRCBijectionTypeE67(KRTToRCBijectionAbstract): r""" Specific implementation of the bijection from KR tableaux to rigged configurations for type `E_{6,7}^{(1)}`. """ + def next_state(self, val): r""" Build the next state for type `E_{6,7}^{(1)}`. @@ -211,11 +213,13 @@ def _endpoint(self, r): else: return endpoint7(r) + class RCToKRTBijectionTypeE67(RCToKRTBijectionAbstract): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `E_{6,7}^{(1)}`. """ + def next_state(self, r): r""" Build the next state for type `E_{6,7}^{(1)}`. @@ -321,6 +325,7 @@ def _endpoint(self, r): else: return endpoint7(r) + def endpoint6(r): """ Return the endpoint for `B^{r,1}` in type `E_6^{(1)}`. @@ -355,6 +360,7 @@ def endpoint6(r): elif r == 6: return C((-1, 6)) + def endpoint7(r): """ Return the endpoint for `B^{r,1}` in type `E_7^{(1)}`. diff --git a/src/sage/combinat/rigged_configurations/bijection.py b/src/sage/combinat/rigged_configurations/bijection.py index 56fcbb98394..f312f12930c 100644 --- a/src/sage/combinat/rigged_configurations/bijection.py +++ b/src/sage/combinat/rigged_configurations/bijection.py @@ -56,6 +56,7 @@ from sage.combinat.rigged_configurations.bij_type_E67 import KRTToRCBijectionTypeE67 from sage.combinat.rigged_configurations.bij_type_E67 import RCToKRTBijectionTypeE67 + def KRTToRCBijection(tp_krt): r""" Return the correct KR tableaux to rigged configuration bijection helper class. @@ -97,6 +98,7 @@ def KRTToRCBijection(tp_krt): return KRTToRCBijectionTypeDTri(tp_krt) raise NotImplementedError + def RCToKRTBijection(rigged_configuration_elt): r""" Return the correct rigged configuration to KR tableaux bijection helper class. diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index df5db0f5fe8..e6c73af4d50 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -90,6 +90,7 @@ # Latex method for viewing the trees # ###################################### + def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=white', style_line=None, hspace=2.5, vspace=-2.5, start=None, rpos=None, node_id=0, node_prefix='T', edge_labels=True, use_vector_notation=False): @@ -199,6 +200,7 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh # Kleber tree nodes # ##################### + class KleberTreeNode(Element): r""" A node in the Kleber tree. @@ -218,6 +220,7 @@ class KleberTreeNode(Element): - ``dominant_root`` -- The dominating root - ``parent_node`` -- (default:None) The parent node of this node """ + def __init__(self, parent_obj, node_weight, dominant_root, parent_node=None): r""" Initialize the tree node. @@ -495,6 +498,7 @@ def _latex_(self): # Kleber tree classes # ####################### + class KleberTree(UniqueRepresentation, Parent): r""" The tree that is generated by Kleber's algorithm. @@ -1317,6 +1321,7 @@ def base_tree(self): """ return KleberTree(self._folded_ct.folding_of(), self.B) + class KleberTreeTypeA2Even(VirtualKleberTree): r""" Kleber tree for types `A_{2n}^{(2)}` and `A_{2n}^{(2)\dagger}`. diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 5c79e0807ef..84e3db6640c 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -62,6 +62,7 @@ from sage.combinat.partition import Partition from sage.combinat.tableau import Tableau + class KirillovReshetikhinTableaux(CrystalOfWords): r""" Kirillov-Reshetikhin tableaux. @@ -568,6 +569,7 @@ def _tableau_height(self): """ return self._r + class KRTableauxRectangle(KirillovReshetikhinTableaux): r""" Kirillov-Reshetkhin tableaux `B^{r,s}` whose module generator is a single @@ -585,6 +587,7 @@ class KRTableauxRectangle(KirillovReshetikhinTableaux): sage: KRT = crystals.KirillovReshetikhin(['C', 3, 1], 3, 2, model='KR') sage: TestSuite(KRT).run() # long time """ + def _build_module_generators(self): r""" Build the module generators. @@ -625,6 +628,7 @@ def from_kirillov_reshetikhin_crystal(self, krc): f_str = reversed(krc.lift().to_highest_weight()[1]) return self.module_generators[0].f_string(f_str) + class KRTableauxTypeVertical(KirillovReshetikhinTableaux): r""" Kirillov-Reshetkihn tableaux `B^{r,s}` of type: @@ -642,6 +646,7 @@ class KRTableauxTypeVertical(KirillovReshetikhinTableaux): sage: KRT = crystals.KirillovReshetikhin(['A', 5, 2], 2, 2, model='KR') sage: TestSuite(KRT).run() # long time """ + def _fill(self, weight): r""" Return the highest weight KR tableau of weight ``weight``. @@ -754,6 +759,7 @@ def from_kirillov_reshetikhin_crystal(self, krc): f_str = reversed(lifted.to_highest_weight()[1]) return self._fill(weight).f_string(f_str) + class KRTableauxTypeHorizonal(KirillovReshetikhinTableaux): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type: @@ -768,6 +774,7 @@ class KRTableauxTypeHorizonal(KirillovReshetikhinTableaux): sage: KRT = crystals.KirillovReshetikhin(CartanType(['A', 4, 2]).dual(), 2, 2, model='KR') sage: TestSuite(KRT).run() """ + def _fill(self, shape): r""" Return the highest weight KR tableau of weight ``shape``. @@ -847,6 +854,7 @@ def from_kirillov_reshetikhin_crystal(self, krc): f_str = reversed(lifted.to_highest_weight()[1]) return self._fill(shape).f_string(f_str) + class KRTableauxTypeBox(KRTableauxTypeVertical): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type: @@ -864,6 +872,7 @@ class KRTableauxTypeBox(KRTableauxTypeVertical): sage: KRT = crystals.KirillovReshetikhin(['D', 4, 3], 1, 2, model='KR') sage: TestSuite(KRT).run() # long time """ + def _fill(self, weight): r""" Return the highest weight KR tableau of weight ``weight``. @@ -951,6 +960,7 @@ def _build_module_generators(self): """ return tuple(self._fill(weight) for weight in partitions_in_box(self._s, self._r)) + class KRTableauxSpin(KRTableauxRectangle): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type `D_n^{(1)}` with @@ -963,6 +973,7 @@ class KRTableauxSpin(KRTableauxRectangle): sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 4, 2, model='KR') sage: TestSuite(KRT).run() """ + def _build_module_generators(self): r""" Build the module generators. @@ -989,6 +1000,7 @@ def _build_module_generators(self): return (self.element_class(self, [self.letters(x) for x in flatten(tableau)]),) + class KRTableauxBn(KRTableauxTypeHorizonal): """ Kirillov-Reshetkhin tableaux `B^{n,s}` of type `B_n^{(1)}`. @@ -998,6 +1010,7 @@ class KRTableauxBn(KRTableauxTypeHorizonal): sage: KRT = crystals.KirillovReshetikhin(['B', 2, 1], 2, 3, model='KR') sage: TestSuite(KRT).run() """ + def _build_module_generators(self): """ Build the module generators. @@ -1040,6 +1053,7 @@ def from_kirillov_reshetikhin_crystal(self, krc): return x.f_string(f_str) raise ValueError("no matching highest weight element found") + class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): r""" A Kirillov-Reshetikhin tableau. @@ -1049,6 +1063,7 @@ class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): and :class:`~sage.combinat.rigged_configurations.tensor_product_kr_tableaux.TensorProductOfKirillovReshetikhinTableaux`. """ + def __init__(self, parent, list, **options): r""" Initialize ``self``. @@ -1459,8 +1474,10 @@ def right_split(self): """ return self.lusztig_involution().left_split().lusztig_involution() + KirillovReshetikhinTableaux.Element = KirillovReshetikhinTableauxElement + class KRTableauxSpinElement(KirillovReshetikhinTableauxElement): r""" Kirillov-Reshetikhin tableau for spinors. @@ -1470,6 +1487,7 @@ class KRTableauxSpinElement(KirillovReshetikhinTableauxElement): respectively for all `i \neq 0`. We do this so our columns are full width (as opposed to half width and/or uses a `\pm` representation). """ + def e(self, i): r""" Calculate the action of `e_i` on ``self``. @@ -1650,9 +1668,11 @@ def classical_weight(self): WLR = F.ambient_space() return sum((self[j].weight() for j in range(len(self))), WLR.zero()) / 2 + KRTableauxBn.Element = KRTableauxSpinElement KRTableauxSpin.Element = KRTableauxSpinElement + class KRTableauxDTwistedSpin(KRTableauxRectangle): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type `D_n^{(2)}` with `r = n`. @@ -1668,11 +1688,13 @@ class KRTableauxDTwistedSpin(KRTableauxRectangle): """ Element = KRTableauxSpinElement + class KRTableauxTypeFromRCElement(KirillovReshetikhinTableauxElement): r""" A Kirillov-Reshetikhin tableau constructed from rigged configurations under the bijection `\Phi`. """ + def e(self, i): """ Perform the action of `e_i` on ``self``. @@ -1787,6 +1809,7 @@ class KRTableauxTypeFromRC(KirillovReshetikhinTableaux): non-trivial multiplicities of classical components, so :meth:`classical_decomposition` does not work. """ + def __init__(self, cartan_type, r, s): r""" Initialize ``self``. diff --git a/src/sage/combinat/rigged_configurations/rc_crystal.py b/src/sage/combinat/rigged_configurations/rc_crystal.py index 21ffd583378..fbd149f6c0c 100644 --- a/src/sage/combinat/rigged_configurations/rc_crystal.py +++ b/src/sage/combinat/rigged_configurations/rc_crystal.py @@ -38,6 +38,8 @@ from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition # Note on implementation, this class is used for simply-laced types only + + class CrystalOfRiggedConfigurations(UniqueRepresentation, Parent): r""" A highest weight crystal of rigged configurations. @@ -296,10 +298,12 @@ def weight_lattice_realization(self): Element = RCHighestWeightElement + class CrystalOfNonSimplyLacedRC(CrystalOfRiggedConfigurations): """ Highest weight crystal of rigged configurations in non-simply-laced type. """ + def __init__(self, vct, wt, WLR): """ Initialize ``self``. diff --git a/src/sage/combinat/rigged_configurations/rc_infinity.py b/src/sage/combinat/rigged_configurations/rc_infinity.py index d9940e12dfb..b988c5a6ff2 100644 --- a/src/sage/combinat/rigged_configurations/rc_infinity.py +++ b/src/sage/combinat/rigged_configurations/rc_infinity.py @@ -300,6 +300,7 @@ class Element(RiggedConfigurationElement): sage: elt = RC(partition_list=[[1,1]]*4, rigging_list=[[1,1], [0,0], [0,0], [-1,-1]]) sage: TestSuite(elt).run() """ + def weight(self): """ Return the weight of ``self``. @@ -315,10 +316,12 @@ def weight(self): alpha = list(P.simple_roots()) return -sum(sum(x) * alpha[i] for i, x in enumerate(self)) + class InfinityCrystalOfNonSimplyLacedRC(InfinityCrystalOfRiggedConfigurations): r""" Rigged configurations for `\mathcal{B}(\infty)` in non-simply-laced types. """ + def __init__(self, vct): """ Initialize ``self``. @@ -506,6 +509,7 @@ class Element(RCNonSimplyLacedElement): sage: elt = RC(partition_list=[[1],[1,1],[1]]) sage: TestSuite(elt).run() """ + def weight(self): """ Return the weight of ``self``. diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 4ef6be9811c..727322f0efd 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -144,6 +144,7 @@ class RiggedConfigurationElement(ClonableArray): (/) 1[ ]0 0[ ]0 0[ ]0 0[ ]0 0[ ]0 """ + def __init__(self, parent, rigged_partitions=[], **options): r""" Construct a rigged configuration element. @@ -600,7 +601,6 @@ def e(self, a): new_vac_nums[i] += M[a,b] new_rigging[i] += M[a,b] - if k != 1 and not set_vac_num: # If we did not remove a row nor found another row of length k-1 new_vac_nums[rigging_index] += 2 @@ -922,6 +922,7 @@ def partition_rigging_lists(self): riggings.append(list(p.rigging)) return [partitions, riggings] + class RCNonSimplyLacedElement(RiggedConfigurationElement): """ Rigged configuration elements for non-simply-laced types. @@ -941,6 +942,7 @@ class RCNonSimplyLacedElement(RiggedConfigurationElement): <BLANKLINE> sage: TestSuite(elt).run() """ + def to_virtual_configuration(self): """ Return the corresponding rigged configuration in the virtual crystal. @@ -1040,6 +1042,7 @@ def f(self, a): ## Highest weight crystal rigged configuration elements ## ########################################################## + class RCHighestWeightElement(RiggedConfigurationElement): """ Rigged configurations in highest weight crystals. @@ -1059,6 +1062,7 @@ class RCHighestWeightElement(RiggedConfigurationElement): <BLANKLINE> sage: TestSuite(elt).run() """ + def check(self): """ Make sure all of the riggings are less than or equal to the @@ -1144,6 +1148,7 @@ def weight(self): alpha = list(P.simple_roots()) return self.parent()._wt - sum(sum(x) * alpha[i] for i,x in enumerate(self)) + class RCHWNonSimplyLacedElement(RCNonSimplyLacedElement): """ Rigged configurations in highest weight crystals. @@ -1158,6 +1163,7 @@ class RCHWNonSimplyLacedElement(RCNonSimplyLacedElement): -1[ ]-1 sage: TestSuite(elt).run() """ + def check(self): """ Make sure all of the riggings are less than or equal to the @@ -1226,6 +1232,7 @@ def weight(self): ## KR crystal rigged configuration elements ## ############################################## + class KRRiggedConfigurationElement(RiggedConfigurationElement): r""" `U_q^{\prime}(\mathfrak{g})` rigged configurations. @@ -1251,6 +1258,7 @@ class KRRiggedConfigurationElement(RiggedConfigurationElement): sage: tp_krtab.to_rigged_configuration() == rc_elt True """ + def __init__(self, parent, rigged_partitions=[], **options): r""" Construct a rigged configuration element. @@ -1552,7 +1560,6 @@ def classical_weight(self): wt -= sum(nu) * alpha[rc_index[a]] return wt - def to_tensor_product_of_kirillov_reshetikhin_tableaux(self, display_steps=False, build_graph=False): r""" Perform the bijection from this rigged configuration to a tensor @@ -2064,6 +2071,7 @@ def complement_rigging(self, reverse_factors=False): rc = P(partition_list=nu, rigging_list=rig) return rc.f_string(reversed(e_str)) + class KRRCSimplyLacedElement(KRRiggedConfigurationElement): r""" `U_q^{\prime}(\mathfrak{g})` rigged configurations in simply-laced types. @@ -2143,6 +2151,7 @@ def charge(self): B._max_charge = max(b.cocharge() for b in B.module_generators) return B._max_charge - self.cocharge() + class KRRCNonSimplyLacedElement(KRRiggedConfigurationElement, RCNonSimplyLacedElement): r""" `U_q^{\prime}(\mathfrak{g})` rigged configurations in non-simply-laced @@ -2158,6 +2167,7 @@ class KRRCNonSimplyLacedElement(KRRiggedConfigurationElement, RCNonSimplyLacedEl 0[ ][ ]0 sage: TestSuite(elt).run() """ + def e(self, a): r""" Return the action of `e_a` on ``self``. @@ -2289,11 +2299,13 @@ def cocharge(self): cc = cocharge + class KRRCTypeA2DualElement(KRRCNonSimplyLacedElement): r""" `U_q^{\prime}(\mathfrak{g})` rigged configurations in type `A_{2n}^{(2)\dagger}`. """ + def epsilon(self, a): r""" Return the value of `\varepsilon_a` of ``self``. diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 4850d20cbb9..a7073a700bb 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -1065,6 +1065,7 @@ def tensor(self, *crystals, **options): Element = KRRCSimplyLacedElement + class RCNonSimplyLaced(RiggedConfigurations): r""" Rigged configurations in non-simply-laced types. @@ -1405,6 +1406,7 @@ def _test_virtual_vacancy_numbers(self, **options): Element = KRRCNonSimplyLacedElement + class RCTypeA2Even(RCNonSimplyLaced): """ Rigged configurations for type `A_{2n}^{(2)}`. @@ -1424,6 +1426,7 @@ class RCTypeA2Even(RCNonSimplyLaced): sage: RC = RiggedConfigurations(['A',4,2], [[2,1]]) sage: TestSuite(RC).run() # long time """ + def cardinality(self): """ Return the cardinality of ``self``. @@ -1550,7 +1553,6 @@ def to_virtual(self, rc): [vac_num*g for vac_num in rp.vacancy_numbers]) return self.virtual.element_class(self.virtual, partitions, use_vacancy_numbers=True) - def from_virtual(self, vrc): """ Convert ``vrc`` in the virtual crystal into a rigged configuration of @@ -1611,6 +1613,7 @@ class RCTypeA2Dual(RCTypeA2Even): sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,1]]) sage: TestSuite(RC).run() # long time """ + def _calc_vacancy_number(self, partitions, a, i, **options): r""" Calculate the vacancy number `p_i^{(a)}` in ``self``. A special case diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 1f63c5fd6f7..7f66de8c32d 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -74,6 +74,7 @@ from sage.rings.integer import Integer + class HighestWeightTensorKRT(UniqueRepresentation): """ Class so we do not have to build the module generators for @@ -84,6 +85,7 @@ class HighestWeightTensorKRT(UniqueRepresentation): This class is for internal use only! """ + def __init__(self, tp_krt): """ Initialize ``self``. @@ -171,6 +173,7 @@ def cardinality(self): __len__ = cardinality + class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCrystals): r""" A tensor product of @@ -491,4 +494,5 @@ def tensor(self, *crystals, **options): return TensorProductOfKirillovReshetikhinTableaux(ct, dims) return super(TensorProductOfKirillovReshetikhinTableaux, self).tensor(*crystals, **options) + TensorProductOfKirillovReshetikhinTableaux.Element = TensorProductOfKirillovReshetikhinTableauxElement diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py index 87e8d0c97d0..fb43642a004 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py @@ -26,6 +26,7 @@ from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement + class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): """ An element in a tensor product of Kirillov-Reshetikhin tableaux. @@ -109,6 +110,7 @@ class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCr sage: T.to_rigged_configuration().to_tensor_product_of_kirillov_reshetikhin_tableaux() [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] """ + def __init__(self, parent, list=[[]], **options): r""" Construct a TensorProductOfKirillovReshetikhinTableauxElement. diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index e49d72ea222..263a4d916d2 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -76,7 +76,7 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` -- :ref:`sage.combinat.root_system.fusion_ring` +- :ref:`sage.algebras.fusion_rings.fusion_ring` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` @@ -136,7 +136,6 @@ lazy_import('sage.combinat.root_system.coxeter_group', 'CoxeterGroup') lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', 'WeightRing']) -lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index b1499f4eb4d..b5e4a90e6ad 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -1,13 +1,13 @@ r""" Ambient lattices and ambient spaces """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008-2009 Daniel Bump # Copyright (C) 2008-2013 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.misc.cachefunc import cached_method from sage.combinat.free_module import CombinatorialFreeModule from .weight_lattice_realizations import WeightLatticeRealizations @@ -71,6 +71,7 @@ class AmbientSpace(CombinatorialFreeModule): sage: e1 == e2 False """ + def __init__(self, root_system, base_ring, index_set=None): """ EXAMPLES:: @@ -194,7 +195,6 @@ def __call__(self, v): else: return CombinatorialFreeModule.__call__(self, v) - def __getitem__(self,i): """ Note that indexing starts at 1. @@ -353,6 +353,7 @@ def to_ambient_space_morphism(self): """ return End(self).identity() + class AmbientSpaceElement(CombinatorialFreeModule.Element): # For backward compatibility def _repr_(self): @@ -487,7 +488,7 @@ def coerce_to_e6(self): v0 = self.parent()._v0 v1 = self.parent()._v1 x = x - (x.inner_product(v0)/2)*v0 - return x - (x.inner_product(v1)/6)*v1 + return x - (x.inner_product(v1)/6)*v1 def to_ambient(self): r""" @@ -506,4 +507,5 @@ def to_ambient(self): """ return self + AmbientSpace.Element = AmbientSpaceElement diff --git a/src/sage/combinat/root_system/associahedron.py b/src/sage/combinat/root_system/associahedron.py index c6765e71eb9..578007985fd 100644 --- a/src/sage/combinat/root_system/associahedron.py +++ b/src/sage/combinat/root_system/associahedron.py @@ -11,14 +11,14 @@ - Christian Stump """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2011-2012 Christian Stump <christian.stump@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd @@ -261,18 +261,23 @@ def vertices_in_root_space(self): return tuple(root_space.from_vector(vector(V)) for V in self.vertex_generator()) + class Associahedron_class_ppl(Associahedron_class_base, Polyhedron_QQ_ppl): pass + class Associahedron_class_normaliz(Associahedron_class_base, Polyhedron_QQ_normaliz): pass + class Associahedron_class_cdd(Associahedron_class_base, Polyhedron_QQ_cdd): pass + class Associahedron_class_polymake(Associahedron_class_base, Polyhedron_polymake): pass + class Associahedron_class_field(Associahedron_class_base, Polyhedron_field): pass @@ -338,6 +343,7 @@ class Associahedra_base(): ... ValueError: V-representation data requires a list of length ambient_dim """ + def _element_constructor_(self, cartan_type, **kwds): """ The element constructor. @@ -444,17 +450,22 @@ def _pushout_(self, other): if hasattr(super(), '_pushout_'): return super()._pushout_(other) + class Associahedra_ppl(Associahedra_base, Polyhedra_QQ_ppl): Element = Associahedron_class_ppl + class Associahedra_normaliz(Associahedra_base, Polyhedra_QQ_normaliz): Element = Associahedron_class_normaliz + class Associahedra_cdd(Associahedra_base, Polyhedra_QQ_cdd): Element = Associahedron_class_cdd + class Associahedra_polymake(Associahedra_base, Polyhedra_polymake): Element = Associahedron_class_polymake + class Associahedra_field(Associahedra_base, Polyhedra_field): Element = Associahedron_class_field diff --git a/src/sage/combinat/root_system/braid_move_calculator.py b/src/sage/combinat/root_system/braid_move_calculator.py index bbada153247..10991bf2504 100644 --- a/src/sage/combinat/root_system/braid_move_calculator.py +++ b/src/sage/combinat/root_system/braid_move_calculator.py @@ -22,6 +22,7 @@ class BraidMoveCalculator(): """ Helper class to compute braid moves. """ + def __init__(self, coxeter_group): """ Initialize ``self``. diff --git a/src/sage/combinat/root_system/branching_rules.py b/src/sage/combinat/root_system/branching_rules.py index 4947d1357db..9c8967331b8 100644 --- a/src/sage/combinat/root_system/branching_rules.py +++ b/src/sage/combinat/root_system/branching_rules.py @@ -1009,6 +1009,7 @@ class BranchingRule(SageObject): """ A class for branching rules. """ + def __init__(self, R, S, f, name="default", intermediate_types=[], intermediate_names=[]): """ @@ -1950,6 +1951,7 @@ def f(x): raise ValueError("Wrong source Cartan Type for rule %s" % rule) raise ValueError("Rule not found") + get_branching_rule = branching_rule diff --git a/src/sage/combinat/root_system/cartan_matrix.py b/src/sage/combinat/root_system/cartan_matrix.py index a4d66256bf5..2cf987db202 100644 --- a/src/sage/combinat/root_system/cartan_matrix.py +++ b/src/sage/combinat/root_system/cartan_matrix.py @@ -1014,6 +1014,7 @@ def indecomposable_blocks(self): subgraphs = self.dynkin_diagram().connected_components_subgraphs() return tuple(CartanMatrix(subg._matrix_().rows()) for subg in subgraphs) + def is_borcherds_cartan_matrix(M): """ Return ``True`` if ``M`` is an even, integral Borcherds-Cartan matrix. @@ -1054,6 +1055,7 @@ def is_borcherds_cartan_matrix(M): return False return True + def is_generalized_cartan_matrix(M): """ Return ``True`` if ``M`` is a generalized Cartan matrix. For a definition @@ -1083,6 +1085,7 @@ def is_generalized_cartan_matrix(M): n = M.ncols() return all(M[i,i] == 2 for i in range(n)) + def find_cartan_type_from_matrix(CM): r""" Find a Cartan type by direct comparison of Dynkin diagrams given from diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index dafe2f62366..825fc95ee16 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -465,7 +465,7 @@ .. TODO:: Should those indexes come before the introduction? """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, # Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net>, # @@ -473,8 +473,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.cachefunc import cached_method from sage.misc.abstract_method import abstract_method @@ -594,7 +594,7 @@ def __call__(self, *args): if isinstance(t, (CartanType_abstract, SuperCartanType_standard)): return t - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if isinstance(t, str): if "x" in t: from . import type_reducible @@ -828,29 +828,29 @@ def _samples(self): ['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['BC', 1, 2], ['BC', 5, 2], ['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2]^*, ['BC', 5, 2]^*] """ - finite_crystallographic = \ - [CartanType (t) for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], - ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5], - ["E", 6], ["E", 7], ["E", 8], - ["F", 4], - ["G", 2]]] + finite_crystallographic = [CartanType(t) + for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], + ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5], + ["E", 6], ["E", 7], ["E", 8], + ["F", 4], + ["G", 2]]] # Support for hand constructed Dynkin diagrams as Cartan types is not yet ready enough for including an example here. # from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class # g = DynkinDiagram_class.an_instance() return finite_crystallographic + \ - [CartanType(t) for t in [["I", 5], ["H", 3], ["H", 4]]] + \ - [t.affine() for t in finite_crystallographic if t.is_irreducible()] + \ - [CartanType(t) for t in [["BC", 1, 2], ["BC", 5, 2]]] + \ + [CartanType(t) for t in [["I", 5], ["H", 3], ["H", 4]]] + \ + [t.affine() for t in finite_crystallographic if t.is_irreducible()] + \ + [CartanType(t) for t in [["BC", 1, 2], ["BC", 5, 2]]] + \ [CartanType(t).dual() for t in [["B", 5, 1], ["C", 4, 1], ["F", 4, 1], ["G", 2, 1],["BC", 1, 2], ["BC", 5, 2]]] #+ \ # [ g ] - _colors = {1: 'blue', -1: 'blue', - 2: 'red', -2: 'red', - 3: 'green', -3: 'green', - 4: 'cyan', -4: 'cyan', + _colors = {1: 'blue', -1: 'blue', + 2: 'red', -2: 'red', + 3: 'green', -3: 'green', + 4: 'cyan', -4: 'cyan', 5: 'magenta', -5: 'magenta', - 6: 'yellow', -6: 'yellow'} + 6: 'yellow', -6: 'yellow'} @classmethod def color(cls, i): @@ -973,6 +973,7 @@ class options(GlobalOptions): CartanType = CartanTypeFactory() CartanType.__doc__ = __doc__ + class CartanType_abstract(): r""" Abstract class for Cartan types @@ -1524,6 +1525,7 @@ def _default_folded_cartan_type(self): options = CartanType.options + class CartanType_crystallographic(CartanType_abstract): """ An abstract class for crystallographic Cartan types. @@ -1778,10 +1780,12 @@ def index_set_bipartition(self): raise ValueError("the Dynkin diagram must be bipartite") return G.bipartite_sets() + class CartanType_simply_laced(CartanType_crystallographic): """ An abstract class for simply laced Cartan types. """ + def is_simply_laced(self): """ Return whether ``self`` is simply laced, which is ``True``. @@ -1816,10 +1820,12 @@ def dual(self): """ return self + class CartanType_simple(CartanType_abstract): """ An abstract class for simple Cartan types. """ + def is_irreducible(self): """ Return whether ``self`` is irreducible, which is ``True``. @@ -1831,10 +1837,12 @@ def is_irreducible(self): """ return True + class CartanType_finite(CartanType_abstract): """ An abstract class for simple affine Cartan types. """ + def is_finite(self): """ EXAMPLES:: @@ -1853,6 +1861,7 @@ def is_affine(self): """ return False + class CartanType_affine(CartanType_simple, CartanType_crystallographic): """ An abstract class for simple affine Cartan types @@ -1875,7 +1884,7 @@ def _ascii_art_node(self, label): if (label == self.special_node() and self.options('mark_special_node') in ['printing', 'both']): return self.options('special_node_str') - return super(CartanType_affine, self)._ascii_art_node(label) + return super()._ascii_art_node(label) def _latex_draw_node(self, x, y, label, position="below=4pt"): r""" @@ -1896,7 +1905,7 @@ def _latex_draw_node(self, x, y, label, position="below=4pt"): fill = 'black' else: fill = 'white' - return super(CartanType_affine, self)._latex_draw_node(x, y, label, position, fill) + return super()._latex_draw_node(x, y, label, position, fill) def is_finite(self): """ @@ -2025,7 +2034,7 @@ def classical(self): We check that :meth:`classical`, :meth:`sage.combinat.root_system.cartan_type.CartanType_crystallographic.dynkin_diagram`, - and :meth:`.special_node` are consistent:: + and :meth:`special_node` are consistent:: sage: for ct in CartanType.samples(affine = True): ....: g1 = ct.classical().dynkin_diagram() @@ -2193,12 +2202,15 @@ def c(self): def translation_factors(self): r""" - Returns the translation factors for ``self``. Those are the - smallest factors `t_i` such that the translation by `t_i - \alpha_i` maps the fundamental polygon to another polygon in - the alcove picture. + Return the translation factors for ``self``. - OUTPUT: a dictionary from ``self.index_set()`` to `\ZZ` + Those are the smallest factors `t_i` such that the translation + by `t_i \alpha_i` maps the fundamental polygon to another + polygon in the alcove picture. + + OUTPUT: + + a dictionary from ``self.index_set()`` to `\ZZ` (or `\QQ` for affine type `BC`) Those coefficients are all `1` for dual untwisted, and in @@ -2206,9 +2218,11 @@ def translation_factors(self): `c_i` coefficients (see :meth:`c`) for untwisted and dual thereof. See the discussion below for affine type `BC`. - Note: one usually realizes the alcove picture in the coweight - lattice, with translations by coroots; in that case, one will - use the translation factors for the dual Cartan type. + .. NOTE:: + + One usually realizes the alcove picture in the coweight + lattice, with translations by coroots; in that case, one will + use the translation factors for the dual Cartan type. FIXME: the current implementation assumes that the Cartan matrix is indexed by `[0,1,...]`, in the same order as the @@ -2406,6 +2420,7 @@ def other_affinization(self): ############################################################################## # Concrete base classes + class CartanType_standard(UniqueRepresentation, SageObject): # Technical methods def _repr_(self, compact = False): @@ -2470,6 +2485,7 @@ class CartanType_standard_finite(CartanType_standard, CartanType_finite): sage: ct1 != ct3 True """ + def __init__(self, letter, n): """ EXAMPLES:: @@ -2658,6 +2674,8 @@ def opposition_automorphism(self): return Family(d) ########################################################################## + + class CartanType_standard_affine(CartanType_standard, CartanType_affine): r""" A concrete class for affine simple Cartan types. @@ -2827,10 +2845,13 @@ def type(self): return self.letter ########################################################################## + + class CartanType_standard_untwisted_affine(CartanType_standard_affine): r""" A concrete class for the standard untwisted affine Cartan types. """ + def classical(self): r""" Return the classical Cartan type associated with ``self``. @@ -2911,10 +2932,13 @@ def _latex_(self): return self.classical()._latex_()+"^{(1)}" ########################################################################## + + class CartanType_decorator(UniqueRepresentation, SageObject, CartanType_abstract): """ Concrete base class for Cartan types that decorate another Cartan type. """ + def __init__(self, ct): """ Initialize ``self``. @@ -2988,6 +3012,8 @@ def index_set(self): ############################################################################## # Base concrete class for superalgebras + + class SuperCartanType_standard(UniqueRepresentation, SageObject): # Technical methods def _repr_(self, compact = False): @@ -3037,6 +3063,8 @@ def __getitem__(self, i): ############################################################################## # For backward compatibility + + class CartanType_simple_finite(): def __setstate__(self, dict): """ diff --git a/src/sage/combinat/root_system/coxeter_group.py b/src/sage/combinat/root_system/coxeter_group.py index 7ca11e52a14..8c7e6306508 100644 --- a/src/sage/combinat/root_system/coxeter_group.py +++ b/src/sage/combinat/root_system/coxeter_group.py @@ -1,18 +1,19 @@ """ Coxeter Groups """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2010 Nicolas Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.root_system.reflection_group_real import ReflectionGroup from sage.combinat.root_system.cartan_type import CartanType + def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=None): """ Return an implementation of the Coxeter group given by ``data``. @@ -155,5 +156,6 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No raise NotImplementedError("Coxeter group of type {} as {} group not implemented".format(cartan_type, implementation)) + from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.root_system.coxeter_group', 'CoxeterGroupAsPermutationGroup', ReflectionGroup) diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index 8eb2f743fa5..9ae647552e2 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -205,7 +205,7 @@ def __classcall_private__(cls, data=None, index_set=None, coxeter_type=None, else: index_set = tuple(range(1, n + 1)) if len(set(index_set)) != n: - raise ValueError("the given index set is not valid") + raise ValueError("the given index set is not valid") return cls._from_matrix(data, coxeter_type, index_set, coxeter_type_check) @@ -653,7 +653,7 @@ def _repr_option(self, key): """ if key == 'ascii_art' or key == 'element_ascii_art': return self._matrix.nrows() > 1 - return super(CoxeterMatrix, self)._repr_option(key) + return super()._repr_option(key) def _latex_(self): r""" diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 9507858663a..9c10c3c04db 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -1,7 +1,7 @@ """ Coxeter Types """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Travis Scrimshaw <tscrim at ucdavis.edu>, # 2015 Jean-Philippe Labbe <labbe at math.huji.ac.il>, # @@ -14,8 +14,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -54,7 +54,7 @@ def __classcall_private__(cls, *x): except (ValueError, TypeError): pass - if len(x) == 1: # In case the input is similar to CoxeterType([['A',2]]) + if len(x) == 1: # In case the input is similar to CoxeterType([['A',2]]) return CoxeterType(x[0]) raise NotImplementedError("Coxeter types not from Cartan types not yet implemented") @@ -173,12 +173,12 @@ def _samples(self): Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1]] """ - finite = [CoxeterType(t) for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], - ['C', 1], ['C', 5], ['D', 4], ['D', 5], - ['E', 6], ['E', 7], ['E', 8], ['F', 4], - ['H', 3], ['H', 4], ['I', 10]]] + finite = [CoxeterType(t) for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], + ['C', 1], ['C', 5], ['D', 4], ['D', 5], + ['E', 6], ['E', 7], ['E', 8], ['F', 4], + ['H', 3], ['H', 4], ['I', 10]]] - affine = [CoxeterType(t) for t in [['A', 2, 1], ['B', 5, 1], + affine = [CoxeterType(t) for t in [['A', 2, 1], ['B', 5, 1], ['C', 5, 1], ['D', 5, 1], ['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['A', 1, 1]]] @@ -433,8 +433,7 @@ def __classcall_private__(cls, cartan_type): sage: C1 is C2 True """ - return super(CoxeterTypeFromCartanType, cls).__classcall__(cls, - CartanType(cartan_type)) + return super().__classcall__(cls, CartanType(cartan_type)) def __init__(self, cartan_type): """ diff --git a/src/sage/combinat/root_system/dynkin_diagram.py b/src/sage/combinat/root_system/dynkin_diagram.py index a31b08d75dc..89826d15d12 100644 --- a/src/sage/combinat/root_system/dynkin_diagram.py +++ b/src/sage/combinat/root_system/dynkin_diagram.py @@ -33,6 +33,7 @@ from sage.combinat.root_system.cartan_type import CartanType, CartanType_abstract from sage.combinat.root_system.cartan_matrix import CartanMatrix + def DynkinDiagram(*args, **kwds): r""" Return the Dynkin diagram corresponding to the input. @@ -246,6 +247,7 @@ class DynkinDiagram_class(DiGraph, CartanType_abstract): Implementation note: if a Cartan type is given, then the nodes are initialized from the index set of this Cartan type. """ + def __init__(self, t=None, index_set=None, odd_isotropic_roots=[], **options): """ @@ -843,6 +845,7 @@ def coxeter_diagram(self): coxeter_diagram.add_edge(i,j, val) return coxeter_diagram.copy(immutable=True) + def precheck(t, letter=None, length=None, affine=None, n_ge=None, n=None): """ EXAMPLES:: diff --git a/src/sage/combinat/root_system/extended_affine_weyl_group.py b/src/sage/combinat/root_system/extended_affine_weyl_group.py index bd02024afc6..f4a2e54a88e 100644 --- a/src/sage/combinat/root_system/extended_affine_weyl_group.py +++ b/src/sage/combinat/root_system/extended_affine_weyl_group.py @@ -10,7 +10,7 @@ - Nicolas M. Thiery (2012): initial version - Mark Shimozono (2013): twisted affine root systems, multiple realizations, GL_n """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2012 Daniel Bump <bump at match.stanford.edu>, # 2012 Daniel Orr <danorr at live.unc.edu> # 2012 Anne Schilling <anne at math.ucdavis.edu> @@ -23,8 +23,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.weyl_group import WeylGroup from sage.categories.groups import Groups @@ -423,6 +423,7 @@ def ExtendedAffineWeylGroup(cartan_type, general_linear=None, **print_options): return ExtendedAffineWeylGroup_Class(cartan_type, general_linear, **print_options) + class ExtendedAffineWeylGroup_Class(UniqueRepresentation, Parent): r""" The parent-with-realization class of an extended affine Weyl group. @@ -1030,6 +1031,7 @@ class Realizations(Category_realization_of_parent): r""" The category of the realizations of an extended affine Weyl group """ + def super_categories(self): r""" EXAMPLES:: @@ -2059,6 +2061,7 @@ class ExtendedAffineWeylGroupW0PElement(GroupSemidirectProduct.Element): r""" The element class for the W0P realization. """ + def has_descent(self, i, side='right', positive=False): r""" Return whether ``self`` has `i` as a descent. @@ -2141,6 +2144,7 @@ class ExtendedAffineWeylGroupW0P(GroupSemidirectProduct, BindableClass): sage: ExtendedAffineWeylGroup(['A',2,1]).W0P() Extended affine Weyl group of type ['A', 2, 1] realized by Semidirect product of Weyl Group of type ['A', 2] (as a matrix group acting on the coweight lattice) acting on Multiplicative form of Coweight lattice of the Root system of type ['A', 2] """ + def __init__(self, E): r""" EXAMPLES:: @@ -2402,6 +2406,7 @@ class ExtendedAffineWeylGroupFWElement(GroupSemidirectProduct.Element): r""" The element class for the "FW" realization. """ + def has_descent(self, i, side='right', positive=False): r""" Return whether ``self`` has descent at `i`. @@ -2489,6 +2494,7 @@ class ExtendedAffineWeylGroupFW(GroupSemidirectProduct, BindableClass): sage: ExtendedAffineWeylGroup(['A',2,1]).FW() Extended affine Weyl group of type ['A', 2, 1] realized by Semidirect product of Fundamental group of type ['A', 2, 1] acting on Weyl Group of type ['A', 2, 1] (as a matrix group acting on the root lattice) """ + def __init__(self, E): r""" @@ -2558,6 +2564,7 @@ class ExtendedAffineWeylGroupPvW0Element(GroupSemidirectProduct.Element): r""" The element class for the "PvW0" realization. """ + def has_descent(self, i, side='right', positive=False): r""" Return whether ``self`` has `i` as a descent. @@ -2657,6 +2664,7 @@ class ExtendedAffineWeylGroupPvW0(GroupSemidirectProduct, BindableClass): sage: ExtendedAffineWeylGroup(['A',2,1]).PvW0() Extended affine Weyl group of type ['A', 2, 1] realized by Semidirect product of Multiplicative form of Weight lattice of the Root system of type ['A', 2] acted upon by Weyl Group of type ['A', 2] (as a matrix group acting on the weight lattice) """ + def __init__(self, E): r""" @@ -2728,6 +2736,7 @@ class ExtendedAffineWeylGroupW0PvElement(GroupSemidirectProduct.Element): r""" The element class for the "W0Pv" realization. """ + def dual_action(self, la): r""" Return the action of ``self`` on an element ``la`` of the dual version of the translation lattice. @@ -2889,6 +2898,7 @@ def from_dual_classical_weyl(self, w): """ return self((w,self.cartesian_factors()[1].one())) + ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupPW0.Element = ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupPW0Element ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupW0P.Element = ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupW0PElement ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupWF.Element = ExtendedAffineWeylGroup_Class.ExtendedAffineWeylGroupWFElement diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index bde28234214..6b756bafc30 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -1,13 +1,13 @@ r""" Hecke algebra representations """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2013 Nicolas M. Thiery <nthiery at users.sf.net> # Anne Schilling <anne at math.ucdavis.edu> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** import functools from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -19,6 +19,7 @@ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ + class HeckeAlgebraRepresentation(WithEqualityById, SageObject): r""" A representation of an (affine) Hecke algebra given by the action of the `T` generators @@ -82,6 +83,7 @@ class HeckeAlgebraRepresentation(WithEqualityById, SageObject): - [HST2008]_ """ + def __init__(self, domain, on_basis, cartan_type, q1, q2, q=ZZ.one(), side="right"): r""" TESTS:: @@ -397,7 +399,6 @@ def Tw(self, word, signs=None, scalar=None): result.scalar = scalar return result - def Tw_inverse(self, word): r""" Return `T_w^{-1}`. @@ -753,6 +754,8 @@ def Y_eigenvectors(self): return CherednikOperatorsEigenvectors(self) # TODO: this should probably inherit from family! + + class CherednikOperatorsEigenvectors(UniqueRepresentation, SageObject): r""" A class for the family of eigenvectors of the `Y` Cherednik @@ -798,6 +801,7 @@ class CherednikOperatorsEigenvectors(UniqueRepresentation, SageObject): Add tests for the above assumptions, and also that the classical operators `T_1, \ldots, T_n` from `T` and `T_Y` coincide. """ + def __init__(self, T, T_Y = None, normalized = True): r""" INPUT: diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 6aedd4aefb2..687a3b7abf7 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -1,16 +1,14 @@ """ Integrable Representations of Affine Lie Algebras """ - -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2014, 2105 Daniel Bump <bump at match.stanford.edu> # Travis Scrimshaw <tscrim at ucdavis.edu> # Valentin Buciumas <buciumas at stanford.edu> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.structure.unique_representation import UniqueRepresentation from sage.structure.category_object import CategoryObject from sage.categories.modules import Modules @@ -20,6 +18,7 @@ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.combinat.root_system.weyl_characters import WeylCharacterRing + # TODO: Make this a proper parent and implement actions class IntegrableRepresentation(UniqueRepresentation, CategoryObject): r""" @@ -176,6 +175,7 @@ class IntegrableRepresentation(UniqueRepresentation, CategoryObject): Lambda[0] + 2*Lambda[2] - 2*delta: 3 54 499 3349 18166 84836 353092 1341250 4725259 15625727 48938396 146190544 Lambda[0] + 2*Lambda[3] - 4*delta: 15 195 1539 9186 45804 200073 789201 2866560 9723582 31120281 94724550 275919741 """ + def __init__(self, Lam): """ Initialize ``self``. diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index 2e52b7fd7ee..ae39e62ce01 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -1203,7 +1203,7 @@ def __classcall__(cls, KL, q='q', q1='q1', q2='q2', normalized=True): q = K(q) q1 = K(q1) q2 = K(q2) - return super(NonSymmetricMacdonaldPolynomials, cls).__classcall__(cls, KL, q, q1, q2, normalized) + return super().__classcall__(cls, KL, q, q1, q2, normalized) def __init__(self, KL, q, q1, q2, normalized): r""" @@ -1233,7 +1233,6 @@ def __init__(self, KL, q, q1, q2, normalized): Traceback (most recent call last): ... AssertionError: The weight lattice needs to be extended! - """ # TODO: check all the choices! self._KL = KL @@ -1243,9 +1242,9 @@ def __init__(self, KL, q, q1, q2, normalized): self._q1 = q1 self._q2 = q2 assert self.L_prime().classical() is self.L().classical() - T = KL.twisted_demazure_lusztig_operators ( q1, q2, convention="dominant") + T = KL.twisted_demazure_lusztig_operators(q1, q2, convention="dominant") T_Y = KL.demazure_lusztig_operators_on_classical(q, q1, q2, convention="dominant") - CherednikOperatorsEigenvectors.__init__(self, T, T_Y, normalized = normalized) + CherednikOperatorsEigenvectors.__init__(self, T, T_Y, normalized=normalized) def _repr_(self): r""" @@ -1581,12 +1580,11 @@ def __getitem__(self, mu): """ muaff = self._L.embed_at_level(mu, 0) if not all(muaff.scalar(coroot) in ZZ for coroot in self._L.simple_coroots()): - raise ValueError("%s does not lift to a level 0 element of the affine weight lattice"%muaff) - return super(NonSymmetricMacdonaldPolynomials, self).__getitem__(mu) - + raise ValueError("%s does not lift to a level 0 element of the affine weight lattice" % muaff) + return super().__getitem__(mu) @cached_method - def rho_prime(self): # Should be rho_prime_check + def rho_prime(self): # Should be rho_prime_check r""" Return the level 0 sum of the classical fundamental weights in `L'`. diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index c2e098af316..106b5e0efd8 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -7,8 +7,8 @@ # Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.constant_function import ConstantFunction @@ -325,7 +325,7 @@ def maximal_elements(self): ct_aff = ct.dual().affine() - max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements() + max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements() for w in max_elts_affine: if 0 not in w.reduced_word(): @@ -542,7 +542,7 @@ def __classcall__(cls, W, min_length=0, max_length=infinity, max_support = frozenset(max_support) min_length = max(min_length, len(min_support)) max_length = min(len(max_support), max_length, len(W.index_set()) - 1) - return super(PieriFactors_type_A_affine, cls).__classcall__(cls, W, min_length, max_length, min_support, max_support) + return super().__classcall__(cls, W, min_length, max_length, min_support, max_support) def __init__(self, W, min_length, max_length, min_support, max_support): r""" @@ -656,7 +656,7 @@ def _test_maximal_elements(self, **options): if self._min_length > 0 or self._max_length < len(self.W.index_set())-1 or self._max_support != frozenset(index_set): tester.info("\n Strict subset of the Pieri factors; skipping test") return - return super(PieriFactors_type_A_affine, self)._test_maximal_elements(**options) + return super()._test_maximal_elements(**options) def __contains__(self, w): r""" @@ -918,6 +918,7 @@ class PieriFactors_type_B_affine(PieriFactors_affine_type): sage: W.from_reduced_word([0,2,0]) in PF True """ + def __init__(self, W): r""" diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py index 35f2e88f8b5..ce3cce65ef4 100644 --- a/src/sage/combinat/root_system/plot.py +++ b/src/sage/combinat/root_system/plot.py @@ -828,6 +828,7 @@ class PlotOptions(): - :ref:`sage.combinat.root_system.plot` for a tutorial on root system plotting """ + def __init__(self, space, projection=True, bounding_box=3, @@ -1026,7 +1027,7 @@ def index_of_object(self, i): sage: options.index_of_object(30) sage: options.index_of_object("bla") """ - if parent(i) in RootLatticeRealizations and len(i) == 1 and i.leading_coefficient().is_one(): + if parent(i) in RootLatticeRealizations and len(i) == 1 and i.leading_coefficient().is_one(): i = i.leading_support() if i in self.space.cartan_type().index_set(): return i diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 8ae19776db4..626c7e2c2a8 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -185,15 +185,15 @@ - Christian Stump (2015): initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011-2016 Christian Stump <christian.stump at gmail.com> # # 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, cached_function from sage.misc.misc_c import prod @@ -205,6 +205,7 @@ from sage.sets.family import Family from sage.structure.unique_representation import UniqueRepresentation from sage.groups.perm_gps.permgroup import PermutationGroup_generic +from sage.combinat.permutation import Permutation from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.matrix.all import Matrix, identity_matrix @@ -225,6 +226,7 @@ class ComplexReflectionGroup(UniqueRepresentation, PermutationGroup_generic): :func:`ReflectionGroup` """ + def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflection_index_set=None): r""" TESTS:: @@ -236,26 +238,26 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio W_components = [] reflection_type = [] for W_type in W_types: - if W_type == (1,1,1): + if W_type == (1, 1, 1): raise ValueError("the one element group is not considered a reflection group") elif W_type in ZZ: - call_str = 'ComplexReflectionGroup(%s)'%W_type - elif isinstance(W_type,CartanMatrix): - call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._M._gap_())) + call_str = 'ComplexReflectionGroup(%s)' % W_type + elif isinstance(W_type, CartanMatrix): + call_str = 'PermRootGroup(IdentityMat(%s),%s)' % (W_type._rank, str(W_type._M._gap_())) elif is_Matrix(W_type): - call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._gap_())) - elif W_type in ZZ or ( isinstance(W_type, tuple) and len(W_type) == 3 ): - call_str = 'ComplexReflectionGroup%s'%str(W_type) + call_str = 'PermRootGroup(IdentityMat(%s),%s)' % (W_type._rank, str(W_type._gap_())) + elif W_type in ZZ or (isinstance(W_type, tuple) and len(W_type) == 3): + call_str = 'ComplexReflectionGroup%s' % str(W_type) else: if W_type[0] == "I": - call_str = 'CoxeterGroup("I",2,%s)'%W_type[1] + call_str = 'CoxeterGroup("I",2,%s)' % W_type[1] else: - call_str = 'CoxeterGroup("%s",%s)'%W_type + call_str = 'CoxeterGroup("%s",%s)' % W_type W_components.append(gap3(call_str)) X = list(W_components[-1].ReflectionType()) if len(X) > 1: - raise ValueError("input data %s is invalid"%W_type) + raise ValueError("input data %s is invalid" % W_type) X = X[0] type_dict = {} type_dict["series"] = X.series.sage() @@ -366,17 +368,21 @@ def _repr_(self): type_str = type_str[:-3] return 'Reducible complex reflection group of rank %s and type %s' % (self._rank, type_str) - def __iter__(self): + def iteration_tracking_words(self): r""" - Return an iterator going through all elements in ``self``. + Return an iterator going through all elements in ``self`` that + tracks the reduced expressions. + + This can be much slower than using the iteration as a permutation + group with strong generating set. EXAMPLES:: sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: w # optional - gap3 + sage: for w in W.iteration_tracking_words(): w # optional - gap3 () - (1,3)(2,5)(4,6) (1,4)(2,3)(5,6) + (1,3)(2,5)(4,6) (1,6,2)(3,5,4) (1,2,6)(3,4,5) (1,5)(2,4)(3,6) @@ -1389,7 +1395,7 @@ def braid_relations(self): [[[1, 2, 1], [2, 1, 2]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] """ if self.is_real(): - return super(ComplexReflectionGroup,self).braid_relations() + return super().braid_relations() else: return self._gap_group.BraidRelations().sage() @@ -1910,7 +1916,7 @@ def coxeter_number(self, chi=None): 24 """ if chi is None: - return super(ComplexReflectionGroup, self).coxeter_number() + return super().coxeter_number() G = self.gens() cox_chi = 0 @@ -2029,6 +2035,7 @@ def reflection_length(self, in_unitary_group=False): assert w in self.parent().conjugacy_classes_representatives() return w.reflection_length(in_unitary_group=in_unitary_group) + class IrreducibleComplexReflectionGroup(ComplexReflectionGroup): def _repr_(self): @@ -2201,6 +2208,7 @@ def is_regular(self, h, is_class_representative=False): return True return False + def multi_partitions(n, S, i=None): r""" Return all vectors as lists of the same length as ``S`` whose @@ -2239,6 +2247,7 @@ def multi_partitions(n, S, i=None): coeffs = coeffs1 + coeffs2 return coeffs + @cached_function def power(f, k): r""" diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index c08a6001524..02761726799 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -11,7 +11,7 @@ AUTHORS: - Christian Stump (initial version 2011--2015) - Travis Scrimshaw (14-03-2017): moved element code """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2011-2016 Christian Stump <christian.stump at gmail.com> # Copyright (C) 2017 Travis Scrimshaw <tcscrims at gmail.com> # @@ -19,8 +19,8 @@ AUTHORS: # 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 from sage.misc.lazy_attribute import lazy_attribute @@ -71,8 +71,21 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): sage: WB_hash.intersection(WC_hash) # optional - gap3 set() + + Check that :trac:`34912` is fixed:: + + sage: G4 = ReflectionGroup(4) # optional - gap3 + sage: g0, g1 = G4.gens() # optional - gap3 + sage: elt = g0^2 * g1 * g0^2 * g1 # optional - gap3 + sage: elt # optional - gap3 + (1,12)(2,24)(3,19)(4,22)(5,17)(6,20)(7,23)(8,9)(10,21)(11,13)(14,18)(15,16) + sage: y = (elt * G4.gen(1)) * G4.gen(1) * G4.gen(1) # optional - gap3 + sage: elt == y # optional - gap3 + True + sage: hash(elt) == hash(y) # optional - gap3 + True """ - return hash(self._parent) | hash(tuple(self._reduced_word)) + return hash(self._parent) | super().__hash__() def reduced_word(self): r""" @@ -136,7 +149,7 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): EXAMPLES:: sage: W = ReflectionGroup(4) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: for w in W.iteration_tracking_words(): # optional - gap3 ....: print("{} {}".format(w.reduced_word(), w.length())) [] 0 [1] 1 @@ -162,6 +175,11 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): [1, 2, 2, 1, 1] 5 [1, 1, 2, 1, 1, 2] 6 [1, 1, 2, 2, 1, 1] 6 + + sage: data = {w: (len(w.reduced_word()), w.length()) # optional - gap3 + ....: for w in W.iteration_tracking_words()} + sage: for w in W: # optional - gap3 + ....: assert data[w] == (w.length(), w.length()), w """ return ZZ(len(self.reduced_word())) @@ -178,10 +196,13 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): EXAMPLES:: - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: for w in W: # optional - gap3 - ....: w.reduced_word() # optional - gap3 - ....: [w.to_matrix(), w.to_matrix(on_space="dual")] # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: data = {w: [w.to_matrix(), w.to_matrix(on_space="dual")] for w in W} # optional - gap3 + sage: for w in W.iteration_tracking_words(): # optional - gap3 + ....: w.reduced_word() # optional - gap3 + ....: mats = [w.to_matrix(), w.to_matrix(on_space="dual")] # optional - gap3 + ....: mats + ....: assert data[w] == mats [] [ [1 0] [1 0] @@ -525,7 +546,8 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): EXAMPLES:: sage: W = ReflectionGroup(4) # optional - gap3 - sage: for w in W: w.reflection_eigenvalues() # optional - gap3 + sage: for w in W.iteration_tracking_words(): # optional - gap3 + ....: w.reflection_eigenvalues() [0, 0] [1/3, 0] [1/3, 0] @@ -550,6 +572,10 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): [2/3, 0] [1/2, 1/2] [1/4, 3/4] + + sage: data = {w: w.reflection_eigenvalues() for w in W} # optional - gap3 + sage: all(w.reflection_eigenvalues() == data[w] for w in W.iteration_tracking_words()) # optional - gap3 + True """ return self._parent.reflection_eigenvalues(self, is_class_representative=is_class_representative) @@ -561,7 +587,8 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): EXAMPLES:: sage: W = ReflectionGroup(4) # optional - gap3 - sage: for w in W: print(w.galois_conjugates()) # optional - gap3 + sage: for w in W.iteration_tracking_words(): # optional - gap3 + ....: print(w.galois_conjugates()) [[1 0] [0 1]] [[ 1 0] @@ -650,6 +677,10 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): [ 2/3*E(3) - 2/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2], [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] [-2/3*E(3) + 2/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2]] + + sage: data = {w: w.galois_conjugates() for w in W} # optional - gap3 + sage: all(w.galois_conjugates() == data[w] for w in W.iteration_tracking_words()) # optional - gap3 + True """ rk = self._parent.rank() M = self.to_matrix().list() diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py index 9e5c2e2c1b0..9d7c9a92bbf 100644 --- a/src/sage/combinat/root_system/reflection_group_real.py +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -249,6 +249,7 @@ class RealReflectionGroup(ComplexReflectionGroup): :func:`ReflectionGroup` """ + def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflection_index_set=None): r""" Initialize ``self``. @@ -608,7 +609,7 @@ def coxeter_diagram(self): EXAMPLES:: sage: G = ReflectionGroup(['B',3]) # optional - gap3 - sage: sorted(G.coxeter_diagram().edges(labels=True)) # optional - gap3 + sage: G.coxeter_diagram().edges(labels=True, sort=True) # optional - gap3 [(1, 2, 4), (2, 3, 3)] """ from sage.graphs.graph import Graph @@ -706,7 +707,7 @@ def simple_root_index(self, i): [0, 1, 2] """ return self._index_set_inverse[i] - + def bruhat_cone(self, x, y, side='upper', backend='cdd'): r""" Return the (upper or lower) Bruhat cone associated to the interval ``[x,y]``. 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 4d26225011a..3e70a2e8fd9 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -202,9 +202,8 @@ def isobaric_divided_difference_on_basis(self, weight, i): raise ValueError("the weight does not have an integral scalar product with the coroot") alphai = P.simple_root(i) if n >= 0: - return self.sum_of_monomials(weight-j*alphai for j in range(n + 1)) - else: - return -self.sum_of_monomials(weight-j*alphai for j in range(n+1,0)) + return self.sum_of_monomials(weight-j*alphai for j in range(n + 1)) + return -self.sum_of_monomials(weight-j*alphai for j in range(n + 1, 0)) def demazure_operators(self): r""" @@ -317,7 +316,6 @@ def _test_demazure_operators(self, **options): tester.assertEqual(result * (self.one()-emalphai), x - emalphai * x.map_support(s[i])) - def demazure_lusztig_operator_on_basis(self, weight, i, q1, q2, convention="antidominant"): r""" Return the result of applying the `i`-th Demazure-Lusztig operator on ``weight``. @@ -582,7 +580,6 @@ def demazure_lusztig_operators(self, q1, q2, convention="antidominant"): q1 = q1, q2 = q2, convention = convention) return HeckeAlgebraRepresentation(self, T_on_basis, self.cartan_type(), q1, q2, side="left") - def demazure_lusztig_operator_on_classical_on_basis(self, weight, i, q, q1, q2, convention="antidominant"): r""" Return the result of applying the `i`-th Demazure-Lusztig operator on the classical weight ``weight`` embedded at level 0. diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 8e97587945d..fb63471ffb0 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -325,7 +325,7 @@ def _test_root_lattice_realization(self, **options): # Check the embeddings from the root lattice and the root space over the same base ring root_lattice = self.root_system.root_lattice() - root_space = self.root_system.root_space (R) + root_space = self.root_system.root_space(R) tester.assertIsNot(self.coerce_map_from(root_lattice), None) tester.assertIsNot(self.coerce_map_from(root_space), None) for i in self.index_set(): @@ -417,7 +417,6 @@ def a_long_simple_root(self): longest=j return self.simple_roots()[longest[0]] - ########################################################################## # simple roots ########################################################################## @@ -1233,7 +1232,6 @@ def coroot_space(self, base_ring = QQ): """ return self.root_system.coroot_space(base_ring = base_ring) - def simple_coroot(self, i): """ Returns the `i^{th}` simple coroot. @@ -1365,7 +1363,6 @@ def null_coroot(self): coef = self.cartan_type().acheck() return sum(coef[k]*self.simple_coroots()[k] for k in coef.keys()) - ########################################################################## # fundamental weights ########################################################################## @@ -1446,7 +1443,6 @@ def fundamental_weights_from_simple_roots(self): raise ValueError("The fundamental weights do not live in this realization of the root lattice") return Family(dict(zip(self.index_set(),fundamental_weights))) - ########################################################################## # reflections ########################################################################## @@ -1789,6 +1785,7 @@ def almost_positive_roots_decomposition(self): # orbits under the action of a group: # def orbits(seeds, operators) # INPUT: + # - seeds: a list of elements # - operators: a list of functions # @@ -1825,7 +1822,6 @@ def almost_positive_roots_decomposition(self): orbits.append(orbit) return orbits - ########################################################################## # Methods for affine root lattice realizations # Should eventually go in an Affine nested class @@ -2497,7 +2493,6 @@ def plot_reflection_hyperplanes(self, collection="simple", **options): G += plot_options.reflection_hyperplane(coroot) return plot_options.finalize(G) - def plot_hedron(self, **options): r""" Plot the polyhedron whose vertices are given by the orbit @@ -2820,7 +2815,6 @@ def alcove_label(w): # rgbcolor = plot_options.color(i), # thickness = 2 if i == special_node else 1) - def plot_bounding_box(self, **options): r""" Plot the bounding box. @@ -2924,20 +2918,20 @@ def plot_alcove_walk(self, word, start=None, foldings=None, color="orange", **op if foldings is None: foldings = [False] * len(word) w = W.one() - source = plot_options.projection(start) + source = plot_options.projection(start) G = plot_options.empty() for (i, folding) in zip(word, foldings): w = w * s[i] target = plot_options.projection(w.action(start)) if folding: - middle = (source+target)/2 - G += line ([source, middle], rgbcolor=color) + middle = (source + target) / 2 + G += line([source, middle], rgbcolor=color) G += arrow(middle, source, rgbcolor=color, arrowsize=plot_options._arrowsize) # reset w w = w * s[i] else: G += arrow(source, target, rgbcolor=color, arrowsize=plot_options._arrowsize) - source=target + source = target return G @cached_method @@ -3594,7 +3588,6 @@ def reflection(self, root, use_coroot = False): else: return self - self.scalar(root.associated_coroot()) * root - ########################################################################## # Descents ########################################################################## @@ -3788,7 +3781,6 @@ def reduced_word(self, index_set = None, positive = True): """ return self.to_dominant_chamber(index_set=index_set,positive=positive,reduced_word = True)[1] - def is_dominant(self, index_set = None, positive = True): r""" Returns whether self is dominant. @@ -3848,11 +3840,10 @@ def is_dominant_weight(self): # Or is_dominant_integral_weight? True """ alphacheck = self.parent().simple_coroots() - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return all(self.inner_product(alphacheck[i]) in NN for i in self.parent().index_set()) - ########################################################################## # weak order ########################################################################## @@ -4122,8 +4113,9 @@ def associated_reflection(self): def translation(self, x): """ INPUT: - - ``self`` - an element `t` at level `0` - - ``x`` - an element of the same space + + - ``self`` -- an element `t` at level `0` + - ``x`` -- an element of the same space Returns `x` translated by `t`, that is `x+level(x) t` diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index d52b5d7a8f0..d4e6f8746d2 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -1,13 +1,12 @@ """ Root lattices and root spaces """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.rings.integer_ring import ZZ from sage.combinat.free_module import CombinatorialFreeModule @@ -183,9 +182,9 @@ def _to_root_lattice(self, x): .. TODO:: generalize diagonal module morphisms to implement this """ try: - return self.root_system.root_lattice().sum_of_terms( (i, ZZ(c)) for (i,c) in x) + return self.root_system.root_lattice().sum_of_terms((i, ZZ(c)) for i, c in x) except TypeError: - raise ValueError("%s does not have integral coefficients"%x) + raise ValueError("%s does not have integral coefficients" % x) @cached_method def _to_classical_on_basis(self, i): @@ -231,6 +230,7 @@ def basis_value(basis, i): return basis[i] return self.module_morphism(on_basis = functools.partial(basis_value, basis) , codomain=L) + class RootSpaceElement(CombinatorialFreeModule.Element): def scalar(self, lambdacheck): """ @@ -461,4 +461,5 @@ def to_ambient(self): """ return self.parent().to_ambient_space_morphism()(self) + RootSpace.Element = RootSpaceElement diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index 9f4764f9d23..fc84a35b4c1 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -4,7 +4,7 @@ See :ref:`sage.combinat.root_system.all` for an overview. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, # Justin Walker <justin at mac.com> # 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net> @@ -12,8 +12,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** # Design largely inspired from MuPAD-Combinat from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation @@ -311,7 +311,7 @@ def __classcall__(cls, cartan_type, as_dual_of=None): sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3") True """ - return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of) + return super().__classcall__(cls, CartanType(cartan_type), as_dual_of) def __init__(self, cartan_type, as_dual_of=None): """ @@ -338,7 +338,6 @@ def __init__(self, cartan_type, as_dual_of=None): self.dual_side = True self.dual = as_dual_of - def _test_root_lattice_realizations(self, **options): """ Runs tests on all the root lattice realizations of this root @@ -507,7 +506,7 @@ def root_poset(self, restricted=False, facade=False): sage: Phi.cover_relations() [[alpha[2], alpha[1] + alpha[2]], [alpha[1], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]] """ - return self.root_lattice().root_poset(restricted=restricted,facade=facade) + return self.root_lattice().root_poset(restricted=restricted, facade=facade) def coroot_lattice(self): """ @@ -617,7 +616,6 @@ def coweight_space(self, base_ring=QQ, extended = False): """ return self.dual.weight_space(base_ring, extended = extended) - def ambient_lattice(self): r""" Return the ambient lattice for this root_system. diff --git a/src/sage/combinat/root_system/type_A.py b/src/sage/combinat/root_system/type_A.py index 8456f800bc9..e48524ffbcc 100644 --- a/src/sage/combinat/root_system/type_A.py +++ b/src/sage/combinat/root_system/type_A.py @@ -14,6 +14,7 @@ from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations from . import ambient_space + class AmbientSpace(ambient_space.AmbientSpace): r""" EXAMPLES:: @@ -170,12 +171,15 @@ def det(self, k=1): from .cartan_type import CartanType_standard_finite, CartanType_simply_laced, CartanType_simple + + class CartanType(CartanType_standard_finite, CartanType_simply_laced, CartanType_simple): """ Cartan Type `A_n` .. SEEALSO:: :func:`~sage.combinat.root_systems.cartan_type.CartanType` """ + def __init__(self, n): """ EXAMPLES:: @@ -335,6 +339,7 @@ def ascii_art(self, label=lambda i: i, node=None): 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) diff --git a/src/sage/combinat/root_system/type_A_affine.py b/src/sage/combinat/root_system/type_A_affine.py index 8c1e6998aea..e5fc4d00935 100644 --- a/src/sage/combinat/root_system/type_A_affine.py +++ b/src/sage/combinat/root_system/type_A_affine.py @@ -9,6 +9,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine, CartanType_simply_laced + + class CartanType(CartanType_standard_untwisted_affine): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_A_infinity.py b/src/sage/combinat/root_system/type_A_infinity.py index 21237bc48c8..c3a2c603266 100644 --- a/src/sage/combinat/root_system/type_A_infinity.py +++ b/src/sage/combinat/root_system/type_A_infinity.py @@ -1,15 +1,12 @@ """ Root system data for type A infinity """ - -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2016 Andrew Mathas <Andrew dot Mathas at Sydney dot edu dot au> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - - +# https://www.gnu.org/licenses/ +# *************************************************************************** from .cartan_type import CartanType_standard, CartanType_simple from sage.rings.integer_ring import ZZ @@ -25,6 +22,7 @@ class CartanType(CartanType_standard, CartanType_simple): """ # We do not inherit from CartanType_crystallographic because it provides # methods that are not implemented for A_oo. + def __init__(self, index_set): """ EXAMPLES:: @@ -57,7 +55,7 @@ def __init__(self, index_set): sage: TestSuite(ct).run() """ - super(CartanType, self).__init__() + super().__init__() self.letter = 'A' self.n = index_set @@ -106,11 +104,11 @@ def ascii_art(self, label=lambda i: i, node=None): node = self._ascii_art_node if self.n == ZZ: - ret = '..---'+'---'.join(node(label(i)) for i in range(7))+'---..\n' - ret += ' '+''.join("{:4}".format(label(i)) for i in range(-3,4)) + ret = '..---' + '---'.join(node(label(i)) for i in range(7)) + '---..\n' + ret += ' ' + ''.join("{:4}".format(label(i)) for i in range(-3, 4)) else: - ret = '---'.join(node(label(i)) for i in range(7))+'---..\n' - ret += '0'+''.join("{:4}".format(label(i)) for i in range(1,7)) + ret = '---'.join(node(label(i)) for i in range(7)) + '---..\n' + ret += '0' + ''.join("{:4}".format(label(i)) for i in range(1, 7)) return ret diff --git a/src/sage/combinat/root_system/type_B.py b/src/sage/combinat/root_system/type_B.py index 206df4859d1..9175d87bfa7 100644 --- a/src/sage/combinat/root_system/type_B.py +++ b/src/sage/combinat/root_system/type_B.py @@ -1,18 +1,18 @@ """ Root system data for type B """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Daniel Bump # Copyright (C) 2008-2009 Justin Walker # Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net>, # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from . import ambient_space -class AmbientSpace(ambient_space.AmbientSpace): + +class AmbientSpace(ambient_space.AmbientSpace): def dimension(self): """ EXAMPLES:: @@ -65,7 +65,7 @@ def simple_root(self, i): """ if i not in self.index_set(): raise ValueError("{} is not in the index set".format(i)) - return self.root(i-1,i) if i < self.n else self.monomial(self.n-1) + return self.root(i-1, i) if i < self.n else self.monomial(self.n-1) def negative_roots(self): """ @@ -81,10 +81,8 @@ def negative_roots(self): (-1, 0, 0), (0, -1, 0), (0, 0, -1)] - """ - return [ -a for a in self.positive_roots()] - + return [-a for a in self.positive_roots()] def positive_roots(self): """ @@ -104,7 +102,7 @@ def positive_roots(self): """ res = [] for i in range(self.n-1): - for j in range(i+1,self.n): + for j in range(i + 1, self.n): res.append(self.monomial(i) - self.monomial(j)) res.append(self.monomial(i) + self.monomial(j)) for i in range(self.n): @@ -122,11 +120,14 @@ def fundamental_weight(self, i): raise ValueError("{} is not in the index set".format(i)) n = self.dimension() if i == n: - return self.sum( self.monomial(j) for j in range(n) ) / 2 + return self.sum(self.monomial(j) for j in range(n)) / 2 else: return self.sum(self.monomial(j) for j in range(i)) + from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_crystallographic, CartanType_simply_laced + + class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_crystallographic): def __init__(self, n): """ @@ -268,8 +269,8 @@ def ascii_art(self, label=lambda i: i, node=None): if n == 1: ret = node(label(1)) + "\n" else: - ret = "---".join(node(label(i)) for i in range(1,n)) + "=>=" + node(label(n)) + '\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)) + "=>=" + node(label(n)) + '\n' + ret += "".join("{!s:4}".format(label(i)) for i in range(1, n + 1)) return ret def _latex_dynkin_diagram(self, label=lambda i: i, node=None, node_dist=2, dual=False): @@ -313,9 +314,9 @@ def _latex_dynkin_diagram(self, label=lambda i: i, node=None, node_dist=2, dual= if self.n == 1: return node(0, 0, label(1)) n = self.n - ret = "\\draw (0 cm,0) -- (%s cm,0);\n"%((n-2)*node_dist) - ret += "\\draw (%s cm, 0.1 cm) -- +(%s cm,0);\n"%((n-2)*node_dist, node_dist) - ret += "\\draw (%s cm, -0.1 cm) -- +(%s cm,0);\n"%((n-2)*node_dist, node_dist) + ret = "\\draw (0 cm,0) -- (%s cm,0);\n" % ((n-2)*node_dist) + ret += "\\draw (%s cm, 0.1 cm) -- +(%s cm,0);\n" % ((n-2)*node_dist, node_dist) + ret += "\\draw (%s cm, -0.1 cm) -- +(%s cm,0);\n" % ((n-2)*node_dist, node_dist) if dual: ret += self._latex_draw_arrow_tip((n-1.5)*node_dist-0.2, 0, 180) else: @@ -335,9 +336,12 @@ def _default_folded_cartan_type(self): """ from sage.combinat.root_system.type_folded import CartanTypeFolded n = self.n - return CartanTypeFolded(self, ['D', n+1], - [[i] for i in range(1, n)] + [[n, n+1]]) + return CartanTypeFolded(self, ['D', n + 1], + [[i] for i in range(1, n)] + [[n, n + 1]]) + + # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_B', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_B', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_BC_affine.py b/src/sage/combinat/root_system/type_BC_affine.py index c21661bb9bc..aaa101b3350 100644 --- a/src/sage/combinat/root_system/type_BC_affine.py +++ b/src/sage/combinat/root_system/type_BC_affine.py @@ -12,6 +12,8 @@ from .cartan_type import CartanType_standard_affine from sage.rings.integer_ring import ZZ + + class CartanType(CartanType_standard_affine): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_B_affine.py b/src/sage/combinat/root_system/type_B_affine.py index bd2702f2efe..fd709894a6e 100644 --- a/src/sage/combinat/root_system/type_B_affine.py +++ b/src/sage/combinat/root_system/type_B_affine.py @@ -9,6 +9,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine + + class CartanType(CartanType_standard_untwisted_affine): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_C.py b/src/sage/combinat/root_system/type_C.py index ca53585f10d..57fdde0b5a5 100644 --- a/src/sage/combinat/root_system/type_C.py +++ b/src/sage/combinat/root_system/type_C.py @@ -12,6 +12,7 @@ from . import ambient_space + class AmbientSpace(ambient_space.AmbientSpace): """ EXAMPLES:: @@ -33,7 +34,6 @@ class AmbientSpace(ambient_space.AmbientSpace): sage: TestSuite(e).run() """ - def dimension(self): """ EXAMPLES:: @@ -111,7 +111,6 @@ def negative_roots(self): res.extend( [ self.root(i,i,1,1) for i in range(self.n) ] ) return res - def fundamental_weight(self, i): """ EXAMPLES:: @@ -121,7 +120,10 @@ def fundamental_weight(self, i): """ return self.sum(self.monomial(j) for j in range(i)) + from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_crystallographic, CartanType_simply_laced + + class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_crystallographic): def __init__(self, n): """ @@ -305,6 +307,7 @@ def _default_folded_cartan_type(self): 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) diff --git a/src/sage/combinat/root_system/type_C_affine.py b/src/sage/combinat/root_system/type_C_affine.py index acf1594211b..481626372bd 100644 --- a/src/sage/combinat/root_system/type_C_affine.py +++ b/src/sage/combinat/root_system/type_C_affine.py @@ -9,6 +9,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine + + class CartanType(CartanType_standard_untwisted_affine): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_D.py b/src/sage/combinat/root_system/type_D.py index be4534024c2..be106a845a7 100644 --- a/src/sage/combinat/root_system/type_D.py +++ b/src/sage/combinat/root_system/type_D.py @@ -1,17 +1,17 @@ """ Root system data for type D """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Daniel Bump # Copyright (C) 2008-2009 Justin Walker # Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net>, # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from . import ambient_space + class AmbientSpace(ambient_space.AmbientSpace): def dimension(self): """ @@ -34,12 +34,10 @@ def root(self, i, j, p1, p2): (-1, -1, 0) sage: e.root(0, 0, 1, 1) (-1, 0, 0) - """ if i != j: return (-1)**p1 * self.monomial(i) + (-1)**p2 * self.monomial(j) - else: - return (-1)**p1 * self.monomial(i) + return (-1)**p1 * self.monomial(i) def simple_root(self, i): """ @@ -69,12 +67,11 @@ def positive_roots(self): (1, 0, 0, -1), (0, 1, 0, -1), (0, 0, 1, -1)] - """ res = [] - for p in [0,1]: + for p in [0, 1]: for j in range(self.n): - res.extend([self.root(i,j,0,p) for i in range(j)]) + res.extend([self.root(i, j, 0, p) for i in range(j)]) return res def negative_roots(self): @@ -94,15 +91,13 @@ def negative_roots(self): (-1, 0, 0, -1), (0, -1, 0, -1), (0, 0, -1, -1)] - """ res = [] - for p in [0,1]: + for p in [0, 1]: for j in range(self.n): - res.extend([self.root(i,j,1,p) for i in range(j)]) + res.extend([self.root(i, j, 1, p) for i in range(j)]) return res - def fundamental_weight(self, i): """ EXAMPLES:: @@ -114,17 +109,21 @@ def fundamental_weight(self, i): raise ValueError("{} is not in the index set".format(i)) n = self.dimension() if i == n: - return self.sum(self.monomial(j) for j in range(n)) / 2 - elif i == n-1: + return self.sum(self.monomial(j) for j in range(n)) / 2 + elif i == n - 1: return (self.sum(self.monomial(j) for j in range(n-1)) - self.monomial(n-1)) / 2 else: - return self.sum(self.monomial(j) for j in range(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 + + class CartanType(CartanType_standard_finite, CartanType_simply_laced): def __init__(self, n): """ @@ -349,6 +348,7 @@ def ascii_art(self, label=lambda i: i, node=None): 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) diff --git a/src/sage/combinat/root_system/type_D_affine.py b/src/sage/combinat/root_system/type_D_affine.py index 21f241e18dc..ae1b67812ff 100644 --- a/src/sage/combinat/root_system/type_D_affine.py +++ b/src/sage/combinat/root_system/type_D_affine.py @@ -11,6 +11,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine, CartanType_simply_laced + + class CartanType(CartanType_standard_untwisted_affine, CartanType_simply_laced): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_E.py b/src/sage/combinat/root_system/type_E.py index df4d67d6651..05b81a31c60 100644 --- a/src/sage/combinat/root_system/type_E.py +++ b/src/sage/combinat/root_system/type_E.py @@ -15,11 +15,13 @@ from sage.rings.integer_ring import ZZ from sage.sets.family import Family + class AmbientSpace(ambient_space.AmbientSpace): """ The lattice behind E6, E7, or E8. The computations are based on Bourbaki, Groupes et Algèbres de Lie, Ch. 4,5,6 (planche V-VII). """ + def __init__(self, root_system, baseRing): """ Create the ambient space for the root system for E6, E7, E8. @@ -433,9 +435,9 @@ def fundamental_weights(self): 8: self.root(6,7)}) +from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_simply_laced -from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_simply_laced class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_simply_laced): def __init__(self, n): """ @@ -631,6 +633,7 @@ def ascii_art(self, label=lambda i: i, node=None): ret = " {} {}\n |\n |\n".format(node(label(2)), label(2)) return ret + '---'.join(node(i) for i in labels) + '\n' + "".join("{!s:4}".format(i) for i in labels) + # 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) diff --git a/src/sage/combinat/root_system/type_E_affine.py b/src/sage/combinat/root_system/type_E_affine.py index 4d286a4708c..d7b8bbb2a34 100644 --- a/src/sage/combinat/root_system/type_E_affine.py +++ b/src/sage/combinat/root_system/type_E_affine.py @@ -11,6 +11,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine, CartanType_simply_laced + + class CartanType(CartanType_standard_untwisted_affine, CartanType_simply_laced): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_F.py b/src/sage/combinat/root_system/type_F.py index c899c09739e..a7ab233b1b2 100644 --- a/src/sage/combinat/root_system/type_F.py +++ b/src/sage/combinat/root_system/type_F.py @@ -17,11 +17,13 @@ # TODO: double check that this can't be defined over ZZ + class AmbientSpace(ambient_space.AmbientSpace): """ The lattice behind `F_4`. The computations are based on Bourbaki, Groupes et Algèbres de Lie, Ch. 4,5,6 (planche VIII). """ + def __init__(self, root_system, base_ring): r""" Initialize the ambient lattice for the root system of type `F_4`. @@ -44,7 +46,6 @@ def __init__(self, root_system, base_ring): self.root(3), v*(self.root(0)-self.root(1)-self.root(2)-self.root(3))] - def dimension(self): """ Return the dimension of ``self``. @@ -57,7 +58,6 @@ def dimension(self): """ return self.root_system.cartan_type().rank() - def root(self, i, j=None, k=None, l=None, p1=0, p2=0, p3=0, p4=0): """ Compute a root from base elements of the underlying lattice. @@ -198,7 +198,10 @@ def fundamental_weights(self): 3: v*(3*self.monomial(0)+self.monomial(1)+self.monomial(2)+self.monomial(3)), 4: self.monomial(0)}) + from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_crystallographic + + class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_crystallographic): def __init__(self): """ @@ -375,6 +378,7 @@ def _default_folded_cartan_type(self): from sage.combinat.root_system.type_folded import CartanTypeFolded return CartanTypeFolded(self, ['E', 6], [[2], [4], [3, 5], [1, 6]]) + # 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) diff --git a/src/sage/combinat/root_system/type_F_affine.py b/src/sage/combinat/root_system/type_F_affine.py index 998b32fead8..5e27c4c6aff 100644 --- a/src/sage/combinat/root_system/type_F_affine.py +++ b/src/sage/combinat/root_system/type_F_affine.py @@ -11,6 +11,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine + + class CartanType(CartanType_standard_untwisted_affine): def __init__(self): """ diff --git a/src/sage/combinat/root_system/type_G.py b/src/sage/combinat/root_system/type_G.py index 6eadc0e99ab..ce953036dc1 100644 --- a/src/sage/combinat/root_system/type_G.py +++ b/src/sage/combinat/root_system/type_G.py @@ -13,6 +13,8 @@ from . import ambient_space from sage.sets.family import Family from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations + + class AmbientSpace(ambient_space.AmbientSpace): """ EXAMPLES:: @@ -54,6 +56,7 @@ class AmbientSpace(ambient_space.AmbientSpace): sage: [WeylDim(['G',2],[a,b]) for a,b in [[0,0], [1,0], [0,1], [1,1]]] # indirect doctest [1, 7, 14, 64] """ + def dimension(self): """ EXAMPLES:: @@ -72,6 +75,7 @@ def simple_root(self, i): Finite family {1: (0, 1, -1), 2: (1, -2, 1)} """ return self.monomial(1)-self.monomial(2) if i == 1 else self.monomial(0)-2*self.monomial(1)+self.monomial(2) + def positive_roots(self): """ EXAMPLES:: @@ -106,6 +110,8 @@ def fundamental_weights(self): from .cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_crystallographic + + class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_crystallographic): def __init__(self): """ @@ -275,6 +281,7 @@ def _default_folded_cartan_type(self): from sage.combinat.root_system.type_folded import CartanTypeFolded return CartanTypeFolded(self, ['D', 4], [[1, 3, 4], [2]]) + # 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) diff --git a/src/sage/combinat/root_system/type_G_affine.py b/src/sage/combinat/root_system/type_G_affine.py index f40f026a004..cde34a41753 100644 --- a/src/sage/combinat/root_system/type_G_affine.py +++ b/src/sage/combinat/root_system/type_G_affine.py @@ -11,6 +11,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_untwisted_affine + + class CartanType(CartanType_standard_untwisted_affine): def __init__(self): """ diff --git a/src/sage/combinat/root_system/type_H.py b/src/sage/combinat/root_system/type_H.py index 9ca5acf5f4d..13d5b6eb04a 100644 --- a/src/sage/combinat/root_system/type_H.py +++ b/src/sage/combinat/root_system/type_H.py @@ -9,6 +9,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_finite, CartanType_simple + + class CartanType(CartanType_standard_finite, CartanType_simple): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_I.py b/src/sage/combinat/root_system/type_I.py index b8f23bb98d4..bafa43b8410 100644 --- a/src/sage/combinat/root_system/type_I.py +++ b/src/sage/combinat/root_system/type_I.py @@ -9,6 +9,8 @@ #***************************************************************************** from .cartan_type import CartanType_standard_finite, CartanType_simple + + class CartanType(CartanType_standard_finite, CartanType_simple): def __init__(self, n): """ diff --git a/src/sage/combinat/root_system/type_Q.py b/src/sage/combinat/root_system/type_Q.py index c0b6bdfb7c6..a8884c8905a 100644 --- a/src/sage/combinat/root_system/type_Q.py +++ b/src/sage/combinat/root_system/type_Q.py @@ -13,12 +13,14 @@ from .cartan_type import CartanType_standard_finite from sage.combinat.root_system.root_system import RootSystem + class CartanType(CartanType_standard_finite): """ Cartan Type `Q_n` .. SEEALSO:: :func:`~sage.combinat.root_systems.cartan_type.CartanType` """ + def __init__(self, m): """ EXAMPLES:: diff --git a/src/sage/combinat/root_system/type_dual.py b/src/sage/combinat/root_system/type_dual.py index 3c9e48397fe..8922a4cd9ac 100644 --- a/src/sage/combinat/root_system/type_dual.py +++ b/src/sage/combinat/root_system/type_dual.py @@ -16,6 +16,7 @@ from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations from sage.combinat.root_system import ambient_space + class CartanType(cartan_type.CartanType_decorator, cartan_type.CartanType_crystallographic): r""" A class for dual Cartan types. @@ -75,6 +76,7 @@ class CartanType(cartan_type.CartanType_decorator, cartan_type.CartanType_crysta .. NOTE:: F4d is pickled by construction as F4.dual() hence the above failure. """ + def __init__(self, type): """ INPUT: @@ -345,6 +347,7 @@ def dynkin_diagram(self): ########################################################################### + class AmbientSpace(ambient_space.AmbientSpace): """ Ambient space for a dual finite Cartan type. @@ -516,6 +519,8 @@ class CartanType_finite(CartanType, cartan_type.CartanType_finite): AmbientSpace = AmbientSpace ########################################################################### + + class CartanType_affine(CartanType, cartan_type.CartanType_affine): def classical(self): """ @@ -675,20 +680,20 @@ def _default_folded_cartan_type(self): """ from sage.combinat.root_system.type_folded import CartanTypeFolded letter = self._type.type() - if letter == 'BC': # A_{2n}^{(2)\dagger} + if letter == 'BC': # A_{2n}^{(2)\dagger} n = self._type.classical().rank() return CartanTypeFolded(self, ['A', 2*n - 1, 1], [[0]] + [[i, 2*n-i] for i in range(1, n)] + [[n]]) - if letter == 'B': # A_{2n-1}^{(2)} + if letter == 'B': # A_{2n-1}^{(2)} n = self._type.classical().rank() return CartanTypeFolded(self, ['D', n + 1, 1], [[i] for i in range(n)] + [[n, n+1]]) - if letter == 'C': # D_{n+1}^{(2)} + if letter == 'C': # D_{n+1}^{(2)} n = self._type.classical().rank() return CartanTypeFolded(self, ['A', 2*n-1, 1], [[0]] + [[i, 2*n-i] for i in range(1, n)] + [[n]]) - if letter == 'F': # E_6^{(2)} + if letter == 'F': # E_6^{(2)} return CartanTypeFolded(self, ['E', 6, 1], [[0], [2], [4], [3, 5], [1, 6]]) - if letter == 'G': # D_4^{(3)} + if letter == 'G': # D_4^{(3)} return CartanTypeFolded(self, ['D', 4, 1], [[0], [1, 3, 4], [2]]) - return super(CartanType, self)._default_folded_cartan_type() + return super()._default_folded_cartan_type() diff --git a/src/sage/combinat/root_system/type_folded.py b/src/sage/combinat/root_system/type_folded.py index b8ef07d88e2..bfb0d1513c6 100644 --- a/src/sage/combinat/root_system/type_folded.py +++ b/src/sage/combinat/root_system/type_folded.py @@ -178,13 +178,13 @@ def __classcall_private__(cls, cartan_type, virtual, orbit): virtual = CartanType(virtual) if isinstance(orbit, dict): i_set = cartan_type.index_set() - orb = [None]*len(i_set) - for k,v in orbit.items(): + orb = [None] * len(i_set) + for k, v in orbit.items(): orb[i_set.index(k)] = tuple(v) orbit = tuple(orb) else: orbit = tuple(map(tuple, orbit)) - return super(CartanTypeFolded, cls).__classcall__(cls, cartan_type, virtual, orbit) + return super().__classcall__(cls, cartan_type, virtual, orbit) def __init__(self, cartan_type, folding_of, orbit): """ diff --git a/src/sage/combinat/root_system/type_marked.py b/src/sage/combinat/root_system/type_marked.py index 0a506de4165..a15eb8d1547 100644 --- a/src/sage/combinat/root_system/type_marked.py +++ b/src/sage/combinat/root_system/type_marked.py @@ -14,6 +14,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method + class CartanType(cartan_type.CartanType_decorator): r""" A class for Cartan types with marked nodes. @@ -82,7 +83,7 @@ def __classcall__(cls, ct, marked_nodes): if any(node not in ct.index_set() for node in marked_nodes): raise ValueError("invalid marked node") marked_nodes = tuple(sorted(marked_nodes)) - return super(CartanType, cls).__classcall__(cls, ct, marked_nodes) + return super().__classcall__(cls, ct, marked_nodes) def __init__(self, ct, marked_nodes): """ @@ -448,6 +449,7 @@ def type(self): ########################################################################### + class AmbientSpace(ambient_space.AmbientSpace): """ Ambient space for a marked finite Cartan type. @@ -554,6 +556,7 @@ def _plot_projection(self): ########################################################################### + class CartanType_finite(CartanType, cartan_type.CartanType_finite): AmbientSpace = AmbientSpace @@ -590,6 +593,8 @@ def affine(self): return self._type.affine().marked_nodes(self._marked_nodes) ########################################################################### + + class CartanType_affine(CartanType, cartan_type.CartanType_affine): """ TESTS:: @@ -604,6 +609,7 @@ class CartanType_affine(CartanType, cartan_type.CartanType_affine): Ambient space of the Root system of type ['B', 3] with nodes (1, 3) marked sage: TestSuite(L).run() """ + def _latex_draw_node(self, x, y, label, position="below=4pt"): r""" Draw the possibly marked (crossed out) circular node ``i`` at the diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index aa2676f1995..527447b0e41 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -75,6 +75,7 @@ class CartanType(SageObject, CartanType_abstract): 11 12 13 A4xBC5~xC3 """ + def __init__(self, types): """ Initialize ``self``. @@ -454,6 +455,7 @@ def coxeter_diagram(self): g.add_edge(relabelling[i,e1], relabelling[i,e2], label=l) return g + class AmbientSpace(ambient_space.AmbientSpace): """ EXAMPLES:: @@ -462,6 +464,7 @@ class AmbientSpace(ambient_space.AmbientSpace): Ambient space of the Root system of type A2xB2 """ + def cartan_type(self): """ EXAMPLES:: diff --git a/src/sage/combinat/root_system/type_relabel.py b/src/sage/combinat/root_system/type_relabel.py index 07ccea8f45f..c365eb7affb 100644 --- a/src/sage/combinat/root_system/type_relabel.py +++ b/src/sage/combinat/root_system/type_relabel.py @@ -15,6 +15,7 @@ from sage.combinat.root_system import ambient_space from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations + class CartanType(cartan_type.CartanType_decorator): r""" A class for relabelled Cartan types. @@ -421,6 +422,7 @@ def coxeter_diagram(self): ########################################################################### + class AmbientSpace(ambient_space.AmbientSpace): """ Ambient space for a relabelled finite Cartan type. @@ -542,6 +544,7 @@ def _plot_projection(self): else: RootLatticeRealizations.ParentMethods.__dict__["_plot_projection"] + class CartanType_finite(CartanType, cartan_type.CartanType_finite): AmbientSpace = AmbientSpace @@ -619,6 +622,8 @@ def affine(self): return self._type.affine().relabel(relabelling) ########################################################################### + + class CartanType_affine(CartanType, cartan_type.CartanType_affine): """ TESTS:: diff --git a/src/sage/combinat/root_system/type_super_A.py b/src/sage/combinat/root_system/type_super_A.py index 16df61caee3..c014dfa7ee5 100644 --- a/src/sage/combinat/root_system/type_super_A.py +++ b/src/sage/combinat/root_system/type_super_A.py @@ -33,6 +33,7 @@ class AmbientSpace(ambient_space.AmbientSpace): 1: (0, 0, 0, 1, 0), 2: (0, 0, 0, 0, 1)} """ + def __init__(self, root_system, base_ring, index_set=None): """ Initialize ``self``. @@ -442,16 +443,18 @@ def is_dominant_weight(self): """ alpha = self.parent().simple_roots() l = self.parent().cartan_type().symmetrizer() - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return all(l[i] * self.inner_product(alpha[i]) in NN for i in self.parent().index_set()) + class CartanType(SuperCartanType_standard): """ Cartan Type `A(m|n)`. .. SEEALSO:: :func:`~sage.combinat.root_systems.cartan_type.CartanType` """ + def __init__(self, m, n): """ EXAMPLES:: diff --git a/src/sage/combinat/root_system/weight_lattice_realizations.py b/src/sage/combinat/root_system/weight_lattice_realizations.py index d6092d01968..a088fd912c8 100644 --- a/src/sage/combinat/root_system/weight_lattice_realizations.py +++ b/src/sage/combinat/root_system/weight_lattice_realizations.py @@ -231,7 +231,6 @@ def __init_extra__(self): codomain = self ).register_as_coercion() - def _test_weight_lattice_realization(self, **options): """ Runs sanity checks on this weight lattice realization @@ -263,9 +262,9 @@ def _test_weight_lattice_realization(self, **options): # For an affine root system, this will check the embedding of # the extended ones, and also of the non extended ones if this # realization is not extended - domains = [self.root_system.weight_space(base_ring, extended = extended) + domains = [self.root_system.weight_space(base_ring, extended=extended) for base_ring in set([ZZ, self.base_ring()]) - for extended in set([self.cartan_type().is_affine(), self.is_extended()])] + for extended in set([self.cartan_type().is_affine(), self.is_extended()])] for domain in domains: tester.assertIsNot(self._internal_coerce_map_from(domain), None) for i in self.index_set(): diff --git a/src/sage/combinat/root_system/weight_space.py b/src/sage/combinat/root_system/weight_space.py index 03409e334f9..808067aef1f 100644 --- a/src/sage/combinat/root_system/weight_space.py +++ b/src/sage/combinat/root_system/weight_space.py @@ -1,12 +1,12 @@ """ Weight lattices and weight spaces """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.sets.family import Family @@ -14,6 +14,7 @@ from .weight_lattice_realizations import WeightLatticeRealizations import functools + class WeightSpace(CombinatorialFreeModule): r""" INPUT: @@ -152,8 +153,7 @@ def __classcall_private__(cls, root_system, base_ring, extended=False): sage: WeightSpace(R, QQ) is WeightSpace(R, QQ, False) True """ - return super(WeightSpace, cls).__classcall__(cls, root_system, base_ring, extended) - + return super().__classcall__(cls, root_system, base_ring, extended) def __init__(self, root_system, base_ring, extended): """ @@ -307,7 +307,6 @@ def basis_extension(self): else: return Family([]) - @cached_method def simple_root(self, j): r""" @@ -383,8 +382,7 @@ def _repr_term(self, m): """ if m == "delta": return "deltacheck" if self.root_system.dual_side else "delta" - else: - return super(WeightSpace, self)._repr_term(m) + return super()._repr_term(m) def _latex_term(self, m): r""" @@ -402,8 +400,7 @@ def _latex_term(self, m): """ if m == "delta": return "\\delta^\\vee" if self.root_system.dual_side else "\\delta" - else: - return super(WeightSpace, self)._latex_term(m) + return super()._latex_term(m) @cached_method def _to_classical_on_basis(self, i): @@ -456,6 +453,7 @@ def basis_value(basis, i): return basis[i] return self.module_morphism(on_basis = functools.partial(basis_value, basis), codomain=L) + class WeightSpaceElement(CombinatorialFreeModule.Element): def scalar(self, lambdacheck): @@ -578,4 +576,5 @@ def to_weight_space(self): """ return self + WeightSpace.Element = WeightSpaceElement diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index af95fbce583..f306df7eddf 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -18,6 +18,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even +from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -90,7 +91,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ TESTS:: @@ -103,12 +104,12 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, c ct = CartanType(ct) if prefix is None: if ct.is_atomic(): - prefix = ct[0]+str(ct[1]) + prefix = ct[0] + str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) + return super().__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order, fusion_labels=fusion_labels, inject_variables=inject_variables) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ EXAMPLES:: @@ -129,7 +130,11 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._prefix = prefix self._style = style self._fusion_labels = None + self._field = None + self._basecoer = None self._k = k + if k is not None: + self._k = Integer(k) if ct.is_irreducible(): self._opposition = ct.opposition_automorphism() self._highest = self._space.highest_root() @@ -170,7 +175,7 @@ def next_level(wt): self._m_g = 2 else: self._m_g = 3 - if ct[0] in ['B','F']: + if ct[0] in ['B', 'F']: self._nf = 2 else: self._nf = 1 @@ -194,6 +199,9 @@ def next_level(wt): self._cyclotomic_order = self._fg * self._l else: self._cyclotomic_order = cyclotomic_order + self._fusion_labels = fusion_labels + if fusion_labels: + self.fusion_labels(labels=fusion_labels, inject_variables=inject_variables) @cached_method def ambient(self): @@ -448,7 +456,7 @@ def __call__(self, *args): # object which can't be coerced into self if len(args) > 1: args = (args,) - return super(WeylCharacterRing, self).__call__(*args) + return super().__call__(*args) def _element_constructor_(self, weight): """ @@ -1178,6 +1186,7 @@ class Element(CombinatorialFreeModule.Element): """ A class for Weyl characters. """ + def cartan_type(self): """ Return the Cartan type of ``self``. @@ -1733,7 +1742,7 @@ def __classcall__(cls, parent, prefix=None): sage: a3.cartan_type(), a3.base_ring(), a3.parent() (['A', 3], Integer Ring, The Weyl Character Ring of Type A3 with Integer Ring coefficients) """ - return super(WeightRing, cls).__classcall__(cls, parent, prefix=prefix) + return super().__classcall__(cls, parent, prefix=prefix) def __init__(self, parent, prefix): """ @@ -1818,7 +1827,7 @@ def __call__(self, *args): # object which can't be coerced into self if len(args) > 1: args = (args,) - return super(WeightRing, self).__call__(*args) + return super().__call__(*args) def _element_constructor_(self, weight): """ @@ -2006,6 +2015,7 @@ class Element(CombinatorialFreeModule.Element): """ A class for weight ring elements. """ + def cartan_type(self): """ Return the Cartan type. diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index 42aa218115c..d64d2c1f366 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -6,13 +6,11 @@ - Daniel Bump (2008): initial version - Mike Hansen (2008): initial version - Anne Schilling (2008): initial version -- Nicolas Thiery (2008): initial version +- Nicolas Thiéry (2008): initial version - Volker Braun (2013): LibGAP-based matrix groups EXAMPLES: -More examples on Weyl Groups should be added here... - The Cayley graph of the Weyl Group of type ['A', 3]:: sage: w = WeylGroup(['A',3]) @@ -26,8 +24,12 @@ sage: d = w.cayley_graph(); d Digraph on 192 vertices sage: d.show3d(color_by_label=True, edge_size=0.01, vertex_size=0.03) #long time (less than one minute) + +.. TODO:: + + More examples on Weyl Groups should be added here. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Daniel Bump <bump at match.stanford.edu>, # Mike Hansen <mhansen@gmail.com> # Anne Schilling <anne at math.ucdavis.edu> @@ -35,8 +37,8 @@ # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap from sage.groups.matrix_gps.group_element import MatrixGroupElement_gap from sage.groups.perm_gps.permgroup import PermutationGroup_generic @@ -59,12 +61,12 @@ def WeylGroup(x, prefix=None, implementation='matrix'): """ - Returns the Weyl group of the root system defined by the Cartan + Return the Weyl group of the root system defined by the Cartan type (or matrix) ``ct``. INPUT: - - ``x`` - a root system or a Cartan type (or matrix) + - ``x`` -- a root system or a Cartan type (or matrix) OPTIONAL: @@ -215,7 +217,7 @@ class WeylGroup_gens(UniqueRepresentation, @staticmethod def __classcall__(cls, domain, prefix=None): - return super(WeylGroup_gens, cls).__classcall__(cls, domain, prefix) + return super().__classcall__(cls, domain, prefix) def __init__(self, domain, prefix): """ @@ -236,7 +238,7 @@ def __init__(self, domain, prefix): category = WeylGroups() if self.cartan_type().is_irreducible(): category = category.Irreducible() - self.n = domain.dimension() # Really needed? + self.n = domain.dimension() # Really needed? self._prefix = prefix # FinitelyGeneratedMatrixGroup_gap takes plain matrices as input @@ -252,7 +254,7 @@ def __init__(self, domain, prefix): @cached_method def cartan_type(self): """ - Returns the CartanType associated to self. + Return the ``CartanType`` associated to ``self``. EXAMPLES:: @@ -265,7 +267,7 @@ def cartan_type(self): @cached_method def index_set(self): """ - Returns the index set of self. + Return the index set of ``self``. EXAMPLES:: @@ -289,7 +291,7 @@ def from_morphism(self, f): @cached_method def simple_reflections(self): """ - Returns the simple reflections of self, as a family. + Return the simple reflections of ``self``, as a family. EXAMPLES: @@ -393,13 +395,13 @@ def _repr_(self): Weyl Group of type ['A', 3, 1] (as a matrix group acting on the root space) """ return "Weyl Group of type %s (as a matrix group acting on the %s)" % (self.cartan_type(), - self._domain._name_string(capitalize=False, - base_ring=False, - type=False)) + self._domain._name_string(capitalize=False, + base_ring=False, + type=False)) def character_table(self): """ - Returns the character table as a matrix + Return the character table as a matrix. Each row is an irreducible character. For larger tables you may preface this with a command such as @@ -421,14 +423,14 @@ def character_table(self): X.4 3 -1 1 . -1 X.5 1 1 1 1 1 """ - gens_str = ', '.join(str(g.gap()) for g in self.gens()) + gens_str = ', '.join(str(g.gap()) for g in self.gens()) ctbl = gap('CharacterTable(Group({0}))'.format(gens_str)) return ctbl.Display() @cached_method def one(self): """ - Returns the unit element of the Weyl group + Return the unit element of the Weyl group. EXAMPLES:: @@ -441,13 +443,13 @@ def one(self): sage: type(e) == W.element_class True """ - return self._element_constructor_(matrix(QQ,self.n,self.n,1)) + return self._element_constructor_(matrix(QQ, self.n, self.n, 1)) - unit = one # For backward compatibility + unit = one # For backward compatibility def domain(self): """ - Returns the domain of the element of ``self``, that is the + Return the domain of the element of ``self``, that is the root lattice realization on which they act. EXAMPLES:: @@ -463,7 +465,7 @@ def domain(self): def simple_reflection(self, i): """ - Returns the `i^{th}` simple reflection. + Return the `i^{th}` simple reflection. EXAMPLES:: @@ -485,7 +487,7 @@ def simple_reflection(self, i): def long_element_hardcoded(self): """ - Returns the long Weyl group element (hardcoded data) + Return the long Weyl group element (hardcoded data). Do we really want to keep it? There is a generic implementation which works in all cases. The hardcoded should @@ -497,22 +499,22 @@ def long_element_hardcoded(self): sage: types = [ ['A',5],['B',3],['C',3],['D',4],['G',2],['F',4],['E',6] ] sage: [WeylGroup(t).long_element().length() for t in types] [15, 9, 9, 12, 6, 24, 36] - sage: all( WeylGroup(t).long_element() == WeylGroup(t).long_element_hardcoded() for t in types ) # long time (17s on sage.math, 2011) + sage: all(WeylGroup(t).long_element() == WeylGroup(t).long_element_hardcoded() for t in types) # long time (17s on sage.math, 2011) True """ type = self.cartan_type() - if type[0] == 'D' and type[1]%2 == 1: - l = [-1 for i in range(self.n-1)] + if type[0] == 'D' and type[1] % 2: + l = [-1 for i in range(self.n - 1)] l.append(1) - m = diagonal_matrix(QQ,l) + m = diagonal_matrix(QQ, l) elif type[0] == 'A': l = [0 for k in range((self.n)**2)] - for k in range(self.n-1, (self.n)**2-1, self.n-1): + for k in range(self.n - 1, (self.n)**2 - 1, self.n - 1): l[k] = 1 m = matrix(QQ, self.n, l) elif type[0] == 'E': if type[1] == 6: - half = ZZ(1)/ZZ(2) + half = ZZ(1) / ZZ(2) l = [[-half, -half, -half, half, 0, 0, 0, 0], [-half, -half, half, -half, 0, 0, 0, 0], [-half, half, -half, -half, 0, 0, 0, 0], @@ -523,10 +525,10 @@ def long_element_hardcoded(self): [0, 0, 0, 0, -half, half, half, half]] m = matrix(QQ, 8, l) else: - raise NotImplementedError("Not implemented yet for this type") + raise NotImplementedError("not implemented yet for this type") elif type[0] == 'G': - third = ZZ(1)/ZZ(3) - twothirds = ZZ(2)/ZZ(3) + third = ZZ(1) / ZZ(3) + twothirds = ZZ(2) / ZZ(3) l = [[-third, twothirds, twothirds], [twothirds, -third, twothirds], [twothirds, twothirds, -third]] @@ -559,6 +561,7 @@ def classical(self): raise ValueError("classical subgroup only defined for affine types") return ClassicalWeylSubgroup(self._domain, prefix=self._prefix) + class ClassicalWeylSubgroup(WeylGroup_gens): """ A class for Classical Weyl Subgroup of an affine Weyl Group @@ -638,9 +641,9 @@ def __repr__(self): Parabolic Subgroup of the Weyl Group of type ['C', 4, 1]^* (as a matrix group acting on the coweight lattice) """ return "Parabolic Subgroup of the Weyl Group of type %s (as a matrix group acting on the %s)" % (self.domain().cartan_type(), - self._domain._name_string(capitalize=False, - base_ring=False, - type=False)) + self._domain._name_string(capitalize=False, + base_ring=False, + type=False)) def weyl_group(self, prefix="hereditary"): """ @@ -672,10 +675,12 @@ def _test_is_finite(self, **options): tester.assertTrue(not self.weyl_group(self._prefix).is_finite()) tester.assertTrue(self.is_finite()) + class WeylGroupElement(MatrixGroupElement_gap): """ Class for a Weyl Group elements """ + def __init__(self, parent, g, check=False): """ EXAMPLES:: @@ -705,7 +710,7 @@ def to_matrix(self): def domain(self): """ - Returns the ambient lattice associated with self. + Return the ambient lattice associated with ``self``. EXAMPLES:: @@ -789,8 +794,8 @@ def __eq__(self, other): purposes. """ return (self.__class__ == other.__class__ and - self._parent == other._parent and - self.matrix() == other.matrix()) + self._parent == other._parent and + self.matrix() == other.matrix()) def _richcmp_(self, other, op): """ @@ -835,15 +840,14 @@ def action(self, v): alpha[0] + alpha[1] """ if v not in self.domain(): - raise ValueError("{} is not in the domain".format(v)) - return self.domain().from_vector(self.matrix()*v.to_vector()) + raise ValueError(f"{v} is not in the domain") + return self.domain().from_vector(self.matrix() * v.to_vector()) - - ########################################################################## + # ####################################################################### # Descents - ########################################################################## + # ####################################################################### - def has_descent(self, i, positive=False, side = "right"): + def has_descent(self, i, positive=False, side="right"): """ Test if ``self`` has a descent at position ``i``. @@ -934,7 +938,7 @@ def has_left_descent(self, i): sage: [(s[3]*s[2]).has_left_descent(i) for i in W.domain().index_set()] [False, False, True] """ - return self.has_descent(i, side = "left") + return self.has_descent(i, side="left") def has_right_descent(self, i): """ @@ -957,13 +961,14 @@ def has_right_descent(self, i): """ return self.has_descent(i, side="right") - def apply_simple_reflection(self, i, side = "right"): + def apply_simple_reflection(self, i, side="right"): s = self.parent().simple_reflections() if side == "right": return self * s[i] else: return s[i] * self +# TODO # The methods first_descent, descents, reduced_word appear almost verbatim in # root_lattice_realizations and need to be factored out! @@ -978,9 +983,8 @@ def to_permutation(self): """ W = self.parent() e = W.domain().basis() - return tuple( c*(j+1) - for i in e.keys() - for (j,c) in self.action(e[i]) ) + return tuple(c * (j + 1) for i in e.keys() + for (j, c) in self.action(e[i])) def to_permutation_string(self): """ @@ -993,6 +997,7 @@ def to_permutation_string(self): """ return "".join(str(i) for i in self.to_permutation()) + WeylGroup_gens.Element = WeylGroupElement @@ -1012,7 +1017,7 @@ def __classcall__(cls, cartan_type, prefix=None): sage: W1 is W2 True """ - return super(WeylGroup_permutation, cls).__classcall__(cls, CartanType(cartan_type), prefix) + return super().__classcall__(cls, CartanType(cartan_type), prefix) def __init__(self, cartan_type, prefix): """ @@ -1025,13 +1030,12 @@ def __init__(self, cartan_type, prefix): """ self._cartan_type = cartan_type self._index_set = cartan_type.index_set() - self._index_set_inverse = {ii: i for i,ii in enumerate(cartan_type.index_set())} + self._index_set_inverse = {ii: i for i, ii in enumerate(cartan_type.index_set())} self._reflection_representation = None self._prefix = prefix - #from sage.libs.gap.libgap import libgap Q = cartan_type.root_system().root_lattice() Phi = list(Q.positive_roots()) + [-x for x in Q.positive_roots()] - p = [[Phi.index(x.weyl_action([i]))+1 for x in Phi] + p = [[Phi.index(x.weyl_action([i])) + 1 for x in Phi] for i in self._cartan_type.index_set()] cat = FiniteWeylGroups() if self._cartan_type.is_irreducible(): @@ -1207,7 +1211,7 @@ def reflection_index_set(self): sage: W.reflection_index_set() (1, 2, 3, 4, 5, 6) """ - return tuple(range(1, self.number_of_reflections()+1)) + return tuple(range(1, self.number_of_reflections() + 1)) def cartan_type(self): """ @@ -1301,9 +1305,9 @@ def distinguished_reflections(self): def build_elt(index): r = pos_roots[index] - perm = [Phi.index(x.reflection(r))+1 for x in Phi] + perm = [Phi.index(x.reflection(r)) + 1 for x in Phi] return self.element_class(perm, self, check=False) - return Family(self.reflection_index_set(), lambda i: build_elt(i-1)) + return Family(self.reflection_index_set(), lambda i: build_elt(i - 1)) reflections = distinguished_reflections diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index b28b1b89b9e..fa260e70343 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -517,6 +517,7 @@ class RootedTrees_all(DisjointUnionEnumeratedSets, RootedTrees): See :class:`RootedTree` for a definition. """ + def __init__(self): """ TESTS:: @@ -626,6 +627,7 @@ class RootedTrees_size(RootedTrees): sage: from sage.combinat.rooted_tree import RootedTrees_size sage: for i in range(1, 6): TestSuite(RootedTrees_size(i)).run() """ + def __init__(self, n): """ TESTS:: diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index 2400ef86fdd..eb3d2880727 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -202,6 +202,7 @@ class Rule(UniqueRepresentation): e.g., the case of Hecke insertion, in which a letter bumped into a row may change a different row). """ + def to_pairs(self, obj1=None, obj2=None, check=True): r""" Given a valid input for the RSK algorithm, such as @@ -530,6 +531,7 @@ class RuleRSK(Rule): sage: RSK_inverse(p, q, insertion=RSK.rules.RSK) [[1, 2, 3, 3], [2, 1, 2, 2]] """ + def insertion(self, j, r): r""" Insert the letter ``j`` from the second row of the biword @@ -700,6 +702,7 @@ class RuleEG(Rule): ....: for wd in p.reduced_words()) True """ + def insertion(self, j, r): r""" Insert the letter ``j`` from the second row of the biword @@ -866,6 +869,7 @@ class RuleHecke(Rule): sage: wp == w True """ + def forward_rule(self, obj1, obj2, check_standard=False): r""" Return a pair of tableaux obtained by applying Hecke @@ -1306,6 +1310,7 @@ class RuleDualRSK(Rule): ... ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape """ + def to_pairs(self, obj1=None, obj2=None, check=True): r""" Given a valid input for the dual RSK algorithm, such as @@ -1686,6 +1691,7 @@ class RuleCoRSK(RuleRSK): ... ValueError: invalid strict cobiword """ + def to_pairs(self, obj1=None, obj2=None, check=True): r""" Given a valid input for the coRSK algorithm, such as @@ -2008,6 +2014,7 @@ class RuleSuperRSK(RuleRSK): ... ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape """ + def to_pairs(self, obj1=None, obj2=None, check=True): r""" Given a valid input for the super RSK algorithm, such as @@ -2644,6 +2651,7 @@ class RuleStar(Rule): ....: insertion='Star', output='word') for w in words)]) True """ + def forward_rule(self, obj1, obj2=None, check_braid=True): r""" Return a pair of tableaux obtained by applying forward insertion @@ -2958,6 +2966,7 @@ def _backward_format_output(self, obj1, obj2, output): df.append([]) return DecreasingHeckeFactorization(df) + class InsertionRules(): r""" Catalog of rules for RSK-like insertion algorithms. @@ -2972,6 +2981,7 @@ class InsertionRules(): ##################################################################### + def RSK(obj1=None, obj2=None, insertion=InsertionRules.RSK, check_standard=False, **options): r""" Perform the Robinson-Schensted-Knuth (RSK) correspondence. @@ -3180,6 +3190,7 @@ def RSK(obj1=None, obj2=None, insertion=InsertionRules.RSK, check_standard=False robinson_schensted_knuth = RSK RSK.rules = InsertionRules + def RSK_inverse(p, q, output='array', insertion=InsertionRules.RSK): r""" Return the generalized permutation corresponding to the pair of diff --git a/src/sage/combinat/schubert_polynomial.py b/src/sage/combinat/schubert_polynomial.py index 794dc7ee2c6..7e9164db5de 100644 --- a/src/sage/combinat/schubert_polynomial.py +++ b/src/sage/combinat/schubert_polynomial.py @@ -2,7 +2,7 @@ Schubert Polynomials -See :wikipedia:`Schubert_polynomial` and +See :wikipedia:`Schubert_polynomial` and `SymmetricFunctions.com <https://www.symmetricfunctions.com/schubert.htm#schubert>`_. Schubert polynomials are representatives of cohomology classes in flag varieties. In `n` variables, they are indexed by permutations `w \in S_n`. They also form @@ -73,15 +73,18 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.all import GradedAlgebrasWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.key_polynomial import KeyPolynomial +from sage.combinat.permutation import Permutations, Permutation +from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_sparse from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.multi_polynomial import is_MPolynomial -from sage.combinat.permutation import Permutations, Permutation + import sage.libs.symmetrica.all as symmetrica -from sage.misc.cachefunc import cached_method def SchubertPolynomialRing(R): @@ -414,6 +417,17 @@ def _element_constructor_(self, x): sage: X(x1^2*x2) X[3, 2, 1] + sage: S.<x> = InfinitePolynomialRing(QQ) + sage: X(x[0]^2*x[1]) + X[3, 2, 1] + sage: X(x[0]*x[1]^2*x[2]^2*x[3] + x[0]^2*x[1]^2*x[2]*x[3] + x[0]^2*x[1]*x[2]^2*x[3]) + X[2, 4, 5, 3, 1] + + sage: from sage.combinat.key_polynomial import KeyPolynomialBasis + sage: k = KeyPolynomialBasis(QQ) + sage: X(k([3,2,1])) + X[4, 3, 2, 1] + TESTS: We check that :trac:`12924` is fixed:: @@ -429,6 +443,15 @@ def _element_constructor_(self, x): sage: X([]) X[1] + + Check the round trip from key polynomials:: + + sage: k = KeyPolynomials(ZZ) + sage: X = SchubertPolynomialRing(ZZ) + sage: it = iter(Permutations()) + sage: for _ in range(50): + ....: P = next(it) + ....: assert X(k(X(P))) == X(P), P """ if isinstance(x, list): # checking the input to avoid symmetrica crashing Sage, see trac 12924 @@ -441,6 +464,14 @@ def _element_constructor_(self, x): return self._from_dict({perm: self.base_ring().one()}) elif is_MPolynomial(x): return symmetrica.t_POLYNOM_SCHUBERT(x) + elif isinstance(x, InfinitePolynomial_sparse): + R = x.polynomial().parent() + # massage the term order to be what symmetrica expects + S = PolynomialRing(R.base_ring(), + names=list(map(repr, reversed(R.gens())))) + return symmetrica.t_POLYNOM_SCHUBERT(S(x.polynomial())) + elif isinstance(x, KeyPolynomial): + return self(x.expand()) else: raise TypeError diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 89b7066a08a..eaaf5bf5cf3 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -61,6 +61,7 @@ class AbstractSetPartition(ClonableArray, r""" Methods of set partitions which are independent of the base set """ + def _repr_(self): """ Return a string representation of ``self``. @@ -2663,6 +2664,7 @@ class SetPartitions_all(SetPartitions): r""" All set partitions. """ + def __init__(self): """ Initialize ``self``. @@ -3112,7 +3114,6 @@ def __contains__(self, x): return False return sorted(map(len, x), reverse=True) == self._parts - def random_element(self): r""" Return a random set partition of ``self``. diff --git a/src/sage/combinat/set_partition_ordered.py b/src/sage/combinat/set_partition_ordered.py index f2f201eb7f2..268126d65ef 100644 --- a/src/sage/combinat/set_partition_ordered.py +++ b/src/sage/combinat/set_partition_ordered.py @@ -25,36 +25,36 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from sage.arith.all import factorial, multinomial -from sage.sets.set import Set, Set_generic +from sage.categories.cartesian_product import cartesian_product from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet -from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.structure.parent import Parent -from sage.structure.element import parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.list_clone import ClonableArray -from sage.structure.richcmp import richcmp -from sage.rings.integer import Integer -from sage.rings.integer_ring import ZZ -from sage.combinat.combinatorial_map import combinatorial_map from sage.combinat.combinat import stirling_number2 +from sage.combinat.combinatorial_map import combinatorial_map from sage.combinat.composition import Composition, Compositions -from sage.combinat.words.words import Words from sage.combinat.words.finite_word import FiniteWord_class -import sage.combinat.permutation as permutation -from functools import reduce -from sage.categories.cartesian_product import cartesian_product +from sage.combinat.words.words import Words +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.persist import register_unpickle_override +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.sets.set import Set, Set_generic +from sage.structure.list_clone import ClonableArray +from sage.structure.element import parent +from sage.structure.parent import Parent +from sage.structure.richcmp import richcmp +from sage.structure.unique_representation import UniqueRepresentation class OrderedSetPartition(ClonableArray, - metaclass=InheritComparisonClasscallMetaclass): + metaclass=InheritComparisonClasscallMetaclass): r""" An ordered partition of a set. An ordered set partition `p` of a set `s` is a list of pairwise disjoint nonempty subsets of `s` such that the union of these subsets is `s`. These subsets are called the parts of the partition. + We represent an ordered set partition as a list of sets. By extension, an ordered set partition of a nonnegative integer `n` is the set partition of the integers from `1` to `n`. The number of @@ -146,12 +146,16 @@ class OrderedSetPartition(ClonableArray, sage: OrderedSetPartition(from_word='bdab') [{3}, {1, 4}, {2}] + .. WARNING:: + + The elements of the underlying set should be hashable. + REFERENCES: :wikipedia:`Ordered_partition_of_a_set` """ @staticmethod - def __classcall_private__(cls, parts=None, from_word=None): + def __classcall_private__(cls, parts=None, from_word=None, check=True): """ Create a set partition from ``parts`` with the appropriate parent. @@ -179,17 +183,16 @@ def __classcall_private__(cls, parts=None, from_word=None): if parts is None and from_word is None: P = OrderedSetPartitions([]) return P.element_class(P, []) + W = Words(infinite=False) if from_word: - return OrderedSetPartitions().from_finite_word(Words()(from_word)) + return OrderedSetPartitions().from_finite_word(W(from_word)) # if `parts` looks like a sequence of "letters" then treat it like a word. - if parts in Words() or (parts and (parts[0] in ZZ or isinstance(parts[0], str))): - return OrderedSetPartitions().from_finite_word(Words()(parts)) - else: - P = OrderedSetPartitions(reduce(lambda x, y: x.union(y), - parts, frozenset())) - return P.element_class(P, parts) + if parts in W or (parts and (parts[0] in ZZ or isinstance(parts[0], str))): + return OrderedSetPartitions().from_finite_word(W(parts)) + P = OrderedSetPartitions(set(x for p in parts for x in p)) + return P.element_class(P, parts, check=check) - def __init__(self, parent, s): + def __init__(self, parent, s, check=True): """ Initialize ``self``. @@ -199,8 +202,8 @@ def __init__(self, parent, s): sage: s = OS([[1, 3], [2, 4]]) sage: TestSuite(s).run() """ - self._base_set = reduce(lambda x, y: x.union(y), map(Set, s), Set([])) - ClonableArray.__init__(self, parent, [Set(elt) for elt in s]) + ClonableArray.__init__(self, parent, + [frozenset(part) for part in s], check=check) def _repr_(self): """ @@ -228,42 +231,41 @@ def check(self): sage: s = OS([[1, 3], [2, 4]]) sage: s.check() """ - assert self in self.parent(), "%s not in %s" % (self, self.parent()) - - def _hash_(self): - """ - Return the hash of ``self``. - - EXAMPLES:: - - sage: OS = OrderedSetPartitions(4) - sage: s = OS([[1, 3], [2, 4]]) - sage: OSP = OrderedSetPartitions() - sage: hash(s) == hash(OSP(s)) - True - """ - return hash(tuple(self)) + par = parent(self) + assert self in par, "%s not in %s" % (self, par) def base_set(self): """ - Return the base set of ``self``, which is the union of all parts - of ``self``. + Return the base set of ``self``. + + This is the union of all parts of ``self``. EXAMPLES:: sage: OrderedSetPartition([[1], [2,3], [4]]).base_set() - {1, 2, 3, 4} + frozenset({1, 2, 3, 4}) sage: OrderedSetPartition([[1,2,3,4]]).base_set() - {1, 2, 3, 4} + frozenset({1, 2, 3, 4}) sage: OrderedSetPartition([]).base_set() - {} + frozenset() + + TESTS:: + + sage: S = OrderedSetPartitions() + sage: x = S([['a', 'c', 'e'], ['b', 'd']]) + sage: x.base_set() + frozenset({'a', 'b', 'c', 'd', 'e'}) """ - return Set([e for p in self for e in p]) + try: + return parent(self)._set + except AttributeError: # in OrderedSetPartitions_all + return frozenset(x for part in self for x in part) def base_set_cardinality(self): """ - Return the cardinality of the base set of ``self``, which is the sum - of the sizes of the parts of ``self``. + Return the cardinality of the base set of ``self``. + + This is the sum of the sizes of the parts of ``self``. This is also known as the *size* (sometimes the *weight*) of an ordered set partition. @@ -274,8 +276,17 @@ def base_set_cardinality(self): 4 sage: OrderedSetPartition([[1,2,3,4]]).base_set_cardinality() 4 + + TESTS:: + + sage: S = OrderedSetPartitions() + sage: S([[1,4],[3],[2]]).base_set_cardinality() + 4 """ - return sum(len(x) for x in self) + try: + return len(parent(self)._set) + except AttributeError: # in OrderedSetPartitions_all + return sum(len(part) for part in self) size = base_set_cardinality @@ -327,7 +338,7 @@ def sum(osps): Any iterable can be provided as input:: - sage: Composition.sum([OrderedSetPartition([[2*i,2*i+1]]) for i in [4,1,3]]) + sage: OrderedSetPartition.sum([OrderedSetPartition([[2*i,2*i+1]]) for i in [4,1,3]]) [{8, 9}, {2, 3}, {6, 7}] Empty inputs are handled gracefully:: @@ -342,9 +353,10 @@ def sum(osps): sage: C = OrderedSetPartition.sum([A, B]); C [{2}, {1, 3}, {5}] sage: C.parent() - Ordered set partitions + Ordered set partitions of {1, 2, 3, 5} """ - return OrderedSetPartitions()(sum((list(i) for i in osps), [])) + lset = set(x for osp in osps for x in osp.base_set()) + return OrderedSetPartitions(lset)(sum((list(i) for i in osps), [])) def reversed(self): r""" @@ -403,7 +415,8 @@ def complement(self): m = min(base_set) M = max(base_set) mM = m + M - return OrderedSetPartitions()([[mM - i for i in part] for part in self]) + par = parent(self) + return par([[mM - i for i in part] for part in self]) def finer(self): """ @@ -438,9 +451,8 @@ def finer(self): par = parent(self) if not self: return FiniteEnumeratedSet([self]) - else: - return FiniteEnumeratedSet([par(sum((list(i) for i in C), [])) - for C in cartesian_product([OrderedSetPartitions(X) for X in self])]) + return FiniteEnumeratedSet([par(sum((list(i) for i in C), [])) + for C in cartesian_product([OrderedSetPartitions(X) for X in self])]) def is_finer(self, co2): """ @@ -480,9 +492,9 @@ def is_finer(self, co2): i1 = 0 for j2 in co2: - sum1 = Set([]) + sum1 = set() while len(sum1) < len(j2): - sum1 += co1[i1] + sum1 = sum1.union(co1[i1]) i1 += 1 if not sum1.issubset(j2): return False @@ -536,7 +548,7 @@ def fatten(self, grouping): result = [None] * len(grouping) j = 0 for i in range(len(grouping)): - result[i] = sum(self[j:j+grouping[i]], Set([])) + result[i] = set().union(*self[j:j+grouping[i]]) j += grouping[i] return parent(self)(result) @@ -553,10 +565,10 @@ def fatter(self): sage: C.cardinality() 4 sage: sorted(C) - [[{1, 2, 3, 4, 5}], - [{1, 2, 5}, {3, 4}], + [[{2, 5}, {1}, {3, 4}], [{2, 5}, {1, 3, 4}], - [{2, 5}, {1}, {3, 4}]] + [{1, 2, 5}, {3, 4}], + [{1, 2, 3, 4, 5}]] sage: OrderedSetPartition([[4, 9], [-1, 2]]).fatter().list() [[{4, 9}, {-1, 2}], [{-1, 2, 4, 9}]] @@ -568,14 +580,14 @@ def fatter(self): sage: list(Composition([]).fatter()) [[]] sage: sorted(OrderedSetPartition([[1], [2], [3], [4]]).fatter()) - [[{1, 2, 3, 4}], - [{1, 2, 3}, {4}], - [{1, 2}, {3, 4}], - [{1, 2}, {3}, {4}], - [{1}, {2, 3, 4}], - [{1}, {2, 3}, {4}], + [[{1}, {2}, {3}, {4}], [{1}, {2}, {3, 4}], - [{1}, {2}, {3}, {4}]] + [{1}, {2, 3}, {4}], + [{1}, {2, 3, 4}], + [{1, 2}, {3}, {4}], + [{1, 2}, {3, 4}], + [{1, 2, 3}, {4}], + [{1, 2, 3, 4}]] """ return Compositions(len(self)).map(self.fatten) @@ -614,7 +626,7 @@ def bottom_up_osp(X, comp): sage: buo = OrderedSetPartition.bottom_up_osp sage: parent(buo(Set([1, 4, 7, 9]), [2, 1, 1])) - Ordered set partitions + Ordered set partitions of {1, 4, 9, 7} sage: buo((3, 5, 6), (2, 1)) [{3, 5}, {6}] sage: buo([3, 5, 6], Composition([1, 2])) @@ -624,9 +636,9 @@ def bottom_up_osp(X, comp): result = [None] * len(comp) j = 0 for i in range(len(comp)): - result[i] = Set(xs[j:j+comp[i]]) + result[i] = set(xs[j:j+comp[i]]) j += comp[i] - return OrderedSetPartitions()(result) + return OrderedSetPartitions(X)(result) def strongly_finer(self): """ @@ -657,10 +669,9 @@ def strongly_finer(self): par = parent(self) if not self: return FiniteEnumeratedSet([self]) - else: - buo = OrderedSetPartition.bottom_up_osp - return FiniteEnumeratedSet([par(sum((list(P) for P in C), [])) - for C in cartesian_product([[buo(X, comp) for comp in Compositions(len(X))] for X in self])]) + buo = OrderedSetPartition.bottom_up_osp + return FiniteEnumeratedSet([par(sum((list(P) for P in C), [])) + for C in cartesian_product([[buo(X, comp) for comp in Compositions(len(X))] for X in self])]) def is_strongly_finer(self, co2): r""" @@ -703,12 +714,12 @@ def is_strongly_finer(self, co2): i1 = 0 for j2 in co2: - sum1 = Set([]) + sum1 = set() while len(sum1) < len(j2): next = co1[i1] if sum1 and max(sum1) >= min(next): return False - sum1 += next + sum1 = sum1.union(next) i1 += 1 if not sum1.issubset(j2): return False @@ -728,8 +739,7 @@ def strongly_fatter(self): sage: C.cardinality() 2 sage: sorted(C) - [[{2, 5}, {1, 3, 4}], - [{2, 5}, {1}, {3, 4}]] + [[{2, 5}, {1}, {3, 4}], [{2, 5}, {1, 3, 4}]] sage: OrderedSetPartition([[4, 9], [-1, 2]]).strongly_fatter().list() [[{4, 9}, {-1, 2}]] @@ -741,21 +751,21 @@ def strongly_fatter(self): sage: list(OrderedSetPartition([]).strongly_fatter()) [[]] sage: sorted(OrderedSetPartition([[1], [2], [3], [4]]).strongly_fatter()) - [[{1, 2, 3, 4}], - [{1, 2, 3}, {4}], - [{1, 2}, {3, 4}], - [{1, 2}, {3}, {4}], - [{1}, {2, 3, 4}], - [{1}, {2, 3}, {4}], + [[{1}, {2}, {3}, {4}], [{1}, {2}, {3, 4}], - [{1}, {2}, {3}, {4}]] + [{1}, {2, 3}, {4}], + [{1}, {2, 3, 4}], + [{1, 2}, {3}, {4}], + [{1, 2}, {3, 4}], + [{1, 2, 3}, {4}], + [{1, 2, 3, 4}]] sage: sorted(OrderedSetPartition([[1], [3], [2], [4]]).strongly_fatter()) - [[{1, 3}, {2, 4}], - [{1, 3}, {2}, {4}], + [[{1}, {3}, {2}, {4}], [{1}, {3}, {2, 4}], - [{1}, {3}, {2}, {4}]] + [{1, 3}, {2}, {4}], + [{1, 3}, {2, 4}]] sage: sorted(OrderedSetPartition([[4], [1], [5], [3]]).strongly_fatter()) - [[{4}, {1, 5}, {3}], [{4}, {1}, {5}, {3}]] + [[{4}, {1}, {5}, {3}], [{4}, {1, 5}, {3}]] """ c = [sorted(X) for X in self] l = len(c) - 1 @@ -791,7 +801,7 @@ def to_packed_word(self): .. WARNING:: This assumes there is a total order on the underlying - set (``self._base_set``). + set. EXAMPLES:: @@ -799,16 +809,18 @@ def to_packed_word(self): sage: x = S([[3,5], [2], [1,4,6]]) sage: x.to_packed_word() word: 321313 + sage: x = S([['a', 'c', 'e'], ['b', 'd']]) sage: x.to_packed_word() word: 12121 """ - X = sorted(self._base_set) + X = sorted(self.base_set()) out = {} for i in range(len(self)): for letter in self[i]: out[letter] = i - return Words()([out[letter] + 1 for letter in X]) + W = Words(infinite=False) + return W([out[letter] + 1 for letter in X]) def number_of_inversions(self): r""" @@ -841,6 +853,7 @@ def number_of_inversions(self): num_invs += sum(1 for j in self[ell] if i < j) return ZZ(num_invs) + class OrderedSetPartitions(UniqueRepresentation, Parent): """ Return the combinatorial class of ordered set partitions of ``s``. @@ -898,6 +911,13 @@ class OrderedSetPartitions(UniqueRepresentation, Parent): [{'t'}, {'a', 'c'}], [{'t'}, {'a'}, {'c'}], [{'t'}, {'c'}, {'a'}]] + + TESTS:: + + sage: S = OrderedSetPartitions() + sage: x = S([[3,5], [2], [1,4,6]]) + sage: x.parent() + Ordered set partitions """ @staticmethod def __classcall_private__(cls, s=None, c=None): @@ -918,7 +938,7 @@ def __classcall_private__(cls, s=None, c=None): if isinstance(s, (int, Integer)): if s < 0: raise ValueError("s must be non-negative") - s = frozenset(range(1, s+1)) + s = frozenset(range(1, s + 1)) else: s = frozenset(s) @@ -950,12 +970,14 @@ def _element_constructor_(self, s): EXAMPLES:: sage: OS = OrderedSetPartitions(4) - sage: OS([[1,3],[2,4]]) + sage: x = OS([[1,3],[2,4]]); x [{1, 3}, {2, 4}] + sage: x.parent() + Ordered set partitions of {1, 2, 3, 4} """ if isinstance(s, OrderedSetPartition): raise ValueError("cannot convert %s into an element of %s" % (s, self)) - return self.element_class(self, list(s)) + return self.element_class(self, list(s)) # HERE the parent "self" is not good Element = OrderedSetPartition @@ -971,11 +993,9 @@ def __contains__(self, x): sage: [Set([1,2]), Set([3,4])] in OS True sage: [set([1,2]), set([3,4])] in OS - Traceback (most recent call last): - ... - TypeError: X (=...1, 2...) must be a Set + True """ - #x must be a list + # x must be a list if not isinstance(x, (OrderedSetPartition, list, tuple)): return False @@ -986,7 +1006,7 @@ def __contains__(self, x): # Check to make sure each element of the list # is a nonempty set - u = Set([]) + u = set() for s in x: if not s or not isinstance(s, (set, frozenset, Set_generic)): return False @@ -994,12 +1014,9 @@ def __contains__(self, x): # Make sure that the union of all the # sets is the original set - if u != Set(self._set): - return False - - return True + return len(u) == len(self._set) - def from_finite_word(self, w): + def from_finite_word(self, w, check=True): r""" Return the unique ordered set partition of `\{1, 2, \ldots, n\}` corresponding to a word `w` of length `n`. @@ -1015,19 +1032,38 @@ def from_finite_word(self, w): sage: B = OrderedSetPartitions().from_finite_word([1,2,3,1,2,3,1,2,4]) sage: A == B True + + TESTS:: + + sage: A = OrderedSetPartitions().from_finite_word('abcabca') + sage: A.parent() + Ordered set partitions + + sage: A = OrderedSetPartitions(7).from_finite_word('abcabca') + sage: A.parent() + Ordered set partitions of {1, 2, 3, 4, 5, 6, 7} """ # TODO: fix this if statement. # In fact, what we need is for the underlying alphabet to be sortable. if isinstance(w, (list, tuple, str, FiniteWord_class)): - return self.element_class(self, Words()(w).to_ordered_set_partition()) - else: - raise ValueError("Something is wrong: `from_finite_word` expects an object of type list/tuple/str/Word representing a finite word, received {}.".format(str(w))) + W = Words(infinite=False) + if check: + try: + X = self._set + if len(X) != len(w) or X != frozenset(range(1, len(w) + 1)): + raise ValueError(f"result not in {self}") + except AttributeError: + pass + return self.element_class(self, W(w).to_ordered_set_partition()) + raise TypeError(f"`from_finite_word` expects an object of type list/tuple/str/Word " + f"representing a finite word, received {w}") class OrderedSetPartitions_s(OrderedSetPartitions): """ Class of ordered partitions of a set `S`. """ + def _repr_(self): """ TESTS:: @@ -1056,14 +1092,15 @@ def cardinality(self): sage: OrderedSetPartitions(5).cardinality() 541 """ - return sum([factorial(k)*stirling_number2(len(self._set), k) - for k in range(len(self._set)+1)]) + N = len(self._set) + return sum(factorial(k) * stirling_number2(N, k) + for k in range(N + 1)) def __iter__(self): """ EXAMPLES:: - sage: [ p for p in OrderedSetPartitions([1,2,3]) ] + sage: list(OrderedSetPartitions([1,2,3])) [[{1}, {2}, {3}], [{1}, {3}, {2}], [{2}, {1}, {3}], @@ -1080,7 +1117,7 @@ def __iter__(self): """ for x in Compositions(len(self._set)): for z in OrderedSetPartitions(self._set, x): - yield self.element_class(self, z) + yield self.element_class(self, z, check=False) class OrderedSetPartitions_sn(OrderedSetPartitions): @@ -1158,7 +1195,7 @@ def __iter__(self): """ for x in Compositions(len(self._set), length=self.n): for z in OrderedSetPartitions_scomp(self._set, x): - yield self.element_class(self, z) + yield self.element_class(self, z, check=False) class OrderedSetPartitions_scomp(OrderedSetPartitions): @@ -1262,21 +1299,98 @@ def __iter__(self): {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}, {42}] + + EXAMPLES:: + + sage: list(OrderedSetPartitions(range(5), [2,1,2])) + [[{0, 1}, {2}, {3, 4}], + [{0, 1}, {3}, {2, 4}], + ... + [{2, 4}, {3}, {0, 1}], + [{3, 4}, {2}, {0, 1}]] """ - comp = self.c - lset = [x for x in self._set] - l = len(self.c) - dcomp = [-1] + comp.descents(final_descent=True) + part_sizes = self.c + N = len(part_sizes) + if not N: + yield self.element_class(self, [], check=False) + return + + l = [] + lset = list(self._set) + for i, j in enumerate(part_sizes): + l.extend([i] * j) - p = [] - for j in range(l): - p += [j + 1] * comp[j] + pi = multiset_permutation_to_ordered_set_partition(l, N) + converted = [frozenset(lset[i] for i in part) for part in pi] + yield self.element_class(self, converted, check=False) - for x in permutation.Permutations_mset(p): - res = permutation.to_standard(x).inverse() - res = [lset[x - 1] for x in res] - yield self.element_class(self, [Set(res[dcomp[i]+1:dcomp[i+1]+1]) - for i in range(l)]) + while multiset_permutation_next_lex(l): + pi = multiset_permutation_to_ordered_set_partition(l, N) + converted = [frozenset(lset[i] for i in part) for part in pi] + yield self.element_class(self, converted, check=False) + + +def multiset_permutation_to_ordered_set_partition(l, m): + r""" + Convert a multiset permutation to an ordered set partition. + + INPUT: + + - ``l`` -- a multiset permutation + - ``m`` -- number of parts + + EXAMPLES:: + + sage: from sage.combinat.set_partition_ordered import multiset_permutation_to_ordered_set_partition + sage: l = [0, 0, 1, 1, 2] + sage: multiset_permutation_to_ordered_set_partition(l, 3) + [[0, 1], [2, 3], [4]] + """ + p = [[] for _ in range(m)] + for i, j in enumerate(l): + p[j].append(i) + return p + + +def multiset_permutation_next_lex(l): + r""" + Return the next multiset permutation after ``l``. + + EXAMPLES:: + + sage: from sage.combinat.set_partition_ordered import multiset_permutation_next_lex + sage: l = [0, 0, 1, 1, 2] + sage: while multiset_permutation_next_lex(l): + ....: print(l) + [0, 0, 1, 2, 1] + [0, 0, 2, 1, 1] + [0, 1, 0, 1, 2] + [0, 1, 0, 2, 1] + [0, 1, 1, 0, 2] + [0, 1, 1, 2, 0] + ... + [1, 1, 2, 0, 0] + [1, 2, 0, 0, 1] + [1, 2, 0, 1, 0] + [1, 2, 1, 0, 0] + [2, 0, 0, 1, 1] + [2, 0, 1, 0, 1] + [2, 0, 1, 1, 0] + [2, 1, 0, 0, 1] + [2, 1, 0, 1, 0] + [2, 1, 1, 0, 0] + """ + i = len(l) - 2 + while i >= 0 and l[i] >= l[i + 1]: + i -= 1 + if i == -1: + return 0 + j = len(l) - 1 + while l[j] <= l[i]: + j -= 1 + l[i], l[j] = l[j], l[i] + l[i + 1:] = l[:i:-1] + return 1 class OrderedSetPartitions_all(OrderedSetPartitions): @@ -1284,6 +1398,7 @@ class OrderedSetPartitions_all(OrderedSetPartitions): Ordered set partitions of `\{1, \ldots, n\}` for all `n \in \ZZ_{\geq 0}`. """ + def __init__(self): """ Initialize ``self``. @@ -1325,7 +1440,7 @@ def __iter__(self): n = 0 while True: for X in OrderedSetPartitions(n): - yield self.element_class(self, list(X)) + yield self.element_class(self, list(X), check=False) n += 1 def _element_constructor_(self, s): @@ -1368,7 +1483,7 @@ def __contains__(self, x): if x.parent() is self: return True gset = x.parent()._set - return gset == frozenset(range(1, len(gset)+1)) + return gset == frozenset(range(1, len(gset) + 1)) # x must be a list or a tuple if not isinstance(x, (list, tuple)): @@ -1380,8 +1495,8 @@ def __contains__(self, x): if not all(isinstance(s, (set, frozenset, Set_generic)) or len(s) == len(set(s)) for s in x): return False - X = set(reduce(lambda A,B: A.union(B), x, set())) - return len(X) == sum(len(s) for s in x) and X == set(range(1,len(X)+1)) + X = set(y for p in x for y in p) + return len(X) == sum(len(s) for s in x) and X == frozenset(range(1, len(X) + 1)) def _coerce_map_from_(self, X): """ @@ -1448,8 +1563,7 @@ def __setstate__(self, state): self.__class__ = OrderedSetPartitions_scomp n = state['_n'] k = state['_k'] - OrderedSetPartitions_scomp.__init__(self, range(state['_n']), (k, n-k)) + OrderedSetPartitions_scomp.__init__(self, range(state['_n']), (k, n - k)) -from sage.misc.persist import register_unpickle_override register_unpickle_override("sage.combinat.split_nk", "SplitNK_nk", SplitNK) diff --git a/src/sage/combinat/sf/character.py b/src/sage/combinat/sf/character.py index e5752c307eb..c3b2ba786ce 100644 --- a/src/sage/combinat/sf/character.py +++ b/src/sage/combinat/sf/character.py @@ -201,6 +201,7 @@ class induced_trivial_character_basis(generic_character): sage: TestSuite(ht).run() """ + def __init__(self, Sym, pfix): r""" Initialize the basis and register coercions. @@ -424,6 +425,7 @@ class irreducible_character_basis(generic_character): sage: TestSuite(st).run() """ + def __init__(self, Sym, pfix): r""" Initialize the basis and register coercions. diff --git a/src/sage/combinat/sf/classical.py b/src/sage/combinat/sf/classical.py index ea77ed7c8a3..eda10b82730 100644 --- a/src/sage/combinat/sf/classical.py +++ b/src/sage/combinat/sf/classical.py @@ -36,6 +36,7 @@ conversion_functions = {} + def init(): """ Set up the conversion functions between the classical bases. @@ -119,6 +120,24 @@ def _element_constructor_(self, x): Traceback (most recent call last): ... TypeError: do not know how to make x (= [[2, 1], [1]]) an element of self + + Check that :trac:`34576` is fixed:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: f = s(0/2); f + 0 + sage: f == 0 + True + sage: f._monomial_coefficients + {} + + sage: s2 = SymmetricFunctions(GF(2)).s() + sage: f = s2(2*s[2,1]); f + 0 + sage: f == 0 + True + sage: f._monomial_coefficients + {} """ R = self.base_ring() @@ -126,7 +145,6 @@ def _element_constructor_(self, x): if isinstance(x, int): x = Integer(x) - ############## # Partitions # ############## @@ -140,9 +158,9 @@ def _element_constructor_(self, x): # Dual bases # ############## elif sfa.is_SymmetricFunction(x) and hasattr(x, 'dual'): - #Check to see if it is the dual of some other basis - #If it is, try to coerce its corresponding element - #in the other basis + # Check to see if it is the dual of some other basis + # If it is, try to coerce its corresponding element + # in the other basis return self(x.dual()) ################################################################## @@ -161,105 +179,97 @@ def _element_constructor_(self, x): elif isinstance(x, self.Element): P = x.parent() - #same base ring + # same base ring if P is self: return x - #different base ring + # different base ring else: - return eclass(self, dict([ (e1,R(e2)) for e1,e2 in x._monomial_coefficients.items()])) + return eclass(self, {la: rc for la, c in x._monomial_coefficients.items() + if (rc := R(c))}) ################################################## # Classical Symmetric Functions, different basis # ################################################## elif isinstance(x, SymmetricFunctionAlgebra_classical.Element): + P = x.parent() + m = x.monomial_coefficients() - R = self.base_ring() - xP = x.parent() - xm = x.monomial_coefficients() - - #determine the conversion function. + # determine the conversion function. try: - t = conversion_functions[(xP.basis_name(),self.basis_name())] + t = conversion_functions[(P.basis_name(), self.basis_name())] except AttributeError: - raise TypeError("do not know how to convert from %s to %s"%(xP.basis_name(), self.basis_name())) - - if R == QQ and xP.base_ring() == QQ: - if xm: - return self._from_dict(t(xm)._monomial_coefficients, coerce=True) - else: - return self.zero() + raise TypeError("do not know how to convert from %s to %s" + % (P.basis_name(), self.basis_name())) + + if R == QQ and P.base_ring() == QQ: + if m: + return self._from_dict(t(m)._monomial_coefficients, + coerce=True) + return self.zero() else: - f = lambda part: self._from_dict(t( {part: ZZ.one()} )._monomial_coefficients) + f = lambda part: self._from_dict(t({part: ZZ.one()})._monomial_coefficients) return self._apply_module_endomorphism(x, f) - ############################### # Hall-Littlewood Polynomials # ############################### elif isinstance(x, hall_littlewood.HallLittlewood_generic.Element): # - #Qp: Convert to Schur basis and then convert to self + # Qp: Convert to Schur basis and then convert to self # - if isinstance(x, hall_littlewood.HallLittlewood_qp.Element): - Qp = x.parent() - sx = Qp._s._from_cache(x, Qp._s_cache, Qp._self_to_s_cache, t=Qp.t) - return self(sx) - # - #P: Convert to Schur basis and then convert to self - # - elif isinstance(x, hall_littlewood.HallLittlewood_p.Element): + if isinstance(x, (hall_littlewood.HallLittlewood_qp.Element, + hall_littlewood.HallLittlewood_p.Element)): P = x.parent() sx = P._s._from_cache(x, P._s_cache, P._self_to_s_cache, t=P.t) return self(sx) # - #Q: Convert to P basis and then convert to self + # Q: Convert to P basis and then convert to self # elif isinstance(x, hall_littlewood.HallLittlewood_q.Element): - return self( x.parent()._P( x ) ) + return self(x.parent()._P(x)) ####### # LLT # ####### - #Convert to m and then to self. + # Convert to m and then to self. elif isinstance(x, llt.LLT_generic.Element): P = x.parent() - BR = self.base_ring() - zero = BR.zero() - PBR = P.base_ring() - if not BR.has_coerce_map_from(PBR): - raise TypeError("no coerce map from x's parent's base ring (= %s) to self's base ring (= %s)"%(PBR, self.base_ring())) + Rx = P.base_ring() + zero = R.zero() + if not R.has_coerce_map_from(Rx): + raise TypeError("no coerce map from x's parent's base ring (= %s) to self's base ring (= %s)" + % (Rx, R)) z_elt = {} for m, c in x._monomial_coefficients.items(): n = sum(m) P._m_cache(n) for part in P._self_to_m_cache[n][m]: - z_elt[part] = z_elt.get(part, zero) + BR(c*P._self_to_m_cache[n][m][part].subs(t=P.t)) + z_elt[part] = z_elt.get(part, zero) + R(c*P._self_to_m_cache[n][m][part].subs(t=P.t)) m = P._sym.monomial() - return self( m._from_dict(z_elt) ) + return self(m._from_dict(z_elt)) ######################### # Macdonald Polynomials # ######################### elif isinstance(x, macdonald.MacdonaldPolynomials_generic.Element): - if isinstance(x, macdonald.MacdonaldPolynomials_j.Element): - J = x.parent() - sx = J._s._from_cache(x, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) + if isinstance(x, (macdonald.MacdonaldPolynomials_j.Element, + macdonald.MacdonaldPolynomials_s.Element)): + P = x.parent() + sx = P._s._from_cache(x, P._s_cache, P._self_to_s_cache, q=P.q, t=P.t) return self(sx) - elif isinstance(x, (macdonald.MacdonaldPolynomials_q.Element, macdonald.MacdonaldPolynomials_p.Element)): + elif isinstance(x, (macdonald.MacdonaldPolynomials_q.Element, + macdonald.MacdonaldPolynomials_p.Element)): J = x.parent()._J jx = J(x) sx = J._s._from_cache(jx, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) return self(sx) - elif isinstance(x, (macdonald.MacdonaldPolynomials_h.Element,macdonald.MacdonaldPolynomials_ht.Element)): - H = x.parent() - sx = H._self_to_s(x) - return self(sx) - elif isinstance(x, macdonald.MacdonaldPolynomials_s.Element): - S = x.parent() - sx = S._s._from_cache(x, S._s_cache, S._self_to_s_cache, q=S.q, t=S.t) + elif isinstance(x, (macdonald.MacdonaldPolynomials_h.Element, + macdonald.MacdonaldPolynomials_ht.Element)): + P = x.parent() + sx = P._self_to_s(x) return self(sx) else: raise TypeError @@ -272,8 +282,9 @@ def _element_constructor_(self, x): P = x.parent() mx = P._m._from_cache(x, P._m_cache, P._self_to_m_cache, t=P.t) return self(mx) - if isinstance(x, (jack.JackPolynomials_j.Element, jack.JackPolynomials_q.Element)): - return self( x.parent()._P(x) ) + if isinstance(x, (jack.JackPolynomials_j.Element, + jack.JackPolynomials_q.Element)): + return self(x.parent()._P(x)) else: raise TypeError @@ -281,21 +292,25 @@ def _element_constructor_(self, x): # Bases defined by orthogonality and triangularity # #################################################### elif isinstance(x, orthotriang.SymmetricFunctionAlgebra_orthotriang.Element): - #Convert to its base and then to self - xp = x.parent() - if self is xp._sf_base: - return xp._sf_base._from_cache(x, xp._base_cache, xp._self_to_base_cache) + # Convert to its base and then to self + P = x.parent() + if self is P._sf_base: + return P._sf_base._from_cache(x, P._base_cache, P._self_to_base_cache) else: - return self( xp._sf_base(x) ) + return self( P._sf_base(x) ) ################################# # Last shot -- try calling R(x) # ################################# else: try: - return eclass(self, {_Partitions([]): R(x)}) + c = R(x) except Exception: raise TypeError("do not know how to make x (= {}) an element of self".format(x)) + else: + if not c: + return self.zero() + return eclass(self, {_Partitions([]): c}) # This subclass is currently needed for the test above: # isinstance(x, SymmetricFunctionAlgebra_classical.Element): diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index 445521e4193..77e8b439d8c 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -536,6 +536,7 @@ class Element(classical.SymmetricFunctionAlgebra_classical.Element): - ``dual`` -- self as an element of the dual basis. """ + def __init__(self, A, dictionary=None, dual=None): """ Create an element of a dual basis. @@ -588,7 +589,6 @@ def __init__(self, A, dictionary=None, dual=None): dual = parent._dual_basis._from_dict(dual_dict) - if dictionary is None: # We need to compute the monomial coefficients dictionary dictionary = {} @@ -611,7 +611,6 @@ def __init__(self, A, dictionary=None, dual=None): self._dual = dual classical.SymmetricFunctionAlgebra_classical.Element.__init__(self, A, dictionary) - def dual(self): """ Return ``self`` in the dual basis. @@ -889,6 +888,7 @@ def expand(self, n, alphabet='x'): """ return self._dual.expand(n, alphabet) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override 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 446acbf23a3..8ec26324de0 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -28,6 +28,8 @@ # Elementary Symmetric Functions # # # ################################### + + class SymmetricFunctionAlgebra_elementary(multiplicative.SymmetricFunctionAlgebra_multiplicative): def __init__(self, Sym): """ @@ -307,7 +309,6 @@ def expand(self, n, alphabet='x'): condition = lambda part: max(part) > n return self._expand(condition, n, alphabet) - def principal_specialization(self, n=infinity, q=None): r""" Return the principal specialization of a symmetric function. @@ -405,7 +406,6 @@ def get_variable(ring, name): return self.parent()._apply_module_morphism(self, f, q.parent()) - def exponential_specialization(self, t=None, q=1): r""" Return the exponential specialization of a diff --git a/src/sage/combinat/sf/hall_littlewood.py b/src/sage/combinat/sf/hall_littlewood.py index 507112d9311..858da2079e9 100644 --- a/src/sage/combinat/sf/hall_littlewood.py +++ b/src/sage/combinat/sf/hall_littlewood.py @@ -39,6 +39,8 @@ # TODO: optimize! which is the fastest way of computing HL's and kostka-polynomials? # Qp basis is computed using symmetrica, while P basis is computed using rigged # configurations + + class HallLittlewood(UniqueRepresentation): r""" The family of Hall-Littlewood symmetric function bases. @@ -58,6 +60,7 @@ class HallLittlewood(UniqueRepresentation): sage: SymmetricFunctions(QQ['t'].fraction_field()).hall_littlewood() Hall-Littlewood polynomials over Fraction Field of Univariate Polynomial Ring in t over Rational Field """ + def __repr__(self): r""" A string representing the family of Hall-Littlewood symmetric function bases @@ -344,6 +347,7 @@ def Qp(self): """ return HallLittlewood_qp(self) + class HallLittlewood_generic(sfa.SymmetricFunctionAlgebra_generic): def __init__(self, hall_littlewood): r""" @@ -887,7 +891,6 @@ def _p_to_q_normalization(self, m): return coeff - ############ # Qp basis # ############ diff --git a/src/sage/combinat/sf/hecke.py b/src/sage/combinat/sf/hecke.py index fa911bbbd0b..2fda46380a3 100644 --- a/src/sage/combinat/sf/hecke.py +++ b/src/sage/combinat/sf/hecke.py @@ -120,6 +120,7 @@ class HeckeCharacter(SymmetricFunctionAlgebra_multiplicative): - [Ram1991]_ - [RR1997]_ """ + def __init__(self, sym, q='q'): r""" Initialize ``self``. diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index 3157a9bf0eb..d339118ccc7 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -315,7 +315,6 @@ def get_variable(ring, name): return self.parent()._apply_module_morphism(self, f, q.parent()) - def exponential_specialization(self, t=None, q=1): r""" Return the exponential specialization of a diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index c46c78afefe..617f31f7f1d 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -45,6 +45,8 @@ p_to_m_cache = {} m_to_p_cache = {} + + class Jack(UniqueRepresentation): def __init__(self, Sym, t='t'): @@ -374,6 +376,8 @@ def Qp(self): return JackPolynomials_qp(self) ################################################################### + + def c1(part, t): r""" Returns the `t`-Jack scalar product between ``J(part)`` and ``P(part)``. @@ -399,6 +403,7 @@ def c1(part, t): return prod([1+t*part.arm_lengths(flat=True)[i]+part.leg_lengths(flat=True)[i] for i in range(sum(part))], t.parent().one()) + def c2(part, t): r""" Returns the t-Jack scalar product between ``J(part)`` and ``Q(part)``. @@ -425,6 +430,7 @@ def c2(part, t): return prod([t+t*part.arm_lengths(flat=True)[i]+part.leg_lengths(flat=True)[i] for i in range(sum(part))], t.parent().one()) + def normalize_coefficients(self, c): r""" If our coefficient ring is the field of fractions over a univariate @@ -481,6 +487,7 @@ def normalize_coefficients(self, c): #################################################################### + class JackPolynomials_generic(sfa.SymmetricFunctionAlgebra_generic): def __init__(self, jack): r""" @@ -773,7 +780,6 @@ def coproduct_by_coercion(self, elt): return self.tensor_square().sum(normalize(coeff)*tensor([self(x), self(y)]) for ((x,y), coeff) in g) - class Element(sfa.SymmetricFunctionAlgebra_generic.Element): def scalar_jack(self, x, t=None): r""" @@ -841,6 +847,7 @@ def part_scalar_jack(part1, part2, t): #P basis + class JackPolynomials_p(JackPolynomials_generic): def __init__(self, jack): @@ -1017,7 +1024,6 @@ def scalar_jack_basis(self, part1, part2=None): return self.base_ring().zero() return self.c2(part1) / self.c1(part1) - class Element(JackPolynomials_generic.Element): def scalar_jack(self, x, t=None): r""" @@ -1047,6 +1053,7 @@ def scalar_jack(self, x, t=None): #J basis + class JackPolynomials_j(JackPolynomials_generic): def __init__(self, jack): @@ -1082,7 +1089,6 @@ class Element(JackPolynomials_generic.Element): pass - #Q basis class JackPolynomials_q(JackPolynomials_generic): @@ -1318,6 +1324,8 @@ class Element(JackPolynomials_generic.Element): pass #Zonal polynomials ( =P(at t=2) ) + + class SymmetricFunctionAlgebra_zonal(sfa.SymmetricFunctionAlgebra_generic): def __init__(self, Sym): r""" diff --git a/src/sage/combinat/sf/k_dual.py b/src/sage/combinat/sf/k_dual.py index 425af1bc96f..84577daf725 100644 --- a/src/sage/combinat/sf/k_dual.py +++ b/src/sage/combinat/sf/k_dual.py @@ -453,6 +453,7 @@ def realizations(self): """ return [ self.km(), self.kHLP(), self.affineSchur(), self.dual_k_Schur()] + class KBoundedQuotientBases(Category_realization_of_parent): r""" The category of bases for the `k`-bounded subspace of symmetric functions. @@ -496,7 +497,6 @@ def super_categories(self): category = GradedHopfAlgebrasWithBasis(R) return [Realizations(self.base()), category.Quotients()] - class ParentMethods: def retract(self,la): @@ -879,10 +879,12 @@ def counit(self, element): class ElementMethods: pass + class KBoundedQuotientBasis(CombinatorialFreeModule): r""" Abstract base class for the bases of the `k`-bounded quotient. """ + def __init__(self, kBoundedRing, prefix): r""" Initializes ``self``. @@ -1038,6 +1040,7 @@ def lift(self, la): m = self._kBoundedRing.ambient().m() return m._from_dict(dict(self(la))) + class kbounded_HallLittlewoodP(KBoundedQuotientBasis): r""" The basis of P Hall-Littlewood symmetric functions indexed by partitions with first @@ -1235,6 +1238,7 @@ def lift(self, la): HLP = self._kBoundedRing.ambient().hall_littlewood(t=self.t).P() return HLP._from_dict(dict(self(la))) + class DualkSchurFunctions(KBoundedQuotientBasis): r""" This basis is dual to the `k`-Schur functions. The expansion is given @@ -1358,6 +1362,7 @@ def _khlp_to_dks_on_basis(self, la): ks = kB.kschur() return sum( Qp(ks(x)).coefficient(la) * self(x) for x in PartitionsGreatestLE(sum(la), self.k)) + class AffineSchurFunctions(KBoundedQuotientBasis): r""" This basis is dual to the `k`-Schur functions at `t=1`. This realization diff --git a/src/sage/combinat/sf/kfpoly.py b/src/sage/combinat/sf/kfpoly.py index cf465b3e8c4..88eb77485e8 100644 --- a/src/sage/combinat/sf/kfpoly.py +++ b/src/sage/combinat/sf/kfpoly.py @@ -201,6 +201,7 @@ def schur_to_hl(mu, t=None): d[ key.conjugate() ] = res[key] return d + def riggings(part): r""" Generate all possible rigging sequences for a fixed partition ``part``. @@ -242,6 +243,7 @@ def riggings(part): return [x[:l] for x in res] + def compat(n, mu, nu): r""" Generate all possible partitions of `n` that can precede `\mu, \nu` diff --git a/src/sage/combinat/sf/llt.py b/src/sage/combinat/sf/llt.py index b6b19659a3e..47eca5e9470 100644 --- a/src/sage/combinat/sf/llt.py +++ b/src/sage/combinat/sf/llt.py @@ -50,6 +50,7 @@ QQt = QQ['t'].fraction_field() # This is to become the "abstract algebra" for llt polynomials + class LLT_class(UniqueRepresentation): r""" A class for working with LLT symmetric functions. @@ -406,7 +407,6 @@ def hspin(self): return LLT_spin(self) - class LLT_generic(sfa.SymmetricFunctionAlgebra_generic): def __init__(self, llt, prefix): @@ -645,7 +645,6 @@ def __init__(self, llt): LLT_generic.__init__(self, llt, prefix="HSp%s" % level) - def _to_m(self, part): r""" Returns a function which gives the coefficient of a partition @@ -745,6 +744,7 @@ def _to_m(self, part): class Element(LLT_generic.Element): pass + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.sf.llt', 'LLTElement_spin', LLT_spin.Element) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index d30339bc82d..5702e469658 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -539,10 +539,12 @@ def S(self): """ return MacdonaldPolynomials_s(self) + QQqt = QQ['q,t'].fraction_field() ############################################## + def c1(part, q, t): r""" This function returns the qt-Hall scalar product between ``J(part)`` @@ -652,6 +654,7 @@ def cmunu1(mu, nu): / (q**nu.arm_length(s, 0) - t**(nu.leg_length(s, 0)+1)) for s in range(len(nu._list)) )) + @cached_function def cmunu(mu, nu): r""" @@ -715,6 +718,8 @@ def Bmu_skew(outer, inner): for al in nu.up()) / Bmu_skew(mu, nulist)) #Generic MacdonaldPolynomials + + class MacdonaldPolynomials_generic(sfa.SymmetricFunctionAlgebra_generic): def __init__(self, macdonald): @@ -985,6 +990,8 @@ def nabla(self, q=None, t=None, power=1): return parent(Ht(self).nabla(power=power)) #P basis + + class MacdonaldPolynomials_p(MacdonaldPolynomials_generic): def __init__(self, macdonald): r""" @@ -1407,6 +1414,7 @@ def _m_to_self( self, f ): class Element(MacdonaldPolynomials_generic.Element): pass + class MacdonaldPolynomials_ht(MacdonaldPolynomials_generic): def __init__(self, macdonald): r""" @@ -1700,6 +1708,7 @@ def nabla(self, q=None, t=None, power=1): f = lambda part: t**(part.weighted_size()*power)*q**(part.conjugate().weighted_size()*power)*Ht(part) return P(Ht._apply_module_morphism(selfHt, f)) + class MacdonaldPolynomials_s(MacdonaldPolynomials_generic): def __init__(self, macdonald): r""" @@ -1821,7 +1830,6 @@ def _s_cache(self, n): self._s_to_self_cache, to_other_function=self._to_s) - class Element(MacdonaldPolynomials_generic.Element): def _creation_by_determinant_helper(self, k, part): diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index f833be60c3b..ea1b9da133d 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -22,7 +22,7 @@ import sage.libs.symmetrica.all as symmetrica from sage.rings.integer import Integer from sage.rings.infinity import infinity -from sage.combinat.partition import Partition, _Partitions +from sage.combinat.partition import _Partitions from sage.arith.misc import multinomial, factorial, binomial @@ -189,7 +189,7 @@ def from_polynomial_exp(self, p): INPUT: - ``self`` -- a monomial symmetric function basis - - ``p`` -- a multivariate polynomial over the same base ring as ``self`` + - ``p`` -- a polynomial over the same base ring as ``self`` OUTPUT: @@ -228,8 +228,8 @@ def from_polynomial_exp(self, p): :func:`Partition`, :meth:`Partition.to_exp` """ assert self.base_ring() == p.parent().base_ring() - return self.sum_of_terms((Partition(exp=monomial), coeff) - for monomial, coeff in p.dict().items()) + from sage.combinat.sf.sfa import _from_polynomial + return _from_polynomial(p, self) def antipode_by_coercion(self, element): r""" @@ -488,6 +488,7 @@ def f(partition): # introduce singularities, because it is not a Z-basis return self.parent().realization_of().elementary()(self).exponential_specialization(t=t, q=q) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override diff --git a/src/sage/combinat/sf/new_kschur.py b/src/sage/combinat/sf/new_kschur.py index edc52845be3..551e776ebb6 100644 --- a/src/sage/combinat/sf/new_kschur.py +++ b/src/sage/combinat/sf/new_kschur.py @@ -974,6 +974,7 @@ class kSchur(CombinatorialFreeModule): sage: f.coefficient(f.support()[0]) 1 """ + def __init__(self, kBoundedRing): r""" TESTS:: diff --git a/src/sage/combinat/sf/orthogonal.py b/src/sage/combinat/sf/orthogonal.py index b0ed060ee85..0e9e2053d51 100644 --- a/src/sage/combinat/sf/orthogonal.py +++ b/src/sage/combinat/sf/orthogonal.py @@ -157,6 +157,7 @@ class SymmetricFunctionAlgebra_orthogonal(sfa.SymmetricFunctionAlgebra_generic): sage: o.one().counit() 1 """ + def __init__(self, Sym): """ Initialize ``self``. diff --git a/src/sage/combinat/sf/orthotriang.py b/src/sage/combinat/sf/orthotriang.py index 6905cbfd3d9..96cae8e7384 100644 --- a/src/sage/combinat/sf/orthotriang.py +++ b/src/sage/combinat/sf/orthotriang.py @@ -49,7 +49,6 @@ class SymmetricFunctionAlgebra_orthotriang(sfa.SymmetricFunctionAlgebra_generic) class Element(sfa.SymmetricFunctionAlgebra_generic.Element): pass - def __init__(self, Sym, base, scalar, prefix, basis_name, leading_coeff=None): r""" Initialization of the symmetric function algebra defined via orthotriangular rules. @@ -251,6 +250,7 @@ def product(self, left, right): """ return self(self._sf_base(left) * self._sf_base(right)) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override 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 f7542d7929e..ab216cf3e84 100644 --- a/src/sage/combinat/sf/powersum.py +++ b/src/sage/combinat/sf/powersum.py @@ -24,6 +24,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.misc.misc_c import prod + class SymmetricFunctionAlgebra_power(multiplicative.SymmetricFunctionAlgebra_multiplicative): def __init__(self, Sym): """ @@ -476,8 +477,8 @@ def frobenius(self, n): :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.plethysm` """ - dct = {Partition([n * i for i in lam]): coeff - for (lam, coeff) in self.monomial_coefficients().items()} + dct = {lam.stretch(n): coeff + for lam, coeff in self.monomial_coefficients().items()} return self.parent()._from_dict(dct) adams_operation = frobenius @@ -935,6 +936,7 @@ def f(partition): return self.parent()._apply_module_morphism(self, f, t.parent()) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override 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 e132b753e11..914d5aef8d6 100644 --- a/src/sage/combinat/sf/schur.py +++ b/src/sage/combinat/sf/schur.py @@ -719,7 +719,6 @@ def f(partition): return self.parent()._apply_module_morphism(self, f, q.parent()) - def exponential_specialization(self, t=None, q=1): r""" Return the exponential specialization of a diff --git a/src/sage/combinat/sf/sf.py b/src/sage/combinat/sf/sf.py index f18f6fb75a2..56b3b96f279 100644 --- a/src/sage/combinat/sf/sf.py +++ b/src/sage/combinat/sf/sf.py @@ -22,6 +22,8 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.graded_hopf_algebras import GradedHopfAlgebras +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.unique_factorization_domains import UniqueFactorizationDomains from sage.categories.fields import Fields from sage.categories.rings import Rings from sage.combinat.partition import Partitions @@ -38,6 +40,7 @@ from . import macdonald from . import llt + class SymmetricFunctions(UniqueRepresentation, Parent): r""" The abstract algebra of commutative symmetric functions @@ -70,6 +73,7 @@ class SymmetricFunctions(UniqueRepresentation, Parent): sage: Sym.category() Join of Category of hopf algebras over Rational Field + and Category of unique factorization domains and Category of graded algebras over Rational Field and Category of commutative algebras over Rational Field and Category of monoids with realizations @@ -864,6 +868,8 @@ def __init__(self, R): assert(R in Fields() or R in Rings()) # side effect of this statement assures MRO exists for R self._base = R # Won't be needed when CategoryObject won't override anymore base_ring cat = GradedHopfAlgebras(R).Commutative().Cocommutative() + if R in PrincipalIdealDomains(): + cat &= UniqueFactorizationDomains() Parent.__init__(self, category=cat.WithRealizations()) def a_realization(self): @@ -1072,7 +1078,6 @@ def induced_trivial_character(self): ht = induced_trivial_character - def forgotten(self): r""" The forgotten basis of the Symmetric Functions (or the basis dual to @@ -1572,6 +1577,7 @@ def kBoundedQuotient(self, k, t='t'): from sage.combinat.sf.k_dual import KBoundedQuotient return KBoundedQuotient(self, k, t) + class SymmetricaConversionOnBasis: def __init__(self, t, domain, codomain): """ @@ -1605,6 +1611,8 @@ def __init__(self, t, domain, codomain): def __call__(self, partition): """ + EXAMPLES:: + sage: Sym = SymmetricFunctions(QQ['x']) sage: p = Sym.p(); s = Sym.s() sage: p[1] + s[1] # indirect doctest diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 7eac828fa4b..b435764a746 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -222,9 +222,13 @@ from sage.combinat.partition import _Partitions, Partitions, Partitions_n, Partition from sage.categories.hopf_algebras import HopfAlgebras from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.unique_factorization_domains import UniqueFactorizationDomains from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix +from sage.structure.factorization import Factorization +from sage.structure.element import coerce_binop from sage.misc.misc_c import prod from sage.data_structures.blas_dict import convert_remove_zeroes, linear_combination from copy import copy @@ -253,6 +257,7 @@ def is_SymmetricFunctionAlgebra(x): """ return isinstance(x, SymmetricFunctionAlgebra_generic) + def zee(part): r""" Return the size of the centralizer of any permutation of cycle type @@ -302,6 +307,7 @@ def is_SymmetricFunction(x): ##################################################################### ## Bases categories + from sage.categories.realizations import Category_realization_of_parent @@ -323,6 +329,7 @@ class SymmetricFunctionsBases(Category_realization_of_parent): sage: Sym.schur() in bases True """ + def _repr_(self): r""" Return the representation of ``self``. @@ -355,14 +362,28 @@ def super_categories(self): Category of commutative hopf algebras with basis over Rational Field, Join of Category of realizations of hopf algebras over Rational Field and Category of graded algebras over Rational Field - and Category of graded coalgebras over Rational Field] + and Category of graded coalgebras over Rational Field, + Category of unique factorization domains] + + sage: Sym = SymmetricFunctions(ZZ["x"]) + sage: bases = SymmetricFunctionsBases(Sym) + sage: bases.super_categories() + [Category of realizations of Symmetric Functions over Univariate Polynomial Ring in x over Integer Ring, + Category of commutative hopf algebras with basis over Univariate Polynomial Ring in x over Integer Ring, + Join of Category of realizations of hopf algebras over Univariate Polynomial Ring in x over Integer Ring + and Category of graded algebras over Univariate Polynomial Ring in x over Integer Ring + and Category of graded coalgebras over Univariate Polynomial Ring in x over Integer Ring] """ # FIXME: The last one should also be commutative, but this triggers a # KeyError when doing the C3 algorithm!!! - cat = HopfAlgebras(self.base().base_ring()) - return [self.base().Realizations(), - cat.Commutative().WithBasis(), - cat.Graded().Realizations()] + R = self.base().base_ring() + cat = HopfAlgebras(R) + categories = [self.base().Realizations(), + cat.Commutative().WithBasis(), + cat.Graded().Realizations()] + if R in PrincipalIdealDomains: + categories.append(UniqueFactorizationDomains()) + return categories class ParentMethods: @@ -1470,6 +1491,7 @@ def formal_series_ring(self): from sage.rings.lazy_series_ring import LazySymmetricFunctions return LazySymmetricFunctions(self) + class FilteredSymmetricFunctionsBases(Category_realization_of_parent): r""" The category of filtered bases of the ring of symmetric functions. @@ -1485,6 +1507,7 @@ class FilteredSymmetricFunctionsBases(Category_realization_of_parent): sage: Sym.sp() in bases True """ + def _repr_(self): r""" Return the representation of ``self``. @@ -1515,6 +1538,7 @@ def super_categories(self): cat = HopfAlgebras(self.base().base_ring()).Commutative().WithBasis().Filtered() return [SymmetricFunctionsBases(self.base()), cat] + class GradedSymmetricFunctionsBases(Category_realization_of_parent): r""" The category of graded bases of the ring of symmetric functions. @@ -1533,6 +1557,7 @@ class GradedSymmetricFunctionsBases(Category_realization_of_parent): sage: Sym.sp() in bases False """ + def _repr_(self): r""" Return the representation of ``self``. @@ -1722,6 +1747,24 @@ def degree_zero_coefficient(self): """ return self.coefficient([]) + def is_unit(self): + """ + Return whether this element is a unit in the ring. + + EXAMPLES:: + + sage: m = SymmetricFunctions(ZZ).monomial() + sage: (2*m[2,1] + m[[]]).is_unit() + False + + sage: m = SymmetricFunctions(QQ).monomial() + sage: (3/2*m([])).is_unit() + True + """ + m = self.monomial_coefficients(copy=False) + return len(m) <= 1 and self.coefficient([]).is_unit() + + #SymmetricFunctionsBases.Filtered = FilteredSymmetricFunctionsBases #SymmetricFunctionsBases.Graded = GradedSymmetricFunctionsBases @@ -1744,6 +1787,7 @@ class SymmetricFunctionAlgebra_generic(CombinatorialFreeModule): sage: s(m([2,1])) -2*s[1, 1, 1] + s[2, 1] """ + def __init__(self, Sym, basis_name=None, prefix=None, graded=True): r""" Initializes the symmetric function algebra. @@ -2307,14 +2351,14 @@ def _invert_morphism(self, n, base_ring, self_to_other_cache, other_to_self_cach for i in range(len_pn): for j in range(len_pn): - if inverse[i,j] != zero: + if inverse[i, j] != zero: if hasattr(self, '_normalize_coefficients'): - unknown_cache_n[ pn[i] ] [ pn[j] ] = self._normalize_coefficients(inverse[i,j]) + unknown_cache_n[ pn[i] ][ pn[j] ] = self._normalize_coefficients(inverse[i, j]) else: - unknown_cache_n[ pn[i] ] [ pn[j] ] = inverse[i,j] + unknown_cache_n[ pn[i] ][ pn[j] ] = inverse[i, j] - known_cache[ n ] = known_cache_n - unknown_cache[ n ] = unknown_cache_n + known_cache[n] = known_cache_n + unknown_cache[n] = unknown_cache_n def symmetric_function_ring(self): r""" @@ -2449,7 +2493,6 @@ def transition_matrix(self, basis, n): m.append( [z.coefficient(col_part) for col_part in Plist] ) return matrix(m) - def _gram_schmidt(self, n, source, scalar, cache, leading_coeff=None, upper_triangular=True): r""" Apply Gram-Schmidt to ``source`` with respect to the scalar product @@ -2535,7 +2578,6 @@ def _gram_schmidt(self, n, source, scalar, cache, leading_coeff=None, upper_tria for j in range(i+1): cache[l[i]][l[j]] = res.coefficient(l[j]) - def _inner_plethysm_pk_g(self, k, g, cache): r""" Return the inner plethysm between the power-sum symmetric @@ -2679,7 +2721,6 @@ def _inner_plethysm_pnu_g(self, p_x, cache, nu): #of all the symmetric functions in res return self(reduce(lambda x, y: 0 if x==0 else x.itensor(y), res)) - def _dual_basis_default(self): """ Return the default value for ``self.dual_basis()`` @@ -2993,6 +3034,161 @@ class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): sage: m.set_print_style('lex') """ + def factor(self): + """ + Return the factorization of this symmetric function. + + EXAMPLES:: + + sage: e = SymmetricFunctions(QQ).e() + sage: factor((5*e[3] + e[2,1] + e[1])*(7*e[2] + e[5,1])) + (e[1] + e[2, 1] + 5*e[3]) * (7*e[2] + e[5, 1]) + + sage: R.<x, y> = QQ[] + sage: s = SymmetricFunctions(R.fraction_field()).s() + sage: factor((s[3] + x*s[2,1] + 1)*(3*y*s[2] + s[4,1] + x*y)) + (-s[] + (-x)*s[2, 1] - s[3]) * ((-x*y)*s[] + (-3*y)*s[2] - s[4, 1]) + + TESTS:: + + sage: p = SymmetricFunctions(QQ).p() + sage: factor((p[3] + p[2,1])*(p[2] + p[4,1])) + (p[2, 1] + p[3]) * (p[2] + p[4, 1]) + + sage: s = SymmetricFunctions(QQ).s() + sage: factor((s[3] + s[2,1])*(s[2] + s[4,1])) + (-1) * s[1] * s[2] * (-s[2] - s[4, 1]) + + sage: s = SymmetricFunctions(ZZ).s() + sage: factor((s[3] + s[2,1])*(s[2] + s[4,1])) + (-1) * s[1] * s[2] * (-s[2] - s[4, 1]) + + sage: R.<t> = QQ[] + sage: JP = SymmetricFunctions(FractionField(R)).jack(t).P() + sage: f = (JP[2,1]*t + JP[1,1,1])^2 + sage: f.factor() + (1/(t^2 + 4*t + 4)) * ((-t-2)*JackP[1, 1, 1] + (-t^2-2*t)*JackP[2, 1])^2 + + Some corner cases:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: factor(s(6)) + 2 * 3 + + sage: factor(6*s[1]) + 2*s[] * 3*s[] * s[1] + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + L = self.parent() + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + + poly = _to_polynomials([self], self.base_ring())[0] + factors = poly.factor() + unit = factors.unit() + if factors.universe() == self.base_ring(): + return Factorization(factors, unit=unit) + factors = [(_from_polynomial(factor, M), exponent) + for factor, exponent in factors] + + if not isinstance(L, SymmetricFunctionAlgebra_multiplicative): + factors = [(L(factor), exponent) for factor, exponent in factors] + return Factorization(factors, unit=unit) + + def _floordiv_(self, other): + """ + Perform division with remainder and return the quotient. + + INPUT: + + - ``right`` - something coercible to a symmetric function in + ``self.parent()`` + + EXAMPLES:: + + sage: e = SymmetricFunctions(ZZ).e() + sage: h = SymmetricFunctions(ZZ).h() + sage: e[3,2,1] // h[2] + -e[3, 1] + + TESTS:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: s(0) // s[1] + 0 + + sage: s(6) // s(2) + 3*s[] + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + # we can assume that the parents of self and other are the same + L = self.parent() + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + other = M(other) + + p1, p2 = _to_polynomials([self, other], self.base_ring()) + g = p1 // p2 + return L(_from_polynomial(g, M)) + + @coerce_binop + def gcd(self, other): + """ + Return the greatest common divisor with ``other``. + + INPUT: + + - ``other`` -- the other symmetric function + + EXAMPLES:: + + sage: e = SymmetricFunctions(ZZ).e() + sage: A = 5*e[3] + e[2,1] + e[1] + sage: B = 7*e[2] + e[5,1] + sage: C = 3*e[1,1] + e[2] + sage: gcd(A*B^2, B*C) + 7*e[2] + e[5, 1] + + sage: p = SymmetricFunctions(ZZ).p() + sage: gcd(e[2,1], p[1,1]-p[2]) + e[2] + sage: gcd(p[2,1], p[3,2]-p[2,1]) + p[2] + + TESTS:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: gcd(s(0), s[1]) + s[1] + + sage: gcd(s(0), s(1)) + s[] + + sage: gcd(s(9), s(6)) + 3*s[] + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + L = self.parent() + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + other = M(other) + + p1, p2 = _to_polynomials([self, other], self.base_ring()) + g = p1.gcd(p2) + return L(_from_polynomial(g, M)) + def plethysm(self, x, include=None, exclude=None): r""" Return the outer plethysm of ``self`` with ``x``. @@ -3013,33 +3209,53 @@ def plethysm(self, x, include=None, exclude=None): - ``x`` -- a symmetric function over the same base ring as ``self`` - - ``include`` -- a list of variables to be treated as degree one elements instead of the default degree one elements - - ``exclude`` -- a list of variables to be excluded from the default degree one elements + OUTPUT: + + An element in the parent of ``x`` or the base ring `R` of ``self`` + when ``x`` is in `R`. + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: s = Sym.s() sage: h = Sym.h() - sage: s ( h([3])( h([2]) ) ) + sage: h3h2 = h[3](h[2]); h3h2 + h[2, 2, 2] - 2*h[3, 2, 1] + h[3, 3] + h[4, 1, 1] - h[5, 1] + h[6] + sage: s(h3h2) s[2, 2, 2] + s[4, 2] + s[6] sage: p = Sym.p() - sage: p([3])( s([2,1]) ) + sage: p3s21 = p[3](s[2,1]); p3s21 + s[2, 2, 2, 1, 1, 1] - s[2, 2, 2, 2, 1] - s[3, 2, 1, 1, 1, 1] + + s[3, 2, 2, 2] + s[3, 3, 1, 1, 1] - s[3, 3, 2, 1] + 2*s[3, 3, 3] + + s[4, 1, 1, 1, 1, 1] - s[4, 3, 2] + s[4, 4, 1] - s[5, 1, 1, 1, 1] + + s[5, 2, 2] - s[5, 4] + s[6, 1, 1, 1] - s[6, 2, 1] + s[6, 3] + sage: p(p3s21) 1/3*p[3, 3, 3] - 1/3*p[9] sage: e = Sym.e() - sage: e([3])( e([2]) ) + sage: e[3](e[2]) e[3, 3] + e[4, 1, 1] - 2*e[4, 2] - e[5, 1] + e[6] - :: + Note that the output is in the basis of the input ``x``:: + + sage: s[2,1](h[3]) + h[4, 3, 2] - h[4, 4, 1] - h[5, 2, 2] + h[5, 3, 1] + h[5, 4] + + h[6, 2, 1] - 2*h[6, 3] - h[7, 1, 1] + h[7, 2] + h[8, 1] - h[9] + + sage: h[2,1](s[3]) + s[4, 3, 2] + s[4, 4, 1] + s[5, 2, 2] + s[5, 3, 1] + s[5, 4] + + s[6, 2, 1] + 2*s[6, 3] + 2*s[7, 2] + s[8, 1] + s[9] + + Examples over a polynomial ring:: sage: R.<t> = QQ[] sage: s = SymmetricFunctions(R).s() sage: a = s([3]) - sage: f = t*s([2]) + sage: f = t * s([2]) sage: a(f) t^3*s[2, 2, 2] + t^3*s[4, 2] + t^3*s[6] sage: f(a) @@ -3051,6 +3267,12 @@ def plethysm(self, x, include=None, exclude=None): sage: s(1).plethysm(s(0)) s[] + When ``x`` is a constant, then it is returned as an element + of the base ring:: + + sage: s[3](2).parent() is R + True + Sage also handles plethysm of tensor products of symmetric functions:: sage: s = SymmetricFunctions(QQ).s() @@ -3062,8 +3284,8 @@ def plethysm(self, x, include=None, exclude=None): s[1, 1, 1] # s[3] + s[2, 1] # s[2, 1] + s[3] # s[1, 1, 1] One can use this to work with symmetric functions in two sets of - commuting variables. For example, we verify the Cauchy identities (in - degree 5):: + commuting variables. For example, we verify the Cauchy identities + (in degree 5):: sage: m = SymmetricFunctions(QQ).m() sage: P5 = Partitions(5) @@ -3072,6 +3294,32 @@ def plethysm(self, x, include=None, exclude=None): sage: sum(s[mu](X)*s[mu.conjugate()](Y) for mu in P5) == sum(m[mu](X)*e[mu](Y) for mu in P5) True + Sage can also do the plethysm with an element in the completion:: + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LazySymmetricFunctions(s) + sage: f = s[2,1] + sage: g = L(s[1]) / (1 - L(s[1])); g + s[1] + (s[1,1]+s[2]) + (s[1,1,1]+2*s[2,1]+s[3]) + + (s[1,1,1,1]+3*s[2,1,1]+2*s[2,2]+3*s[3,1]+s[4]) + + (s[1,1,1,1,1]+4*s[2,1,1,1]+5*s[2,2,1]+6*s[3,1,1]+5*s[3,2]+4*s[4,1]+s[5]) + + ... + O^8 + sage: fog = f(g) + sage: fog[:8] + [s[2, 1], + s[1, 1, 1, 1] + 3*s[2, 1, 1] + 2*s[2, 2] + 3*s[3, 1] + s[4], + 2*s[1, 1, 1, 1, 1] + 8*s[2, 1, 1, 1] + 10*s[2, 2, 1] + + 12*s[3, 1, 1] + 10*s[3, 2] + 8*s[4, 1] + 2*s[5], + 3*s[1, 1, 1, 1, 1, 1] + 17*s[2, 1, 1, 1, 1] + 30*s[2, 2, 1, 1] + + 16*s[2, 2, 2] + 33*s[3, 1, 1, 1] + 54*s[3, 2, 1] + 16*s[3, 3] + + 33*s[4, 1, 1] + 30*s[4, 2] + 17*s[5, 1] + 3*s[6], + 5*s[1, 1, 1, 1, 1, 1, 1] + 30*s[2, 1, 1, 1, 1, 1] + 70*s[2, 2, 1, 1, 1] + + 70*s[2, 2, 2, 1] + 75*s[3, 1, 1, 1, 1] + 175*s[3, 2, 1, 1] + + 105*s[3, 2, 2] + 105*s[3, 3, 1] + 100*s[4, 1, 1, 1] + 175*s[4, 2, 1] + + 70*s[4, 3] + 75*s[5, 1, 1] + 70*s[5, 2] + 30*s[6, 1] + 5*s[7]] + sage: parent(fog) + Lazy completion of Symmetric Functions over Rational Field in the Schur basis + .. SEEALSO:: :meth:`frobenius` @@ -3080,68 +3328,110 @@ def plethysm(self, x, include=None, exclude=None): sage: (1+p[2]).plethysm(p[2]) p[] + p[4] + + Check that degree one elements are treated in the correct way:: + + sage: R.<a1,a2,a11,b1,b21,b111> = QQ[] + sage: p = SymmetricFunctions(R).p() + sage: f = a1*p[1] + a2*p[2] + a11*p[1,1] + sage: g = b1*p[1] + b21*p[2,1] + b111*p[1,1,1] + sage: r = f(g); r + a1*b1*p[1] + a11*b1^2*p[1, 1] + a1*b111*p[1, 1, 1] + + 2*a11*b1*b111*p[1, 1, 1, 1] + a11*b111^2*p[1, 1, 1, 1, 1, 1] + + a2*b1^2*p[2] + a1*b21*p[2, 1] + 2*a11*b1*b21*p[2, 1, 1] + + 2*a11*b21*b111*p[2, 1, 1, 1, 1] + a11*b21^2*p[2, 2, 1, 1] + + a2*b111^2*p[2, 2, 2] + a2*b21^2*p[4, 2] + sage: r - f(g, include=[]) + (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] + + Check that we can compute the plethysm with a constant:: + + sage: p[2,2,1](2) + 8 + + sage: p[2,2,1](int(2)) + 8 + + sage: p[2,2,1](a1) + a1^5 + + sage: X = algebras.Shuffle(QQ, 'ab') + sage: Y = algebras.Shuffle(QQ, 'bc') + sage: T = tensor([X, Y]) + sage: s = SymmetricFunctions(T).s() + sage: s[2](5) + 15*B[] # B[] + + .. TODO:: + + The implementation of plethysm in + :class:`sage.data_structures.stream.Stream_plethysm` seems + to be faster. This should be investigated. """ + from sage.structure.element import parent as get_parent + Px = get_parent(x) parent = self.parent() R = parent.base_ring() - tHA = HopfAlgebrasWithBasis(R).TensorProducts() - tensorflag = tHA in x.parent().categories() - if not (is_SymmetricFunction(x) or tensorflag): - raise TypeError("only know how to compute plethysms " - "between symmetric functions or tensors " - "of symmetric functions") - p = parent.realization_of().power() - if self == parent.zero(): - return self - # Handle degree one elements - if include is not None and exclude is not None: - raise RuntimeError("include and exclude cannot both be specified") + if not self: + return R(0) - try: - degree_one = [R(g) for g in R.variable_names_recursive()] - except AttributeError: - try: - degree_one = R.gens() - except NotImplementedError: - degree_one = [] - - if include: - degree_one = [R(g) for g in include] - if exclude: - degree_one = [g for g in degree_one if g not in exclude] + tHA = HopfAlgebrasWithBasis(R).TensorProducts() + tensorflag = Px in tHA + if not is_SymmetricFunction(x): + if R.has_coerce_map_from(Px) or x in R: + x = R(x) + Px = R + elif (not tensorflag or any(not isinstance(factor, SymmetricFunctionAlgebra_generic) + for factor in Px._sets)): + from sage.rings.lazy_series import LazySymmetricFunction + if isinstance(x, LazySymmetricFunction): + from sage.rings.lazy_series_ring import LazySymmetricFunctions + L = LazySymmetricFunctions(parent) + return L(self)(x) + + # Try to coerce into a symmetric function + phi = parent.coerce_map_from(Px) + if phi is not None: + x = phi(x) + elif not tensorflag: + raise TypeError("only know how to compute plethysms " + "between symmetric functions or tensors " + "of symmetric functions") - degree_one = [g for g in degree_one if g != R.one()] + p = parent.realization_of().power() - def raise_c(n): - return lambda c: c.subs(**{str(g): g ** n for g in degree_one}) + degree_one = _variables_recursive(R, include=include, exclude=exclude) if tensorflag: - tparents = x.parent()._sets - return tensor([parent]*len(tparents))(sum(d*prod(sum(raise_c(r)(c) - * tensor([p[r].plethysm(base(la)) - for (base,la) in zip(tparents,trm)]) - for (trm,c) in x) - for r in mu) - for (mu, d) in p(self))) - - # Takes in n, and returns a function which takes in a partition and - # scales all of the parts of that partition by n - def scale_part(n): - return lambda m: m.__class__(m.parent(), [i * n for i in m]) - - # Takes n an symmetric function f, and an n and returns the + tparents = Px._sets + lincomb = Px.linear_combination + elt = lincomb((prod(lincomb((tensor([p[r].plethysm(base(la)) + for base, la in zip(tparents, trm)]), + _raise_variables(c, r, degree_one)) + for trm, c in x) + for r in mu), + d) + for mu, d in p(self)) + return Px(elt) + + # Takes a symmetric function f, and an n and returns the # symmetric function with all of its basis partitions scaled # by n def pn_pleth(f, n): - return f.map_support(scale_part(n)) + return f.map_support(lambda mu: mu.stretch(n)) # Takes in a partition and applies p_x = p(x) def f(part): - return p.prod(pn_pleth(p_x.map_coefficients(raise_c(i)), i) + return p.prod(pn_pleth(p_x.map_coefficients(lambda c: _raise_variables(c, i, degree_one)), i) for i in part) - return parent(p._apply_module_morphism(p(self), f, codomain=p)) + ret = p._apply_module_morphism(p(self), f, codomain=p) + if Px is R: + # special case for things in the base ring + return next(iter(ret._monomial_coefficients.values())) + return Px(ret) __call__ = plethysm @@ -3334,7 +3624,6 @@ def inner_plethysm(self, x): return parent.sum(c*ip_pnu_g(p(x), cache, nu) for (nu, c) in p(self).monomial_coefficients().items()) - def omega(self): r""" Return the image of ``self`` under the omega automorphism. @@ -4917,9 +5206,7 @@ def frobenius(self, n): # then convert back. parent = self.parent() m = parent.realization_of().monomial() - from sage.combinat.partition import Partition - dct = {Partition([n * i for i in lam]): coeff - for (lam, coeff) in m(self)} + dct = {lam.stretch(n): coeff for lam, coeff in m(self)} result_in_m_basis = m._from_dict(dct) return parent(result_in_m_basis) @@ -5267,7 +5554,6 @@ def is_schur_positive(self): """ return self._is_positive( self.parent().realization_of().schur() ) - def _is_positive(self, s): r""" Return ``True`` if and only if ``self`` has nonnegative coefficients @@ -5319,7 +5605,8 @@ def degree(self): sage: s(0).degree() 0 """ - return max([sum(cfs) for cfs in self._monomial_coefficients] + [0]) + return max((sum(cfs) for cfs in self._monomial_coefficients), + default=0) def restrict_degree(self, d, exact=True): r""" @@ -6083,6 +6370,7 @@ def exponential_specialization(self, t=None, q=1): e = self.parent().realization_of().elementary() return e(self).exponential_specialization(t=t, q=q) + SymmetricFunctionAlgebra_generic.Element = SymmetricFunctionAlgebra_generic_Element @@ -6125,3 +6413,157 @@ def _nonnegative_coefficients(x): return all(c >= 0 for c in x.coefficients(sparse=False)) else: return x >= 0 + + +def _variables_recursive(R, include=None, exclude=None): + r""" + Return all variables appearing in the ring ``R``. + + INPUT: + + - ``R`` -- a :class:`Ring` + - ``include``, ``exclude`` -- (optional) iterables of variables in ``R`` + + OUTPUT: + + If ``include`` is specified, only these variables are returned + as elements of ``R``. Otherwise, all variables in ``R`` + (recursively) with the exception of those in ``exclude`` are + returned. + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _variables_recursive + sage: R.<a, b> = QQ[] + sage: S.<t> = R[] + sage: _variables_recursive(S) + [a, b, t] + + sage: _variables_recursive(S, exclude=[b]) + [a, t] + + sage: _variables_recursive(S, include=[b]) + [b] + + TESTS:: + + sage: _variables_recursive(R.fraction_field(), exclude=[b]) + [a] + + sage: _variables_recursive(S.fraction_field(), exclude=[b]) # known bug + [a, t] + """ + if include is not None and exclude is not None: + raise RuntimeError("include and exclude cannot both be specified") + + if include is not None: + degree_one = [R(g) for g in include] + else: + try: + degree_one = [R(g) for g in R.variable_names_recursive()] + except AttributeError: + try: + degree_one = R.gens() + except (NotImplementedError, AttributeError): + degree_one = [] + if exclude is not None: + degree_one = [g for g in degree_one if g not in exclude] + + return [g for g in degree_one if g != R.one()] + + +def _raise_variables(c, n, variables): + r""" + Replace the given variables in the ring element ``c`` with their + ``n``-th power. + + INPUT: + + - ``c`` -- an element of a ring + - ``n`` -- the power to raise the given variables to + - ``variables`` -- the variables to raise + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _raise_variables + sage: R.<a, b> = QQ[] + sage: S.<t> = R[] + sage: _raise_variables(2*a + 3*b*t, 2, [a, t]) + 3*b*t^2 + 2*a^2 + """ + try: + return c.subs(**{str(g): g ** n for g in variables}) + except AttributeError: + return c + + +def _to_polynomials(lf, R): + """ + Return the symmetric functions as polynomials, where each + part of a partition corresponds to a variable. + + The result makes sense only if the symmetric functions are all + given in the same basis, which is multiplicative, but we do not + check this. + + INPUT: + + - ``lf`` -- a list of symmetric functions + - ``R`` -- the base ring + + .. SEEALSO:: + + :func:`_from_polynomial` + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _to_polynomials + sage: e = SymmetricFunctions(QQ).e() + sage: _to_polynomials([5*e[3] + e[2,1] + e[1]], QQ) + [v1*v2 + v1 + 5*v3] + """ + n = max(max((part[0] for part in f.support() if part), default=0) + for f in lf) + # the polynomial ring with no variables is not well supported, + # eg., gcd does not work + n = max(n, 1) + P = PolynomialRing(R, ["v%s" % a for a in range(1, n + 1)]) + if n == 1: + return [P({part.to_exp(n)[0]: c for part, c in f}) + for f in lf] + return [P({tuple(part.to_exp(n)): c for part, c in f}) + for f in lf] + + +def _from_polynomial(p, f): + """ + Return the polynomial as a symmetric function in the given + basis , where the `n`th variable corresponds to the symmetric + function`f[n]`. + + INPUT: + + - ``p`` -- a polynomial + - ``f`` -- a basis of the ring of symmetric functions + + .. SEEALSO:: + + :func:`_to_polynomials` + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _to_polynomials, _from_polynomial + sage: e = SymmetricFunctions(QQ).e() + sage: p = _to_polynomials([5*e[3] + e[2,1] + e[1]], ZZ)[0]; p + v1*v2 + v1 + 5*v3 + sage: _from_polynomial(p, e) + e[1] + e[2, 1] + 5*e[3] + """ + n = p.parent().ngens() + if n == 1: + d = {_Partitions.from_exp([e]): c + for e, c in p.dict().items()} + else: + d = {_Partitions.from_exp(e): c + for e, c in p.iterator_exp_coeff(False)} + return f.element_class(f, d) diff --git a/src/sage/combinat/sf/symplectic.py b/src/sage/combinat/sf/symplectic.py index bd5a44bd615..7ee859ce6db 100644 --- a/src/sage/combinat/sf/symplectic.py +++ b/src/sage/combinat/sf/symplectic.py @@ -165,6 +165,7 @@ class SymmetricFunctionAlgebra_symplectic(sfa.SymmetricFunctionAlgebra_generic): sage: sp.one().counit() 1 """ + def __init__(self, Sym): """ Initialize ``self``. diff --git a/src/sage/combinat/sf/witt.py b/src/sage/combinat/sf/witt.py index 2f942af97f3..a129a267229 100644 --- a/src/sage/combinat/sf/witt.py +++ b/src/sage/combinat/sf/witt.py @@ -395,6 +395,7 @@ class SymmetricFunctionAlgebra_witt(multiplicative.SymmetricFunctionAlgebra_mult ....: #this holds for all odd i and is easily proven by induction True """ + def __init__(self, Sym, coerce_h=True, coerce_e=False, coerce_p=False): """ Initialize ``self``. diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 5d59921c629..5b3354953f4 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -843,6 +843,7 @@ class CrystalElementShiftedPrimedTableau(ShiftedPrimedTableau): """ Class for elements of ``crystals.ShiftedPrimedTableau``. """ + def _to_matrix(self): """ Return a 2-dimensional array representation of a shifted tableau. @@ -1364,6 +1365,7 @@ class PrimedEntry(SageObject): possibly ending in ``p`` or ``'`` - ``double`` -- the doubled value """ + def __init__(self, entry=None, double=None): """ Normalize the entry. @@ -1996,6 +1998,7 @@ class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): """ The class of all shifted primed tableaux. """ + def __init__(self, skew=None, primed_diagonal=False): """ Initialize the class of all shifted tableaux. @@ -2380,6 +2383,7 @@ class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): sage: T.cardinality() 16 """ + def __init__(self, weight, skew=None, primed_diagonal=False): """ Initialize the class of shifted primed tableaux of a given weight. @@ -2498,6 +2502,7 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): sage: T.cardinality() 32 """ + def __init__(self, weight, shape, skew=None, primed_diagonal=False): """ Initialize the class of shifted primed tableaux of the given weight diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index ff42faaad20..4c8a9cdd217 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -73,6 +73,7 @@ class ShuffleProduct_abstract(Parent): """ Abstract base class for shuffle products. """ + def __init__(self, l1, l2, element_constructor=None): """ Initialize ``self``. @@ -196,6 +197,7 @@ class SetShuffleProduct(ShuffleProduct_abstract): [6, 1], [6, 2, 3]] """ + def __init__(self, l1, l2, element_constructor=None): """ Construct the set of all possible shuffle products of two sets of iterables. @@ -336,6 +338,7 @@ class ShuffleProduct(ShuffleProduct_abstract): ['de'] """ + def __init__(self, l1, l2, element_constructor=None): """ Construct the shuffle product of two iterable. @@ -556,6 +559,7 @@ class ShuffleProduct_overlapping_r(ShuffleProduct_abstract): word: 939, word: 9,2,10] """ + def __init__(self, w1, w2, r, element_constructor=None, add=operator.add): """ Initialize ``self``. @@ -765,6 +769,7 @@ class ShuffleProduct_overlapping(ShuffleProduct_abstract): [{2, 3}, {1, 2}, {3, 4, 5, 6}], [{1, 2, 3}, {3, 4, 5, 6}]] """ + def __init__(self, w1, w2, element_constructor=None, add=operator.add): """ Initialize ``self``. diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 88515bb3cfe..cf32b79905c 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -156,7 +156,7 @@ class type, it is also possible to compute the number of classes of that type .. [PSS13] Prasad, A., Singla, P., and Spallone, S., *Similarity of matrices over local rings of length two*. :arxiv:`1212.6157` -.. [PR22] Prasad, A., Ram, S., *Splitting subspaces and a finite field +.. [PR22] Prasad, A., Ram, S., *Splitting subspaces and a finite field interpretation of the Touchard-Riordan formula*. :arxiv:`2205.11076` .. [R17] Ramaré, O., *Rationality of the zeta function of the subgroups of @@ -170,7 +170,7 @@ class type, it is also possible to compute the number of classes of that type - Amritanshu Prasad (2013-09-09): added functions for similarity classes over rings of length two -- Amritanshu Prasad (2022-07-31): added computation of similarity class type of +- Amritanshu Prasad (2022-07-31): added computation of similarity class type of a given matrix and invariant subspace generating function """ # **************************************************************************** @@ -206,6 +206,7 @@ class type, it is also possible to compute the number of classes of that type from sage.combinat.misc import IterableFunctionCall from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + @cached_function def fq(n, q=None): r""" @@ -387,7 +388,7 @@ def invariant_subspace_generating_function(la, q=None, t=None): u = invariant_subspace_generating_function(la[1:], q=q, t=t) return R((t**(la[0]+1) * q**(sum(la[1:])) * u.substitute(t=t/q) - u.substitute(t=t*q)) / (t - 1)) - + class PrimarySimilarityClassType(Element, metaclass=InheritComparisonClasscallMetaclass): r""" @@ -1068,7 +1069,7 @@ def invariant_subspace_generating_function(self, q=None, t=None): .. MATH:: \sum_{j\geq 0} a_j(q) t^j, - + where `a_j(q)` denotes the number of `j`-dimensional invariant subspaces of dimensiona `j` for any matrix with the similarity class type ``self`` with entries in a field of order `q`. diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index c9a4ab2d8ba..e98c393ea08 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -45,16 +45,9 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.functions.trig import cos, sin from sage.misc.lazy_import import lazy_import -lazy_import("sage.plot.plot", "parametric_plot") -lazy_import("sage.plot.graphics", "Graphics") -lazy_import("sage.plot.polygon", "polygon2d") -lazy_import("sage.plot.circle", "circle") -lazy_import("sage.plot.bezier_path", "bezier_path") -lazy_import("sage.plot.point", "point") -lazy_import("sage.plot.line", "line") from sage.symbolic.constants import pi, I from sage.functions.log import exp from sage.functions.other import ceil @@ -62,6 +55,13 @@ from sage.symbolic.ring import SR from sage.functions.other import real_part, imag_part from sage.misc.cachefunc import cached_method +lazy_import("sage.plot.plot", "parametric_plot") +lazy_import("sage.plot.graphics", "Graphics") +lazy_import("sage.plot.polygon", "polygon2d") +lazy_import("sage.plot.circle", "circle") +lazy_import("sage.plot.bezier_path", "bezier_path") +lazy_import("sage.plot.point", "point") +lazy_import("sage.plot.line", "line") class SineGordonYsystem(SageObject): @@ -99,6 +99,7 @@ class SineGordonYsystem(SageObject): (103, 105)) sage: Y.plot() #not tested """ + def __init__(self, X, na): """ TESTS:: @@ -157,7 +158,7 @@ def _repr_(self): """ msg = "A sine-Gordon Y-system of type {}" msg += " with defining integer tuple {}" - return msg.format(self._type, self._na) + return msg.format(self._type, self._na) def type(self): r""" @@ -234,8 +235,8 @@ def pa(self): pa = [1] if F > 1: pa.append(na[0]) - for k in range(2, F): - pa.append(na[k-1] * pa[k-1] + pa[k-2]) + for k in range(1, F - 1): + pa.append(na[k] * pa[k] + pa[k - 1]) return tuple(pa) @cached_method @@ -347,7 +348,7 @@ def triangulation(self): done = False while not done: if left: - edge = (edge[0] + vert(rk[a+1]), edge[1]) + edge = (edge[0] + vert(rk[a + 1]), edge[1]) else: edge = (edge[0], edge[1] - vert(rk[a + 1])) left = not left @@ -403,7 +404,7 @@ def intervals(self): last_ccw = vert(last_cw + rk[a + 2]) x = first while x < last_cw: - new_intervals.append((vert(x), vert(x + rk[a+1]), "L")) + new_intervals.append((vert(x), vert(x + rk[a + 1]), "L")) x = vert(x + rk[a + 1]) if typ == "L": new_intervals.append((last_cw, last_ccw, "NL")) @@ -411,7 +412,7 @@ def intervals(self): new_intervals.append((last_cw, last_ccw, "NR")) x = last_ccw while x != last: - new_intervals.append((vert(x), vert(x+rk[a+1]), "R")) + new_intervals.append((vert(x), vert(x + rk[a + 1]), "R")) x = vert(x + rk[a + 1]) else: for (first, last, typ) in intervals[a]: @@ -427,15 +428,15 @@ def intervals(self): last_ccw = vert(last_cw + rk[a + 2]) x = first while x < last_cw: - new_intervals.append((vert(x), vert(x + rk[a+1]), "L")) - x = vert(x + rk[a+1]) + new_intervals.append((vert(x), vert(x + rk[a + 1]), "L")) + x = vert(x + rk[a + 1]) if typ == "L": new_intervals.append((last_cw, last_ccw, "NR")) else: new_intervals.append((last_cw, last_ccw, "NL")) x = last_ccw while x != last: - new_intervals.append((vert(x), vert(x + rk[a+1]), "R")) + new_intervals.append((vert(x), vert(x + rk[a + 1]), "R")) x = vert(x + rk[a + 1]) intervals.append(new_intervals) return tuple(map(tuple, intervals)) @@ -534,7 +535,7 @@ def f(t): if internal_angle > pi: (angle_p, angle_q) = (angle_q + 2 * pi, angle_p) internal_angle = angle_p - angle_q - angle_center = (angle_p+angle_q) / 2 + angle_center = (angle_p + angle_q) / 2 hypotenuse = radius / cos(internal_angle / 2) radius_arc = hypotenuse * sin(internal_angle / 2) center = (hypotenuse * cos(angle_center), diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index a3b28126923..9bd289ff221 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -13,6 +13,7 @@ class SixVertexConfiguration(ClonableArray): """ A configuration in the six vertex model. """ + def check(self): """ Check if ``self`` is a valid 6 vertex configuration. @@ -290,6 +291,7 @@ def energy(self, epsilon): raise ValueError("there must be 6 energy constants") return sum(epsilon[entry] for row in self for entry in row) + class SixVertexModel(UniqueRepresentation, Parent): """ The six vertex model. @@ -658,6 +660,7 @@ def partition_function(self, beta, epsilon): from sage.functions.log import exp return sum(exp(-beta * nu.energy(epsilon)) for nu in self) + class SquareIceModel(SixVertexModel): r""" The square ice model. @@ -670,6 +673,7 @@ class SquareIceModel(SixVertexModel): Configurations of the 6 vertex model with domain wall boundary conditions are in bijection with alternating sign matrices. """ + def __init__(self, n): """ Initialize ``self``. diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index ea6b3b52057..7c002e151ad 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -126,6 +126,7 @@ - Mike Hansen: Initial version - Travis Scrimshaw (2013-02-11): Factored out ``CombinatorialClass`` +- Trevor K. Karn (2022-08-03): Add ``outside_corners`` """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, @@ -159,6 +160,7 @@ from sage.combinat.tableau import Tableaux from sage.combinat.composition import Compositions + class SkewPartition(CombinatorialElement): r""" A skew partition. @@ -267,8 +269,16 @@ def _latex_diagram(self): &\lr{\ast}&\lr{\ast}\\\cline{2-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_diagram()) + {\emptyset} + """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" char = self.parent().options.latex_diagram_str @@ -294,8 +304,15 @@ def _latex_young_diagram(self): &\lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{2-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_young_diagram()) + {\emptyset} """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" from sage.combinat.output import tex_from_array @@ -319,8 +336,15 @@ def _latex_marked(self): \lr{X}&\lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_marked()) + {\emptyset} """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" from sage.combinat.output import tex_from_array @@ -741,6 +765,22 @@ def outer_corners(self): """ Return a list of the outer corners of ``self``. + These are corners that are contained inside of the shape. + For the corners which are outside of the shape, + use :meth:`outside_corners`. + + .. WARNING:: + + In the case that ``self`` is an honest (rather than skew) partition, + these are the :meth:`~sage.combinat.partition.Partition.corners` + of the outer partition. In the language of [Sag2001]_ these would + be the "inner corners" of the outer partition. + + .. SEEALSO:: + + - :meth:`sage.combinat.skew_partition.SkewPartition.outside_corners` + - :meth:`sage.combinat.partition.Partition.outside_corners` + EXAMPLES:: sage: SkewPartition([[4, 3, 1], [2]]).outer_corners() @@ -1209,6 +1249,33 @@ def jacobi_trudi(self): m.append(row) return H(m) + def outside_corners(self): + r""" + Return the outside corners of ``self``. + + The outside corners are corners which are outside of the shape. This + should not be confused with :meth:`outer_corners` which consists of + corners inside the shape. It returns a result analogous to the + ``.outside_corners()`` method on (non-skew) ``Partitions``. + + .. SEEALSO:: + + - :meth:`sage.combinat.skew_partition.SkewPartition.outer_corners` + - :meth:`sage.combinat.partition.Partition.outside_corners` + + EXAMPLES:: + + sage: mu = SkewPartition([[3,2,1],[2,1]]) + sage: mu.pp() + * + * + * + sage: mu.outside_corners() + [(0, 3), (1, 2), (2, 1), (3, 0)] + """ + return self.outer().outside_corners() + + def row_lengths_aux(skp): """ EXAMPLES:: @@ -1224,6 +1291,7 @@ def row_lengths_aux(skp): else: return [x[0] - x[1] for x in zip(skp[0], skp[1])] + class SkewPartitions(UniqueRepresentation, Parent): """ Skew partitions. @@ -1530,6 +1598,7 @@ class SkewPartitions_all(SkewPartitions): """ Class of all skew partitions. """ + def __init__(self): """ Initialize ``self``. @@ -1806,6 +1875,8 @@ def __iter__(self): ###################################### # Skew Partitions (from row lengths) # ###################################### + + class SkewPartitions_rowlengths(SkewPartitions): """ All skew partitions with given row lengths. @@ -1922,5 +1993,6 @@ def __iter__(self): for sp in self._from_row_lengths_aux(sskp, self.co[-2], self.co[-1], self.overlap): yield self.element_class(self, sp) + from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.skew_partition', 'SkewPartition_class', SkewPartition) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 32b116784dd..66c0a410f8f 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -7,6 +7,7 @@ - Mike Hansen: Initial version - Travis Scrimshaw, Arthur Lubovsky (2013-02-11): Factored out ``CombinatorialClass`` +- Trevor K. Karn (2022-08-03): added `backward_slide` """ # **************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, @@ -907,6 +908,140 @@ def slide(self, corner=None, return_vacated=False): return (SkewTableau(new_st), (spotl, spotc)) return SkewTableau(new_st) + def backward_slide(self, corner=None): + r""" + Apply a backward jeu de taquin slide on the specified outside + ``corner`` of ``self``. + + Backward jeu de taquin slides are defined in Section 3.7 of + [Sag2001]_. + + .. WARNING:: + + The :meth:`inner_corners` and :meth:`outer_corners` are the + :meth:`sage.combinat.partition.Partition.corners` of the inner and + outer partitions of the skew shape. They are different from the + inner/outer corners defined in [Sag2001]_. + + The "inner corners" of [Sag2001]_ may be found by calling + :meth:`outer_corners`. The "outer corners" of [Sag2001]_ may be + found by calling ``self.outer_shape().outside_corners()``. + + EXAMPLES:: + + sage: T = SkewTableaux()([[2, 2], [4, 4], [5]]) + sage: Tableaux.options.display='array' + sage: Q = T.backward_slide(); Q + . 2 2 + 4 4 + 5 + sage: Q.backward_slide((1, 2)) + . 2 2 + . 4 4 + 5 + sage: Q.reverse_slide((1, 2)) == Q.backward_slide((1, 2)) + True + + sage: T = SkewTableaux()([[1, 3],[3],[5]]); T + 1 3 + 3 + 5 + sage: T.reverse_slide((1,1)) + . 1 + 3 3 + 5 + + TESTS:: + + sage: T = SkewTableaux()([[2, 2], [4, 4], [5]]) + sage: Q = T.backward_slide((0, 2)) + sage: Q.backward_slide((2,1)) + . 2 2 + . 4 + 4 5 + sage: Q.backward_slide((3,0)) + . 2 2 + . 4 + 4 + 5 + sage: Q = T.backward_slide((2,1)); Q + . 2 + 2 4 + 4 5 + sage: Q.backward_slide((3,0)) + . 2 + . 4 + 2 5 + 4 + sage: Q = T.backward_slide((3,0)); Q + . 2 + 2 4 + 4 + 5 + sage: Q.backward_slide((4,0)) + . 2 + . 4 + 2 + 4 + 5 + sage: Tableaux.options.display='list' + """ + new_st = self.to_list() + inner_outside_corners = self.inner_shape().outside_corners() + outer_outisde_corners = self.outer_shape().outside_corners() + if corner is not None: + if tuple(corner) not in outer_outisde_corners: + raise ValueError("corner must be an outside corner" + " of the outer shape") + else: + if not outer_outisde_corners: + return self + else: + corner = outer_outisde_corners[0] + + i, j = corner + + # add the empty cell + # the column only matters if it is zeroth column, in which + # case we need to add a new row. + if not j: + new_st.append(list()) + new_st[i].append(None) + + while (i, j) not in inner_outside_corners: + # get the value of the cell above the temporarily empty cell (if + # it exists) + if i > 0: + P_up = new_st[i-1][j] + else: + P_up = -1 # a dummy value less than all positive numbers + + # get the value of the cell to the left of the temp. empty cell + # (if it exists) + if j > 0: + P_left = new_st[i][j-1] + else: + P_left = -1 # a dummy value less than all positive numbers + + # get the next cell + # p_left will always be positive, but if P_left + # and P_up are both None, then it will return + # -1, which doesn't trigger the conditional + if P_left > P_up: + new_st[i][j] = P_left + i, j = (i, j - 1) + else: # if they are equal, we slide up + new_st[i][j] = P_up + i, j = (i - 1, j) + # We don't need to reset the intermediate cells inside the loop + # because the conditional above will continue to overwrite it until + # the while loop terminates. We do need to reset it at the end. + new_st[i][j] = None + + return SkewTableaux()(new_st) + + reverse_slide = backward_slide + def rectify(self, algorithm=None): """ Return a :class:`StandardTableau`, :class:`SemistandardTableau`, @@ -1647,6 +1782,7 @@ class SkewTableaux(UniqueRepresentation, Parent): r""" Class of all skew tableaux. """ + def __init__(self, category=None): """ Initialize ``self``. @@ -1858,6 +1994,7 @@ class StandardSkewTableaux_all(StandardSkewTableaux): """ Class of all standard skew tableaux. """ + def __init__(self): """ EXAMPLES:: @@ -1902,6 +2039,7 @@ class StandardSkewTableaux_size(StandardSkewTableaux): """ Standard skew tableaux of a fixed size `n`. """ + def __init__(self, n): """ EXAMPLES:: @@ -2224,6 +2362,7 @@ class SemistandardSkewTableaux_all(SemistandardSkewTableaux): Class of all semistandard skew tableaux, possibly with a given maximum entry. """ + def __init__(self, max_entry): """ Initialize ``self``. @@ -2312,6 +2451,7 @@ class SemistandardSkewTableaux_size(SemistandardSkewTableaux): Class of all semistandard skew tableaux of a fixed size `n`, possibly with a given maximum entry. """ + def __init__(self, n, max_entry): """ EXAMPLES:: @@ -2585,6 +2725,7 @@ class SkewTableau_class(SkewTableau): """ This exists solely for unpickling ``SkewTableau_class`` objects. """ + def __setstate__(self, state): r""" Unpickle old ``SkewTableau_class`` objects. diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 38e3709a06e..ba937db12ff 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -143,6 +143,7 @@ class SloaneSequence(SageObject): r""" Base class for a Sloane integer sequence. """ + def __init__(self, offset=1): r""" A sequence starting at offset (=1 by default). @@ -205,7 +206,6 @@ def _sage_src_(self): from sage.misc.sageinspect import sage_getsource return sage_getsource(self.__class__) - def __call__(self, n): """ EXAMPLES:: @@ -584,7 +584,6 @@ def __init__(self): """ SloaneSequence.__init__(self, offset=0) - def _repr_(self): """ EXAMPLES:: @@ -594,7 +593,6 @@ def _repr_(self): """ return "Number of ways of making change for n cents using coins of 1, 2, 5, 10 cents." - def _eval(self, n): """ EXAMPLES:: @@ -684,7 +682,6 @@ def _precompute(self, how_many=50): f = self._f self._b += [next(f) for i in range(how_many)] - def _eval(self, n): """ EXAMPLES:: @@ -774,7 +771,6 @@ def pi(self): a, a1 = 10*(a % b), 10*(a1 % b1) d, d1 = a//b, a1//b1 - def _precompute(self, how_many=1000): """ EXAMPLES:: @@ -791,7 +787,6 @@ def _precompute(self, how_many=1000): f = self._f self._b += [next(f) for i in range(how_many)] - def _eval(self, n): """ EXAMPLES:: @@ -850,7 +845,6 @@ def __init__(self): """ SloaneSequence.__init__(self, offset=0) - def _repr_(self): """ EXAMPLES:: @@ -860,7 +854,6 @@ def _repr_(self): """ return "Least common multiple (or lcm) of {1, 2, ..., n}." - def _eval(self, n): """ EXAMPLES:: @@ -917,7 +910,6 @@ def _repr_(self): """ return "Pascal's triangle read by rows: C(n,k) = binomial(n,k) = n!/(k!*(n-k)!), 0<=k<=n." - def _eval(self, n): """ EXAMPLES:: @@ -1067,7 +1059,6 @@ def _repr_(self): """ return "Triangle of Stirling numbers of 2nd kind, S2(n,k), n >= 1, 1<=k<=n." - def s2(self, n, k): """ Returns the Stirling number S2(n,k) of the 2nd kind. @@ -1142,8 +1133,6 @@ def _repr_(self): """ return "Triangle of coefficients of Chebyshev's S(n,x) := U(n,x/2) polynomials (exponents in increasing order)." - - def _eval(self, n): """ EXAMPLES:: @@ -3959,7 +3948,6 @@ def fib(self): x, y = y, x+y yield x - def _eval(self, n): """ EXAMPLES:: @@ -4830,7 +4818,6 @@ def df(self): x, y = y, k*x yield x - def _eval(self, n): """ EXAMPLES:: @@ -5102,7 +5089,6 @@ def gen(self, a0, a1, d): x, y = y, (k)*y+(k-d)*x yield x - def _eval(self, n): """ EXAMPLES:: @@ -5635,8 +5621,7 @@ def _eval(self, n): """ if n == 1: return ZZ(3) - else: - return sloane.A000153(n+1) + sloane.A000153(n) + return sloane.A000153(n+1) + sloane.A000153(n) class A090013(SloaneSequence): @@ -5709,8 +5694,7 @@ def _eval(self, n): """ if n == 1: return ZZ(4) - else: - return sloane.A000261(n+2) + sloane.A000261(n+1) + return sloane.A000261(n+2) + sloane.A000261(n+1) class A090014(SloaneSequence): @@ -5783,8 +5767,7 @@ def _eval(self, n): """ if n == 1: return ZZ(5) - else: - return sloane.A001909(n+3) + sloane.A001909(n+2) + return sloane.A001909(n+3) + sloane.A001909(n+2) class A090015(SloaneSequence): @@ -5857,8 +5840,7 @@ def _eval(self, n): """ if n == 1: return ZZ(6) - else: - return sloane.A001910(n+4) + sloane.A001910(n+3) + return sloane.A001910(n+4) + sloane.A001910(n+3) class A090016(SloaneSequence): @@ -5924,7 +5906,6 @@ def _repr_(self): """ return "Permanent of (0,1)-matrix of size n X (n+d) with d=6 and n-1 zeros not on a line." - def _eval(self, n): """ EXAMPLES:: @@ -5934,8 +5915,7 @@ def _eval(self, n): """ if n == 1: return ZZ(7) - else: - return sloane.A090010(n-1) + sloane.A090010(n) + return sloane.A090010(n-1) + sloane.A090010(n) class A000166(SloaneSequence): @@ -6114,7 +6094,7 @@ def _eval(self, n): sage: [sloane.A001157._eval(n) for n in range(1,11)] [1, 5, 10, 21, 26, 50, 50, 85, 91, 130] """ - return arith.sigma(n, 2) + return arith.sigma(n, 2) class A008683(SloaneSequence): @@ -6167,7 +6147,7 @@ def _eval(self, n): sage: [sloane.A008683._eval(n) for n in range(1,11)] [1, -1, -1, 0, -1, 1, -1, 0, 0, 1] """ - return arith.moebius(n) + return arith.moebius(n) class A000204(SloaneSequence): @@ -6231,8 +6211,7 @@ def _eval(self, n): return ZZ.one() elif n == 2: return 3 - else: - return sloane.A000045(n+1) + sloane.A000045(n-1) + return sloane.A000045(n+1) + sloane.A000045(n-1) class A000217(SloaneSequence): @@ -6564,7 +6543,6 @@ def __init__(self): self._b = [] self._precompute(2) - def _repr_(self): """ EXAMPLES:: @@ -7139,7 +7117,6 @@ def __init__(self): """ SloaneSequence.__init__(self, offset=1) - def _repr_(self): """ EXAMPLES:: @@ -8320,8 +8297,6 @@ def list(self, n): return self._b[:n] - - def perm_mh(m, h): r""" This functions calculates `f(g,h)` from Sloane's sequences @@ -8415,7 +8390,6 @@ def _repr_(self): """ return "Solutions to the Dancing School problem with n girls and n+3 boys" - def _eval(self, n): """ EXAMPLES:: @@ -8543,6 +8517,7 @@ class A109814(SloaneSequence): - Jaap Spies (2007-01-13) """ + def __init__(self): r""" EXAMPLES:: @@ -9154,6 +9129,7 @@ class Sloane(SageObject): - Nick Alexander """ + def __dir__(self): r""" List Sloane generating functions for tab-completion. diff --git a/src/sage/combinat/species/all.py b/src/sage/combinat/species/all.py index 65e18ade5d2..6bda0eab5db 100644 --- a/src/sage/combinat/species/all.py +++ b/src/sage/combinat/species/all.py @@ -11,14 +11,6 @@ - :ref:`section-examples-catalan` - :ref:`section-generic-species` -Lazy Power Series ------------------ - -- :ref:`sage.combinat.species.stream` -- :ref:`sage.combinat.species.series_order` -- :ref:`sage.combinat.species.series` -- :ref:`sage.combinat.species.generating_series` - Basic Species ------------- @@ -52,6 +44,7 @@ from sage.misc.namespace_package import install_doc install_doc(__package__, __doc__) -from .series import LazyPowerSeriesRing -from .recursive_species import CombinatorialSpecies -from . import library as species +from sage.misc.lazy_import import lazy_import +lazy_import("sage.combinat.species.recursive_species", "CombinatorialSpecies") +lazy_import("sage.combinat.species", "library", as_="species") +del lazy_import diff --git a/src/sage/combinat/species/characteristic_species.py b/src/sage/combinat/species/characteristic_species.py index dbb1d5c262a..910ce3e96fc 100644 --- a/src/sage/combinat/species/characteristic_species.py +++ b/src/sage/combinat/species/characteristic_species.py @@ -44,7 +44,6 @@ def __repr__(self): else: return "{" + s[1:-1] + "}" - def canonical_label(self): """ EXAMPLES:: @@ -59,7 +58,6 @@ def canonical_label(self): rng = list(range(1, P._n + 1)) return CharacteristicSpeciesStructure(P, self._labels, rng) - def transport(self, perm): """ Returns the transport of this structure along the permutation @@ -109,15 +107,15 @@ def __init__(self, n, min=None, max=None, weight=None): [1] sage: X.structures([1,2]).list() [] - sage: X.generating_series().coefficients(4) + sage: X.generating_series()[0:4] [0, 1, 0, 0] - sage: X.isotype_generating_series().coefficients(4) + sage: X.isotype_generating_series()[0:4] [0, 1, 0, 0] - sage: X.cycle_index_series().coefficients(4) + sage: X.cycle_index_series()[0:4] [0, p[1], 0, 0] sage: F = species.CharacteristicSpecies(3) - sage: c = F.generating_series().coefficients(4) + sage: c = F.generating_series()[0:4] sage: F._check() True sage: F == loads(dumps(F)) @@ -163,7 +161,7 @@ def _gs_term(self, base_ring): EXAMPLES:: sage: F = species.CharacteristicSpecies(2) - sage: F.generating_series().coefficients(5) + sage: F.generating_series()[0:5] [0, 0, 1/2, 0, 0] sage: F.generating_series().count(2) 1 @@ -187,7 +185,7 @@ def _itgs_term(self, base_ring): EXAMPLES:: sage: F = species.CharacteristicSpecies(2) - sage: F.isotype_generating_series().coefficients(5) + sage: F.isotype_generating_series()[0:5] [0, 0, 1, 0, 0] Here we test out weighting each structure by q. @@ -196,7 +194,7 @@ def _itgs_term(self, base_ring): sage: R.<q> = ZZ[] sage: Fq = species.CharacteristicSpecies(2, weight=q) - sage: Fq.isotype_generating_series().coefficients(5) + sage: Fq.isotype_generating_series()[0:5] [0, 0, q, 0, 0] """ return base_ring(self._weight) @@ -207,7 +205,7 @@ def _cis_term(self, base_ring): sage: F = species.CharacteristicSpecies(2) sage: g = F.cycle_index_series() - sage: g.coefficients(5) + sage: g[0:5] [0, 0, 1/2*p[1, 1] + 1/2*p[2], 0, 0] """ cis = SetSpecies(weight=self._weight).cycle_index_series(base_ring) @@ -230,9 +228,11 @@ def _equation(self, var_mapping): """ return var_mapping['z']**(self._n) + #Backward compatibility CharacteristicSpecies_class = CharacteristicSpecies + class EmptySetSpecies(CharacteristicSpecies): def __init__(self, min=None, max=None, weight=None): """ @@ -248,11 +248,11 @@ def __init__(self, min=None, max=None, weight=None): [{}] sage: X.structures([1,2]).list() [] - sage: X.generating_series().coefficients(4) + sage: X.generating_series()[0:4] [1, 0, 0, 0] - sage: X.isotype_generating_series().coefficients(4) + sage: X.isotype_generating_series()[0:4] [1, 0, 0, 0] - sage: X.cycle_index_series().coefficients(4) + sage: X.cycle_index_series()[0:4] [p[], 0, 0, 0] TESTS:: @@ -272,9 +272,11 @@ def __init__(self, min=None, max=None, weight=None): self._name = "Empty set species" self._state_info = [] + #Backward compatibility EmptySetSpecies_class = EmptySetSpecies._cached_constructor = EmptySetSpecies + class SingletonSpecies(CharacteristicSpecies): def __init__(self, min=None, max=None, weight=None): """ @@ -290,11 +292,11 @@ def __init__(self, min=None, max=None, weight=None): [1] sage: X.structures([1,2]).list() [] - sage: X.generating_series().coefficients(4) + sage: X.generating_series()[0:4] [0, 1, 0, 0] - sage: X.isotype_generating_series().coefficients(4) + sage: X.isotype_generating_series()[0:4] [0, 1, 0, 0] - sage: X.cycle_index_series().coefficients(4) + sage: X.cycle_index_series()[0:4] [0, p[1], 0, 0] TESTS:: @@ -314,5 +316,6 @@ def __init__(self, min=None, max=None, weight=None): self._name = "Singleton species" self._state_info = [] + #Backward compatibility SingletonSpecies_class = SingletonSpecies diff --git a/src/sage/combinat/species/composition_species.py b/src/sage/combinat/species/composition_species.py index efc77ce508b..18aca4861d7 100644 --- a/src/sage/combinat/species/composition_species.py +++ b/src/sage/combinat/species/composition_species.py @@ -20,6 +20,7 @@ from .partition_species import PartitionSpecies from sage.structure.unique_representation import UniqueRepresentation + class CompositionSpeciesStructure(GenericSpeciesStructure): def __init__(self, parent, labels, pi, f, gs): """ @@ -62,7 +63,7 @@ def transport(self, perm): f, gs = self._list pi = self._partition.transport(perm) f = f.change_labels(pi._list) - g = [g.change_labels(part) for g, part in zip(gs, pi)] # BUG HERE ? + _ = [g.change_labels(part) for g, part in zip(gs, pi)] # TODO: BUG HERE ? return self.__class__(self, self._labels, pi, f, gs) def change_labels(self, labels): @@ -105,7 +106,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: E = species.SetSpecies() sage: C = species.CycleSpecies() sage: S = E(C) - sage: S.generating_series().coefficients(5) + sage: S.generating_series()[:5] [1, 1, 1, 1, 1] sage: E(C) is S True @@ -114,7 +115,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) - sage: c = L.generating_series().coefficients(3) + sage: c = L.generating_series()[:3] sage: L._check() #False due to isomorphism types not being implemented False sage: L == loads(dumps(L)) @@ -186,14 +187,13 @@ def _isotypes(self, structure_class, labels): """ raise NotImplementedError - def _gs(self, series_ring, base_ring): """ EXAMPLES:: sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) - sage: L.generating_series().coefficients(5) + sage: L.generating_series()[:5] [1, 1, 1, 1, 1] """ return self._F.generating_series(base_ring)(self._G.generating_series(base_ring)) @@ -204,7 +204,7 @@ def _itgs(self, series_ring, base_ring): sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) - sage: L.isotype_generating_series().coefficients(10) + sage: L.isotype_generating_series()[:10] [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] """ cis = self.cycle_index_series(base_ring) @@ -216,7 +216,7 @@ def _cis(self, series_ring, base_ring): sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) - sage: L.cycle_index_series().coefficients(5) + sage: L.cycle_index_series()[:5] [p[], p[1], p[1, 1] + p[2], @@ -233,7 +233,7 @@ def _cis(self, series_ring, base_ring): sage: E = species.SetSpecies() sage: C = species.CycleSpecies(weight=t) sage: S = E(C) - sage: S.isotype_generating_series().coefficients(5) #indirect + sage: S.isotype_generating_series()[:5] #indirect [1, t, t^2 + t, t^3 + t^2 + t, t^4 + t^3 + 2*t^2 + t] We do the same thing with set partitions weighted by the number of @@ -245,17 +245,11 @@ def _cis(self, series_ring, base_ring): sage: E = species.SetSpecies() sage: E_t = species.SetSpecies(min=1,weight=t) sage: Par = E(E_t) - sage: Par.isotype_generating_series().coefficients(5) + sage: Par.isotype_generating_series()[:5] [1, t, t^2 + t, t^3 + t^2 + t, t^4 + t^3 + 2*t^2 + t] """ f_cis = self._F.cycle_index_series(base_ring) g_cis = self._G.cycle_index_series(base_ring) - - #If G is a weighted species, then we can't use the default - #algorithm for the composition of the cycle index series - #since we must raise the weighting to the power. - if self._G.is_weighted(): - return f_cis.weighted_composition(self._G) return f_cis(g_cis) def weight_ring(self): diff --git a/src/sage/combinat/species/cycle_species.py b/src/sage/combinat/species/cycle_species.py index 83c82fc44a3..a0afba3b1d0 100644 --- a/src/sage/combinat/species/cycle_species.py +++ b/src/sage/combinat/species/cycle_species.py @@ -14,12 +14,11 @@ from .species import GenericCombinatorialSpecies from .structure import GenericSpeciesStructure -from .generating_series import _integers_from from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.integer_ring import ZZ from sage.arith.all import divisors, euler_phi from sage.combinat.species.misc import accept_size + class CycleSpeciesStructure(GenericSpeciesStructure): def __repr__(self): """ @@ -142,7 +141,7 @@ def __init__(self, min=None, max=None, weight=None): True sage: P = species.CycleSpecies() - sage: c = P.generating_series().coefficients(3) + sage: c = P.generating_series()[:3] sage: P._check() True sage: P == loads(dumps(P)) @@ -165,7 +164,6 @@ def _structures(self, structure_class, labels): for c in CyclicPermutations(range(1, len(labels)+1)): yield structure_class(self, labels, c) - def _isotypes(self, structure_class, labels): """ EXAMPLES:: @@ -177,7 +175,7 @@ def _isotypes(self, structure_class, labels): if len(labels) != 0: yield structure_class(self, labels, range(1, len(labels)+1)) - def _gs_iterator(self, base_ring): + def _gs_callable(self, base_ring, n): r""" The generating series for cyclic permutations is `-\log(1-x) = \sum_{n=1}^\infty x^n/n`. @@ -186,20 +184,19 @@ def _gs_iterator(self, base_ring): sage: P = species.CycleSpecies() sage: g = P.generating_series() - sage: g.coefficients(10) + sage: g[0:10] [0, 1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9] TESTS:: sage: P = species.CycleSpecies() sage: g = P.generating_series(RR) - sage: g.coefficients(3) + sage: g[0:3] [0.000000000000000, 1.00000000000000, 0.500000000000000] """ - one = base_ring(1) - yield base_ring(0) - for n in _integers_from(ZZ(1)): - yield self._weight*one/n + if n: + return self._weight * base_ring.one() / n + return base_ring.zero() def _order(self): """ @@ -213,7 +210,7 @@ def _order(self): """ return 1 - def _itgs_list(self, base_ring): + def _itgs_list(self, base_ring, n): """ The isomorphism type generating series for cyclic permutations is given by `x/(1-x)`. @@ -222,19 +219,21 @@ def _itgs_list(self, base_ring): sage: P = species.CycleSpecies() sage: g = P.isotype_generating_series() - sage: g.coefficients(5) + sage: g[0:5] [0, 1, 1, 1, 1] TESTS:: sage: P = species.CycleSpecies() sage: g = P.isotype_generating_series(RR) - sage: g.coefficients(3) + sage: g[0:3] [0.000000000000000, 1.00000000000000, 1.00000000000000] """ - return [base_ring(0), self._weight*base_ring(1)] + if n: + return self._weight * base_ring.one() + return base_ring.zero() - def _cis_iterator(self, base_ring): + def _cis_callable(self, base_ring, n): r""" The cycle index series of the species of cyclic permutations is given by @@ -256,7 +255,7 @@ def _cis_iterator(self, base_ring): sage: P = species.CycleSpecies() sage: cis = P.cycle_index_series() - sage: cis.coefficients(7) + sage: cis[0:7] [0, p[1], 1/2*p[1, 1] + 1/2*p[2], @@ -268,15 +267,16 @@ def _cis_iterator(self, base_ring): from sage.combinat.sf.sf import SymmetricFunctions p = SymmetricFunctions(base_ring).power() - zero = base_ring(0) + zero = base_ring.zero() + + if not n: + return zero + res = zero + for k in divisors(n): + res += euler_phi(k)*p([k])**(n//k) + res /= n + return self._weight * res - yield zero - for n in _integers_from(1): - res = zero - for k in divisors(n): - res += euler_phi(k)*p([k])**(n//k) - res /= n - yield self._weight*res #Backward compatibility CycleSpecies_class = CycleSpecies diff --git a/src/sage/combinat/species/empty_species.py b/src/sage/combinat/species/empty_species.py index 600ea3ee2b7..a6eb69597bc 100644 --- a/src/sage/combinat/species/empty_species.py +++ b/src/sage/combinat/species/empty_species.py @@ -16,9 +16,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from .species import GenericCombinatorialSpecies -from .series_order import inf from sage.structure.unique_representation import UniqueRepresentation + class EmptySpecies(GenericCombinatorialSpecies, UniqueRepresentation): """ Returns the empty species. This species has no structure at all. @@ -34,11 +34,11 @@ class EmptySpecies(GenericCombinatorialSpecies, UniqueRepresentation): [] sage: X.structures([1,2]).list() [] - sage: X.generating_series().coefficients(4) + sage: X.generating_series()[0:4] [0, 0, 0, 0] - sage: X.isotype_generating_series().coefficients(4) + sage: X.isotype_generating_series()[0:4] [0, 0, 0, 0] - sage: X.cycle_index_series().coefficients(4) + sage: X.cycle_index_series()[0:4] [0, 0, 0, 0] The empty species is the zero of the semi-ring of species. @@ -49,14 +49,14 @@ class EmptySpecies(GenericCombinatorialSpecies, UniqueRepresentation): sage: X = S + Empt sage: X == S # TODO: Not Implemented True - sage: (X.generating_series().coefficients(4) == - ....: S.generating_series().coefficients(4)) + sage: (X.generating_series()[0:4] == + ....: S.generating_series()[0:4]) True - sage: (X.isotype_generating_series().coefficients(4) == - ....: S.isotype_generating_series().coefficients(4)) + sage: (X.isotype_generating_series()[0:4] == + ....: S.isotype_generating_series()[0:4]) True - sage: (X.cycle_index_series().coefficients(4) == - ....: S.cycle_index_series().coefficients(4)) + sage: (X.cycle_index_series()[0:4] == + ....: S.cycle_index_series()[0:4]) True The following tests that it is the zero element with respect to @@ -65,11 +65,11 @@ class EmptySpecies(GenericCombinatorialSpecies, UniqueRepresentation): sage: Y = Empt*S sage: Y == Empt # TODO: Not Implemented True - sage: Y.generating_series().coefficients(4) + sage: Y.generating_series()[0:4] [0, 0, 0, 0] - sage: Y.isotype_generating_series().coefficients(4) + sage: Y.isotype_generating_series()[0:4] [0, 0, 0, 0] - sage: Y.cycle_index_series().coefficients(4) + sage: Y.cycle_index_series()[0:4] [0, 0, 0, 0] TESTS:: @@ -79,6 +79,7 @@ class EmptySpecies(GenericCombinatorialSpecies, UniqueRepresentation): sage: Empt is Empt2 True """ + def __init__(self, min=None, max=None, weight=None): """ Initializer for the empty species. @@ -102,7 +103,7 @@ def _gs(self, series_ring, base_ring): EXAMPLES:: sage: F = species.EmptySpecies() - sage: F.generating_series().coefficients(5) # indirect doctest + sage: F.generating_series()[0:5] # indirect doctest [0, 0, 0, 0, 0] sage: F.generating_series().count(3) 0 @@ -114,18 +115,6 @@ def _gs(self, series_ring, base_ring): _itgs = _gs _cis = _gs - def _order(self): - """ - Returns the order of the generating series. - - EXAMPLES:: - - sage: F = species.EmptySpecies() - sage: F._order() - Infinite series order - """ - return inf - def _structures(self, structure_class, labels): """ Thanks to the counting optimisation, this is never called... Otherwise @@ -161,4 +150,5 @@ def _equation(self, var_mapping): """ return 0 + EmptySpecies_class = EmptySpecies diff --git a/src/sage/combinat/species/functorial_composition_species.py b/src/sage/combinat/species/functorial_composition_species.py index 4930a70e6da..d97460e29e4 100644 --- a/src/sage/combinat/species/functorial_composition_species.py +++ b/src/sage/combinat/species/functorial_composition_species.py @@ -1,7 +1,7 @@ """ Functorial composition species """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>, # # Distributed under the terms of the GNU General Public License (GPL) @@ -13,14 +13,16 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from .species import GenericCombinatorialSpecies from .structure import GenericSpeciesStructure + class FunctorialCompositionStructure(GenericSpeciesStructure): pass + class FunctorialCompositionSpecies(GenericCombinatorialSpecies): def __init__(self, F, G, min=None, max=None, weight=None): """ @@ -33,11 +35,11 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: WP = species.SubsetSpecies() sage: P2 = E2*E sage: G = WP.functorial_composition(P2) - sage: G.isotype_generating_series().coefficients(5) + sage: G.isotype_generating_series()[0:5] [1, 1, 2, 4, 11] sage: G = species.SimpleGraphSpecies() - sage: c = G.generating_series().coefficients(2) + sage: c = G.generating_series()[0:2] sage: type(G) <class 'sage.combinat.species.functorial_composition_species.FunctorialCompositionSpecies'> sage: G == loads(dumps(G)) @@ -87,13 +89,12 @@ def _isotypes(self, structure_class, s): """ raise NotImplementedError - def _gs(self, series_ring, base_ring): """ EXAMPLES:: sage: G = species.SimpleGraphSpecies() - sage: G.generating_series().coefficients(5) + sage: G.generating_series()[0:5] [1, 1, 1, 4/3, 8/3] """ return self._F.generating_series(base_ring).functorial_composition(self._G.generating_series(base_ring)) @@ -103,7 +104,7 @@ def _itgs(self, series_ring, base_ring): EXAMPLES:: sage: G = species.SimpleGraphSpecies() - sage: G.isotype_generating_series().coefficients(5) + sage: G.isotype_generating_series()[0:5] [1, 1, 2, 4, 11] """ return self.cycle_index_series(base_ring).isotype_generating_series() @@ -113,14 +114,14 @@ def _cis(self, series_ring, base_ring): EXAMPLES:: sage: G = species.SimpleGraphSpecies() - sage: G.cycle_index_series().coefficients(5) + sage: G.cycle_index_series()[0:5] [p[], p[1], p[1, 1] + p[2], 4/3*p[1, 1, 1] + 2*p[2, 1] + 2/3*p[3], 8/3*p[1, 1, 1, 1] + 4*p[2, 1, 1] + 2*p[2, 2] + 4/3*p[3, 1] + p[4]] """ - return self._F.cycle_index_series(base_ring).functorial_composition(self._G.cycle_index_series(base_ring)) + return self._F.cycle_index_series(base_ring).functorial_composition(self._G.cycle_index_series(base_ring)) def weight_ring(self): """ @@ -142,5 +143,6 @@ def weight_ring(self): return cm.explain(f_weights, g_weights, verbosity=0) -#Backward compatibility + +# Backward compatibility FunctorialCompositionSpecies_class = FunctorialCompositionSpecies diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index 02e1bb52332..7f2d1f27c38 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -15,37 +15,11 @@ TESTS:: - sage: from sage.combinat.species.stream import Stream, _integers_from sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing sage: p = SymmetricFunctions(QQ).power() sage: CIS = CycleIndexSeriesRing(QQ) - -:: - - sage: geo1 = CIS((p([1])^i for i in _integers_from(0))) - sage: geo2 = CIS((p([2])^i for i in _integers_from(0))) - sage: s = geo1 * geo2 - sage: s[0] - p[] - sage: s[1] - p[1] + p[2] - sage: s[2] - p[1, 1] + p[2, 1] + p[2, 2] - sage: s[3] - p[1, 1, 1] + p[2, 1, 1] + p[2, 2, 1] + p[2, 2, 2] - -Whereas the coefficients of the above test are homogeneous with -respect to total degree, the following test groups with respect to -weighted degree where each variable x_i has weight i. - -:: - - sage: def g(): - ....: for i in _integers_from(0): - ....: yield p([2])^i - ....: yield p(0) - sage: geo1 = CIS((p([1])^i for i in _integers_from(0))) - sage: geo2 = CIS(g()) + sage: geo1 = CIS(lambda i: p([1])^i) + sage: geo2 = CIS(lambda i: p([2])^(i // 2) if is_even(i) else 0) sage: s = geo1 * geo2 sage: s[0] p[] @@ -55,8 +29,6 @@ p[1, 1] + p[2] sage: s[3] p[1, 1, 1] + p[2, 1] - sage: s[4] - p[1, 1, 1, 1] + p[2, 1, 1] + p[2, 2] REFERENCES: @@ -77,67 +49,41 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from .series import LazyPowerSeriesRing, LazyPowerSeries -from .stream import Stream, _integers_from +from sage.rings.lazy_series import LazyPowerSeries, LazySymmetricFunction +from sage.rings.lazy_series_ring import LazyPowerSeriesRing, LazySymmetricFunctions from sage.rings.integer import Integer -from sage.rings.rational_field import RationalField -from sage.arith.all import moebius, gcd, lcm, divisors +from sage.rings.rational_field import QQ +from sage.arith.all import divisors from sage.combinat.partition import Partition, Partitions -from functools import partial from sage.combinat.sf.sf import SymmetricFunctions from sage.misc.cachefunc import cached_function from sage.arith.misc import factorial -@cached_function -def OrdinaryGeneratingSeriesRing(R): - """ - Return the ring of ordinary generating series over ``R``. +class OrdinaryGeneratingSeries(LazyPowerSeries): + r""" + A class for ordinary generating series. - Note that it is just a - :class:`LazyPowerSeriesRing` whose elements have - some extra methods. + Note that it is just a :class:`LazyPowerSeries` whose elements + have some extra methods. EXAMPLES:: sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing - sage: R = OrdinaryGeneratingSeriesRing(QQ); R - Lazy Power Series Ring over Rational Field - sage: R([1]).coefficients(4) - [1, 1, 1, 1] - sage: R([1]).counts(4) - [1, 1, 1, 1] - - TESTS: - - We test to make sure that caching works. - - :: - - sage: R is OrdinaryGeneratingSeriesRing(QQ) - True + sage: R = OrdinaryGeneratingSeriesRing(QQ) + sage: f = R(lambda n: n) + sage: f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) """ - return OrdinaryGeneratingSeriesRing_class(R) - -class OrdinaryGeneratingSeriesRing_class(LazyPowerSeriesRing): - def __init__(self, R): - """ - EXAMPLES:: - - sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing - sage: R = OrdinaryGeneratingSeriesRing(QQ) - sage: R == loads(dumps(R)) - True - """ - LazyPowerSeriesRing.__init__(self, R, element_class=OrdinaryGeneratingSeries) - - -class OrdinaryGeneratingSeries(LazyPowerSeries): def count(self, n): """ Return the number of structures on a set of size ``n``. + INPUT: + + - ``n`` -- the size of the set + EXAMPLES:: sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing @@ -164,10 +110,9 @@ def counts(self, n): return [self.count(i) for i in range(n)] -@cached_function -def ExponentialGeneratingSeriesRing(R): - """ - Return the ring of exponential generating series over ``R``. +class OrdinaryGeneratingSeriesRing(LazyPowerSeriesRing): + r""" + Return the ring of ordinary generating series over ``R``. Note that it is just a :class:`LazyPowerSeriesRing` whose elements have @@ -175,39 +120,59 @@ def ExponentialGeneratingSeriesRing(R): EXAMPLES:: - sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing - sage: R = ExponentialGeneratingSeriesRing(QQ); R - Lazy Power Series Ring over Rational Field - sage: R([1]).coefficients(4) + sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing + sage: R = OrdinaryGeneratingSeriesRing(QQ); R + Lazy Taylor Series Ring in z over Rational Field + sage: [R(lambda n: 1).coefficient(i) for i in range(4)] [1, 1, 1, 1] - sage: R([1]).counts(4) - [1, 1, 2, 6] + sage: R(lambda n: 1).counts(4) + [1, 1, 1, 1] + sage: R == loads(dumps(R)) + True TESTS: - We test to make sure that caching works. - - :: + We test to make sure that caching works:: - sage: R is ExponentialGeneratingSeriesRing(QQ) + sage: R is OrdinaryGeneratingSeriesRing(QQ) True """ - return ExponentialGeneratingSeriesRing_class(R) - -class ExponentialGeneratingSeriesRing_class(LazyPowerSeriesRing): - def __init__(self, R): + def __init__(self, base_ring): """ - EXAMPLES:: + Initialize ``self``. - sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing - sage: R = ExponentialGeneratingSeriesRing(QQ) - sage: R == loads(dumps(R)) - True + TESTS:: + + sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing + sage: OrdinaryGeneratingSeriesRing.options.halting_precision(15) + sage: R = OrdinaryGeneratingSeriesRing(QQ) + sage: TestSuite(R).run() + + sage: OrdinaryGeneratingSeriesRing.options._reset() # reset options """ - LazyPowerSeriesRing.__init__(self, R, element_class=ExponentialGeneratingSeries) + super().__init__(base_ring, names="z") + + Element = OrdinaryGeneratingSeries + class ExponentialGeneratingSeries(LazyPowerSeries): + r""" + A class for ordinary generating series. + + Note that it is just a + :class:`LazyPowerSeries` whose elements have + some extra methods. + + EXAMPLES:: + + sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing + sage: R = OrdinaryGeneratingSeriesRing(QQ) + sage: f = R(lambda n: n) + sage: f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) + """ + def count(self, n): """ Return the number of structures of size ``n``. @@ -216,7 +181,7 @@ def count(self, n): sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing sage: R = ExponentialGeneratingSeriesRing(QQ) - sage: f = R([1]) + sage: f = R(lambda n: 1) sage: [f.count(i) for i in range(7)] [1, 1, 2, 6, 24, 120, 720] """ @@ -248,7 +213,7 @@ def functorial_composition(self, y): .. MATH:: - f \Box g = \sum_{n=0}^{\infty} f_{g_n} \frac{x^n}{n!} + f \Box g = \sum_{n=0}^{\infty} f_{g_n} \frac{x^n}{n!}. REFERENCES: @@ -258,17 +223,8 @@ def functorial_composition(self, y): sage: G = species.SimpleGraphSpecies() sage: g = G.generating_series() - sage: g.coefficients(10) + sage: [g.coefficient(i) for i in range(10)] [1, 1, 1, 4/3, 8/3, 128/15, 2048/45, 131072/315, 2097152/315, 536870912/2835] - """ - return self._new(partial(self._functorial_compose_gen, y), lambda a,b: 0, self, y) - - def _functorial_compose_gen(self, y, ao): - """ - Returns a generator for the coefficients of the functorial - composition of self with y. - - EXAMPLES:: sage: E = species.SetSpecies() sage: E2 = E.restricted(min=2, max=3) @@ -276,117 +232,58 @@ def _functorial_compose_gen(self, y, ao): sage: P2 = E2*E sage: g1 = WP.generating_series() sage: g2 = P2.generating_series() - sage: g = g1._functorial_compose_gen(g2, 0) - sage: [next(g) for i in range(10)] + sage: g1.functorial_composition(g2)[:10] [1, 1, 1, 4/3, 8/3, 128/15, 2048/45, 131072/315, 2097152/315, 536870912/2835] """ - n = 0 - while True: - yield self.count(y.count(n)) / factorial(n) - n += 1 + P = self.parent() + return P(lambda n: self.count(y.count(n)) / factorial(n), 0) -def factorial_gen(): - """ - A generator for the factorials starting at 0. - - EXAMPLES:: - - sage: from sage.combinat.species.generating_series import factorial_gen - sage: g = factorial_gen() - sage: [next(g) for i in range(5)] - [1, 1, 2, 6, 24] - """ - z = Integer(1) - yield z - yield z - n = Integer(2) - while True: - z *= n - yield z - n += 1 - - -@cached_function -def CycleIndexSeriesRing(R): +class ExponentialGeneratingSeriesRing(LazyPowerSeriesRing): r""" - Return the ring of cycle index series over ``R``. - - This is the ring of formal power series `\Lambda[x]`, where - `\Lambda` is the ring of symmetric functions over ``R`` in the - `p`-basis. Its purpose is to house the cycle index series of - species (in a somewhat nonstandard notation tailored to Sage): - If `F` is a species, then the *cycle index series* of `F` is - defined to be the formal power series - - .. MATH:: - - \sum_{n \geq 0} \frac{1}{n!} (\sum_{\sigma \in S_n} - \operatorname{fix} F[\sigma] - \prod_{z \text{ is a cycle of } \sigma} - p_{\text{length of } z}) x^n - \in \Lambda_\QQ [x], - - where `\operatorname{fix} F[\sigma]` denotes the number of - fixed points of the permutation `F[\sigma]` of `F[n]`. We - notice that this power series is "equigraded" (meaning that - its `x^n`-coefficient is homogeneous of degree `n`). A more - standard convention in combinatorics would be to use - `x_i` instead of `p_i`, and drop the `x` (that is, evaluate - the above power series at `x = 1`); but this would be more - difficult to implement in Sage, as it would be an element - of a power series ring in infinitely many variables. + Return the ring of exponential generating series over ``R``. - Note that it is just a :class:`LazyPowerSeriesRing` (whose base - ring is `\Lambda`) whose elements have some extra methods. + Note that it is just a + :class:`LazyPowerSeriesRing` whose elements have + some extra methods. EXAMPLES:: - sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing - sage: R = CycleIndexSeriesRing(QQ); R - Cycle Index Series Ring over Symmetric Functions over Rational Field in the powersum basis - sage: R([1]).coefficients(4) # This is not combinatorially - ....: # meaningful. + sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing + sage: R = ExponentialGeneratingSeriesRing(QQ); R + Lazy Taylor Series Ring in z over Rational Field + sage: [R(lambda n: 1).coefficient(i) for i in range(4)] [1, 1, 1, 1] + sage: R(lambda n: 1).counts(4) + [1, 1, 2, 6] TESTS: - We test to make sure that caching works. - - :: + We test to make sure that caching works:: - sage: R is CycleIndexSeriesRing(QQ) + sage: R is ExponentialGeneratingSeriesRing(QQ) True """ - return CycleIndexSeriesRing_class(R) - -class CycleIndexSeriesRing_class(LazyPowerSeriesRing): - def __init__(self, R): + def __init__(self, base_ring): """ - EXAMPLES:: + Initialize ``self``. - sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing - sage: R = CycleIndexSeriesRing(QQ); R - Cycle Index Series Ring over Symmetric Functions over Rational Field in the powersum basis - sage: R == loads(dumps(R)) - True - """ - R = SymmetricFunctions(R).power() - LazyPowerSeriesRing.__init__(self, R, element_class=CycleIndexSeries) + TESTS:: - def __repr__(self): - """ - EXAMPLES:: + sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing + sage: ExponentialGeneratingSeriesRing.options.halting_precision(15) + sage: R = ExponentialGeneratingSeriesRing(QQ) + sage: TestSuite(R).run() - sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing - sage: CycleIndexSeriesRing(QQ) - Cycle Index Series Ring over Symmetric Functions over Rational Field in the powersum basis + sage: ExponentialGeneratingSeriesRing.options._reset() # reset options """ - return "Cycle Index Series Ring over %s" % self.base_ring() + super().__init__(base_ring, names="z") + + Element = ExponentialGeneratingSeries -class CycleIndexSeries(LazyPowerSeries): +class CycleIndexSeries(LazySymmetricFunction): def count(self, t): """ Return the number of structures corresponding to a certain cycle @@ -410,7 +307,7 @@ def count(self, t): def coefficient_cycle_type(self, t): """ - Returns the coefficient of a cycle type ``t`` in ``self``. + Return the coefficient of a cycle type ``t`` in ``self``. EXAMPLES:: @@ -429,668 +326,75 @@ def coefficient_cycle_type(self, t): p = self.coefficient(t.size()) return p.coefficient(t) - - def stretch(self, k): - r""" - Return the stretch of the cycle index series ``self`` by a positive - integer `k`. - - If - - .. MATH:: - - f = \sum_{n=0}^{\infty} f_n(p_1, p_2, p_3, \ldots ), - - then the stretch `g` of `f` by `k` is - - .. MATH:: - - g = \sum_{n=0}^{\infty} f_n(p_k, p_{2k}, p_{3k}, \ldots ). - - EXAMPLES:: - - sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing - sage: p = SymmetricFunctions(QQ).power() - sage: CIS = CycleIndexSeriesRing(QQ) - sage: f = CIS([p([]), p([1]), p([2]), p.zero()]) - sage: f.stretch(3).coefficients(10) - [p[], 0, 0, p[3], 0, 0, p[6], 0, 0, 0] - """ - return self._new(partial(self._stretch_gen, k), lambda ao: k*ao, self) - - def _stretch_gen(self, k, ao): - """ - EXAMPLES:: - - sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing - sage: p = SymmetricFunctions(QQ).power() - sage: CIS = CycleIndexSeriesRing(QQ) - sage: f = CIS([p([1])]) # This is the power series whose all coefficients - ....: # are p[1]. Not combinatorially meaningful! - sage: g = f._stretch_gen(2,0) - sage: [next(g) for i in range(10)] - [p[2], 0, p[2], 0, p[2], 0, p[2], 0, p[2], 0] - """ - from sage.combinat.partition import Partition - BR = self.base_ring() - zero = BR.zero() - - stretch_k = lambda p: Partition([k*i for i in p]) - - yield self.coefficient(0).map_support(stretch_k) - - n = 1 - while True: - for i in range(k-1): - yield zero - yield self.coefficient(n).map_support(stretch_k) - n += 1 - def isotype_generating_series(self): """ + Return the isotype generating series of ``self``. + EXAMPLES:: sage: P = species.PermutationSpecies() sage: cis = P.cycle_index_series() sage: f = cis.isotype_generating_series() - sage: f.coefficients(10) + sage: f[:10] [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] """ - R = self.base_ring().base_ring() - OGS = OrdinaryGeneratingSeriesRing(R)() - return OGS._new(self._ogs_gen, lambda ao: ao, self) - - def expand_as_sf(self, n, alphabet='x'): - """ - Returns the expansion of a cycle index series as a symmetric function in - ``n`` variables. - - Specifically, this returns a :class:`~sage.combinat.species.series.LazyPowerSeries` whose - ith term is obtained by calling :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.expand` - on the ith term of ``self``. - - This relies on the (standard) interpretation of a cycle index series as a symmetric function - in the power sum basis. - - INPUT: - - - ``self`` -- a cycle index series - - - ``n`` -- a positive integer - - - ``alphabet`` -- a variable for the expansion (default: `x`) - - EXAMPLES:: - - sage: from sage.combinat.species.set_species import SetSpecies - sage: SetSpecies().cycle_index_series().expand_as_sf(2).coefficients(4) - [1, x0 + x1, x0^2 + x0*x1 + x1^2, x0^3 + x0^2*x1 + x0*x1^2 + x1^3] - - """ - expanded_poly_ring = self.coefficient(0).expand(n, alphabet).parent() - LPSR = LazyPowerSeriesRing(expanded_poly_ring) - - expander_gen = (LPSR.term(self.coefficient(i).expand(n, alphabet), i) for i in _integers_from(0)) + R = self.base_ring() + OGS = OrdinaryGeneratingSeriesRing(R) + return OGS(lambda n: self._ogs_gen(n, self._coeff_stream._approximate_order), + self._coeff_stream._approximate_order) - return LPSR.sum_generator(expander_gen) - - def _ogs_gen(self, ao): + def _ogs_gen(self, n, ao): """ - Returns a generator for the coefficients of the ordinary generating + Return a generator for the coefficients of the ordinary generating series obtained from a cycle index series. EXAMPLES:: sage: P = species.PermutationSpecies() sage: cis = P.cycle_index_series() - sage: g = cis._ogs_gen(0) - sage: [next(g) for i in range(10)] + sage: [cis._ogs_gen(i, 0) for i in range(10)] [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] """ - for i in range(ao): - yield 0 - for i in _integers_from(ao): - yield sum( self.coefficient(i).coefficients() ) + if n < ao: + return 0 + return sum(self.coefficient(n).coefficients()) def generating_series(self): """ + Return the generating series of ``self``. + EXAMPLES:: sage: P = species.PartitionSpecies() sage: cis = P.cycle_index_series() sage: f = cis.generating_series() - sage: f.coefficients(5) + sage: f[:5] [1, 1, 1, 5/6, 5/8] """ - R = self.base_ring().base_ring() - EGS = ExponentialGeneratingSeriesRing(R)() - return EGS._new(self._egs_gen, lambda ao: ao, self) + R = self.base_ring() + EGS = ExponentialGeneratingSeriesRing(R) + return EGS(lambda n: self._egs_gen(n, self._coeff_stream._approximate_order), + self._coeff_stream._approximate_order) - def _egs_gen(self, ao): + def _egs_gen(self, n, ao): """ - Returns a generator for the coefficients of the exponential + Return a generator for the coefficients of the exponential generating series obtained from a cycle index series. EXAMPLES:: sage: P = species.PermutationSpecies() sage: cis = P.cycle_index_series() - sage: g = cis._egs_gen(0) - sage: [next(g) for i in range(10)] + sage: [cis._egs_gen(i, 0) for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - for i in range(ao): - yield 0 - for i in _integers_from(ao): - yield self.coefficient(i).coefficient([1]*i) - - def __invert__(self): - r""" - Return the multiplicative inverse of ``self``. - - This algorithm is derived from [BLL]_. - - EXAMPLES:: - - sage: E = species.SetSpecies().cycle_index_series() - sage: E.__invert__().coefficients(4) - [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]] - - The defining characteristic of the multiplicative inverse `F^{-1}` of - a cycle index series `F` is that `F \cdot F^{-1} = F^{-1} \cdot F = 1` - (that is, both products with `F` yield the multiplicative identity `1`):: - - sage: E = species.SetSpecies().cycle_index_series() - sage: (E * ~E).coefficients(6) - [p[], 0, 0, 0, 0, 0] - - REFERENCES: - - - [BLL]_ - - [BLL-Intro]_ - - http://bergeron.math.uqam.ca/Site/bergeron_anglais_files/livre_combinatoire.pdf - - AUTHORS: - - - Andrew Gainer-Dewar - """ - if self.coefficient(0) == 0: - raise ValueError("constant term must be non-zero") - - def multinv_builder(i): - return self.coefficient(0)**(-i-1) * (self.coefficient(0) + (-1)*self)**i - - return self.parent().sum_generator(multinv_builder(i) for i in _integers_from(0)) - - def _div_(self, y): - """ - TESTS:: - - sage: E = species.SetSpecies().cycle_index_series() - sage: (E / E).coefficients(6) - [p[], 0, 0, 0, 0, 0] - """ - return self*(~y) + if n < ao: + return 0 + return self.coefficient(n).coefficient([1]*n) - def functorial_composition(self, g): + def derivative(self, n=1): r""" - Returns the functorial composition of ``self`` and ``g``. - - If `F` and `G` are species, their functorial composition is the species - `F \Box G` obtained by setting `(F \Box G) [A] = F[ G[A] ]`. - In other words, an `(F \Box G)`-structure on a set `A` of labels is an - `F`-structure whose labels are the set of all `G`-structures on `A`. - - It can be shown (as in section 2.2 of [BLL]_) that there is a - corresponding operation on cycle indices: - - .. MATH:: - - Z_{F} \Box Z_{G} = \sum_{n \geq 0} \frac{1}{n!} - \sum_{\sigma \in \mathfrak{S}_{n}} - \operatorname{fix} F[ (G[\sigma])_{1}, (G[\sigma])_{2}, \ldots ] - \, p_{1}^{\sigma_{1}} p_{2}^{\sigma_{2}} \cdots. - - This method implements that operation on cycle index series. - - EXAMPLES: - - The species `G` of simple graphs can be expressed in terms of a functorial - composition: `G = \mathfrak{p} \Box \mathfrak{p}_{2}`, where - `\mathfrak{p}` is the :class:`~sage.combinat.species.subset_species.SubsetSpecies`. - This is how it is implemented in - :meth:`~sage.combinat.species.library.SimpleGraphSpecies`:: - - sage: S = species.SimpleGraphSpecies() - sage: S.cycle_index_series().coefficients(5) - [p[], - p[1], - p[1, 1] + p[2], - 4/3*p[1, 1, 1] + 2*p[2, 1] + 2/3*p[3], - 8/3*p[1, 1, 1, 1] + 4*p[2, 1, 1] + 2*p[2, 2] + 4/3*p[3, 1] + p[4]] - """ - return self._new(partial(self._functorial_compose_gen, g), lambda a,b: 0, self, g) - - def _functorial_compose_gen(self, g, ao): - """ - Return a generator for the coefficients of the functorial - composition of ``self`` with ``g``. - - EXAMPLES:: - - sage: E = species.SetSpecies() - sage: E2 = species.SetSpecies(size=2) - sage: WP = species.SubsetSpecies() - sage: P2 = E2*E - sage: P2_cis = P2.cycle_index_series() - sage: WP_cis = WP.cycle_index_series() - sage: g = WP_cis._functorial_compose_gen(P2_cis,0) - sage: [next(g) for i in range(5)] - [p[], - p[1], - p[1, 1] + p[2], - 4/3*p[1, 1, 1] + 2*p[2, 1] + 2/3*p[3], - 8/3*p[1, 1, 1, 1] + 4*p[2, 1, 1] + 2*p[2, 2] + 4/3*p[3, 1] + p[4]] - """ - p = self.parent().base_ring() - n = 0 - while True: - res = p(0) - for s in Partitions(n): - t = g._cycle_type(s) - q = self.count(t) / s.aut() - res += q*p(s) - yield res - n += 1 - - def arithmetic_product(self, g, check_input = True): - r""" - Return the arithmetic product of ``self`` with ``g``. - - For species `M` and `N` such that `M[\\varnothing] = N[\\varnothing] = \\varnothing`, - their arithmetic product is the species `M \\boxdot N` of "`M`-assemblies of cloned `N`-structures". - This operation is defined and several examples are given in [MM]_. - - The cycle index series for `M \\boxdot N` can be computed in terms of the component series `Z_M` and `Z_N`, - as implemented in this method. - - INPUT: - - - ``g`` -- a cycle index series having the same parent as ``self``. - - - ``check_input`` -- (default: ``True``) a Boolean which, when set - to ``False``, will cause input checks to be skipped. - - OUTPUT: - - The arithmetic product of ``self`` with ``g``. This is a cycle - index series defined in terms of ``self`` and ``g`` such that - if ``self`` and ``g`` are the cycle index series of two species - `M` and `N`, their arithmetic product is the cycle index series - of the species `M \\boxdot N`. - - EXAMPLES: - - For `C` the species of (oriented) cycles and `L_{+}` the species of nonempty linear orders, `C \\boxdot L_{+}` corresponds - to the species of "regular octopuses"; a `(C \\boxdot L_{+})`-structure is a cycle of some length, each of whose elements - is an ordered list of a length which is consistent for all the lists in the structure. :: - - sage: C = species.CycleSpecies().cycle_index_series() - sage: Lplus = species.LinearOrderSpecies(min=1).cycle_index_series() - sage: RegularOctopuses = C.arithmetic_product(Lplus) - sage: RegOctSpeciesSeq = RegularOctopuses.generating_series().counts(8) - sage: RegOctSpeciesSeq - [0, 1, 3, 8, 42, 144, 1440, 5760] - - It is shown in [MM]_ that the exponential generating function for regular octopuses satisfies - `(C \\boxdot L_{+}) (x) = \\sum_{n \geq 1} \\sigma (n) (n - 1)! \\frac{x^{n}}{n!}` (where `\\sigma (n)` is - the sum of the divisors of `n`). :: - - sage: RegOctDirectSeq = [0] + [sum(divisors(i))*factorial(i-1) for i in range(1,8)] - sage: RegOctDirectSeq == RegOctSpeciesSeq - True - - AUTHORS: - - - Andrew Gainer-Dewar (2013) - - REFERENCES: - - .. [MM] \M. Maia and M. Mendez. "On the arithmetic product of combinatorial species". - Discrete Mathematics, vol. 308, issue 23, 2008, pp. 5407-5427. - :arxiv:`math/0503436v2`. - - """ - from itertools import product, repeat, chain - - p = self.base_ring() - - if check_input: - assert self.coefficient(0) == p.zero() - assert g.coefficient(0) == p.zero() - - # We first define an operation `\\boxtimes` on partitions as in Lemma 2.1 of [MM]_. - def arith_prod_of_partitions(l1, l2): - # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by - # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes - # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2` - # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a - # partition of `nm`. - term_iterable = chain.from_iterable(repeat(lcm(pair), gcd(pair)) - for pair in product(l1, l2)) - return Partition(sorted(term_iterable, reverse=True)) - - # We then extend this to an operation on symmetric functions as per eq. (52) of [MM]_. - # (Maia and Mendez, in [MM]_, are talking about polynomials instead of symmetric - # functions, but this boils down to the same: Their x_i corresponds to the i-th power - # sum symmetric function.) - def arith_prod_sf(x, y): - ap_sf_wrapper = lambda l1, l2: p(arith_prod_of_partitions(l1, l2)) - return p._apply_multi_module_morphism(x, y, ap_sf_wrapper) - - # Sage stores cycle index series by degree. - # Thus, to compute the arithmetic product `Z_M \\boxdot Z_N` it is useful - # to compute all terms of a given degree `n` at once. - def arith_prod_coeff(n): - if n == 0: - res = p.zero() - else: - index_set = ((d, n // d) for d in divisors(n)) - res = sum(arith_prod_sf(self.coefficient(i), g.coefficient(j)) for i,j in index_set) - - # Build a list which has res in the `n`th slot and 0's before and after - # to feed to sum_generator - res_in_seq = [p.zero()]*n + [res, p.zero()] - - return self.parent(res_in_seq) - - # Finally, we use the sum_generator method to assemble these results into a single - # LazyPowerSeries object. - return self.parent().sum_generator(arith_prod_coeff(n) for n in _integers_from(0)) - - def _cycle_type(self, s): - """ - EXAMPLES:: - - sage: cis = species.PartitionSpecies().cycle_index_series() - sage: [cis._cycle_type(p) for p in Partitions(3)] - [[3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] - sage: cis = species.PermutationSpecies().cycle_index_series() - sage: [cis._cycle_type(p) for p in Partitions(3)] - [[3, 1, 1, 1], [2, 2, 1, 1], [1, 1, 1, 1, 1, 1]] - sage: cis = species.SetSpecies().cycle_index_series() - sage: [cis._cycle_type(p) for p in Partitions(3)] - [[1], [1], [1]] - """ - if s == []: - return self._card(0) - res = [] - for k in range(1, self._upper_bound_for_longest_cycle(s)+1): - e = 0 - for d in divisors(k): - m = moebius(d) - if m == 0: - continue - u = s.power(k/d) - e += m*self.count(u) - res.extend([k]*int(e/k)) - res.reverse() - return Partition(res) - - - def _upper_bound_for_longest_cycle(self, s): - """ - EXAMPLES:: - - sage: cis = species.PartitionSpecies().cycle_index_series() - sage: cis._upper_bound_for_longest_cycle([4]) - 4 - sage: cis._upper_bound_for_longest_cycle([3,1]) - 3 - sage: cis._upper_bound_for_longest_cycle([2,2]) - 2 - sage: cis._upper_bound_for_longest_cycle([2,1,1]) - 2 - sage: cis._upper_bound_for_longest_cycle([1,1,1,1]) - 1 - """ - if s == []: - return 1 - return min(self._card(sum(s)), lcm(list(s))) - - def _card(self, n): - r""" - Return the number of structures on an underlying set of size ``n`` for - the species associated with ``self``. - - This is just ``n!`` times the coefficient of ``p[1]n`` in ``self``. - - EXAMPLES:: - - sage: cis = species.PartitionSpecies().cycle_index_series() - sage: cis._card(4) - 15 - """ - p = self.coefficient(n) - return factorial(n) * p.coefficient([1] * n) - - def _compose_gen(self, y, ao): - """ - Return a generator for the coefficients of the composition of this - cycle index series and the cycle index series ``y``. This overrides - the method defined in ``LazyPowerSeries``. - - The notion "composition" means plethystic substitution here, as - defined in Section 2.2 of [BLL-Intro]_. - - EXAMPLES:: - - sage: E = species.SetSpecies(); C = species.CycleSpecies() - sage: E_cis = E.cycle_index_series() - sage: g = E_cis._compose_gen(C.cycle_index_series(),0) - sage: [next(g) for i in range(4)] - [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]] - """ - assert y.coefficient(0) == 0 - y_powers = Stream(y._power_gen()) - - parent = self.parent() - res = parent.sum_generator(self._compose_term(self.coefficient(i), y_powers) - for i in _integers_from(0)) - - for i in _integers_from(0): - yield res.coefficient(i) - - def _compose_term(self, p, y_powers): - r""" - Return the composition of one term in ``self`` with `y`. - - INPUT: - - - ``p`` - a term in ``self`` - - ``y_powers`` - a stream for the powers of `y` - starting with `y` - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: E = species.SetSpecies(); C = species.CycleSpecies() - sage: E_cis = E.cycle_index_series() - sage: C_cis = C.cycle_index_series() - sage: c_powers = Stream(C_cis._power_gen()) - sage: p2 = E_cis.coefficient(2); p2 - 1/2*p[1, 1] + 1/2*p[2] - sage: E_cis._compose_term(p2, c_powers).coefficients(4) - [0, 0, 1/2*p[1, 1] + 1/2*p[2], 1/2*p[1, 1, 1] + 1/2*p[2, 1]] - """ - parent = self.parent() - if p == 0: - return parent(0) - - res = [] - #Go through all the partition, coefficient pairs in the term p - for m, c in p: - res_t = parent.term(c, 0) - - for e,v in enumerate(m.to_exp()): - if v == 0: - continue - res_t = res_t * y_powers[v-1].stretch(e+1) - res.append(res_t) - - return parent.sum(res) - - def weighted_composition(self, y_species): - r""" - Return the composition of this cycle index series with the cycle - index series of the weighted species ``y_species``. - - Note that this is basically the same algorithm as composition - except we can not use the optimization that the powering of cycle - index series commutes with 'stretching'. - - EXAMPLES:: - - sage: E = species.SetSpecies(); C = species.CycleSpecies() - sage: E_cis = E.cycle_index_series() - sage: E_cis.weighted_composition(C).coefficients(4) - [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]] - sage: E(C).cycle_index_series().coefficients(4) - [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]] - """ - base_ring = self.base_ring() - y = y_species.cycle_index_series(base_ring) - assert y.coefficient(0) == 0 - return self._new(partial(self._weighted_compose_gen, y_species), lambda a,b:a*b, self, y) - - - def _weighted_compose_gen(self, y_species, ao): - r""" - Return an iterator for the composition of this cycle index series - and the cycle index series of the weighted species ``y_species``. - - EXAMPLES:: - - sage: E = species.SetSpecies(); C = species.CycleSpecies() - sage: E_cis = E.cycle_index_series() - sage: g = E_cis._weighted_compose_gen(C,0) - sage: [next(g) for i in range(4)] - [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]] - """ - parent = self.parent() - res = parent.sum_generator(self._weighted_compose_term(self.coefficient(i), y_species) - for i in _integers_from(0)) - - for i in _integers_from(0): - yield res.coefficient(i) - - def _weighted_compose_term(self, p, y_species): - r""" - Return the weighted composition of one term in ``self`` with ``y``. - - INPUT: - - - ``p`` -- a term in ``self`` - - ``y_species`` -- a species - - EXAMPLES:: - - sage: E = species.SetSpecies(); C = species.CycleSpecies() - sage: E_cis = E.cycle_index_series() - sage: p2 = E_cis.coefficient(2); p2 - 1/2*p[1, 1] + 1/2*p[2] - sage: E_cis._weighted_compose_term(p2, C).coefficients(4) - [0, 0, 1/2*p[1, 1] + 1/2*p[2], 1/2*p[1, 1, 1] + 1/2*p[2, 1]] - """ - parent = self.parent() - if p == 0: - return parent(0) - - base_ring = self.base_ring().base_ring() - - res = [] - #Go through all the partition, coefficient pairs in the term p - for m, c in p: - res_t = parent.term(c, 0) - - for e,v in enumerate(m.to_exp()): - if v == 0: - continue - res_t = res_t * (y_species.weighted(y_species._weight**(e+1)).cycle_index_series(base_ring)**v).stretch(e+1) - res.append(res_t) - - return parent.sum(res) - - def compositional_inverse(self): - r""" - Return the compositional inverse of ``self`` if possible. - - (Specifically, if ``self`` is of the form `0 + p_{1} + \cdots`.) - - The compositional inverse is the inverse with respect to - plethystic substitution. This is the operation on cycle index - series which corresponds to substitution, a.k.a. partitional - composition, on the level of species. See Section 2.2 of - [BLL]_ for a definition of this operation. - - EXAMPLES:: - - sage: Eplus = species.SetSpecies(min=1).cycle_index_series() - sage: Eplus(Eplus.compositional_inverse()).coefficients(8) - [0, p[1], 0, 0, 0, 0, 0, 0] - - TESTS:: - - sage: Eplus = species.SetSpecies(min=2).cycle_index_series() - sage: Eplus.compositional_inverse() - Traceback (most recent call last): - ... - ValueError: not an invertible series - - ALGORITHM: - - Let `F` be a species satisfying `F = 0 + X + F_2 + F_3 + \cdots` for - `X` the species of singletons. (Equivalently, `\lvert F[\varnothing] - \rvert = 0` and `\lvert F[\{1\}] \rvert = 1`.) Then there exists a - (virtual) species `G` satisfying `F \circ G = G \circ F = X`. - - It follows that `(F - X) \circ G = F \circ G - X \circ G = X - G`. - Rearranging, we obtain the recursive equation `G = X - (F - X) \circ G`, - which can be solved using iterative methods. - - .. WARNING:: - - This algorithm is functional but can be very slow. - Use with caution! - - .. SEEALSO:: - - The compositional inverse `\Omega` of the species `E_{+}` - of nonempty sets can be handled much more efficiently - using specialized methods. See - :func:`~sage.combinat.species.generating_series.LogarithmCycleIndexSeries` - - AUTHORS: - - - Andrew Gainer-Dewar - """ - cisr = self.parent() - sfa = cisr._base - - X = cisr([0, sfa([1]), 0]) - - if self.coefficients(2) != X.coefficients(2): - raise ValueError('not an invertible series') - - res = cisr() - res.define(X - (self - X).compose(res)) - - return res - - def derivative(self, order=1): - r""" - Return the species-theoretic `n`-th derivative of ``self``, - where `n` is ``order``. + Return the species-theoretic `n`-th derivative of ``self``. For a cycle index series `F (p_{1}, p_{2}, p_{3}, \ldots)`, its derivative is the cycle index series `F' = D_{p_{1}} F` (that is, @@ -1105,7 +409,7 @@ def derivative(self, order=1): The species `E` of sets satisfies the relationship `E' = E`:: sage: E = species.SetSpecies().cycle_index_series() - sage: E.coefficients(8) == E.derivative().coefficients(8) + sage: E[:8] == E.derivative()[:8] True The species `C` of cyclic orderings and the species `L` of linear @@ -1113,25 +417,10 @@ def derivative(self, order=1): sage: C = species.CycleSpecies().cycle_index_series() sage: L = species.LinearOrderSpecies().cycle_index_series() - sage: L.coefficients(8) == C.derivative().coefficients(8) + sage: L[:8] == C.derivative()[:8] True """ - # Make sure that order is integral - order = Integer(order) - - if order < 0: - raise ValueError("Order must be a non-negative integer") - - elif order == 0: - return self - - elif order == 1: - parent = self.parent() - derivative_term = lambda n: parent.term(self.coefficient(n+1).derivative_with_respect_to_p1(), n) - return parent.sum_generator(derivative_term(i) for i in _integers_from(0)) - - else: - return self.derivative(order-1) + return self.derivative_with_respect_to_p1(n=n) def pointing(self): r""" @@ -1151,42 +440,11 @@ def pointing(self): sage: E = species.SetSpecies().cycle_index_series() sage: X = species.SingletonSpecies().cycle_index_series() - sage: E.pointing().coefficients(8) == (X*E).coefficients(8) - True - - """ - p1 = self.base_ring()([1]) - X = self.parent()([0, p1, 0]) - - return X*self.derivative() - - def integral(self, *args): - r""" - Given a cycle index `G`, it is not in general possible to recover a - single cycle index `F` such that `F' = G` (even up to addition of a - constant term). - - More broadly, it may be the case that there are many non-isomorphic - species `S` such that `S' = T` for a given species `T`. - For example, the species `3 C_{3}` of 3-cycles from three distinct - classes and the species `X^{3}` of 3-sets are not isomorphic, but - `(3 C_{3})' = (X^{3})' = 3 X^{2}`. - - EXAMPLES:: - - sage: C3 = species.CycleSpecies(size=3).cycle_index_series() - sage: X = species.SingletonSpecies().cycle_index_series() - sage: (3*C3).derivative().coefficients(8) == (3*X^2).coefficients(8) + sage: E.pointing()[:8] == (X*E)[:8] True - sage: (X^3).derivative().coefficients(8) == (3*X^2).coefficients(8) - True - - .. WARNING:: - - This method has no implementation and exists only to prevent you from - doing something strange. Calling it raises a ``NotImplementedError``! """ - raise NotImplementedError + X = self.parent()([1], valuation=1) + return X * self.derivative_with_respect_to_p1() def exponential(self): r""" @@ -1206,12 +464,12 @@ def exponential(self): sage: BT = species.BinaryTreeSpecies().cycle_index_series() sage: BF = species.BinaryForestSpecies().cycle_index_series() - sage: BT.exponential().isotype_generating_series().coefficients(8) == BF.isotype_generating_series().coefficients(8) + sage: BT.exponential().isotype_generating_series()[:8] == BF.isotype_generating_series()[:8] True """ base_ring = self.parent().base_ring().base_ring() E = ExponentialCycleIndexSeries(base_ring) - return E.compose(self) + return E(self) def logarithm(self): r""" @@ -1233,45 +491,112 @@ def logarithm(self): sage: G = species.SimpleGraphSpecies().cycle_index_series() - 1 sage: from sage.combinat.species.generating_series import LogarithmCycleIndexSeries - sage: CG = LogarithmCycleIndexSeries().compose(G) - sage: CG.isotype_generating_series().coefficients(8) + sage: CG = LogarithmCycleIndexSeries()(G) + sage: CG.isotype_generating_series()[0:8] [0, 1, 1, 2, 6, 21, 112, 853] """ base_ring = self.parent().base_ring().base_ring() Omega = LogarithmCycleIndexSeries(base_ring) - return Omega.compose(self) + return Omega(self) -@cached_function -def _exp_term(n, R = RationalField()): - """ - Compute the order-n term of the cycle index series of the species `E` of sets. + +class CycleIndexSeriesRing(LazySymmetricFunctions): + r""" + Return the ring of cycle index series over ``R``. + + This is the ring of formal power series `\Lambda[x]`, where + `\Lambda` is the ring of symmetric functions over ``R`` in the + `p`-basis. Its purpose is to house the cycle index series of + species (in a somewhat nonstandard notation tailored to Sage): + If `F` is a species, then the *cycle index series* of `F` is + defined to be the formal power series + + .. MATH:: + + \sum_{n \geq 0} \frac{1}{n!} (\sum_{\sigma \in S_n} + \operatorname{fix} F[\sigma] + \prod_{z \text{ is a cycle of } \sigma} + p_{\text{length of } z}) x^n + \in \Lambda_\QQ [x], + + where `\operatorname{fix} F[\sigma]` denotes the number of + fixed points of the permutation `F[\sigma]` of `F[n]`. We + notice that this power series is "equigraded" (meaning that + its `x^n`-coefficient is homogeneous of degree `n`). A more + standard convention in combinatorics would be to use + `x_i` instead of `p_i`, and drop the `x` (that is, evaluate + the above power series at `x = 1`); but this would be more + difficult to implement in Sage, as it would be an element + of a power series ring in infinitely many variables. + + Note that it is just a :class:`LazyPowerSeriesRing` (whose base + ring is `\Lambda`) whose elements have some extra methods. EXAMPLES:: - sage: from sage.combinat.species.generating_series import _exp_term - sage: [_exp_term(i) for i in range(4)] - [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]] + sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing + sage: R = CycleIndexSeriesRing(QQ); R + Cycle Index Series Ring over Rational Field + sage: p = SymmetricFunctions(QQ).p() + sage: R(lambda n: p[n]) + p[] + p[1] + p[2] + p[3] + p[4] + p[5] + p[6] + O^7 + + TESTS: + + We test to make sure that caching works:: + + sage: R is CycleIndexSeriesRing(QQ) + True """ - p = SymmetricFunctions(R).power() - return sum(p(part) / part.aut() for part in Partitions(n)) + Element = CycleIndexSeries + + def __init__(self, base_ring, sparse=True): + """ + Initialize ``self``. + + TESTS:: + + sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing + sage: CycleIndexSeriesRing.options.halting_precision(12) + sage: R = CycleIndexSeriesRing(QQ) + sage: TestSuite(R).run() + + sage: CycleIndexSeriesRing.options._reset() # reset options + """ + p = SymmetricFunctions(base_ring).power() + super().__init__(p) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing + sage: CycleIndexSeriesRing(QQ) + Cycle Index Series Ring over Rational Field + """ + return "Cycle Index Series Ring over %s" % self.base_ring() -def _exp_gen(R = RationalField()): +@cached_function +def _exp_term(n, R=QQ): r""" - Produce a generator which yields the terms of the cycle index - series of the species `E` of sets. + Compute the order-``n`` term of the cycle index series of the species + `E` of sets. EXAMPLES:: - sage: from sage.combinat.species.generating_series import _exp_gen - sage: g = _exp_gen() - sage: [next(g) for i in range(4)] + sage: from sage.combinat.species.generating_series import _exp_term + sage: [_exp_term(i) for i in range(4)] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]] """ - return (_exp_term(i, R) for i in _integers_from(0)) + p = SymmetricFunctions(R).power() + return sum(p(part) / part.aut() for part in Partitions(n)) + @cached_function -def ExponentialCycleIndexSeries(R = RationalField()): +def ExponentialCycleIndexSeries(R=QQ): r""" Return the cycle index series of the species `E` of sets. @@ -1285,20 +610,21 @@ def ExponentialCycleIndexSeries(R = RationalField()): EXAMPLES:: sage: from sage.combinat.species.generating_series import ExponentialCycleIndexSeries - sage: ExponentialCycleIndexSeries().coefficients(5) + sage: ExponentialCycleIndexSeries()[:5] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3], 1/24*p[1, 1, 1, 1] + 1/4*p[2, 1, 1] + 1/8*p[2, 2] + 1/3*p[3, 1] + 1/4*p[4]] """ CIS = CycleIndexSeriesRing(R) - return CIS(_exp_gen(R)) + return CIS(_exp_term) @cached_function -def _cl_term(n, R = RationalField()): +def _cl_term(n, R=QQ): r""" - Compute the order-n term of the cycle index series of the virtual species - `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets. + Compute the order-``n`` term of the cycle index series of + the virtual species `\Omega`, the compositional inverse of + the species `E^{+}` of nonempty sets. EXAMPLES:: @@ -1319,24 +645,8 @@ def _cl_term(n, R = RationalField()): return res -def _cl_gen (R = RationalField()): - r""" - Produce a generator which yields the terms of the cycle index series - of the virtual species `\Omega`, the compositional inverse of the - species `E^{+}` of nonempty sets. - - EXAMPLES:: - - sage: from sage.combinat.species.generating_series import _cl_gen - sage: g = _cl_gen() - sage: [next(g) for i in range(4)] - [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] - """ - return (_cl_term(i, R) for i in _integers_from(0)) - - @cached_function -def LogarithmCycleIndexSeries(R = RationalField()): +def LogarithmCycleIndexSeries(R=QQ): r""" Return the cycle index series of the virtual species `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets. @@ -1351,7 +661,7 @@ def LogarithmCycleIndexSeries(R = RationalField()): its cycle index has negative coefficients:: sage: from sage.combinat.species.generating_series import LogarithmCycleIndexSeries - sage: LogarithmCycleIndexSeries().coefficients(4) + sage: LogarithmCycleIndexSeries()[0:4] [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] Its defining property is that `\Omega \circ E^{+} = E^{+} \circ \Omega = X` @@ -1359,8 +669,8 @@ def LogarithmCycleIndexSeries(R = RationalField()): multiplicative identity `X`):: sage: Eplus = sage.combinat.species.set_species.SetSpecies(min=1).cycle_index_series() - sage: LogarithmCycleIndexSeries().compose(Eplus).coefficients(4) + sage: LogarithmCycleIndexSeries()(Eplus)[0:4] [0, p[1], 0, 0] """ CIS = CycleIndexSeriesRing(R) - return CIS(_cl_gen(R)) + return CIS(_cl_term) diff --git a/src/sage/combinat/species/library.py b/src/sage/combinat/species/library.py index c107edbfa33..d4235273a9f 100644 --- a/src/sage/combinat/species/library.py +++ b/src/sage/combinat/species/library.py @@ -1,7 +1,7 @@ """ Examples of Combinatorial Species """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>, # # Distributed under the terms of the GNU General Public License (GPL) @@ -13,8 +13,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from .set_species import SetSpecies from .partition_species import PartitionSpecies @@ -32,23 +32,24 @@ from sage.misc.cachefunc import cached_function + @cached_function def SimpleGraphSpecies(): """ - Returns the species of simple graphs. + Return the species of simple graphs. EXAMPLES:: sage: S = species.SimpleGraphSpecies() sage: S.generating_series().counts(10) [1, 1, 2, 8, 64, 1024, 32768, 2097152, 268435456, 68719476736] - sage: S.cycle_index_series().coefficients(5) + sage: S.cycle_index_series()[:5] [p[], p[1], p[1, 1] + p[2], 4/3*p[1, 1, 1] + 2*p[2, 1] + 2/3*p[3], 8/3*p[1, 1, 1, 1] + 4*p[2, 1, 1] + 2*p[2, 2] + 4/3*p[3, 1] + p[4]] - sage: S.isotype_generating_series().coefficients(6) + sage: S.isotype_generating_series()[:6] [1, 1, 2, 4, 11, 34] TESTS:: @@ -66,18 +67,17 @@ def SimpleGraphSpecies(): E = SetSpecies() E2 = SetSpecies(size=2) WP = SubsetSpecies() - P2 = E2*E + P2 = E2 * E return WP.functorial_composition(P2) @cached_function def BinaryTreeSpecies(): r""" - Return the species of binary trees on n leaves. + Return the species of binary trees on `n` leaves. - The species of - binary trees B is defined by B = X + B\*B where X is the singleton - species. + The species of binary trees `B` is defined by `B = X + B \cdot B`, + where `X` is the singleton species. EXAMPLES:: @@ -103,16 +103,18 @@ def BinaryTreeSpecies(): sage: oeis(seq)[0] # optional -- internet A000108: Catalan numbers: ... """ - B = CombinatorialSpecies() + B = CombinatorialSpecies(min=1) X = SingletonSpecies() - B.define(X+B*B) + B.define(X + B * B) return B + @cached_function def BinaryForestSpecies(): """ - Returns the species of binary forests. Binary forests are defined - as sets of binary trees. + Return the species of binary forests. + + Binary forests are defined as sets of binary trees. EXAMPLES:: @@ -121,7 +123,7 @@ def BinaryForestSpecies(): [1, 1, 3, 19, 193, 2721, 49171, 1084483, 28245729, 848456353] sage: F.isotype_generating_series().counts(10) [1, 1, 2, 4, 10, 26, 77, 235, 758, 2504] - sage: F.cycle_index_series().coefficients(7) + sage: F.cycle_index_series()[:7] [p[], p[1], 3/2*p[1, 1] + 1/2*p[2], @@ -140,3 +142,6 @@ def BinaryForestSpecies(): S = SetSpecies() F = S(B) return F + + +del cached_function # so it doesn't get picked up by tab completion diff --git a/src/sage/combinat/species/linear_order_species.py b/src/sage/combinat/species/linear_order_species.py index 98c4f62eb44..3f8690da3c2 100644 --- a/src/sage/combinat/species/linear_order_species.py +++ b/src/sage/combinat/species/linear_order_species.py @@ -17,7 +17,6 @@ #***************************************************************************** from .species import GenericCombinatorialSpecies from .structure import GenericSpeciesStructure -from .generating_series import _integers_from from sage.structure.unique_representation import UniqueRepresentation from sage.combinat.species.misc import accept_size @@ -87,7 +86,7 @@ def __init__(self, min=None, max=None, weight=None): EXAMPLES:: sage: L = species.LinearOrderSpecies() - sage: L.generating_series().coefficients(5) + sage: L.generating_series()[0:5] [1, 1, 1, 1, 1] sage: L = species.LinearOrderSpecies() @@ -123,7 +122,7 @@ def _isotypes(self, structure_class, labels): """ yield structure_class(self, labels, range(1, len(labels)+1)) - def _gs_list(self, base_ring): + def _gs_list(self, base_ring, n): r""" The generating series for the species of linear orders is `\frac{1}{1-x}`. @@ -132,12 +131,12 @@ def _gs_list(self, base_ring): sage: L = species.LinearOrderSpecies() sage: g = L.generating_series() - sage: g.coefficients(10) + sage: g[0:10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - return [base_ring(1)] + return base_ring.one() - def _itgs_list(self, base_ring): + def _itgs_list(self, base_ring, n): r""" The isomorphism type generating series is given by `\frac{1}{1-x}`. @@ -146,25 +145,24 @@ def _itgs_list(self, base_ring): sage: L = species.LinearOrderSpecies() sage: g = L.isotype_generating_series() - sage: g.coefficients(10) + sage: g[0:10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - return [base_ring(1)] + return base_ring.one() - - def _cis_iterator(self, base_ring): + def _cis_callable(self, base_ring, n): """ EXAMPLES:: sage: L = species.LinearOrderSpecies() sage: g = L.cycle_index_series() - sage: g.coefficients(5) + sage: g[0:5] [p[], p[1], p[1, 1], p[1, 1, 1], p[1, 1, 1, 1]] """ from sage.combinat.sf.sf import SymmetricFunctions p = SymmetricFunctions(base_ring).power() - for n in _integers_from(0): - yield p([1]*n) + return p([1]*n) + #Backward compatibility LinearOrderSpecies_class = LinearOrderSpecies diff --git a/src/sage/combinat/species/misc.py b/src/sage/combinat/species/misc.py index 60436e72d23..f31a0939477 100644 --- a/src/sage/combinat/species/misc.py +++ b/src/sage/combinat/species/misc.py @@ -20,6 +20,7 @@ from sage.misc.misc_c import prod from functools import wraps + def change_support(perm, support, change_perm=None): """ Changes the support of a permutation defined on [1, ..., n] to diff --git a/src/sage/combinat/species/partition_species.py b/src/sage/combinat/species/partition_species.py index 7ba6ea84a13..7ddd9812ba1 100644 --- a/src/sage/combinat/species/partition_species.py +++ b/src/sage/combinat/species/partition_species.py @@ -17,7 +17,6 @@ #***************************************************************************** from .species import GenericCombinatorialSpecies -from .generating_series import _integers_from from sage.arith.misc import factorial from .subset_species import SubsetSpeciesStructure from .set_species import SetSpecies @@ -107,7 +106,6 @@ def automorphism_group(self): return reduce(lambda a,b: a.direct_product(b, maps=False), [SymmetricGroup(block._list) for block in self._list]) - def change_labels(self, labels): """ Return a relabelled structure. @@ -152,9 +150,9 @@ def __init__(self, min=None, max=None, weight=None): EXAMPLES:: sage: P = species.PartitionSpecies() - sage: P.generating_series().coefficients(5) + sage: P.generating_series()[0:5] [1, 1, 1, 5/6, 5/8] - sage: P.isotype_generating_series().coefficients(5) + sage: P.isotype_generating_series()[0:5] [1, 1, 2, 3, 5] sage: P = species.PartitionSpecies() @@ -233,20 +231,19 @@ def _canonical_rep_from_partition(self, structure_class, labels, p): breaks = [sum(p[:i]) for i in range(len(p) + 1)] return structure_class(self, labels, [list(range(breaks[i]+1, breaks[i+1]+1)) for i in range(len(p))]) - def _gs_iterator(self, base_ring): + def _gs_callable(self, base_ring, n): r""" EXAMPLES:: sage: P = species.PartitionSpecies() sage: g = P.generating_series() - sage: g.coefficients(5) + sage: [g.coefficient(i) for i in range(5)] [1, 1, 1, 5/6, 5/8] """ from sage.combinat.combinat import bell_number - for n in _integers_from(0): - yield self._weight * base_ring(bell_number(n) / factorial(n)) + return self._weight * base_ring(bell_number(n) / factorial(n)) - def _itgs_iterator(self, base_ring): + def _itgs_callable(self, base_ring, n): r""" The isomorphism type generating series is given by `\frac{1}{1-x}`. @@ -255,12 +252,11 @@ def _itgs_iterator(self, base_ring): sage: P = species.PartitionSpecies() sage: g = P.isotype_generating_series() - sage: g.coefficients(10) + sage: [g.coefficient(i) for i in range(10)] [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] """ from sage.combinat.partition import number_of_partitions - for n in _integers_from(0): - yield self._weight*base_ring(number_of_partitions(n)) + return self._weight*base_ring(number_of_partitions(n)) def _cis(self, series_ring, base_ring): r""" @@ -276,7 +272,7 @@ def _cis(self, series_ring, base_ring): sage: P = species.PartitionSpecies() sage: g = P.cycle_index_series() - sage: g.coefficients(5) + sage: g[0:5] [p[], p[1], p[1, 1] + p[2], @@ -284,10 +280,11 @@ def _cis(self, series_ring, base_ring): 5/8*p[1, 1, 1, 1] + 7/4*p[2, 1, 1] + 7/8*p[2, 2] + p[3, 1] + 3/4*p[4]] """ ciset = SetSpecies().cycle_index_series(base_ring) - res = ciset.composition(ciset - 1) + res = ciset(ciset - 1) if self.is_weighted(): res *= self._weight return res + #Backward compatibility PartitionSpecies_class = PartitionSpecies diff --git a/src/sage/combinat/species/permutation_species.py b/src/sage/combinat/species/permutation_species.py index d8ae5c216ea..9c34eeb9c98 100644 --- a/src/sage/combinat/species/permutation_species.py +++ b/src/sage/combinat/species/permutation_species.py @@ -18,12 +18,11 @@ from .species import GenericCombinatorialSpecies from .structure import GenericSpeciesStructure -from .generating_series import _integers_from from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.integer_ring import ZZ from sage.combinat.permutation import Permutation, Permutations from sage.combinat.species.misc import accept_size + class PermutationSpeciesStructure(GenericSpeciesStructure): def canonical_label(self): """ @@ -123,13 +122,13 @@ def __init__(self, min=None, max=None, weight=None): EXAMPLES:: sage: P = species.PermutationSpecies() - sage: P.generating_series().coefficients(5) + sage: P.generating_series()[0:5] [1, 1, 1, 1, 1] - sage: P.isotype_generating_series().coefficients(5) + sage: P.isotype_generating_series()[0:5] [1, 1, 2, 3, 5] sage: P = species.PermutationSpecies() - sage: c = P.generating_series().coefficients(3) + sage: c = P.generating_series()[0:3] sage: P._check() True sage: P == loads(dumps(P)) @@ -170,7 +169,6 @@ def _isotypes(self, structure_class, labels): for p in Partitions(len(labels)): yield self._canonical_rep_from_partition(structure_class, labels, p) - def _canonical_rep_from_partition(self, structure_class, labels, p): """ EXAMPLES:: @@ -185,8 +183,7 @@ def _canonical_rep_from_partition(self, structure_class, labels, p): perm = list(Permutation(cycles)) return structure_class(self, labels, perm) - - def _gs_list(self, base_ring): + def _gs_list(self, base_ring, n): r""" The generating series for the species of linear orders is `\frac{1}{1-x}`. @@ -195,13 +192,12 @@ def _gs_list(self, base_ring): sage: P = species.PermutationSpecies() sage: g = P.generating_series() - sage: g.coefficients(10) + sage: g[0:10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - return [base_ring(1)] + return base_ring.one() - - def _itgs_iterator(self, base_ring): + def _itgs_callable(self, base_ring, n): r""" The isomorphism type generating series is given by `\frac{1}{1-x}`. @@ -210,13 +206,11 @@ def _itgs_iterator(self, base_ring): sage: P = species.PermutationSpecies() sage: g = P.isotype_generating_series() - sage: g.coefficients(10) + sage: [g.coefficient(i) for i in range(10)] [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] """ from sage.combinat.partition import number_of_partitions - for n in _integers_from(0): - yield base_ring(number_of_partitions(n)) - + return base_ring(number_of_partitions(n)) def _cis(self, series_ring, base_ring): r""" @@ -226,43 +220,46 @@ def _cis(self, series_ring, base_ring): \prod{n=1}^\infty \frac{1}{1-x_n}. - - EXAMPLES:: sage: P = species.PermutationSpecies() sage: g = P.cycle_index_series() - sage: g.coefficients(5) + sage: g[0:5] [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3], p[1, 1, 1, 1] + p[2, 1, 1] + p[2, 2] + p[3, 1] + p[4]] """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.partition import Partitions + p = SymmetricFunctions(base_ring).p() CIS = series_ring - return CIS.product_generator( CIS(self._cis_gen(base_ring, i)) for i in _integers_from(ZZ(1)) ) + return CIS(lambda n: sum(p(la) for la in Partitions(n))) - def _cis_gen(self, base_ring, n): + def _cis_gen(self, base_ring, m, n): """ EXAMPLES:: sage: P = species.PermutationSpecies() - sage: g = P._cis_gen(QQ, 2) - sage: [next(g) for i in range(10)] + sage: [P._cis_gen(QQ, 2, i) for i in range(10)] [p[], 0, p[2], 0, p[2, 2], 0, p[2, 2, 2], 0, p[2, 2, 2, 2], 0] """ from sage.combinat.sf.sf import SymmetricFunctions p = SymmetricFunctions(base_ring).power() - pn = p([n]) + pn = p([m]) - n = n - 1 - yield p(1) + if not n: + return p(1) + if m == 1: + if n % 2: + return base_ring.zero() + return pn**(n//2) + elif n % m: + return base_ring.zero() + return pn**(n//m) - for k in _integers_from(1): - for i in range(n): - yield base_ring(0) - yield pn**k #Backward compatibility PermutationSpecies_class = PermutationSpecies diff --git a/src/sage/combinat/species/product_species.py b/src/sage/combinat/species/product_species.py index 0f7496a7dca..74004849b79 100644 --- a/src/sage/combinat/species/product_species.py +++ b/src/sage/combinat/species/product_species.py @@ -206,7 +206,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: X = species.SingletonSpecies() sage: A = X*X - sage: A.generating_series().coefficients(4) + sage: A.generating_series()[0:4] [0, 0, 1, 0] sage: P = species.PermutationSpecies() @@ -324,7 +324,7 @@ def _gs(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P * P - sage: F.generating_series().coefficients(5) + sage: F.generating_series()[0:5] [1, 2, 3, 4, 5] """ res = (self.left_factor().generating_series(base_ring) * @@ -339,7 +339,7 @@ def _itgs(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P * P - sage: F.isotype_generating_series().coefficients(5) + sage: F.isotype_generating_series()[0:5] [1, 2, 5, 10, 20] """ res = (self.left_factor().isotype_generating_series(base_ring) * @@ -354,7 +354,7 @@ def _cis(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P * P - sage: F.cycle_index_series().coefficients(5) + sage: F.cycle_index_series()[0:5] [p[], 2*p[1], 3*p[1, 1] + 2*p[2], diff --git a/src/sage/combinat/species/recursive_species.py b/src/sage/combinat/species/recursive_species.py index d6023264808..a361bdfad20 100644 --- a/src/sage/combinat/species/recursive_species.py +++ b/src/sage/combinat/species/recursive_species.py @@ -25,7 +25,7 @@ class CombinatorialSpeciesStructure(SpeciesStructureWrapper): class CombinatorialSpecies(GenericCombinatorialSpecies): - def __init__(self): + def __init__(self, min=None): """ EXAMPLES:: @@ -39,19 +39,16 @@ def __init__(self): sage: E = species.EmptySetSpecies() sage: L = CombinatorialSpecies() sage: L.define(E+X*L) - sage: L.generating_series().coefficients(4) + sage: L.generating_series()[0:4] [1, 1, 1, 1] sage: LL = loads(dumps(L)) - sage: LL.generating_series().coefficients(4) + sage: LL.generating_series()[0:4] [1, 1, 1, 1] """ self._generating_series = {} self._isotype_generating_series = {} self._cycle_index_series = {} - self._min = None - self._max = None - self._weight = 1 - GenericCombinatorialSpecies.__init__(self, min=None, max=None, weight=None) + GenericCombinatorialSpecies.__init__(self, min=min, max=None, weight=None) _default_structure_class = CombinatorialSpeciesStructure @@ -233,10 +230,10 @@ def _gs(self, series_ring, base_ring): sage: F = CombinatorialSpecies() sage: F.generating_series() - Uninitialized lazy power series + Uninitialized Lazy Laurent Series """ if base_ring not in self._generating_series: - self._generating_series[base_ring] = series_ring() + self._generating_series[base_ring] = series_ring.undefined(valuation=(0 if self._min is None else self._min)) res = self._generating_series[base_ring] if hasattr(self, "_reference") and not hasattr(res, "_reference"): @@ -250,10 +247,10 @@ def _itgs(self, series_ring, base_ring): sage: F = CombinatorialSpecies() sage: F.isotype_generating_series() - Uninitialized lazy power series + Uninitialized Lazy Laurent Series """ if base_ring not in self._isotype_generating_series: - self._isotype_generating_series[base_ring] = series_ring() + self._isotype_generating_series[base_ring] = series_ring.undefined(valuation=(0 if self._min is None else self._min)) res = self._isotype_generating_series[base_ring] if hasattr(self, "_reference") and not hasattr(res, "_reference"): @@ -267,10 +264,10 @@ def _cis(self, series_ring, base_ring): sage: F = CombinatorialSpecies() sage: F.cycle_index_series() - Uninitialized lazy power series + Uninitialized Lazy Laurent Series """ if base_ring not in self._cycle_index_series: - self._cycle_index_series[base_ring] = series_ring() + self._cycle_index_series[base_ring] = series_ring.undefined(valuation=(0 if self._min is None else self._min)) res = self._cycle_index_series[base_ring] if hasattr(self, "_reference") and not hasattr(res, "_reference"): @@ -325,7 +322,7 @@ def define(self, x): sage: E = species.EmptySetSpecies() sage: L = CombinatorialSpecies() sage: L.define(E+X*L) - sage: L.generating_series().coefficients(4) + sage: L.generating_series()[0:4] [1, 1, 1, 1] sage: L.structures([1,2,3]).cardinality() 6 @@ -340,7 +337,7 @@ def define(self, x): :: sage: L = species.LinearOrderSpecies() - sage: L.generating_series().coefficients(4) + sage: L.generating_series()[0:4] [1, 1, 1, 1] sage: L.structures([1,2,3]).cardinality() 6 @@ -351,28 +348,28 @@ def define(self, x): sage: A = CombinatorialSpecies() sage: A.define(E+X*A*A) - sage: A.generating_series().coefficients(6) + sage: A.generating_series()[0:6] [1, 1, 2, 5, 14, 42] sage: A.generating_series().counts(6) [1, 1, 4, 30, 336, 5040] sage: len(A.structures([1,2,3,4]).list()) 336 - sage: A.isotype_generating_series().coefficients(6) + sage: A.isotype_generating_series()[0:6] [1, 1, 2, 5, 14, 42] sage: len(A.isotypes([1,2,3,4]).list()) 14 :: - sage: A = CombinatorialSpecies() + sage: A = CombinatorialSpecies(min=1) sage: A.define(X+A*A) - sage: A.generating_series().coefficients(6) + sage: A.generating_series()[0:6] [0, 1, 1, 2, 5, 14] sage: A.generating_series().counts(6) [0, 1, 2, 12, 120, 1680] sage: len(A.structures([1,2,3]).list()) 12 - sage: A.isotype_generating_series().coefficients(6) + sage: A.isotype_generating_series()[0:6] [0, 1, 1, 2, 5, 14] sage: len(A.isotypes([1,2,3,4]).list()) 5 @@ -381,19 +378,17 @@ def define(self, x): sage: X2 = X*X sage: X5 = X2*X2*X - sage: A = CombinatorialSpecies() - sage: B = CombinatorialSpecies() - sage: C = CombinatorialSpecies() + sage: A = CombinatorialSpecies(min=1) + sage: B = CombinatorialSpecies(min=1) + sage: C = CombinatorialSpecies(min=1) sage: A.define(X5+B*B) sage: B.define(X5+C*C) sage: C.define(X2+C*C+A*A) - sage: A.generating_series().coefficients(Integer(10)) - [0, 0, 0, 0, 0, 1, 0, 0, 1, 2] - sage: A.generating_series().coefficients(15) + sage: A.generating_series()[0:15] [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] - sage: B.generating_series().coefficients(15) + sage: B.generating_series()[0:15] [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] - sage: C.generating_series().coefficients(15) + sage: C.generating_series()[0:15] [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] :: @@ -402,9 +397,9 @@ def define(self, x): sage: F.define(E+X+(X*F+X*X*F)) sage: F.generating_series().counts(10) [1, 2, 6, 30, 192, 1560, 15120, 171360, 2217600, 32296320] - sage: F.generating_series().coefficients(10) + sage: F.generating_series()[0:10] [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] - sage: F.isotype_generating_series().coefficients(10) + sage: F.isotype_generating_series()[0:10] [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] """ if not isinstance(x, GenericCombinatorialSpecies): @@ -415,7 +410,6 @@ def define(self, x): self._reference = x - def _add_to_digraph(self, d): """ Adds this species as a vertex to the digraph d along with any diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py deleted file mode 100644 index 37e3e6e19ed..00000000000 --- a/src/sage/combinat/species/series.py +++ /dev/null @@ -1,1832 +0,0 @@ -""" -Lazy Power Series - -This file provides an implementation of lazy univariate power -series, which uses the stream class for its internal data -structure. The lazy power series keep track of their approximate -order as much as possible without forcing the computation of any -additional coefficients. This is required for recursively defined -power series. - -This code is based on the work of Ralf Hemmecke and Martin Rubey's -Aldor-Combinat, which can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/aldor/combinat/index.html. -In particular, the relevant section for this file can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/AldorCombinat/combinatse9.html. -""" -# **************************************************************************** -# Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>, -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code 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. -# -# The full text of the GPL is available at: -# -# https://www.gnu.org/licenses/ -# **************************************************************************** - -import builtins - -from collections.abc import Iterable -from .stream import Stream, Stream_class -from .series_order import bounded_decrement, increment, inf, unk -from sage.rings.integer import Integer -from sage.misc.misc_c import prod -from functools import partial -from sage.misc.misc import is_iterator -from sage.misc.repr import repr_lincomb -from sage.misc.cachefunc import cached_method - -from sage.rings.ring import Algebra -from sage.structure.parent import Parent -from sage.categories.all import Rings -from sage.structure.element import Element, parent, AlgebraElement - - -class LazyPowerSeriesRing(Algebra): - def __init__(self, R, names=None, element_class=None): - """ - TESTS:: - - sage: from sage.combinat.species.series import LazyPowerSeriesRing - sage: L = LazyPowerSeriesRing(QQ) - - Equality testing is undecidable in general, and not much - efforts are done at this stage to implement equality when - possible. Hence the failing tests below:: - - sage: TestSuite(L).run() - Failure in ... - The following tests failed: _test_additive_associativity, _test_associativity, _test_distributivity, _test_elements, _test_one, _test_prod, _test_zero - - :: - - sage: LazyPowerSeriesRing(QQ, 'z').gen() - z - sage: LazyPowerSeriesRing(QQ, ['z']).gen() - z - sage: LazyPowerSeriesRing(QQ, ['x', 'z']) - Traceback (most recent call last): - ... - NotImplementedError: only univariate lazy power series rings are supported - """ - #Make sure R is a ring with unit element - if R not in Rings(): - raise TypeError("argument R must be a ring") - - #Take care of the names - if names is None: - names = 'x' - elif isinstance(names, (list, tuple)): - if len(names) != 1: - raise NotImplementedError( - 'only univariate lazy power series rings are supported') - names = names[0] - else: - names = str(names) - - self._element_class = element_class if element_class is not None else LazyPowerSeries - self._order = None - self._name = names - self._zero_base_ring = R.zero() - Parent.__init__(self, R, category=Rings()) - - def ngens(self): - """ - EXAMPLES:: - - sage: LazyPowerSeriesRing(QQ).ngens() - 1 - """ - return 1 - - def __repr__(self): - """ - EXAMPLES:: - - sage: LazyPowerSeriesRing(QQ) - Lazy Power Series Ring over Rational Field - """ - return "Lazy Power Series Ring over %s" % self.base_ring() - - def __eq__(self, x): - """ - Check whether ``self`` is equal to ``x``. - - EXAMPLES:: - - sage: LQ = LazyPowerSeriesRing(QQ) - sage: LZ = LazyPowerSeriesRing(ZZ) - sage: LQ == LQ - True - sage: LZ == LQ - False - """ - if not isinstance(x, LazyPowerSeriesRing): - return False - return self.base_ring() == x.base_ring() - - def __ne__(self, other): - """ - Check whether ``self`` is not equal to ``other``. - - EXAMPLES:: - - sage: LQ = LazyPowerSeriesRing(QQ) - sage: LZ = LazyPowerSeriesRing(ZZ) - sage: LQ != LQ - False - sage: LZ != LQ - True - """ - return not (self == other) - - def __hash__(self): - """ - Return the hash of ``self``. - - EXAMPLES:: - - sage: LQ = LazyPowerSeriesRing(QQ) - sage: LZ = LazyPowerSeriesRing(ZZ) - sage: hash(LQ) == hash(LQ) - True - sage: hash(LZ) == hash(LQ) - False - """ - # with a random number, so that the hash is not that of the base ring - return hash((16079305, self.base_ring())) - - def _coerce_impl(self, x): - """ - EXAMPLES:: - - sage: L1 = LazyPowerSeriesRing(QQ) - sage: L2 = LazyPowerSeriesRing(RR) - sage: L2.has_coerce_map_from(L1) - True - sage: L1.has_coerce_map_from(L2) - False - - :: - - sage: a = L1([1]) + L2([1]) - sage: a.coefficients(3) - [2.00000000000000, 2.00000000000000, 2.00000000000000] - """ - return self(x) - - def __call__(self, x=None, order=unk): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: L() - Uninitialized lazy power series - sage: L(1) - 1 - sage: L(ZZ).coefficients(10) - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5] - sage: L(iter(ZZ)).coefficients(10) - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5] - sage: L(Stream(ZZ)).coefficients(10) - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5] - - :: - - sage: a = L([1,2,3]) - sage: a.coefficients(3) - [1, 2, 3] - sage: L(a) is a - True - sage: L_RR = LazyPowerSeriesRing(RR) - sage: b = L_RR(a) - sage: b.coefficients(3) - [1.00000000000000, 2.00000000000000, 3.00000000000000] - sage: L(b) - Traceback (most recent call last): - ... - TypeError: do not know how to coerce ... into self - - TESTS:: - - sage: L(pi) - Traceback (most recent call last): - ... - TypeError: do not know how to coerce pi into self - """ - cls = self._element_class - BR = self.base_ring() - - if x is None: - res = cls(self, stream=None, order=unk, aorder=unk, - aorder_changed=True, is_initialized=False) - res.compute_aorder = uninitialized - return res - - if isinstance(x, LazyPowerSeries): - x_parent = x.parent() - if x_parent.__class__ != self.__class__: - raise ValueError - - if x_parent.base_ring() == self.base_ring(): - return x - else: - if self.base_ring().has_coerce_map_from(x_parent.base_ring()): - return x._new(partial(x._change_ring_gen, self.base_ring()), lambda ao: ao, x, parent=self) - - - if BR.has_coerce_map_from(parent(x)): - x = BR(x) - return self.term(x, 0) - - if isinstance(x, Iterable) and not isinstance(x, Stream_class): - x = iter(x) - - if is_iterator(x): - x = Stream(x) - - if isinstance(x, Stream_class): - aorder = order if order != unk else 0 - return cls(self, stream=x, order=order, aorder=aorder, - aorder_changed=False, is_initialized=True) - elif not isinstance(x, Element): - x = BR(x) - return self.term(x, 0) - - raise TypeError("do not know how to coerce %s into self" % x) - - @cached_method - def zero(self): - """ - Return the zero power series. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: L.zero() - 0 - """ - return self.term(self._zero_base_ring, 0) - - def identity_element(self): - """ - Return the one power series. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: L.identity_element() - 1 - """ - return self(self.base_ring()(1)) - - def gen(self, i=0): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: L.gen().coefficients(5) - [0, 1, 0, 0, 0] - """ - res = self._new_initial(1, Stream([0,1,0])) - res._name = self._name - return res - - def term(self, r, n): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: L.term(0,0) - 0 - sage: L.term(3,2).coefficients(5) - [0, 0, 3, 0, 0] - """ - if n < 0: - raise ValueError("n must be non-negative") - BR = self.base_ring() - if r == 0: - res = self._new_initial(inf, Stream([0])) - res._name = "0" - else: - zero = BR(0) - s = [zero]*n+[BR(r),zero] - res = self._new_initial(n, Stream(s)) - - if n == 0: - res._name = repr(r) - elif n == 1: - res._name = repr(r) + "*" + self._name - else: - res._name = "%s*%s^%s" % (repr(r), self._name, n) - - return res - - def _new_initial(self, order, stream): - """ - Return a new power series with specified order. - - INPUT: - - - - ``order`` - a non-negative integer - - - ``stream`` - a Stream object - - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: L._new_initial(0, Stream([1,2,3,0])).coefficients(5) - [1, 2, 3, 0, 0] - """ - return self._element_class(self, stream=stream, order=order, aorder=order, - aorder_changed=False, is_initialized=True) - - - def _sum_gen(self, series_list): - """ - Return a generator for the coefficients of the sum of the lazy - power series in series_list. - - INPUT: - - - - ``series_list`` - a list of lazy power series - - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: series_list = [ L([1]), L([0,1]), L([0,0,1]) ] - sage: g = L._sum_gen(series_list) - sage: [next(g) for i in range(5)] - [1, 2, 3, 3, 3] - """ - last_index = len(series_list) - 1 - assert last_index >= 0 - n = 0 - while True: - r = sum( [f.coefficient(n) for f in series_list] ) - yield r - n += 1 - - def sum(self, a): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: l = [L(ZZ)]*3 - sage: L.sum(l).coefficients(10) - [0, 3, -3, 6, -6, 9, -9, 12, -12, 15] - """ - return self( self._sum_gen(a) ) - - #Potentially infinite sum - def _sum_generator_gen(self, g): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L([1]) - sage: def f(): - ....: while True: - ....: yield s - sage: g = L._sum_generator_gen(f()) - sage: [next(g) for i in range(10)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - """ - s = Stream(g) - n = 0 - while True: - r = s[n].coefficient(n) - for i in range(len(s)-1): - r += s[i].coefficient(n) - yield r - n += 1 - - def sum_generator(self, g): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: g = [L([1])]*6 + [L(0)] - sage: t = L.sum_generator(g) - sage: t.coefficients(10) - [1, 2, 3, 4, 5, 6, 6, 6, 6, 6] - - :: - - sage: s = L([1]) - sage: def g(): - ....: while True: - ....: yield s - sage: t = L.sum_generator(g()) - sage: t.coefficients(9) - [1, 2, 3, 4, 5, 6, 7, 8, 9] - """ - return self(self._sum_generator_gen(g)) - - #Potentially infinite product - def _product_generator_gen(self, g): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import _integers_from - sage: L = LazyPowerSeriesRing(QQ) - sage: g = (L([1]+[0]*i+[1]) for i in _integers_from(0)) - sage: g2 = L._product_generator_gen(g) - sage: [next(g2) for i in range(10)] - [1, 1, 2, 4, 7, 12, 20, 33, 53, 84] - """ - z = next(g) - yield z.coefficient(0) - yield z.coefficient(1) - - n = 2 - - for x in g: - z = z * x - yield z.coefficient(n) - n += 1 - - while True: - yield z.coefficient(n) - n += 1 - - def product_generator(self, g): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s1 = L([1,1,0]) - sage: s2 = L([1,0,1,0]) - sage: s3 = L([1,0,0,1,0]) - sage: s4 = L([1,0,0,0,1,0]) - sage: s5 = L([1,0,0,0,0,1,0]) - sage: s6 = L([1,0,0,0,0,0,1,0]) - sage: s = [s1, s2, s3, s4, s5, s6] - sage: def g(): - ....: for a in s: - ....: yield a - sage: p = L.product_generator(g()) - sage: p.coefficients(26) - [1, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0] - - :: - - sage: def m(n): - ....: yield 1 - ....: while True: - ....: for i in range(n-1): - ....: yield 0 - ....: yield 1 - sage: def s(n): - ....: q = 1/n - ....: yield 0 - ....: while True: - ....: for i in range(n-1): - ....: yield 0 - ....: yield q - - :: - - sage: def lhs_gen(): - ....: n = 1 - ....: while True: - ....: yield L(m(n)) - ....: n += 1 - - :: - - sage: def rhs_gen(): - ....: n = 1 - ....: while True: - ....: yield L(s(n)) - ....: n += 1 - sage: lhs = L.product_generator(lhs_gen()) - sage: rhs = L.sum_generator(rhs_gen()).exponential() - sage: lhs.coefficients(10) - [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] - sage: rhs.coefficients(10) - [1, 1, 2, 3, 5, 7, 11, 15, 22, 30] - """ - return self(self._product_generator_gen(g)) - - - -class LazyPowerSeries(AlgebraElement): - def __init__(self, A, stream=None, order=None, aorder=None, aorder_changed=True, is_initialized=False, name=None): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L() - sage: loads(dumps(f)) - Uninitialized lazy power series - """ - AlgebraElement.__init__(self, A) - self._stream = stream - self.order = unk if order is None else order - self.aorder = unk if aorder is None else aorder - if self.aorder == inf: - self.order = inf - self.aorder_changed = aorder_changed - self.is_initialized = is_initialized - self._name = name - - def compute_aorder(*args, **kwargs): - """ - The default compute_aorder does nothing. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L(1) - sage: a.compute_aorder() is None - True - """ - return None - - def _get_repr_info(self, x): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([1,2,3]) - sage: a.compute_coefficients(5) - sage: a._get_repr_info('x') - [('1', 1), ('x', 2), ('x^2', 3)] - """ - n = len(self._stream) - m = ['1', x] - m += [x + "^" + str(i) for i in range(2, n)] - c = [self._stream[i] for i in range(n)] - return [(mo, co) for mo, co in zip(m, c) if co != 0] - - def __repr__(self): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L(); s._name = 's'; s - s - - :: - - sage: L() - Uninitialized lazy power series - - :: - - sage: a = L([1,2,3]) - sage: a - O(1) - sage: a.compute_coefficients(2) - sage: a - 1 + 2*x + 3*x^2 + O(x^3) - sage: a.compute_coefficients(4) - sage: a - 1 + 2*x + 3*x^2 + 3*x^3 + 3*x^4 + 3*x^5 + ... - - :: - - sage: a = L([1,2,3,0]) - sage: a.compute_coefficients(5) - sage: a - 1 + 2*x + 3*x^2 - """ - if self._name is not None: - return self._name - - if self.is_initialized: - n = len(self._stream) - x = self.parent()._name - baserepr = repr_lincomb(self._get_repr_info(x)) - if self._stream.is_constant(): - if self._stream[n-1] == 0: - l = baserepr - else: - l = baserepr + " + " + repr_lincomb([(x+"^"+str(i), self._stream[n-1]) for i in range(n, n+3)]) + " + ..." - else: - l = baserepr + " + O(x^%s)" % n if n > 0 else "O(1)" - else: - l = 'Uninitialized lazy power series' - return l - - - def refine_aorder(self): - """ - Refines the approximate order of self as much as possible without - computing any coefficients. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([0,0,0,0,1]) - sage: a.aorder - 0 - sage: a.coefficient(2) - 0 - sage: a.aorder - 0 - sage: a.refine_aorder() - sage: a.aorder - 3 - - :: - - sage: a = L([0,0]) - sage: a.aorder - 0 - sage: a.coefficient(5) - 0 - sage: a.refine_aorder() - sage: a.aorder - Infinite series order - - :: - - sage: a = L([0,0,1,0,0,0]) - sage: a[4] - 0 - sage: a.refine_aorder() - sage: a.aorder - 2 - """ - #If we already know the order, then we don't have - #to worry about the approximate order - if self.order != unk: - return - - #aorder can never be infinity since order would have to - #be infinity as well - assert self.aorder != inf - - if self.aorder == unk or not self.is_initialized: - self.compute_aorder() - else: - #Try to improve the approximate order - ao = self.aorder - c = self._stream - n = c.number_computed() - - - if ao == 0 and n > 0: - while ao < n: - if self._stream[ao] == 0: - self.aorder += 1 - ao += 1 - else: - break - - #Try to recognize the zero series - if ao == n: - #For non-constant series, we cannot do anything - if not c.is_constant(): - return - if c[n-1] == 0: - self.aorder = inf - self.order = inf - return - - if ao < n: - self.order = ao - - - if hasattr(self, '_reference') and self._reference is not None: - self._reference._copy(self) - - def initialize_coefficient_stream(self, compute_coefficients): - """ - Initializes the coefficient stream. - - INPUT: compute_coefficients - - TESTS:: - - sage: from sage.combinat.species.series_order import inf, unk - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L() - sage: compute_coefficients = lambda ao: iter(ZZ) - sage: f.order = inf - sage: f.aorder = inf - sage: f.initialize_coefficient_stream(compute_coefficients) - sage: f.coefficients(5) - [0, 0, 0, 0, 0] - - :: - - sage: f = L() - sage: compute_coefficients = lambda ao: iter(ZZ) - sage: f.order = 1 - sage: f.aorder = 1 - sage: f.initialize_coefficient_stream(compute_coefficients) - sage: f.coefficients(5) - [0, 1, -1, 2, -2] - """ - ao = self.aorder - assert ao != unk - - if ao == inf: - self.order = inf - self._stream = Stream(0) - else: - self._stream = Stream(compute_coefficients(ao)) - - self.is_initialized = True - - def compute_coefficients(self, i): - """ - Computes all the coefficients of self up to i. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([1,2,3]) - sage: a.compute_coefficients(5) - sage: a - 1 + 2*x + 3*x^2 + 3*x^3 + 3*x^4 + 3*x^5 + ... - """ - self.coefficient(i) - - def coefficients(self, n): - """ - Return the first n coefficients of self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1,2,3,0]) - sage: f.coefficients(5) - [1, 2, 3, 0, 0] - """ - return [self.coefficient(i) for i in range(n)] - - def is_zero(self): - """ - Return True if and only if self is zero. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L([0,2,3,0]) - sage: s.is_zero() - False - - :: - - sage: s = L(0) - sage: s.is_zero() - True - - :: - - sage: s = L([0]) - sage: s.is_zero() - False - sage: s.coefficient(0) - 0 - sage: s.coefficient(1) - 0 - sage: s.is_zero() - True - """ - self.refine_aorder() - return self.order == inf - - def set_approximate_order(self, new_order): - """ - Sets the approximate order of self and returns True if the - approximate order has changed otherwise it will return False. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([0,0,0,3,2,1,0]) - sage: f.get_aorder() - 0 - sage: f.set_approximate_order(3) - True - sage: f.set_approximate_order(3) - False - """ - self.aorder_changed = ( self.aorder != new_order ) - self.aorder = new_order - return self.aorder_changed - - def _copy(self, x): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L.term(2, 2) - sage: g = L() - sage: g._copy(f) - sage: g.order - 2 - sage: g.aorder - 2 - sage: g.is_initialized - True - sage: g.coefficients(4) - [0, 0, 2, 0] - """ - self.order = x.order - self.aorder = x.aorder - self.aorder_changed = x.aorder_changed - self.compute_aorder = x.compute_aorder - self.is_initialized = x.is_initialized - self._stream = x._stream - - def define(self, x): - """ - EXAMPLES: Test Recursive 0 - - :: - - sage: L = LazyPowerSeriesRing(QQ) - sage: one = L(1) - sage: monom = L.gen() - sage: s = L() - sage: s._name = 's' - sage: s.define(one+monom*s) - sage: s.aorder - 0 - sage: s.order - Unknown series order - sage: [s.coefficient(i) for i in range(6)] - [1, 1, 1, 1, 1, 1] - - Test Recursive 1 - - :: - - sage: s = L() - sage: s._name = 's' - sage: s.define(one+monom*s*s) - sage: s.aorder - 0 - sage: s.order - Unknown series order - sage: [s.coefficient(i) for i in range(6)] - [1, 1, 2, 5, 14, 42] - - Test Recursive 1b - - :: - - sage: s = L() - sage: s._name = 's' - sage: s.define(monom + s*s) - sage: s.aorder - 1 - sage: s.order - Unknown series order - sage: [s.coefficient(i) for i in range(7)] - [0, 1, 1, 2, 5, 14, 42] - - Test Recursive 2 - - :: - - sage: s = L() - sage: s._name = 's' - sage: t = L() - sage: t._name = 't' - sage: s.define(one+monom*t*t*t) - sage: t.define(one+monom*s*s) - sage: [s.coefficient(i) for i in range(9)] - [1, 1, 3, 9, 34, 132, 546, 2327, 10191] - sage: [t.coefficient(i) for i in range(9)] - [1, 1, 2, 7, 24, 95, 386, 1641, 7150] - - Test Recursive 2b - - :: - - sage: s = L() - sage: s._name = 's' - sage: t = L() - sage: t._name = 't' - sage: s.define(monom + t*t*t) - sage: t.define(monom + s*s) - sage: [s.coefficient(i) for i in range(9)] - [0, 1, 0, 1, 3, 3, 7, 30, 63] - sage: [t.coefficient(i) for i in range(9)] - [0, 1, 1, 0, 2, 6, 7, 20, 75] - - Test Recursive 3 - - :: - - sage: s = L() - sage: s._name = 's' - sage: s.define(one+monom*s*s*s) - sage: [s.coefficient(i) for i in range(10)] - [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] - """ - self._copy(x) - x._reference = self - - def coefficient(self, n): - """ - Return the coefficient of xn in self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L(ZZ) - sage: [f.coefficient(i) for i in range(5)] - [0, 1, -1, 2, -2] - """ - # The following line must not be written n < self.get_aorder() - # because comparison of Integer and OnfinityOrder is not implemented. - if self.get_aorder() > n: - return self.parent()._zero_base_ring - - assert self.is_initialized - - return self._stream[n] - - def get_aorder(self): - """ - Return the approximate order of self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L.gen() - sage: a.get_aorder() - 1 - """ - self.refine_aorder() - return self.aorder - - def get_order(self): - """ - Return the order of self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L.gen() - sage: a.get_order() - 1 - """ - self.refine_aorder() - return self.order - - def get_stream(self): - """ - Return self's underlying Stream object. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L.gen() - sage: s = a.get_stream() - sage: [s[i] for i in range(5)] - [0, 1, 0, 0, 0] - """ - self.refine_aorder() - return self._stream - - def _approximate_order(self, compute_coefficients, new_order, *series): - if self.is_initialized: - return - - ochanged = self.aorder_changed - - ao = new_order(*[s.aorder for s in series]) - ao = inf if ao == unk else ao - - tchanged = self.set_approximate_order(ao) - - if len(series) == 0: - must_initialize_coefficient_stream = True - tchanged = ochanged = False - elif len(series) == 1 or len(series) == 2: - must_initialize_coefficient_stream = ( self.aorder == unk or self.is_initialized is False) - else: - raise ValueError - - if ochanged or tchanged: - for s in series: - s.compute_aorder() - ao = new_order(*[s.aorder for s in series]) - tchanged = self.set_approximate_order(ao) - - if must_initialize_coefficient_stream: - self.initialize_coefficient_stream(compute_coefficients) - - if hasattr(self, '_reference') and self._reference is not None: - self._reference._copy(self) - - def _new(self, compute_coefficients, order_op, *series, **kwds): - parent = kwds['parent'] if 'parent' in kwds else self.parent() - new_fps = self.__class__(parent, stream=None, order=unk, aorder=self.aorder, - aorder_changed=True, is_initialized=False) - - new_fps.compute_aorder = lambda: new_fps._approximate_order(compute_coefficients, order_op, *series) - return new_fps - - def _add_(self, y): - """ - EXAMPLES: Test Plus 1 - - :: - - sage: from sage.combinat.species.series import * - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: gs0 = L([0]) - sage: gs1 = L([1]) - sage: sum1 = gs0 + gs1 - sage: sum2 = gs1 + gs1 - sage: sum3 = gs1 + gs0 - sage: [gs0.coefficient(i) for i in range(11)] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - sage: [gs1.coefficient(i) for i in range(11)] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: [sum1.coefficient(i) for i in range(11)] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: [sum2.coefficient(i) for i in range(11)] - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - sage: [sum3.coefficient(i) for i in range(11)] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - - Test Plus 2 - - :: - - sage: gs1 = L([1,2,4,8,0]) - sage: gs2 = L([-1, 0,-1,-9,22,0]) - sage: sum = gs1 + gs2 - sage: sum2 = gs2 + gs1 - sage: [ sum.coefficient(i) for i in range(5) ] - [0, 2, 3, -1, 22] - sage: [ sum.coefficient(i) for i in range(5, 11) ] - [0, 0, 0, 0, 0, 0] - sage: [ sum2.coefficient(i) for i in range(5) ] - [0, 2, 3, -1, 22] - sage: [ sum2.coefficient(i) for i in range(5, 11) ] - [0, 0, 0, 0, 0, 0] - """ - return self._new(partial(self._plus_gen, y), min, self, y) - - add = _add_ - - - - def _plus_gen(self, y, ao): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: gs1 = L([1]) - sage: g = gs1._plus_gen(gs1, 0) - sage: [next(g) for i in range(5)] - [2, 2, 2, 2, 2] - - :: - - sage: g = gs1._plus_gen(gs1, 2) - sage: [next(g) for i in range(5)] - [0, 0, 2, 2, 2] - """ - base_ring = self.parent().base_ring() - zero = base_ring(0) - for n in range(ao): - yield zero - n = ao - while True: - yield self._stream[n] + y._stream[n] - n += 1 - - def _mul_(self, y): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: gs0 = L(0) - sage: gs1 = L([1]) - - :: - - sage: prod0 = gs0 * gs1 - sage: [prod0.coefficient(i) for i in range(11)] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - - :: - - sage: prod1 = gs1 * gs0 - sage: [prod1.coefficient(i) for i in range(11)] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - - :: - - sage: prod2 = gs1 * gs1 - sage: [prod2.coefficient(i) for i in range(11)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - - :: - - sage: gs1 = L([1,2,4,8,0]) - sage: gs2 = L([-1, 0,-1,-9,22,0]) - - :: - - sage: prod1 = gs1 * gs2 - sage: [prod1.coefficient(i) for i in range(11)] - [-1, -2, -5, -19, 0, 0, 16, 176, 0, 0, 0] - - :: - - sage: prod2 = gs2 * gs1 - sage: [prod2.coefficient(i) for i in range(11)] - [-1, -2, -5, -19, 0, 0, 16, 176, 0, 0, 0] - """ - - return self._new(partial(self._times_gen, y), lambda a,b:a+b, self, y) - - times = _mul_ - - def _times_gen(self, y, ao): - r""" - Return an iterator for the coefficients of self \* y. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1,1,0]) - sage: g = f._times_gen(f,0) - sage: [next(g) for i in range(5)] - [1, 2, 1, 0, 0] - """ - base_ring = self.parent().base_ring() - zero = base_ring(0) - - for n in range(ao): - yield zero - - n = ao - while True: - low = self.aorder - high = n - y.aorder - nth_coefficient = zero - - #Handle the zero series - if low == inf or high == inf: - yield zero - n += 1 - continue - - for k in range(low, high+1): - cx = self._stream[k] - if cx == 0: - continue - nth_coefficient += cx * y._stream[n-k] - yield nth_coefficient - n += 1 - - def __pow__(self, n): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1,1,0]) # 1+x - sage: g = f^3 - sage: g.coefficients(4) - [1, 3, 3, 1] - - :: - - sage: f^0 - 1 - """ - if not isinstance(n, (int, Integer)) or n < 0: - raise ValueError("n must be a nonnegative integer") - return prod([self]*n, self.parent().identity_element()) - - def __invert__(self): - """ - Return 1 over this power series, i.e. invert this power series. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: x = L.gen() - - Geometric series:: - - sage: a = ~(1-x); a.compute_coefficients(10); a - 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9 + x^10 + O(x^11) - - (Shifted) Fibonacci numbers:: - - sage: b = ~(1-x-x^2); b.compute_coefficients(10); b - 1 + x + 2*x^2 + 3*x^3 + 5*x^4 + 8*x^5 - + 13*x^6 + 21*x^7 + 34*x^8 + 55*x^9 + 89*x^10 + O(x^11) - - Series whose constant coefficient is `0` cannot be inverted:: - - sage: ~x - Traceback (most recent call last): - .... - ZeroDivisionError: cannot invert x because constant coefficient is 0 - """ - if self.get_aorder() > 0: - raise ZeroDivisionError( - 'cannot invert {} because ' - 'constant coefficient is 0'.format(self)) - return self._new(self._invert_gen, lambda a: 0, self) - - invert = __invert__ - - def _invert_gen(self, ao): - r""" - Return an iterator for the coefficients of 1 over this power series. - - TESTS:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1, -1, 0]) - sage: g = f._invert_gen(0) - sage: [next(g) for i in range(10)] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - """ - from itertools import count - - assert ao == 0 - - ic0 = ~self.coefficient(0) - yield ic0 - if self.order == 0: - return - - one = self.parent()(1) - base = one - ic0 * self - base.coefficient(0) - ao_base = base.get_aorder() - assert ao_base >= 1 - - current = one + base - k = 1 - for n in count(1): - while ao_base*k < n: - current = one + base * current - k += 1 - current.coefficient(n) # make sure new current is initialized - ao_base = base.get_aorder() # update this so that while above is faster - yield current.coefficient(n) * ic0 - - def _div_(self, other): - """ - Divide this power series by ``other``. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: x = L.gen() - - Fibonacci numbers:: - - sage: b = x / (1-x-x^2); b.compute_coefficients(10); b - x + x^2 + 2*x^3 + 3*x^4 + 5*x^5 + 8*x^6 - + 13*x^7 + 21*x^8 + 34*x^9 + 55*x^10 + O(x^11) - """ - return self * ~other - - div = _div_ - - def __call__(self, y): - """ - Return the composition of this power series and the power series y. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L([1]) - sage: t = L([0,0,1]) - sage: u = s(t) - sage: u.coefficients(11) - [1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34] - - Test Compose 2 - - :: - - sage: s = L([1]) - sage: t = L([0,0,1,0]) - sage: u = s(t) - sage: u.aorder - 0 - sage: u.order - Unknown series order - sage: u.coefficients(10) - [1, 0, 1, 0, 1, 0, 1, 0, 1, 0] - sage: u.aorder - 0 - sage: u.order - 0 - - Test Compose 3 s = 1/(1-x), t = x/(1-x) s(t) = (1-x)/(1-2x) - - :: - - sage: s = L([1]) - sage: t = L([0,1]) - sage: u = s(t) - sage: u.coefficients(14) - [1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096] - """ - return self._new(partial(self._compose_gen, y), lambda a,b:a*b, self, y) - - composition = __call__ - - def _compose_gen(self, y, ao): - """ - Return a iterator for the coefficients of the composition of this - power series with the power series y. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L([1]) - sage: t = L([0,1]) - sage: g = s._compose_gen(t, 0) - sage: [next(g) for i in range(10)] - [1, 1, 2, 4, 8, 16, 32, 64, 128, 256] - """ - assert y.coefficient(0) == 0 - yield self._stream[0] - z = self.tail().compose(y) * y - z.coefficient(1) - n = 1 - while True: - yield z._stream[n] - n += 1 - - def tail(self): - """ - Return the power series whose coefficients obtained by subtracting - the constant term from this series and then dividing by x. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L(range(20)) - sage: g = f.tail() - sage: g.coefficients(10) - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - """ - return self._new(lambda a0: self.iterator(1), bounded_decrement, self) - - def iterator(self, n=0, initial=None): - """ - Return an iterator for the coefficients of self starting at n. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L(range(10)) - sage: g = f.iterator(2) - sage: [next(g) for i in range(5)] - [2, 3, 4, 5, 6] - sage: g = f.iterator(2, initial=[0,0]) - sage: [next(g) for i in range(5)] - [0, 0, 2, 3, 4] - """ - if initial is not None: - for x in initial: - yield x - while True: - yield self._stream[n] - n += 1 - - compose = __call__ - - def _power_gen(self): - """ - Return a generator for all the powers self^k starting with k = 1. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1,1,0]) - sage: g = f._power_gen() - sage: next(g).coefficients(5) - [1, 1, 0, 0, 0] - sage: next(g).coefficients(5) - [1, 2, 1, 0, 0] - sage: next(g).coefficients(5) - [1, 3, 3, 1, 0] - """ - z = self - while True: - yield z - z = z*self - - def derivative(self): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: one = L(1) - sage: monom = L.gen() - sage: s = L([1]) - sage: u = s.derivative() - sage: u.coefficients(10) - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - :: - - sage: s = L() - sage: s._name = 's' - sage: s.define(one+monom*s*s) - sage: u = s.derivative() - sage: u.coefficients(5) #[1*1, 2*2, 3*5, 4*14, 5*42] - [1, 4, 15, 56, 210] - - :: - - sage: s = L([1]) - sage: t = L([0,1]) - sage: u = s(t).derivative() - sage: v = (s.derivative().compose(t))*t.derivative() - sage: u.coefficients(11) - [1, 4, 12, 32, 80, 192, 448, 1024, 2304, 5120, 11264] - sage: v.coefficients(11) - [1, 4, 12, 32, 80, 192, 448, 1024, 2304, 5120, 11264] - - :: - - sage: s = L(); s._name='s' - sage: t = L(); t._name='t' - sage: s.define(monom+t*t*t) - sage: t.define(monom+s*s) - sage: u = (s*t).derivative() - sage: v = s.derivative()*t + s*t.derivative() - sage: u.coefficients(10) - [0, 2, 3, 4, 30, 72, 133, 552, 1791, 4260] - sage: v.coefficients(10) - [0, 2, 3, 4, 30, 72, 133, 552, 1791, 4260] - sage: u.coefficients(10) == v.coefficients(10) - True - - :: - - sage: f = L._new_initial(2, Stream([0,0,4,5,6,0])) - sage: d = f.derivative() - sage: d.get_aorder() - 1 - sage: d.coefficients(5) - [0, 8, 15, 24, 0] - """ - return self._new(self._diff_gen, bounded_decrement, self) - - def _diff_gen(self, ao): - """ - Return an iterator for the coefficients of the derivative of self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1]) - sage: g = f._diff_gen(0) - sage: [next(g) for i in range(10)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - """ - n = 1 - while True: - yield n*self._stream[n] - n += 1 - - ########### - #Integrals# - ########### - def integral(self, integration_constant = 0): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: zero = L(0) - sage: s = zero - sage: t = s.integral() - sage: t.is_zero() - True - - :: - - sage: s = zero - sage: t = s.integral(1) - sage: t.coefficients(6) - [1, 0, 0, 0, 0, 0] - sage: t._stream.is_constant() - True - - :: - - sage: s = L.term(1, 0) - sage: t = s.integral() - sage: t.coefficients(6) - [0, 1, 0, 0, 0, 0] - sage: t._stream.is_constant() - True - - :: - - sage: s = L.term(1,0) - sage: t = s.integral(1) - sage: t.coefficients(6) - [1, 1, 0, 0, 0, 0] - sage: t._stream.is_constant() - True - - :: - - sage: s = L.term(1, 4) - sage: t = s.integral() - sage: t.coefficients(10) - [0, 0, 0, 0, 0, 1/5, 0, 0, 0, 0] - - :: - - sage: s = L.term(1,4) - sage: t = s.integral(1) - sage: t.coefficients(10) - [1, 0, 0, 0, 0, 1/5, 0, 0, 0, 0] - - TESTS:: - - sage: from sage.combinat.species.stream import Stream - sage: f = L._new_initial(2, Stream([0,0,4,5,6,0])) - sage: i = f.derivative().integral() - sage: i.get_aorder() - 2 - sage: i.coefficients(5) - [0, 0, 4, 5, 6] - sage: i = f.derivative().integral(1) - sage: i.get_aorder() - 0 - sage: i.coefficients(5) - [1, 0, 4, 5, 6] - """ - if integration_constant == 0: - return self._new(self._integral_zero_gen, increment, self) - else: - L = self.parent() - return L._new_initial(0, Stream(self._integral_nonzero_gen(integration_constant))) - - def _integral_zero_gen(self, ao): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: s = L.gen() - sage: g = s._integral_zero_gen(1) - sage: [next(g) for i in range(5)] - [0, 0, 1/2, 0, 0] - """ - for n in range(ao): - yield self.parent().zero() - n = ao - while True: - #Check to see if the stream is finite - if self.is_finite(n-1): - yield self._stream[n-1] - break - else: - yield (Integer(1)/Integer(n))*self._stream[n-1] - n += 1 - - - def _integral_nonzero_gen(self, integration_constant): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L._new_initial(2, Stream([0,0,4,5,6,0])).derivative() - sage: g = f._integral_nonzero_gen(1) - sage: [next(g) for i in range(5)] - [1, 0, 4, 5, 6] - """ - yield integration_constant - ao = self.aorder - assert ao != unk - - if ao == inf: - yield self.parent()._zero_base_ring - else: - for _ in range(ao-1): - yield self.parent()._zero_base_ring - - n = max(1, ao) - while True: - self.coefficient(n - 1) - - #Check to see if the stream is finite - if self.is_finite(n-1): - yield self.coefficient(n-1) - break - else: - yield (Integer(1)/Integer(n))*self.coefficient(n-1) - n += 1 - - def is_finite(self, n=None): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([0,0,1,0,0]); a - O(1) - sage: a.is_finite() - False - sage: c = a[4] - sage: a.is_finite() - False - sage: a.is_finite(4) - False - sage: c = a[5] - sage: a.is_finite() - True - sage: a.is_finite(4) - True - """ - if self.order is inf: - return True - - s = self._stream - - if n is None: - n = len(s) - - if s.is_constant() and all(s[i] == 0 for i in range(n-1, max(n,len(s)))): - return True - - return False - - def exponential(self): - """ - TESTS:: - - sage: def inv_factorial(): - ....: q = 1 - ....: yield 0 - ....: yield q - ....: n = 2 - ....: while True: - ....: q = q / n - ....: yield q - ....: n += 1 - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L(inv_factorial()) #e^(x)-1 - sage: u = f.exponential() - sage: g = inv_factorial() - sage: z1 = [1,1,2,5,15,52,203,877,4140,21147,115975] - sage: l1 = [z*next(g) for z in z1] - sage: l1 = [1] + l1[1:] - sage: u.coefficients(11) - [1, 1, 1, 5/6, 5/8, 13/30, 203/720, 877/5040, 23/224, 1007/17280, 4639/145152] - sage: l1 == u.coefficients(11) - True - """ - base_ring = self.parent().base_ring() - s = self.parent()() - s.define( (self.derivative()*s).integral(base_ring(1)) ) - return s - - def __getitem__(self, i): - """ - Return the ith coefficient of self. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: f = L([1,2,3,0]) - sage: [f[i] for i in range(5)] - [1, 2, 3, 0, 0] - """ - return self.coefficient(i) - - - ######################### - #Min and max restriction# - ######################### - def restricted(self, min=None, max=None): - """ - Return the power series restricted to the coefficients starting at - ``min`` and going up to, but not including ``max``. - - If ``min`` is not specified, then it is assumed to be zero. If - ``max`` is not specified, then it is assumed to be infinity. - - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([1]) - sage: a.restricted().coefficients(10) - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: a.restricted(min=2).coefficients(10) - [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] - sage: a.restricted(max=5).coefficients(10) - [1, 1, 1, 1, 1, 0, 0, 0, 0, 0] - sage: a.restricted(min=2, max=6).coefficients(10) - [0, 0, 1, 1, 1, 1, 0, 0, 0, 0] - """ - - if ((min is None and max is None) or - (max is None and self.get_aorder() >= min)): - return self - - if min is None: - min = 0 - return self._new(partial(self._restricted_gen, min, max), - lambda ao: builtins.max(ao, min), self) - - def _restricted_gen(self, mn, mx, ao): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: a = L([1]) - sage: g = a._restricted_gen(None, None, 2) - sage: [next(g) for i in range(10)] - [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] - sage: g = a._restricted_gen(1, None, 2) - sage: [next(g) for i in range(10)] - [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] - sage: g = a._restricted_gen(3, None, 2) - sage: [next(g) for i in range(10)] - [0, 0, 0, 1, 1, 1, 1, 1, 1, 1] - - :: - - sage: g = a._restricted_gen(1, 5, 2) - sage: [next(g) for i in range(6)] - [0, 0, 1, 1, 1, 0] - """ - BR = self.parent().base_ring() - for n in range(max(mn,ao)): - yield BR(0) - - n = max(mn, ao) - while True: - if mx is not None and n >= mx: - yield BR(0) - break - else: - yield self._stream[n] - n += 1 - - - ############# - #Change Ring# - ############# - def _change_ring_gen(self, R, ao): - """ - EXAMPLES:: - - sage: L = LazyPowerSeriesRing(QQ) - sage: L2 = LazyPowerSeriesRing(RR) - sage: a = L([1]) - sage: b = L2(a) - sage: b.parent() - Lazy Power Series Ring over Real Field with 53 bits of precision - sage: b.coefficients(3) - [1.00000000000000, 1.00000000000000, 1.00000000000000] - """ - for n in range(ao): - yield R(0) - - n = ao - while True: - yield R(self._stream[n]) - n += 1 - -################################# - - -def uninitialized(): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series import uninitialized - sage: uninitialized() - Traceback (most recent call last): - ... - RuntimeError: we should never be here - """ - raise RuntimeError("we should never be here") diff --git a/src/sage/combinat/species/series_order.py b/src/sage/combinat/species/series_order.py deleted file mode 100644 index f4c42d032bd..00000000000 --- a/src/sage/combinat/species/series_order.py +++ /dev/null @@ -1,294 +0,0 @@ -""" -Series Order - -This file provides some utility classes which are useful when -working with unknown, known, and infinite series orders for -univariate power series. - -This code is based on the work of Ralf Hemmecke and Martin Rubey's -Aldor-Combinat, which can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/aldor/combinat/index.html. -In particular, the relevant section for this file can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/AldorCombinat/combinatsu30.html. -""" -from sage.rings.integer import Integer - -class SeriesOrderElement: - pass - -class InfiniteSeriesOrder(SeriesOrderElement): - def __repr__(self): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: o = InfiniteSeriesOrder(); o - Infinite series order - """ - return "Infinite series order" - - def __add__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: o = InfiniteSeriesOrder() - sage: o + 2 - Infinite series order - sage: o + o - Infinite series order - - :: - - sage: u = UnknownSeriesOrder() - sage: o + u - Unknown series order - - TESTS:: - - sage: o + -1 - Traceback (most recent call last): - ... - ValueError: x must be a positive integer - """ - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return self - - if isinstance(x, InfiniteSeriesOrder): - return self - - if isinstance(x, UnknownSeriesOrder): - return x - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - __radd__ = __add__ - - - def __mul__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: o = InfiniteSeriesOrder() - sage: o * 2 - Infinite series order - sage: o * o - Infinite series order - - :: - - sage: u = UnknownSeriesOrder() - sage: o * u - Unknown series order - - TESTS:: - - sage: o * -1 - Traceback (most recent call last): - ... - ValueError: x must be a positive integer - """ - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - elif x == 0: - return x - return self - - if isinstance(x, InfiniteSeriesOrder): - return self - - if isinstance(x, UnknownSeriesOrder): - return x - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - __rmul__ = __mul__ - - def __lt__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: o = InfiniteSeriesOrder() - sage: o < 2 - False - sage: o < o - False - - :: - - sage: u = UnknownSeriesOrder() - sage: o < u - False - sage: 2 < o # TODO: Not Implemented - True - """ - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return False - - if isinstance(x, InfiniteSeriesOrder): - return False - - if isinstance(x, UnknownSeriesOrder): - return False - - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - - def __gt__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: o = InfiniteSeriesOrder() - sage: o > 2 - True - """ - return True - -class UnknownSeriesOrder(SeriesOrderElement): - def __repr__(self): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder(); u - Unknown series order - """ - return "Unknown series order" - - def __add__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: u + 2 - Unknown series order - sage: u + u - Unknown series order - """ - - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return self - - if isinstance(x, SeriesOrderElement): - return self - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - __radd__ = __add__ - - - def __mul__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: u * 2 - Unknown series order - sage: u * u - Unknown series order - """ - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return self - - if isinstance(x, SeriesOrderElement): - return self - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - __rmul__ = __mul__ - - def __lt__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: u < 2 - True - sage: o = InfiniteSeriesOrder() - sage: u < o - True - """ - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return True - - if isinstance(x, SeriesOrderElement): - return True - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - def __gt__(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: u > 2 - False - """ - return False - -def bounded_decrement(x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: bounded_decrement(u) - Unknown series order - sage: bounded_decrement(4) - 3 - sage: bounded_decrement(0) - 0 - """ - if isinstance(x, SeriesOrderElement): - return x - - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return max(0, x - 1) - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - - -def increment(x): - """ - EXAMPLES:: - - sage: from sage.combinat.species.series_order import * - sage: u = UnknownSeriesOrder() - sage: increment(u) - Unknown series order - sage: increment(2) - 3 - """ - if isinstance(x, SeriesOrderElement): - return x + 1 - - if isinstance(x, (int, Integer)): - if x < 0: - raise ValueError("x must be a positive integer") - return x+1 - - raise TypeError("x must be a positive integer or a SeriesOrderElement") - -inf = InfiniteSeriesOrder() -unk = UnknownSeriesOrder() diff --git a/src/sage/combinat/species/set_species.py b/src/sage/combinat/species/set_species.py index 6bf21d4c462..4bc94c220f8 100644 --- a/src/sage/combinat/species/set_species.py +++ b/src/sage/combinat/species/set_species.py @@ -17,7 +17,6 @@ #***************************************************************************** from .species import GenericCombinatorialSpecies -from .generating_series import _integers_from from sage.combinat.species.structure import GenericSpeciesStructure from sage.combinat.species.misc import accept_size from sage.structure.unique_representation import UniqueRepresentation @@ -81,6 +80,7 @@ def automorphism_group(self): from sage.groups.all import SymmetricGroup return SymmetricGroup(max(1,len(self._labels))) + class SetSpecies(GenericCombinatorialSpecies, UniqueRepresentation): @staticmethod @accept_size @@ -102,11 +102,11 @@ def __init__(self, min=None, max=None, weight=None): sage: E = species.SetSpecies() sage: E.structures([1,2,3]).list() [{1, 2, 3}] - sage: E.isotype_generating_series().coefficients(4) + sage: E.isotype_generating_series()[0:4] [1, 1, 1, 1] sage: S = species.SetSpecies() - sage: c = S.generating_series().coefficients(3) + sage: c = S.generating_series()[0:3] sage: S._check() True sage: S == loads(dumps(S)) @@ -130,7 +130,7 @@ def _structures(self, structure_class, labels): _isotypes = _structures - def _gs_iterator(self, base_ring): + def _gs_callable(self, base_ring, n): r""" The generating series for the species of sets is given by `e^x`. @@ -139,15 +139,14 @@ def _gs_iterator(self, base_ring): sage: S = species.SetSpecies() sage: g = S.generating_series() - sage: g.coefficients(10) + sage: [g.coefficient(i) for i in range(10)] [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] sage: [g.count(i) for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - for n in _integers_from(0): - yield base_ring(self._weight / factorial(n)) + return base_ring(self._weight / factorial(n)) - def _itgs_list(self, base_ring): + def _itgs_list(self, base_ring, n): r""" The isomorphism type generating series for the species of sets is `\frac{1}{1-x}`. @@ -156,12 +155,12 @@ def _itgs_list(self, base_ring): sage: S = species.SetSpecies() sage: g = S.isotype_generating_series() - sage: g.coefficients(10) + sage: g[0:10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] sage: [g.count(i) for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ - return [base_ring(self._weight)] + return base_ring(self._weight) def _cis(self, series_ring, base_ring): r""" @@ -172,7 +171,7 @@ def _cis(self, series_ring, base_ring): sage: S = species.SetSpecies() sage: g = S.cycle_index_series() - sage: g.coefficients(5) + sage: g[0:5] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index 4e82c1b9b39..4cd3403ae61 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -22,9 +22,9 @@ sage: leaf = species.SingletonSpecies() sage: internal_node = species.SingletonSpecies(weight=q) sage: L = species.LinearOrderSpecies(min=1) - sage: T = species.CombinatorialSpecies() + sage: T = species.CombinatorialSpecies(min=1) sage: T.define(leaf + internal_node*L(T)) - sage: T.isotype_generating_series().coefficients(6) + sage: T.isotype_generating_series()[0:6] [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] Consider the following:: @@ -335,7 +335,7 @@ def functorial_composition(self, g): sage: WP = species.SubsetSpecies() sage: P2 = E2*E sage: G = WP.functorial_composition(P2) - sage: G.isotype_generating_series().coefficients(5) + sage: G.isotype_generating_series()[0:5] [1, 1, 2, 4, 11] """ from .functorial_composition_species import FunctorialCompositionSpecies @@ -360,7 +360,7 @@ def restricted(self, min=None, max=None): Set species with min=3 sage: S.structures([1,2]).list() [] - sage: S.generating_series().coefficients(5) + sage: S.generating_series()[0:5] [0, 0, 0, 1/6, 1/24] """ kwargs = {'min': self._min if min is None else min, @@ -431,19 +431,19 @@ def __pow__(self, n): (Singleton species) and (Singleton species)) and (Product of (Singleton species) and (Singleton species))) - sage: (X^2).generating_series().coefficients(4) + sage: (X^2).generating_series()[0:4] [0, 0, 1, 0] - sage: (X^3).generating_series().coefficients(4) + sage: (X^3).generating_series()[0:4] [0, 0, 0, 1] - sage: ((One+X)^3).generating_series().coefficients(4) + sage: ((One+X)^3).generating_series()[0:4] [1, 3, 3, 1] - sage: ((One+X)^7).generating_series().coefficients(8) + sage: ((One+X)^7).generating_series()[0:8] [1, 7, 21, 35, 35, 21, 7, 1] sage: x = QQ[['x']].gen() sage: coeffs = ((1+x+x+x**2)**25+O(x**10)).padded_list() sage: T = ((One+X+X+X^2)^25) - sage: T.generating_series().coefficients(10) == coeffs + sage: T.generating_series()[0:10] == coeffs True sage: X^1 is X True @@ -479,21 +479,23 @@ def _get_series(self, series_ring_class, prefix, base_ring=None): EXAMPLES:: sage: P = species.PermutationSpecies(min=2, max=4) - sage: P.generating_series().coefficients(8) #indirect doctest + sage: P.generating_series()[0:8] #indirect doctest [0, 0, 1, 1, 0, 0, 0, 0] """ series = self._series_helper(series_ring_class, prefix, base_ring=base_ring) - # We need to restrict the series based on the min # and max of this species. Note that if min and max # are both None (as in the default case), then the restrict # method will just return series. - return series.restricted(min=self._min, max=self._max) + if self._min is None and self._max is None: + return series + return series.parent()(lambda n: series[n], + valuation=self._min, degree=self._max) def _series_helper(self, series_ring_class, prefix, base_ring=None): """ This code handles much of the common work involved in getting the - generating series for this species (such has determining the + generating series for this species (such as determining the correct base ring to pass down to the subclass, determining which method on the subclass to call to get the series object, etc.) @@ -516,13 +518,13 @@ def _series_helper(self, series_ring_class, prefix, base_ring=None): sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing sage: S = species.SetSpecies() sage: itgs = S._series_helper(OrdinaryGeneratingSeriesRing, "itgs") - sage: itgs.coefficients(3) + sage: itgs[:3] [1, 1, 1] :: sage: itgs = S._series_helper(OrdinaryGeneratingSeriesRing, "itgs", base_ring=RDF) - sage: itgs.coefficients(3) + sage: itgs[:3] [1.0, 1.0, 1.0] """ prefix = "_" + prefix @@ -550,16 +552,16 @@ def _series_helper(self, series_ring_class, prefix, base_ring=None): except AttributeError: pass - # Try to return things like self._gs_iterator(base_ring). - # This is used when the subclass just provides an iterator + # Try to return things like self._gs_callable(base_ring). + # This is used when the subclass just provides an callable # for the coefficients of the generating series. Optionally, # the subclass can specify the order of the series. try: - iterator = getattr(self, prefix + "_iterator")(base_ring) + callable = getattr(self, prefix + "_callable") try: - return series_ring(iterator, order=self._order()) + return series_ring(lambda n: callable(base_ring, n), valuation=self._order()) except AttributeError: - return series_ring(iterator) + return series_ring(lambda n: callable(base_ring, n)) except AttributeError: pass @@ -567,7 +569,7 @@ def _series_helper(self, series_ring_class, prefix, base_ring=None): # This is used when the generating series is just a single # term. try: - return series_ring.term(getattr(self, prefix + "_term")(base_ring), + return series_ring(getattr(self, prefix + "_term")(base_ring), self._order()) except AttributeError: pass @@ -578,7 +580,7 @@ def _series_helper(self, series_ring_class, prefix, base_ring=None): # The generating series with all ones coefficients is generated this # way. try: - return series_ring(getattr(self, prefix + "_list")(base_ring)) + return series_ring(lambda n: getattr(self, prefix + "_list")(base_ring, n)) except AttributeError: pass @@ -597,7 +599,7 @@ def generating_series(self, base_ring=None): sage: P = species.PermutationSpecies() sage: g = P.generating_series() - sage: g.coefficients(4) + sage: g[:4] [1, 1, 1, 1] sage: g.counts(4) [1, 1, 2, 6] @@ -620,7 +622,7 @@ def isotype_generating_series(self, base_ring=None): sage: P = species.PermutationSpecies() sage: g = P.isotype_generating_series() - sage: g.coefficients(4) + sage: g[0:4] [1, 1, 2, 3] sage: g.counts(4) [1, 1, 2, 3] @@ -642,7 +644,7 @@ def cycle_index_series(self, base_ring=None): sage: P = species.PermutationSpecies() sage: g = P.cycle_index_series() - sage: g.coefficients(4) + sage: g[0:4] [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]] """ return self._get_series(CycleIndexSeriesRing, "cis", base_ring) @@ -774,10 +776,10 @@ def algebraic_equation_system(self): :: sage: sorted(B.digraph().vertex_iterator(), key=str) - [Combinatorial species, - Product of (Combinatorial species) and (Combinatorial species), + [Combinatorial species with min=1, + Product of (Combinatorial species with min=1) and (Combinatorial species with min=1), Singleton species, - Sum of (Singleton species) and (Product of (Combinatorial species) and (Combinatorial species))] + Sum of (Singleton species) and (Product of (Combinatorial species with min=1) and (Combinatorial species with min=1))] :: diff --git a/src/sage/combinat/species/stream.py b/src/sage/combinat/species/stream.py deleted file mode 100644 index 56644b399ef..00000000000 --- a/src/sage/combinat/species/stream.py +++ /dev/null @@ -1,501 +0,0 @@ -""" -Streams or Infinite Arrays - -This code is based on the work of Ralf Hemmecke and Martin Rubey's -Aldor-Combinat, which can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/aldor/combinat/index.html. -In particular, the relevant section for this file can be found at -http://www.risc.uni-linz.ac.at/people/hemmecke/AldorCombinat/combinatse12.html. -""" -import types -from collections.abc import Iterable -from sage.structure.sage_object import SageObject - - -def _integers_from(n): - """ - Returns a generator for the integers starting at n. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import _integers_from - sage: g = _integers_from(5) - sage: [next(g) for i in range(5)] - [5, 6, 7, 8, 9] - """ - while True: - yield n - n += 1 - -def _apply_function(func, list): - """ - Returns an iterator for func(i) for i in list. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import _apply_function - sage: def square(l): - ....: l.append(l[-1]^2) - ....: return l[-1] - ... - sage: l = [2] - sage: g = _apply_function(square, l) - sage: [next(g) for i in range(5)] - [4, 16, 256, 65536, 4294967296] - """ - while True: - try: - yield func(list) - except Exception: - break - -def Stream(x=None, const=None): - """ - Returns a stream. - - EXAMPLES: We can create a constant stream by just passing a - - :: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream(const=0) - sage: [s[i] for i in range(10)] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - """ - if const is not None: - return Stream_class(const=const) - elif isinstance(x, Iterable): - return Stream_class(iter(x)) - elif isinstance(x, (types.FunctionType, types.LambdaType)): - return Stream_class(func=x) - - return Stream_class(iter([x,0])) - -class Stream_class(SageObject): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: from builtins import zip - sage: s = Stream(const=0) - sage: len(s) - 1 - sage: [x for (x,i) in zip(s, range(4))] - [0, 0, 0, 0] - sage: len(s) - 1 - - :: - - sage: s = Stream(const=4) - sage: g = iter(s) - sage: l1 = [x for (x,i) in zip(g, range(10))] - sage: l = [4 for k in range(10)] - sage: l == l1 - True - - :: - - sage: h = lambda l: 1 if len(l) < 2 else l[-1] + l[-2] - sage: fib = Stream(h) - sage: [x for (x,i) in zip(fib, range(11))] - [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] - - :: - - sage: r = [4, 3, 5, 2, 6, 1, 1, 1, 1, 1] - sage: l = [4, 3, 5, 2, 6, 1] - sage: s = Stream(l) - sage: s[3] = -1 - sage: [x for (x,i) in zip(s, r)] - [4, 3, 5, -1, 6, 1, 1, 1, 1, 1] - sage: s[5] = -2 - sage: [x for (x,i) in zip(s, r)] - [4, 3, 5, -1, 6, -2, 1, 1, 1, 1] - sage: s[6] = -3 - sage: [x for (x,i) in zip(s, r)] - [4, 3, 5, -1, 6, -2, -3, 1, 1, 1] - sage: s[8] = -4 - sage: [x for (x,i) in zip(s, r)] - [4, 3, 5, -1, 6, -2, -3, 1, -4, 1] - sage: a = Stream(const=0) - sage: a[2] = 3 - sage: [x for (x,i) in zip(a, range(4))] - [0, 0, 3, 0] - """ - - def __init__(self, gen=None, const=None, func=None): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream_class, Stream - sage: s = Stream_class(const=4) - sage: loads(dumps(s)) - <sage.combinat.species.stream.Stream_class object at ...> - - :: - - sage: sorted(s.__dict__.items()) - [('_constant', 4), - ('_gen', None), - ('_last_index', 0), - ('_list', [4]), - ('end_reached', True)] - - :: - - sage: s = Stream(ZZ) - sage: sorted(s.__dict__.items()) - [('_constant', None), - ('_gen', <generator object at 0x...>), - ('_last_index', -1), - ('_list', []), - ('end_reached', False)] - """ - #We define self._list up here so that - #_apply_function can make use of it if - #it needs to. - self._list = [] - - - if func is not None: - if gen is not None: - raise ValueError("you cannot specify both a function and a generator") - gen = _apply_function(func, self._list) - - #Constant stream - if const is not None: - self._list = [const] - self._last_index = 0 # last_index == len(self._list) - 1 - self._gen = None - self._constant = const - self.end_reached = True - else: - self._last_index = -1 # last_index == len(self._list) - 1 - self._gen = gen - self._constant = const - self.end_reached = False - - def __setitem__(self, i, t): - """ - Set the i-th entry of self to t. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - - :: - - sage: s = Stream(const=0) - sage: s[5] - 0 - sage: s.data() - [0] - sage: s[5] = 5 - sage: s[5] - 5 - sage: s.data() - [0, 0, 0, 0, 0, 5] - - :: - - sage: s = Stream(ZZ) - sage: s[10] - -5 - sage: s.data() - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5] - sage: s[10] = 10 - sage: s.data() - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5, 10] - """ - # Compute all of the coefficients up to (and including) the ith one - self[i] - - if i < len(self._list): - #If we are here, we can just change the entry in self._list - self._list[i] = t - else: - #If we are here, then the stream has become constant. We just - #extend self._list with self._constant and then change the - #last entry. - self._list += [ self._constant ] * (i+1 - len(self._list)) - self._last_index = i - self._list[i] = t - - def set_gen(self, gen): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: from builtins import zip - sage: fib = Stream() - sage: def g(): - ....: yield 1 - ....: yield 1 - ....: n = 0 - ....: while True: - ....: yield fib[n] + fib[n+1] - ....: n += 1 - - :: - - sage: fib.set_gen(g()) - sage: [x for (x,i) in zip(fib, range(11))] - [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] - - :: - - sage: l = [4,3,5,2,6,1] - sage: s = Stream(l) - sage: s[3] - 2 - sage: len(s) - 4 - sage: g = iter(l) - sage: s.set_gen(g) - sage: s[5] - 3 - sage: len(s) - 6 - """ - self._gen = gen - self.end_reached = False - - def map(self, f): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream(ZZ) - sage: square = lambda x: x^2 - sage: ss = s.map(square) - sage: [ss[i] for i in range(10)] - [0, 1, 1, 4, 4, 9, 9, 16, 16, 25] - - TESTS:: - - sage: from builtins import zip - sage: f = lambda l: 0 if len(l) == 0 else l[-1] + 1 - sage: o = Stream(f) - sage: [x for (x,i) in zip(o, range(10))] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - sage: double = lambda z: 2*z - sage: t = o.map(double) - sage: [x for (x,i) in zip(t, range(10))] - [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] - - :: - - sage: double = lambda z: 2*z - sage: o = Stream([0,1,2,3]) - sage: [x for (x,i) in zip(o, range(6))] - [0, 1, 2, 3, 3, 3] - sage: t = o.map(double) - sage: [x for (x,i) in zip(t, range(6))] - [0, 2, 4, 6, 6, 6] - """ - return Stream((f(x) for x in self)) - - def __getitem__(self, i): - """ - Returns the ith entry of self. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream(ZZ) - sage: [s[i] for i in range(10)] - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5] - sage: s[1] - 1 - - :: - - sage: s = Stream([1,2,3]) - sage: [s[i] for i in range(10)] - [1, 2, 3, 3, 3, 3, 3, 3, 3, 3] - - :: - - sage: s = Stream(QQ) - sage: s[10] - -3 - """ - if i <= self._last_index: - return self._list[i] - elif self.end_reached: - if self._constant is not False: - return self._constant - else: - raise IndexError("out of position") - else: - while self._last_index < i: - try: - self._list.append(next(self._gen)) - self._last_index += 1 - except StopIteration: - self.end_reached = True - self._constant = self._list[-1] - return self[i] - - return self._list[i] - - - def __iter__(self): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream([1,2,3]) - sage: g = iter(s) - sage: [next(g) for i in range(5)] - [1, 2, 3, 3, 3] - """ - i = 0 - while True: - try: - yield self[i] - except IndexError: - break - i += 1 - - def __len__(self): - """ - Returns the number of coefficients computed so far. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: l = [4,3,5,7,4,1,9,7] - sage: s = Stream(l) - sage: s[3] - 7 - sage: len(s) - 4 - sage: s[3] - 7 - sage: len(s) - 4 - sage: s[1] - 3 - sage: len(s) - 4 - sage: s[4] - 4 - sage: len(s) - 5 - - TESTS:: - - sage: l = ['Hello', ' ', 'World', '!'] - sage: s = Stream(l) - sage: len(s) - 0 - sage: s[2] - 'World' - sage: len(s) - 3 - sage: u = "" - sage: for i in range(len(s)): u += s[i] - sage: u - 'Hello World' - sage: v = "" - sage: for i in range(10): v += s[i] - sage: v - 'Hello World!!!!!!!' - sage: len(s) - 4 - """ - return len(self._list) - - number_computed = __len__ - - def data(self): - """ - Returns a list of all the coefficients computed so far. - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream, _integers_from - sage: s = Stream(_integers_from(3)) - sage: s.data() - [] - sage: s[5] - 8 - sage: s.data() - [3, 4, 5, 6, 7, 8] - """ - return self._list - - def is_constant(self): - """ - Returns True if and only if - - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream([1,2,3]) - sage: s.is_constant() - False - sage: s[3] - 3 - sage: s.data() - [1, 2, 3] - sage: s.is_constant() - True - - TESTS:: - - sage: l = [2,3,5,7,11,0] - sage: s = Stream(l) - sage: s.is_constant() - False - sage: s[3] - 7 - sage: s.is_constant() - False - sage: s[5] - 0 - sage: s.is_constant() - False - sage: s[6] - 0 - sage: s.is_constant() - True - - :: - - sage: s = Stream(const='I am constant.') - sage: s.is_constant() - True - """ - return self.end_reached - - - def stretch(self, k): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream(range(1, 10)) - sage: s2 = s.stretch(2) - sage: [s2[i] for i in range(10)] - [1, 0, 2, 0, 3, 0, 4, 0, 5, 0] - """ - return Stream(self._stretch_gen(k)) - - def _stretch_gen(self, k): - """ - EXAMPLES:: - - sage: from sage.combinat.species.stream import Stream - sage: s = Stream(range(1, 10)) - sage: g = s._stretch_gen(2) - sage: [next(g) for i in range(10)] - [1, 0, 2, 0, 3, 0, 4, 0, 5, 0] - """ - yield self[0] - for i in _integers_from(1): - for j in range(k-1): - yield 0 - yield self[i] diff --git a/src/sage/combinat/species/structure.py b/src/sage/combinat/species/structure.py index bdd6f710600..371d78eef9d 100644 --- a/src/sage/combinat/species/structure.py +++ b/src/sage/combinat/species/structure.py @@ -203,11 +203,13 @@ def is_isomorphic(self, x): else: return False + #For backward compatibility. This should be removed in the near #future since I doubt that there is any code that depends directly on #SpeciesStructure. SpeciesStructure = GenericSpeciesStructure + class SpeciesStructureWrapper(GenericSpeciesStructure): def __init__(self, parent, s, **options): """ @@ -390,7 +392,7 @@ def __iter__(self): try: if self.cardinality() == 0: return iter([]) - except RuntimeError: + except TypeError: raise NotImplementedError return getattr(self._species, self._iterator)(self._structure_class, self._labels) @@ -407,6 +409,7 @@ def cardinality(self): """ return getattr(self._species, self._generating_series)().count(len(self._labels)) + class StructuresWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ @@ -427,6 +430,7 @@ def __init__(self, species, labels, structure_class): "Structures", structure_class) + class IsotypesWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ diff --git a/src/sage/combinat/species/subset_species.py b/src/sage/combinat/species/subset_species.py index 0a41c7dbc9a..25f4862f4be 100644 --- a/src/sage/combinat/species/subset_species.py +++ b/src/sage/combinat/species/subset_species.py @@ -19,7 +19,6 @@ from .species import GenericCombinatorialSpecies from .set_species import SetSpecies -from .generating_series import _integers_from from .structure import GenericSpeciesStructure from sage.combinat.species.misc import accept_size from sage.structure.unique_representation import UniqueRepresentation @@ -53,7 +52,6 @@ def canonical_label(self): rng = list(range(1, len(self._list) + 1)) return self.__class__(self.parent(), self._labels, rng) - def label_subset(self): r""" Return a subset of the labels that "appear" in this structure. @@ -124,6 +122,7 @@ def complement(self): new_list = [i for i in range(1, len(self._labels)+1) if i not in self._list] return SubsetSpeciesStructure(self.parent(), self._labels, new_list) + class SubsetSpecies(GenericCombinatorialSpecies, UniqueRepresentation): @staticmethod @accept_size @@ -143,13 +142,13 @@ def __init__(self, min=None, max=None, weight=None): EXAMPLES:: sage: S = species.SubsetSpecies() - sage: S.generating_series().coefficients(5) + sage: S.generating_series()[0:5] [1, 2, 2, 4/3, 2/3] - sage: S.isotype_generating_series().coefficients(5) + sage: S.isotype_generating_series()[0:5] [1, 2, 3, 4, 5] sage: S = species.SubsetSpecies() - sage: c = S.generating_series().coefficients(3) + sage: c = S.generating_series()[0:3] sage: S._check() True sage: S == loads(dumps(S)) @@ -187,7 +186,7 @@ def _isotypes(self, structure_class, labels): for i in range(len(labels)+1): yield structure_class(self, labels, range(1, i+1)) - def _gs_iterator(self, base_ring): + def _gs_callable(self, base_ring, n): """ The generating series for the species of subsets is `e^{2x}`. @@ -195,13 +194,12 @@ def _gs_iterator(self, base_ring): EXAMPLES:: sage: S = species.SubsetSpecies() - sage: S.generating_series().coefficients(5) + sage: [S.generating_series().coefficient(i) for i in range(5)] [1, 2, 2, 4/3, 2/3] """ - for n in _integers_from(0): - yield base_ring(2)**n / base_ring(factorial(n)) + return base_ring(2)**n / base_ring(factorial(n)) - def _itgs_iterator(self, base_ring): + def _itgs_callable(self, base_ring, n): r""" The generating series for the species of subsets is `e^{2x}`. @@ -209,11 +207,10 @@ def _itgs_iterator(self, base_ring): EXAMPLES:: sage: S = species.SubsetSpecies() - sage: S.isotype_generating_series().coefficients(5) + sage: S.isotype_generating_series()[0:5] [1, 2, 3, 4, 5] """ - for n in _integers_from(1): - yield base_ring(n) + return base_ring(n + 1) def _cis(self, series_ring, base_ring): r""" @@ -226,7 +223,7 @@ def _cis(self, series_ring, base_ring): EXAMPLES:: sage: S = species.SubsetSpecies() - sage: S.cycle_index_series().coefficients(5) + sage: S.cycle_index_series()[0:5] [p[], 2*p[1], 2*p[1, 1] + p[2], @@ -239,5 +236,6 @@ def _cis(self, series_ring, base_ring): res *= self._weight return res + #Backward compatibility SubsetSpecies_class = SubsetSpecies diff --git a/src/sage/combinat/species/sum_species.py b/src/sage/combinat/species/sum_species.py index 05950420dbb..fe45a352943 100644 --- a/src/sage/combinat/species/sum_species.py +++ b/src/sage/combinat/species/sum_species.py @@ -23,6 +23,7 @@ class SumSpeciesStructure(SpeciesStructureWrapper): pass + class SumSpecies(GenericCombinatorialSpecies, UniqueRepresentation): def __init__(self, F, G, min=None, max=None, weight=None): """ @@ -32,7 +33,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: S = species.PermutationSpecies() sage: A = S+S - sage: A.generating_series().coefficients(5) + sage: A.generating_series()[:5] [2, 2, 2, 2, 2] sage: P = species.PermutationSpecies() @@ -142,13 +143,12 @@ def _gs(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P + P - sage: F.generating_series().coefficients(5) + sage: F.generating_series()[:5] [2, 2, 2, 2, 2] """ return (self.left_summand().generating_series(base_ring) + self.right_summand().generating_series(base_ring)) - def _itgs(self, series_ring, base_ring): """ Returns the isomorphism type generating series of this species. @@ -157,7 +157,7 @@ def _itgs(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P + P - sage: F.isotype_generating_series().coefficients(5) + sage: F.isotype_generating_series()[:5] [2, 2, 4, 6, 10] """ return (self.left_summand().isotype_generating_series(base_ring) + @@ -171,7 +171,7 @@ def _cis(self, series_ring, base_ring): sage: P = species.PermutationSpecies() sage: F = P + P - sage: F.cycle_index_series().coefficients(5) + sage: F.cycle_index_series()[:5] [2*p[], 2*p[1], 2*p[1, 1] + 2*p[2], @@ -219,5 +219,6 @@ def _equation(self, var_mapping): """ return sum(var_mapping[operand] for operand in self._state_info) + #Backward compatibility SumSpecies_class = SumSpecies diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 5f3d481de68..5cae6958510 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -169,6 +169,7 @@ def Subsets(s, k=None, submultiset=False): else: return Subsets_sk(s, k) + class Subsets_s(Parent): r""" Subsets of a given set. @@ -559,6 +560,7 @@ def lattice(self): S = self.underlying_set() return S.subsets_lattice() + class Subsets_sk(Subsets_s): r""" Subsets of fixed size of a set. @@ -584,6 +586,7 @@ class Subsets_sk(Subsets_s): ... ValueError: {0} not in Subsets of {0, 1, 2, 5, 7} of size 3 """ + def __init__(self, s, k): """ TESTS:: @@ -1163,7 +1166,6 @@ def _element_constructor_(self,X): return e - class SubMultiset_sk(SubMultiset_s): """ The combinatorial class of the subsets of size k of a multiset s. Note @@ -1185,6 +1187,7 @@ class SubMultiset_sk(SubMultiset_s): sage: [sub for sub in S] [[1, 2], [1, 3], [2, 3], [3, 3]] """ + def __init__(self, s, k): """ TESTS:: diff --git a/src/sage/combinat/subword.py b/src/sage/combinat/subword.py index 9b201eff72a..50869bd600a 100644 --- a/src/sage/combinat/subword.py +++ b/src/sage/combinat/subword.py @@ -162,6 +162,7 @@ class Subwords_w(Parent): r""" Subwords of a given word. """ + def __init__(self, w, element_constructor): """ TESTS:: @@ -318,6 +319,7 @@ class Subwords_wk(Subwords_w): r""" Subwords with fixed length of a given word. """ + def __init__(self, w, k, element_constructor): """ TESTS:: diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index 15af1d67ba0..b5455260aaa 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -90,7 +90,7 @@ sage: Q = I + W.w0.coxeter_sorting_word(I) sage: S = SubwordComplex(Q,W.w0) sage: S.brick_polytope() - doctest:...: RuntimeWarning: the polytope is build with rational vertices + doctest:...: RuntimeWarning: the polytope is built with rational vertices A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 32 vertices AUTHORS: @@ -768,7 +768,7 @@ def plot(self, list_colors=None, labels=[], thickness=3, fontsize=14, sage: Q = c.reduced_word()*2 + W.w0.coxeter_sorting_word(c) # optional - gap3 sage: SC = SubwordComplex(Q, W.w0) # optional - gap3 sage: F = SC[15]; F.plot() # optional - gap3 - Graphics object consisting of 52 graphics primitives + Graphics object consisting of 53 graphics primitives TESTS:: @@ -1696,7 +1696,7 @@ def minkowski_summand(self, i): else: from sage.rings.cc import CC from warnings import warn - warn("the polytope is build with rational vertices", RuntimeWarning) + warn("the polytope is built with rational vertices", RuntimeWarning) min_sum = [[QQ(CC(v)) for v in F.extended_weight_configuration()[i]] for F in self] return Polyhedron(min_sum) @@ -1737,6 +1737,8 @@ def brick_polytope(self, coefficients=None): sage: c = W.index_set(); Q = c + tuple(W.w0.coxeter_sorting_word(c)) # optional - gap3 sage: SC = SubwordComplex(Q,W.w0) # optional - gap3 sage: SC.brick_polytope() # optional - gap3 + doctest:...: + RuntimeWarning: the polytope is built with rational vertices A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 32 vertices """ BV = self.brick_vectors(coefficients=coefficients) @@ -1747,7 +1749,7 @@ def brick_polytope(self, coefficients=None): else: from sage.rings.cc import CC from warnings import warn - warn("the polytope is build with rational vertices", RuntimeWarning) + warn("the polytope is built with rational vertices", RuntimeWarning) BV = [[QQ(CC(v).real()) for v in V] for V in BV] return Polyhedron(BV) diff --git a/src/sage/combinat/super_tableau.py b/src/sage/combinat/super_tableau.py index 9be91778022..ffcdb0fc374 100644 --- a/src/sage/combinat/super_tableau.py +++ b/src/sage/combinat/super_tableau.py @@ -416,6 +416,7 @@ class SemistandardSuperTableaux_all(SemistandardSuperTableaux): """ All semistandard super tableaux. """ + def __init__(self): r""" Initializes the class of all semistandard super tableaux. @@ -597,6 +598,7 @@ class StandardSuperTableaux_all(StandardSuperTableaux, """ All standard super tableaux. """ + def __init__(self): r""" Initializes the class of all standard super tableaux. @@ -649,6 +651,7 @@ class StandardSuperTableaux_size(StandardSuperTableaux, [[1', 1], [2'], [2]], [[1'], [1], [2'], [2]]] """ + def __init__(self, n): r""" Initializes the class of all standard super tableaux of size ``n``. @@ -729,6 +732,7 @@ class StandardSuperTableaux_shape(StandardSuperTableaux): """ Standard super tableaux of a fixed shape `p`. """ + def __init__(self, p): r""" Initializes the class of all standard super tableaux of a given shape. diff --git a/src/sage/combinat/superpartition.py b/src/sage/combinat/superpartition.py index 6ba79867954..f278eb70e11 100644 --- a/src/sage/combinat/superpartition.py +++ b/src/sage/combinat/superpartition.py @@ -200,18 +200,6 @@ def __richcmp__(self, other, op) -> bool: else: return richcmp(list(self), other, op) - def _hash_(self): - """ - Return the hash of ``self``. - - EXAMPLES:: - - sage: SP = SuperPartition([[1],[1]]) - sage: hash(tuple(SP)) == hash(SP) - True - """ - return hash(tuple(self)) - def _repr_(self) -> str: r""" Return a string representation of ``self``. diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index c3d67555a6b..b8a0bebab44 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -215,7 +215,7 @@ def SymmetricGroupAlgebra(R, W, category=None): sage: SGg.group().conjugacy_classes_representatives() [(), (1,2), (1,2,3)] """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if W in NN: W = Permutations(W) if category is None: diff --git a/src/sage/combinat/symmetric_group_representations.py b/src/sage/combinat/symmetric_group_representations.py index edf733380c4..8a6b58ae87c 100644 --- a/src/sage/combinat/symmetric_group_representations.py +++ b/src/sage/combinat/symmetric_group_representations.py @@ -173,6 +173,7 @@ def SymmetricGroupRepresentation(partition, implementation="specht", ring=ring, cache_matrices=cache_matrices) return Rep(partition) + def SymmetricGroupRepresentations(n, implementation="specht", ring=None, cache_matrices=True): r""" @@ -459,6 +460,7 @@ class SymmetricGroupRepresentations_class(UniqueRepresentation,Parent): Generic methods for the CombinatorialClass of irreducible representations of the symmetric group. """ + def __init__(self, n, ring=None, cache_matrices=True): r""" Irreducible representations of the symmetric group. diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py new file mode 100644 index 00000000000..883d55c87bd --- /dev/null +++ b/src/sage/combinat/t_sequences.py @@ -0,0 +1,857 @@ +r""" +T-sequences + +T-sequences are tuples of four (-1, 0, 1) sequences of length `t` where +for every `i` exactly one sequence has a nonzero entry at index `i` +and for which the nonperiodic autocorrelation function is equal to zero +(i.e. they are complementary). See Definition 7.5 of [Seb2017]_. + +These can be constructed from Turyn sequences. In particular, +if Turyn sequences of length `l` exists, there will be T-sequences +of length `4l-1` and `2l-1`. + +Turyn sequences are tuples of four (-1, +1) sequences `X, U, Y, V` of length +`l`, `l`, `l-1`, `l-1` with nonperiodic autocorrelation equal to zero and +the additional constraints that: + +* the first element of `X` is 1 +* the last element of `X` is -1 +* the last element of `U` is 1 + +The nonperiodic autocorrelation of a familiy of sequences `X=\{A_1, A_2, ..., A_n\}` is defined as +(see Definition 7.2 of [Seb2017]_): + +.. MATH:: + + N_X(j) = \sum_{i=1}^{n-j}(a_{1,i}a_{1,i+j} + a_{2,i}a_{2,i+j} + ... + a_{n,i}a_{n,i+j}) + +AUTHORS: + +- Matteo Cati (2022-11-16): initial version + +""" + +# *************************************************************************** +# Copyright (C) 2022 Matteo Cati matteo.cati@keble.ox.ac.uk +# +# 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.sequence import Sequence + + +def _nonperiodic_autocorrelation(sequences, j): + r""" + Compute the nonperiodic autocorrelation of a familiy of sequences. + + Namely, given a family of sequences `X` it computes: + + .. MATH:: + + N_X(j) = \sum_{i=1}^{n-j}(a_{1,i}a_{1,i+j} + a_{2,i}a_{2,i+j} + ... + a_{n,i}a_{n,i+j}) + + INPUT: + + - ``sequences`` -- either a single sequence or a list of sequences for which we want + to compute the nonperiodic autocorrelation. + + - ``j`` -- integer, the parameter `j` used when calculating the nonperiodic autocorrelation. + """ + if not isinstance(sequences[0], list): + sequences = [sequences] + + t = len(sequences[0]) + result = 0 + for i in range(t-j): + for seq in sequences: + result += seq[i]*seq[i+j] + return result + +def is_skew(seq, verbose=False): + r""" + Check if the given sequence is skew. + + A sequence `X=\{x_1, x_2, ...,x_n\}` is defined skew (according to Definition + 7.4 of [Seb2017]_) if `n` is even and `x_i = -x_{n-i+1}`. + + INPUT: + + - ``seq`` -- the sequence that should be checked. + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import is_skew + sage: is_skew([1, -1, 1, -1, 1, -1]) + True + sage: is_skew([1, -1, -1, -1], verbose=True) + Constraint not satisfied at index 1 + False + + TESTS:: + + sage: is_skew([1, -1, -1]) + False + sage: is_skew([1, -1, -1, 1, -1], verbose=True) + Sequence should be of even length + False + """ + + n = len(seq) + + if n%2 == 1: + if verbose: + print('Sequence should be of even length') + return False + + for i in range(n): + if seq[i] != -seq[n-i-1]: + if verbose: + print(f'Constraint not satisfied at index {i}') + return False + return True + +def is_symmetric(seq, verbose=False): + r""" + Check if the given sequence is symmetric. + + A sequence `X=\{x_1, x_2, ...,x_n\}` is defined symmetric (according to Definition + 7.4 of [Seb2017]_) if `n` is odd and `x_i = x_{n-i+1}`. + + INPUT: + + - ``seq`` -- the sequence that should be checked. + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import is_symmetric + sage: is_symmetric([1, -1, 1, -1, 1]) + True + sage: is_symmetric([1, -1, 1, 1, 1], verbose=True) + Constraint not satisfied at index 1 + False + + TESTS:: + + sage: is_symmetric([1, -1, -1, 1]) + False + sage: is_symmetric([1, -1, -1, 1], verbose=True) + Sequence should be of odd length + False + """ + + n = len(seq) + + if n%2 == 0: + if verbose: + print('Sequence should be of odd length') + return False + + for i in range(n): + if seq[i] != seq[n-i-1]: + if verbose: + print(f'Constraint not satisfied at index {i}') + return False + return True + + +def is_T_sequences_set(sequences, verbose=False): + r""" + Check if a family of sequences is composed of T-sequences. + + Given 4 (-1, 0, +1) sequences, they will be T-sequences if + (Definition 7.4 of [Seb2017]_): + + * they have all the same length `t` + * for each index `i`, exactly one sequence is nonzero at `i` + * the nonperiodic autocorrelation is equal to `0` + + INPUT: + + - ``sequences`` -- a list of four sequences. + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import is_T_sequences_set + sage: seqs = [[1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs) + True + sage: seqs = [[1, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + There should be exactly a nonzero element at every index, found 2 such elemnents at index 3 + False + + + TESTS:: + + sage: seqs = [[1, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs) + False + sage: seqs = [[1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + Nonperiodic autocorrelation should always be zero, found 2 for parameter 1 + False + sage: is_T_sequences_set([[1, 0, ], [0, -1, 0], [0, 0, 1]]) + False + sage: seqs = [[1, 2, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + Elements should be in (-1, 0, +1), but 2 was found at index 1 + False + """ + if len(sequences) != 4: + if verbose: + print(f"T-Sequence should contain 4 sequences, found {len(sequences)} instead") + return False + + + t = len(sequences[0]) + + for i in range(t): + tot = 0 + for seq in sequences: + if seq[i] not in [-1, 0, 1]: + if verbose: + print(f"Elements should be in (-1, 0, +1), but {seq[i]} was found at index {i}") + return False + tot += abs(seq[i]) + if tot != 1: + if verbose: + print(f"There should be exactly a nonzero element at every index, found {tot} such elemnents at index {i}") + return False + + for j in range(1, t): + autocorr = _nonperiodic_autocorrelation(sequences, j) + if autocorr != 0: + if verbose: + print(f"Nonperiodic autocorrelation should always be zero, found {autocorr} for parameter {j}") + return False + + return True + +def turyn_sequences_smallcases(l, existence=False): + r""" + Construction of Turyn sequences for small values of `l`. + + The data is taken from [Seb2017]_ and [CRSKKY1989]_. + + INPUT: + + - ``l`` -- integer, the length of the Turyn sequences. + + - ``existence`` -- boolean (default False). If true, only return whether the + Turyn sequences are available for the given length. + + EXAMPLES: + + By default, this method returns the four Turyn sequences :: + + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases + sage: turyn_sequences_smallcases(4) + [[1, 1, -1, -1], [1, 1, -1, 1], [1, 1, 1], [1, -1, 1]] + + If we pass the ``existence`` flag, the method will return a boolean :: + + sage: turyn_sequences_smallcases(4, existence=True) + True + + TESTS:: + + sage: turyn_sequences_smallcases(17) + Traceback (most recent call last): + ... + ValueError: Turyn sequence of length 17 is not implemented yet. + sage: turyn_sequences_smallcases(17, existence=True) + False + """ + db = { + 2: [[1, -1], [1, 1], [1], [1]], + 3: [[1, 1, 1], [1, 1, -1], [1, -1], [1, -1]], + 4: [[1, 1, -1, -1], [1, 1, -1, 1], [1, 1, 1], [1, -1, 1]], + 5: [[1, 1, -1, 1, 1], [1, 1, 1, 1, -1], [1, 1, -1, -1], [1, -1, 1, -1]], + 6: [[1, 1, 1, -1, -1, -1], [1, 1, -1, 1, -1, 1], [1, 1, -1, 1, 1], [1, 1, -1, 1, 1]], + 7: [[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]], + 8: [[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]], + 13: [[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, 1, 1, -1, -1, 1, -1, -1, -1], [1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1]], + 15: [[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], + [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]], + } + + if existence: + return l in db + + if l not in db: + raise ValueError(f"Turyn sequence of length {l} is not implemented yet.") + + return list(map(Sequence, db[l])) + +def T_sequences_construction_from_base_sequences(base_sequences, check=True): + r""" + Construct T-sequences of length `2n+p` from base sequences of length `n+p, n+p, n, n`. + + Given base sequences `A, B, C, D`, the T-sequences are constructed as described in + [KTR2005]_: + + .. MATH:: + + \begin{aligned} + T_1 &= \frac{1}{2}(A+B); 0_{n} \\ + T_2 &= \frac{1}{2}(A-B); 0_{n} \\ + T_3 &= 0_{n+p} + \frac{1}{2}(C+D) \\ + T_4 &= 0_{n+p} + \frac{1}{2}(C-D) + \end{aligned} + + INPUT: + + - ``base_sequences`` -- the base sequences that should be used to construct the T-sequences. + + - ``check`` -- boolean, if true (default) checks that the sequences created are T-sequences before returning them. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_from_base_sequences + sage: seqs = turyn_sequences_smallcases(4) + sage: T_sequences_construction_from_base_sequences(seqs) + [[1, 1, -1, 0, 0, 0, 0], + [0, 0, 0, -1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 0, 0, 1, 0]] + + TESTS:: + + sage: from sage.combinat.t_sequences import base_sequences_construction, is_T_sequences_set + sage: seqs = turyn_sequences_smallcases(4) + sage: is_T_sequences_set(T_sequences_construction_from_base_sequences(seqs)) + True + sage: T_sequences_construction_from_base_sequences([[1, -1], [-1, 1], [1]]) + Traceback (most recent call last): + ... + AssertionError + sage: X = [1,1,-1,1,-1,1,-1,1] + sage: Y = [1,-1,-1,-1,-1,-1,-1,1] + sage: Z = [1,-1,-1,1,1,1,1,-1] + sage: W = [1,1,1,-1,1,1,-1] + sage: base_seqs = base_sequences_construction([X, Y, Z, W]) + sage: is_T_sequences_set(T_sequences_construction_from_base_sequences(base_seqs)) + True + """ + + assert len(base_sequences) == 4 + + A, B, C, D = base_sequences + n = len(C) + p = len(A)-n + + assert len(A) == len(B) == len(C)+p == len(D)+p + + def seq_sum(seq1, seq2): + return [(a+b)//2 for (a, b) in zip(seq1, seq2)] + + def seq_subtract(seq1, seq2): + return [(a-b)//2 for (a, b) in zip(seq1, seq2)] + + def zero_seq(n): + return [0 for _ in range(n)] + + X1 = Sequence(seq_sum(A, B) + zero_seq(n)) + X2 = Sequence(seq_subtract(A, B) + zero_seq(n)) + X3 = Sequence(zero_seq(n+p) + seq_sum(C, D)) + X4 = Sequence(zero_seq(n+p) + seq_subtract(C, D)) + + res = [X1, X2, X3, X4] + if check: + assert is_T_sequences_set(res) + return res + +def T_sequences_construction_from_turyn_sequences(turyn_sequences, check=True): + r""" + Construct T-sequences of length `4l-1` from Turyn sequences of length `l`. + + Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in + theorem 7.7 of [Seb2017]_: + + .. MATH:: + + \begin{aligned} + T_1 &= 1; 0_{4l-2} \\ + T_2 &= 0; X/Y; 0_{2l-1} \\ + T_3 &= 0_{2l}; U/0_{l-2} \\ + T_4 &= 0_{2l} + 0_{l}/V + \end{aligned} + + INPUT: + + - ``turyn_sequences`` -- the Turyn sequences that should be used to construct the T-sequences . + + - ``check`` -- boolean, if true (default) checks that the sequences created are T-sequences before returning them. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_from_turyn_sequences, is_T_sequences_set + sage: seqs = turyn_sequences_smallcases(4) + sage: T_sequences_construction_from_turyn_sequences(seqs) + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 1, 0]] + + TESTS:: + + sage: seqs = turyn_sequences_smallcases(4) + sage: is_T_sequences_set(T_sequences_construction_from_turyn_sequences(seqs)) + True + sage: T_sequences_construction_from_turyn_sequences([[1, -1], [-1, 1], [1]]) + Traceback (most recent call last): + ... + AssertionError + """ + + assert len(turyn_sequences) == 4 + + X, U, Y, V = turyn_sequences + l = len(X) + + assert len(X) == len(U) == len(Y)+1 == len(V)+1 + + def zero_seq(n): + return [0 for _ in range(n)] + + def interleave(seq1, seq2): + res = [] + for i in range(len(seq1) + len(seq2)): + if i%2 == 0: + res.append(seq1[i//2]) + else: + res.append(seq2[i//2]) + return res + + X1 = Sequence([1]+ zero_seq(4*l-2)) + X2 = Sequence([0] + interleave(X, Y) + zero_seq(2*l-1)) + X3 = Sequence(zero_seq(2*l) + interleave(U, zero_seq(l-1))) + X4 = Sequence(zero_seq(2*l) + interleave(zero_seq(l), V)) + + res = [X1, X2, X3, X4] + if check: + assert is_T_sequences_set(res) + return res + +def T_sequences_smallcases(t, existence=False, check=True): + r""" + Construct T-sequences for some small values of `t`. + + This method will try to use the constructions defined in + :func:`T_sequences_construction_from_base_sequences` and + :func:`T_sequences_construction_from_turyn_sequences` + together with the Turyn sequences stored in :func:`turyn_sequences_smallcases`, + or base sequences created by :func:`base_sequences_smallcases`. + + This function contains also some T-sequences taken directly from [CRSKKY1989]_. + + INPUT: + + - ``t`` -- integer, the length of the T-sequences to construct. + + - ``existence`` -- boolean (default false). If true, this method only returns whether a T-sequences of + the given size can be constructed. + + - ``check`` -- boolean, if true (default) check that the sequences are T-sequences before returning them. + + EXAMPLES: + + By default, this method returns the four T-sequences :: + + sage: from sage.combinat.t_sequences import T_sequences_smallcases, is_T_sequences_set + sage: T_sequences_smallcases(9) + [[1, 1, 0, 1, 0, 0, 0, 0, 0], + [0, 0, -1, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, -1], + [0, 0, 0, 0, 0, 0, 1, -1, 0]] + + If the existence flag is passed, the method returns a boolean :: + + sage: T_sequences_smallcases(9, existence=True) + True + + TESTS:: + + sage: T_sequences_smallcases(66) + Traceback (most recent call last): + ... + ValueError: T Sequences of length 66 not yet implemented. + sage: is_T_sequences_set(T_sequences_smallcases(47)) + True + sage: is_T_sequences_set(T_sequences_smallcases(11)) + True + sage: T_sequences_smallcases(69, existence=True) + False + """ + db = { + 47: [ + [1,-1,-1,0,0,-1,1,-1]+[0]*8+[1,-1,-1,0,0,-1,-1]+[0]*24, + [0,0,0,-1,1,0,0,0,-1,-1,-1,1,1,1,1,1,0,0,0,1,-1,0,0,1]+[0]*23, + [0]*26+[-1,0,1,0,0,0,0,1,-1,1,1,1,0,0,0,0,1,0,-1,0,0], + [0]*24+ [1,1,0,-1,0,-1,1,1,-1,0,0,0,0,0,-1,1,-1,-1,0,-1,0,-1,1] + ], + 65: [ + [0]*33+[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,1,1], + [0]*32+[1]+[0]*32, + [1]*5+[-1,-1,1,1,-1,1,-1,1,1]+[-1]*7+[1,1,-1,1,-1,1,-1,1,1,-1,-1]+[0]*33, + [0]*65 + ], + 93: [ + [0,-1,0,0,-1,1,0,-1,1,0,1,1,0,0,1,1,1,0,0,-1,0,-1,1,1,1,-1,0,1,0,0,1]+[0]*33+[1,1,0,0,1,0,0,-1,0,0,-1,1,0,1]+[0]*15, + [-1,0,-1,1,0,0,1,0,0,-1,0,0,-1,-1,0,0,0,-1,1,0,1]+[0]*5+[-1,0,1,1]+[0]*32+[1,1,0,0,1,1,0,1,-1,0,1,-1,0,0,-1]+[0]*16, + [0]*32+[1,0,0,1,-1,0,1,-1,0,-1,-1,0,0,-1,-1,1,0,0,-1,0,-1,1,1,1,-1,0,1,0,0,1]+[0]*17+[1,1,0,-1]+[0]*5+[1,0,1,-1,0], + [0]*31+[1,0,1,-1,0,0,-1,0,0,1,0,0,1,1,0,0,0,-1,1,0,1]+[0]*5+[-1,0,1,1]+[0]*17+[-1,0,0,-1,0,1,-1,-1,-1,1,0,1,0,0,-1] + ] + } + + if t in db: + if existence: + return True + sequences = list(map(Sequence, db[t])) + if check: + assert is_T_sequences_set(sequences) + return sequences + if (t+1) %2 == 0 and turyn_sequences_smallcases((t+1)//2, existence=True): + if existence: + return True + turyn_seqs = turyn_sequences_smallcases((t+1)//2) + return T_sequences_construction_from_base_sequences(turyn_seqs, check=check) + + if (t+1)%4 == 0 and turyn_sequences_smallcases((t+1)//4, existence=True): + if existence: + return True + turyn_seqs = turyn_sequences_smallcases((t+1)//4) + return T_sequences_construction_from_turyn_sequences(turyn_seqs, check=check) + + for p in range(1, t): + n = (t-p)//2 + if (t-p)%2 == 0 and base_sequences_smallcases(n, p, existence=True): + if existence: + return True + base_seqs = base_sequences_smallcases(n, p, check=False) + return T_sequences_construction_from_base_sequences(base_seqs, check=check) + + if existence: + return False + raise ValueError(f'T Sequences of length {t} not yet implemented.') + + +def base_sequences_construction(turyn_type_seqs, check=True): + r"""Construct base sequences of length `2n-1, 2n-1, n, n` from Turyn type sequences of length `n,n,n,n-1`. + + Given Turyn type sequences `X, Y, Z, W` of length `n,n,n,n-1`, Theorem 1 of [KTR2005]_ shows that the + following are base sequences of length `2n-1, 2n-1, n, n`: + + .. MATH:: + + \begin{aligned} + A &= Z;W \\ + B &= Z; -W \\ + C &= X \\ + D &= Y + \end{aligned} + + INPUT: + + - ``turyn_type_seqs`` -- The list of 4 Turyn type sequences that should be used to construct the base sequences. + + - ``check`` -- boolean, if True (default) check that the resulting sequences are base sequences + before returning them. + + OUTPUT: A list containing the four base sequences. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import base_sequences_construction + sage: X = [1,1,-1,1,-1,1,-1,1] + sage: Y = [1,-1,-1,-1,-1,-1,-1,1] + sage: Z = [1,-1,-1,1,1,1,1,-1] + sage: W = [1,1,1,-1,1,1,-1] + sage: base_sequences_construction([X, Y, Z, W]) + [[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], + [1, 1, -1, 1, -1, 1, -1, 1], + [1, -1, -1, -1, -1, -1, -1, 1]] + + TESTS:: + + sage: base_sequences_construction([[1, -1], [1], [1], [-1]]) + Traceback (most recent call last): + ... + AssertionError + + .. SEEALSO:: + + :func:`is_base_sequences_tuple` + """ + assert len(turyn_type_seqs) == 4 + X, Y, Z, W = turyn_type_seqs + + assert len(X) == len(Y) == len(Z) == len(W)+1 + + A = Sequence(Z + W) + B = Sequence(Z + [-el for el in W]) + C = X + D = Y + + if check: + assert is_base_sequences_tuple([A, B, C, D]) + return [A, B, C, D] + + +def is_base_sequences_tuple(base_sequences, verbose=False): + r"""Check if the given sequences are base sequences. + + Four (-1, +1) sequences `A, B, C, D` of length `n+p, n+p, n, n` are called base sequences if + for all `j \ge 1`: + + .. MATH:: + + N_A(j)+N_B(j)+N_C(j)+N_D(j) = 0 + + where `N_X(j)` is the nonperiodic autocorrelation (See definition in [KTR2005]_). + + INPUT: + + - ``base_sequences`` -- The list of 4 sequences that should be checked. + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import is_base_sequences_tuple + sage: seqs = [[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],[1, 1, -1, 1, -1, 1, -1, 1],[1, -1, -1, -1, -1, -1, -1, 1]] + sage: is_base_sequences_tuple(seqs) + True + + If verbose is true, the function will be verbose :: + + sage: seqs = [[1, -1], [1, 1], [-1], [2]] + sage: is_base_sequences_tuple(seqs, verbose=True) + Base sequences should only contiain -1, +1, found 2 + False + + TESTS: + + sage: seqs = [[1, -1], [1], [-1]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1, -1], [-1], [1]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1, 1], [-1], [2]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1], [-1], [1]] + sage: is_base_sequences_tuple(seqs) + False + + .. SEEALSO:: + + :func:`base_sequences_construction` + """ + if len(base_sequences) != 4: + if verbose: + print(f'Base sequences should be 4, found {len(base_sequences)}') + return False + A, B, C, D = base_sequences + n = len(C) + p = len(A) - len(C) + if not (len(A) == len(B) == len(C)+p == len(D)+p): + if verbose: + print(f'Base sequences should have length n+p, n+p, n, n, found {len(A)}, {len(B)}, {len(C)}, {len(D)}') + return False + + for seq in base_sequences: + for el in seq: + if abs(el) != 1: + if verbose: + print(f'Base sequences should only contiain -1, +1, found {el}') + return False + + + for j in range(1, n+p): + autocorr = _nonperiodic_autocorrelation(A, j) + _nonperiodic_autocorrelation(B, j) + _nonperiodic_autocorrelation(C, j) + _nonperiodic_autocorrelation(D, j) + if autocorr != 0: + if verbose: + print(f"Nonperiodic autocorrelation should always be zero, found {autocorr} for parameter {j}") + return False + + return True + +def turyn_type_sequences_smallcases(n, existence=False): + r""" + Construction of Turyn type sequences for small values of `n`. + + The data is taken from [KTR2005]_ for `n= 36`, and from [BDKR2013]_ for `n\le 32`. + + INPUT: + + - ``n`` -- integer, the length of the Turyn type sequences. + + - ``existence`` -- boolean (default False). If true, only return whether the + Turyn type sequences are available for the given length. + + EXAMPLES: + + By default, this method returns the four Turyn type sequences :: + + sage: from sage.combinat.t_sequences import turyn_type_sequences_smallcases + sage: turyn_type_sequences_smallcases(4) + [[1, 1, 1, 1], [1, 1, -1, 1], [1, 1, -1, -1], [1, -1, 1]] + + If we pass the ``existence`` flag, the method will return a boolean :: + + sage: turyn_type_sequences_smallcases(4, existence=True) + True + + TESTS:: + + sage: turyn_type_sequences_smallcases(17) + Traceback (most recent call last): + ... + ValueError: Turyn type sequences of length 17 are not implemented yet. + sage: turyn_type_sequences_smallcases(17, existence=True) + False + + ALGORITHM: + + The Turyn type sequences are stored in hexadecimal format. + Given `n` hexadecimal digits `h_1, h_2,...,h_n`, it is possible to get the Turyn type sequences + by converting each `h_i` (`1 \le i \le n-1`) into a four digits binary number. Then, the j-th binary digit is + `0` if the i-th number in the j-th sequence is `1`, and it is `1` if the number in the sequence is -1. + + For the n-th digit, it should be converted to a 3 digits binary number, and then the same mapping + as before can be used (see also [BDKR2013]_). + """ + def convertLists(hexstring): + seqs = [Sequence([]), Sequence([]), Sequence([]), Sequence([])] + for c in hexstring[:-1]: + binary = bin(int(c, 16))[2:].zfill(4) + for i in range(4): + if binary[i] == '0': + seqs[i].append(1) + else: + seqs[i].append(-1) + last = bin(int(hexstring[-1], 16))[2:].zfill(3) + for i in range(3): + if last[i] == '0': + seqs[i].append(1) + else: + seqs[i].append(-1) + return seqs + + db = { + 2: '01', + 4: '0161', + 6: '006d61', + 8: '06e5c4d1', + 10: '0001f4a961', + 12: '0004f90bc961', + 14: '00036ac71c7651', + 16: '0000778e52de5561', + 18: '00006758b30d1e9a51', + 20: '000038e2739c7a0b6951', + 22: '00000f702c71a9ad565961', + 24: '00000b7c2cb2bc4b6cd9a961', + 26: '000000ff0f846f1ca5a5aa9551', + 28: '0000067cde3e50639ab46135aa51', + 30: '000000f70b106f9d427a25e9a96951', + 32: '00000138f64f1c1e77844f26d95a5961', + 36: '060989975b685d8fc80750b21c0212eceb26', + } + + if existence: + return n in db + + if n not in db: + raise ValueError(f"Turyn type sequences of length {n} are not implemented yet.") + + return convertLists(db[n]) + +def base_sequences_smallcases(n, p, existence=False, check=True): + r"""Construct base sequences of length `n+p, n+p, n, n` from available data. + + The function uses the construction :func:`base_sequences_construction`, together with + Turyn type sequences from :func:`turyn_type_sequences_smallcases` to construct base sequences + with `p = n-1`. + + Furthermore, this function uses also Turyn sequences (i.e. base sequences with `p=1`) from + :func:`turyn_sequences_smallcases`. + + INPUT: + + - ``n`` -- integer, the length of the last two base sequences. + + - ``p`` -- integer, `n+p` will be the length of the first two base sequences. + + - ``existence`` -- boolean (default False). If True, the function will only check whether the base + sequences can be constructed. + + - ``check`` -- boolean, if True (default) check that the resulting sequences are base sequences + before returning them. + + OUTPUT: + + If ``existence`` is ``False``, the function returns a list containing the four base sequences, or raises + an error if the base sequences cannot be constructed. If ``existence`` is ``True``, the function returns a + boolean, which is ``True`` if the base sequences can be constructed and ``False`` otherwise. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import base_sequences_smallcases + sage: base_sequences_smallcases(8, 7) + [[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], + [1, 1, -1, 1, -1, 1, -1, 1], + [1, -1, -1, -1, -1, -1, -1, 1]] + + If ``existence`` is ``True``, the function returns a boolean :: + + sage: base_sequences_smallcases(8, 7, existence=True) + True + sage: base_sequences_smallcases(7, 5, existence=True) + False + + TESTS:: + + sage: base_sequences_smallcases(7, 5) + Traceback (most recent call last): + ... + ValueError: Base sequences of order 12, 12, 7, 7 not yet implemented. + sage: seqs = base_sequences_smallcases(16, 15) + sage: len(seqs[0]) == len(seqs[1]) == 16+15 + True + sage: len(seqs[2]) == len(seqs[3]) == 16 + True + """ + + if existence: + return p == n-1 and turyn_type_sequences_smallcases(n, existence=True) + + if p == n-1 and turyn_type_sequences_smallcases(n, existence=True): + if existence: + return True + turyn_type_seqs = turyn_type_sequences_smallcases(n) + return base_sequences_construction(turyn_type_seqs, check=check) + if p == 1 and turyn_sequences_smallcases(n+p, existence=True): + if existence: + return True + return turyn_sequences_smallcases(n+p) + + raise ValueError(f'Base sequences of order {n+p}, {n+p}, {n}, {n} not yet implemented.') diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index f5652f86733..1105a3ea177 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -6098,6 +6098,7 @@ class SemistandardTableaux_all(SemistandardTableaux, DisjointUnionEnumeratedSets """ All semistandard tableaux. """ + def __init__(self, max_entry=None): r""" Initializes the class of all semistandard tableaux. @@ -6156,6 +6157,7 @@ class SemistandardTableaux_size_inf(SemistandardTableaux): """ Semistandard tableaux of fixed size `n` with no maximum entry. """ + def __init__(self, n): r""" Initialize the class of semistandard tableaux of size ``n`` with no @@ -6256,6 +6258,7 @@ class SemistandardTableaux_shape_inf(SemistandardTableaux): """ Semistandard tableaux of fixed shape `p` and no maximum entry. """ + def __init__(self, p): r""" Initializes the class of semistandard tableaux of shape ``p`` and no @@ -6343,6 +6346,7 @@ class SemistandardTableaux_size(SemistandardTableaux): """ Semistandard tableaux of fixed size `n`. """ + def __init__(self, n, max_entry=None): r""" Initializes the class of semistandard tableaux of size ``n``. @@ -6546,6 +6550,7 @@ class SemistandardTableaux_shape(SemistandardTableaux): - ``p`` -- a partition - ``max_entry`` -- the max entry; defaults to the size of ``p`` """ + def __init__(self, p, max_entry=None): r""" Initializes the class of semistandard tableaux of shape ``p``, with a @@ -6757,6 +6762,7 @@ class SemistandardTableaux_shape_weight(SemistandardTableaux_shape): r""" Semistandard tableaux of fixed shape `p` and weight `\mu`. """ + def __init__(self, p, mu): r""" Initializes the class of all semistandard tableaux of shape ``p`` and @@ -6871,6 +6877,7 @@ class SemistandardTableaux_size_weight(SemistandardTableaux): r""" Semistandard tableaux of fixed size `n` and weight `\mu`. """ + def __init__(self, n, mu): r""" Initializes the class of semistandard tableaux of size ``n`` and @@ -7119,6 +7126,7 @@ class RowStandardTableaux_all(RowStandardTableaux, DisjointUnionEnumeratedSets): """ All row standard tableaux. """ + def __init__(self): r""" Initializes the class of all standard tableaux. @@ -7183,6 +7191,7 @@ class RowStandardTableaux_size(RowStandardTableaux, DisjointUnionEnumeratedSets) sage: RowStandardTableaux(40).cardinality() # not tested, too long 2063837185739279909309355007659204891024472174278 """ + def __init__(self, n): r""" Initializes the class of all row standard tableaux of size ``n``. @@ -7252,6 +7261,7 @@ class RowStandardTableaux_shape(RowStandardTableaux): """ Row Standard tableaux of a fixed shape `p`. """ + def __init__(self, p): r""" Initializes the class of all row standard tableaux of a given shape. @@ -7519,6 +7529,7 @@ class StandardTableaux_all(StandardTableaux, DisjointUnionEnumeratedSets): """ All standard tableaux. """ + def __init__(self): r""" Initializes the class of all standard tableaux. @@ -7570,6 +7581,7 @@ class StandardTableaux_size(StandardTableaux, DisjointUnionEnumeratedSets): sage: TestSuite( StandardTableaux(4) ).run() """ + def __init__(self, n): r""" Initializes the class of all standard tableaux of size ``n``. @@ -7756,6 +7768,7 @@ class StandardTableaux_shape(StandardTableaux): """ Semistandard tableaux of a fixed shape `p`. """ + def __init__(self, p): r""" Initializes the class of all semistandard tableaux of a given shape. @@ -8148,6 +8161,7 @@ class Tableau_class(Tableau): """ This exists solely for unpickling ``Tableau_class`` objects. """ + def __setstate__(self, state): r""" Unpickle old ``Tableau_class`` objects. @@ -8653,6 +8667,7 @@ class IncreasingTableaux_all(IncreasingTableaux, DisjointUnionEnumeratedSets): sage: list(T) [[]] """ + def __init__(self, max_entry=None): r""" Initializes the class of all increasing tableaux. @@ -8707,6 +8722,7 @@ class IncreasingTableaux_size_inf(IncreasingTableaux): """ Increasing tableaux of fixed size `n` with no maximum entry. """ + def __init__(self, n): r""" Initializes the class of increasing tableaux of size ``n`` with no @@ -8787,6 +8803,7 @@ class IncreasingTableaux_shape_inf(IncreasingTableaux): """ Increasing tableaux of fixed shape `p` and no maximum entry. """ + def __init__(self, p): r""" Initializes the class of increasing tableaux of shape ``p`` and no @@ -8868,6 +8885,7 @@ class IncreasingTableaux_size(IncreasingTableaux): """ Increasing tableaux of fixed size `n`. """ + def __init__(self, n, max_entry=None): r""" Initializes the class of increasing tableaux of size ``n``. @@ -8987,6 +9005,7 @@ class IncreasingTableaux_shape(IncreasingTableaux): - ``p`` -- a partition - ``max_entry`` -- the max entry; defaults to the size of ``p`` """ + def __init__(self, p, max_entry=None): r""" Initializes the class of increasing tableaux of shape ``p``, with a @@ -9105,6 +9124,7 @@ class IncreasingTableaux_shape_weight(IncreasingTableaux_shape): r""" Increasing tableaux of fixed shape `p` and binary weight `wt`. """ + def __init__(self, p, wt): r""" Initializes the class of all increasing tableaux of shape ``p`` and @@ -9249,6 +9269,7 @@ class IncreasingTableaux_size_weight(IncreasingTableaux): r""" Increasing tableaux of fixed size `n` and weight `wt`. """ + def __init__(self, n, wt): r""" Initializes the class of increasing tableaux of size ``n`` and diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 34565ecb642..8b9af538b00 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -231,7 +231,7 @@ from sage.arith.all import factorial from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer import Integer -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family from sage.sets.positive_integers import PositiveIntegers @@ -2980,6 +2980,7 @@ class RowStandardTableauTuples_all(RowStandardTableauTuples, DisjointUnionEnumer Default class of all :class:`RowStandardTableauTuples` with an arbitrary :meth:`~TableauTuples.level` and :meth:`~TableauTuples.size`. """ + def __init__(self): r""" Initializes the class of all row standard tableaux. @@ -3030,6 +3031,7 @@ class RowStandardTableauTuples_level(RowStandardTableauTuples, DisjointUnionEnum Class of all :class:`RowStandardTableauTuples` with a fixed ``level`` and arbitrary ``size``. """ + def __init__(self, level): r""" Initializes the class of row standard tableaux of level @@ -3127,6 +3129,7 @@ class RowStandardTableauTuples_size(RowStandardTableauTuples, DisjointUnionEnume Class of all :class:`RowStandardTableauTuples` with an arbitrary ``level`` and a fixed ``size``. """ + def __init__(self, size): r""" Initializes the class of row standard tableaux of size ``size`` of @@ -3226,6 +3229,7 @@ class RowStandardTableauTuples_level_size(RowStandardTableauTuples, DisjointUnio Class of all :class:`RowStandardTableauTuples` with a fixed ``level`` and a fixed ``size``. """ + def __init__(self, level, size): r""" Initializes the class of row standard tableaux of level ``level`` @@ -3339,6 +3343,7 @@ class RowStandardTableauTuples_shape(RowStandardTableauTuples): """ Class of all :class:`RowStandardTableauTuples` of a fixed shape. """ + def __init__(self, shape): r""" Initializes the class of row standard tableaux of shape ``p`` @@ -3563,6 +3568,7 @@ class RowStandardTableauTuples_residue(RowStandardTableauTuples): sage: RowStandardTableauTuple([[[5,6],[7]],[[1,2,3],[4]]]).residue_sequence(3,(0,1)).row_standard_tableaux() Row standard tableaux with 3-residue sequence (1,2,0,0,0,1,2) and multicharge (0,1) """ + def __init__(self, residue): r""" Initialize ``self``. @@ -3804,6 +3810,7 @@ class RowStandardTableauTuples_residue_shape(RowStandardTableauTuples_residue): ([[5, 6], [4]], [[3, 7], [1], [2]]), ([[5, 6], [1]], [[3, 7], [4], [2]])] """ + def __init__(self, residue, shape): r""" Initialize ``self``. @@ -5071,6 +5078,7 @@ class StandardTableaux_residue(StandardTableauTuples): sage: StandardTableauTuple([[[5,6],[7]],[[1,2,3],[4]]]).residue_sequence(3,(0,1)).standard_tableaux() Standard tableaux with 3-residue sequence (1,2,0,0,0,1,2) and multicharge (0,1) """ + def __init__(self, residue): r""" Initialize ``self``. @@ -5206,6 +5214,7 @@ class StandardTableaux_residue_shape(StandardTableaux_residue): ([[2, 5], [6]], [[1, 3], [4], [7]]), ([[1, 5], [6]], [[2, 3], [4], [7]])] """ + def __init__(self, residue, shape): r""" Initialize ``self``. diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index 648fe2f8423..d426e115a8a 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -467,6 +467,8 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): ############################## # Class Polyomino ############################## + + class Polyomino(SageObject): r""" A polyomino in `\ZZ^d`. @@ -489,6 +491,7 @@ class Polyomino(SageObject): sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue """ + def __init__(self, coords, color='gray', dimension=None): r""" Constructor. @@ -1440,7 +1443,6 @@ def show2d(self, size=0.7, color='black', thickness=1): G += line(edge, color=color, thickness=thickness) return G - def self_surrounding(self, radius, remove_incomplete_copies=True, ncpus=None): r""" @@ -1594,6 +1596,7 @@ class TilingSolver(SageObject): ... NotImplementedError: When reflection is allowed and rotation is not allowed """ + def __init__(self, pieces, box, rotation=True, reflection=False, reusable=False, outside=False): r""" diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py new file mode 100644 index 00000000000..7fca1bd0252 --- /dev/null +++ b/src/sage/combinat/triangles_FHM.py @@ -0,0 +1,744 @@ +""" +Combinatorial triangles for posets and fans + +This provides several classes and methods to convert between them. +Elements of the classes are polynomials in two variables `x` and `y`, +possibly with other parameters. The conversion methods amount to specific +invertible rational change-of-variables involving `x` and `y`. + +These polynomial are called triangles because their supports, the sets +of exponents where their coefficients can be non-zero, have a triangular shape. + +The M-triangle class is motivated by the generating series of Möbius numbers +for graded posets. A typical example is:: + + sage: W = SymmetricGroup(4) + sage: posets.NoncrossingPartitions(W).M_triangle() + M: x^3*y^3 - 6*x^2*y^3 + 6*x^2*y^2 + 10*x*y^3 - 16*x*y^2 + - 5*y^3 + 6*x*y + 10*y^2 - 6*y + 1 + sage: unicode_art(_) + ⎛ -5 10 -6 1⎞ + ⎜ 10 -16 6 0⎟ + ⎜ -6 6 0 0⎟ + ⎝ 1 0 0 0⎠ + +The F-triangle class is motivated by the generating series of pure +simplicial complexes endowed with a distinguished facet. One can also +think about complete fans endowed with a distinguished maximal +cone. A typical example is:: + + sage: C = ClusterComplex(['A',3]) + sage: f = C.greedy_facet() + sage: C.F_triangle(f) + F: 5*x^3 + 5*x^2*y + 3*x*y^2 + y^3 + 10*x^2 + 8*x*y + 3*y^2 + 6*x + 3*y + 1 + sage: unicode_art(_) + ⎛ 1 0 0 0⎞ + ⎜ 3 3 0 0⎟ + ⎜ 3 8 5 0⎟ + ⎝ 1 6 10 5⎠ + +The H-triangles are related to the F-triangles by a relationship +similar to the classical link between the f-vector and the h-vector of a +simplicial complex. + +The Gamma-triangles are related to the H-triangles by an +analog of the relationship between gamma-vectors and h-vectors of flag +simplicial complexes. +""" +from sage.matrix.constructor import matrix +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.structure.sage_object import SageObject + + +def _matrix_display(self, variables=None): + """ + Return the 2-variable polynomial ``self`` as a matrix for display. + + INPUT: + + - ``variables`` -- optional choice of 2 variables + + OUPUT: + + matrix + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import _matrix_display + sage: x, y = PolynomialRing(QQ,['x', 'y']).gens() + sage: _matrix_display(x**2+x*y+y**3) + [1 0 0] + [0 0 0] + [0 1 0] + [0 0 1] + + With a specific choice of variables:: + + sage: x, y, z = PolynomialRing(QQ,['x','y','z']).gens() + sage: _matrix_display(x**2+z*x*y+z*y**3+z*x,[y,z]) + [ x x 0 1] + [x^2 0 0 0] + sage: _matrix_display(x**2+z*x*y+z*y**3+z*x,[x,z]) + [ y^3 y + 1 0] + [ 0 0 1] + """ + support = self.exponents() + if variables is None: + ring = self.parent().base_ring() + x, y = self.parent().gens() + ix = 0 + iy = 1 + else: + x, y = variables + ring = self.parent() + all_vars = x.parent().gens() + ix = all_vars.index(x) + iy = all_vars.index(y) + minx = min(u[ix] for u in support) + maxy = max(u[iy] for u in support) + deltax = max(u[ix] for u in support) - minx + 1 + deltay = maxy - min(u[iy] for u in support) + 1 + mat = matrix(ring, deltay, deltax) + for u in support: + ex = u[ix] + ey = u[iy] + mat[maxy - ey, ex - minx] = self.coefficient({x: ex, y: ey}) + return mat + + +class Triangle(SageObject): + """ + Common class for different kinds of triangles. + + This serves as a base class for F-triangles, H-triangles, M-triangles + and Gamma-triangles. + + The user should use these subclasses directly. + + The input is a polynomial in two variables. One can also give a + polynomial with more variables and specify two chosen variables. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = Triangle(1+4*x+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 4⎠ + """ + + def __init__(self, poly, variables=None): + """ + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = Triangle(1+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 0⎠ + """ + if variables is None: + self._vars = poly.parent().gens() + else: + self._vars = variables + self._poly = poly + self._n = max(self._poly.degree(v) for v in self._vars) + + def _ascii_art_(self): + """ + Return the ascii-art representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: ascii_art(ht) + [0 2] + [1 0] + """ + return self.matrix()._ascii_art_() + + def _unicode_art_(self): + """ + Return the unicode representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 0⎠ + """ + return self.matrix()._unicode_art_() + + def _repr_(self) -> str: + """ + Return the string representation (as a polynomial). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: ht + H: 2*x*y + 1 + """ + return self._prefix + ": " + repr(self._poly) + + def _latex_(self): + r""" + Return the LaTeX representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: latex(ht) + \left(\begin{array}{rr} + 0 & 2 \\ + 1 & 0 + \end{array}\right) + """ + return self.matrix()._latex_() + + def __eq__(self, other) -> bool: + """ + Test for equality. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h1 = H_triangle(1+2*x*y) + sage: h2 = H_triangle(1+3*x*y) + sage: h1 == h1 + True + sage: h1 == h2 + False + """ + if isinstance(other, Triangle): + return self._poly == other._poly + return self._poly == other + + def __ne__(self, other) -> bool: + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h1 = H_triangle(1+2*x*y) + sage: h2 = H_triangle(1+3*x*y) + sage: h1 != h1 + False + sage: h1 != h2 + True + """ + return not self == other + + def __call__(self, *args): + """ + Return the evaluation (as a polynomial). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+3*x*y) + sage: h(4,5) + 61 + """ + return self._poly(*args) + + def __getitem__(self, *args): + """ + Return some coefficient. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x+3*x*y) + sage: h[1,1] + 3 + """ + return self._poly.__getitem__(*args) + + def __hash__(self): + """ + Return the hash value. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: g = H_triangle(1+2*x*y) + sage: hash(h) == hash(g) + True + """ + return hash(self._poly) + + def matrix(self): + """ + Return the associated matrix for display. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.matrix() + [0 2] + [1 0] + """ + return _matrix_display(self._poly, variables=self._vars) + + def polynomial(self): + """ + Return the triangle as a bare polynomial. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.polynomial() + 2*x*y + 1 + """ + return self._poly + + def truncate(self, d): + """ + Return the truncated triangle. + + INPUT: + + - ``d`` -- integer + + As a polynomial, this means that all monomials with a power + of either `x` or `y` greater than or equal to ``d`` are dismissed. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.truncate(2) + H: 2*x*y + 1 + """ + p = self._poly + for v in self._vars: + p = p.truncate(v, d) + return self.__class__(p, self._vars) + + +class M_triangle(Triangle): + """ + Class for the M-triangles. + + This is motivated by generating series of Möbius numbers of graded posets. + + EXAMPLES:: + + sage: x, y = polygens(ZZ, 'x,y') + sage: P = Poset({2:[1]}) + sage: P.M_triangle() + M: x*y - y + 1 + """ + _prefix = 'M' + + def dual(self): + """ + Return the dual M-triangle. + + This is the M-triangle of the dual poset, hence an involution. + + When seen as a matrix, this performs a symmetry with respect to the + northwest-southeast diagonal. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: mt = M_triangle(x*y - y + 1) + sage: mt.dual() == mt + True + """ + x, y = self._vars + n = self._n + A = self._poly.parent() + + dict_dual = {(n - dy, n - dx): coeff + for (dx, dy), coeff in self._poly.dict().items()} + return M_triangle(A(dict_dual), variables=(x, y)) + + def transmute(self): + """ + Return the image of ``self`` by an involution. + + OUTPUT: + + another M-triangle + + The involution is defined by converting to an H-triangle, + transposing the matrix, and then converting back to an M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: nc3 = x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + sage: m = M_triangle(nc3) + sage: m2 = m.transmute(); m2 + M: 2*x^2*y^2 - 3*x*y^2 + 2*x*y + y^2 - 2*y + 1 + sage: m2.transmute() == m + True + """ + return self.h().transpose().m() + + def h(self): + """ + Return the associated H-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: M_triangle(1-y+x*y).h() + H: x*y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 + sage: M_triangle(mt, [x,y]).h() + H: x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=y / (y - 1), y=(y - 1) * x / (1 + (y - 1) * x)) + step *= (1 + (y - 1) * x)**n + polyh = step.numerator() + return H_triangle(polyh, variables=(x, y)) + + def f(self): + """ + Return the associated F-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: M_triangle(1-y+x*y).f() + F: x + y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 + sage: M_triangle(mt, [x,y]).f() + F: (2*h - 3)*x^2 + 2*x*y + y^2 + (2*h - 2)*x + 2*y + 1 + """ + return self.h().f() + + +class H_triangle(Triangle): + """ + Class for the H-triangles. + """ + _prefix = 'H' + + def transpose(self): + """ + Return the transposed H-triangle. + + OUTPUT: + + another H-triangle + + This operation is an involution. When seen as a matrix, it + performs a symmetry with respect to the northwest-southeast + diagonal. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).transpose() + H: x*y + 1 + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).transpose() + H: x^2*y^2 + x^2*y + 2*x*y + 1 + """ + x, y = self._vars + n = self._n + A = self._poly.parent() + + dict_dual = {(n - dy, n - dx): coeff + for (dx, dy), coeff in self._poly.dict().items()} + return H_triangle(A(dict_dual), variables=(x, y)) + + def m(self): + """ + Return the associated M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ht = H_triangle(x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1, variables=[x,y]) + sage: ht.m() + M: x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + + (2*h - 3)*y^2 + (-2*h + 2)*y + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=(x - 1) * y / (1 - y), y=x / (x - 1)) * (1 - y)**n + polym = step.numerator() + return M_triangle(polym, variables=(x, y)) + + def f(self): + """ + Return the associated F-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).f() + F: x + y + 1 + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() + F: 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: flo = H_triangle(1+4*x+2*x**2+x*y*(4+8*x)+ + ....: x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4).f(); flo + F: 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + + 6*y^2 + 8*x + 4*y + 1 + sage: flo(-1-x,-1-y) == flo + True + + TESTS:: + + sage: x,y,h = polygens(ZZ,'x,y,h') + sage: ht = x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1 + sage: H_triangle(ht,[x,y]).f() + F: 2*x^2*h - 3*x^2 + 2*x*y + y^2 + 2*x*h - 2*x + 2*y + 1 + """ + x, y = self._vars + n = self._n + step1 = self._poly(x=x / (1 + x), y=y) * (x + 1)**n + step2 = step1(x=x, y=y / x) + polyf = step2.numerator() + return F_triangle(polyf, variables=(x, y)) + + def gamma(self): + """ + Return the associated Gamma-triangle. + + In some cases, this is a more condensed way to encode + the same amount of information. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ht = x**2*y**2 + 2*x*y + x + 1 + sage: H_triangle(ht).gamma() + Γ: y^2 + x + + sage: W = SymmetricGroup(5) + sage: P = posets.NoncrossingPartitions(W) + sage: P.M_triangle().h().gamma() + Γ: y^4 + 3*x*y^2 + 2*x^2 + 2*x*y + x + """ + x, y = self._vars + n = self._n + remain = self._poly + gamma = x.parent().zero() + for k in range(n, -1, -1): + step = remain.coefficient({x: k}) + gamma += x**(n - k) * step + remain -= x**(n - k) * step.homogenize(x)(x=1 + x, y=1 + x * y) + return Gamma_triangle(gamma, variables=(x, y)) + + def vector(self): + """ + Return the h-vector as a polynomial in one variable. + + This is obtained by letting `y=1`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ht = x**2*y**2 + 2*x*y + x + 1 + sage: H_triangle(ht).vector() + x^2 + 3*x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + return anneau(self._poly(y=1)) + + +class F_triangle(Triangle): + """ + Class for the F-triangles. + """ + _prefix = 'F' + + def h(self): + """ + Return the associated H-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: x,y = polygens(ZZ,'x,y') + sage: ft = F_triangle(1+x+y) + sage: ft.h() + H: x*y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ft = 1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2 + sage: F_triangle(ft, [x,y]).h() + H: x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + """ + x, y = self._vars + n = self._n + step = (1 - x)**n * self._poly(x=x / (1 - x), y=x * y / (1 - x)) + polyh = step.numerator() + return H_triangle(polyh, variables=(x, y)) + + def m(self): + """ + Return the associated M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).f() + F: x + y + 1 + sage: _.m() + M: x*y - y + 1 + + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() + F: 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: _.m() + M: x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + + TESTS:: + + sage: p = 1+4*x+2*x**2+x*y*(4+8*x) + sage: p += x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4 + sage: flo = H_triangle(p).f(); flo + F: 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + + 6*y^2 + 8*x + 4*y + 1 + sage: flo.m() + M: x^4*y^4 - 8*x^3*y^4 + 8*x^3*y^3 + 20*x^2*y^4 - 36*x^2*y^3 + - 20*x*y^4 + 16*x^2*y^2 + 48*x*y^3 + 7*y^4 - 36*x*y^2 - 20*y^3 + + 8*x*y + 20*y^2 - 8*y + 1 + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ft = F_triangle(1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2,(x,y)) + sage: ft.m() + M: x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + + (2*h - 3)*y^2 + (-2*h + 2)*y + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=y * (x - 1) / (1 - x * y), y=x * y / (1 - x * y)) + step *= (1 - x * y)**n + polym = step.numerator() + return M_triangle(polym, variables=(x, y)) + + def vector(self): + """ + Return the f-vector as a polynomial in one variable. + + This is obtained by letting `y=x`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ft = 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: F_triangle(ft).vector() + 5*x^2 + 5*x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + x = anneau.gen() + return anneau(self._poly(y=x)) + + +class Gamma_triangle(Triangle): + """ + Class for the Gamma-triangles. + """ + _prefix = 'Γ' + + def h(self): + r""" + Return the associated H-triangle. + + The transition between Gamma-triangles and H-triangles is defined by + + .. MATH:: + + H(x,y) = (1+x)^d \sum_{0\leq i; 0\leq j \leq d-2i} \gamma_{i,j} + \left(\frac{x}{(1+x)^2}\right)^i \left(\frac{1+xy}{1+x}\right)^j + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Gamma_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: g = y**2 + x + sage: Gamma_triangle(g).h() + H: x^2*y^2 + 2*x*y + x + 1 + + sage: a, b = polygen(ZZ, 'a, b') + sage: x, y = polygens(a.parent(),'x,y') + sage: g = Gamma_triangle(y**3+a*x*y+b*x,(x,y)) + sage: hh = g.h() + sage: hh.gamma() == g + True + """ + x, y = self._vars + n = self._n + resu = (1 + x)**n * self._poly(x=x / (1 + x)**2, + y=(1 + x * y) / (1 + x)) + polyh = resu.numerator() + return H_triangle(polyh, variables=(x, y)) + + def vector(self): + """ + Return the gamma-vector as a polynomial in one variable. + + This is obtained by letting `y=1`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Gamma_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: gt = y**2 + x + sage: Gamma_triangle(gt).vector() + x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + return anneau(self._poly(y=1)) diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index e9df13c746c..6422d920378 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -310,7 +310,7 @@ It is unfortunate to have to recalculate everything if at some point we wanted the 101-st coefficient. Lazy power series (see -:mod:`sage.combinat.species.series`) come into their own here, in that +:mod:`sage.rings.lazy_series_ring`) come into their own here, in that one can define them from a system of equations without solving it, and, in particular, without needing a closed form for the answer. We begin by defining the ring of lazy power series:: @@ -320,8 +320,7 @@ Then we create a “free” power series, which we name, and which we then define by a recursive equation:: - sage: C = L() - sage: C._name = 'C' + sage: C = L.undefined(valuation=1) sage: C.define( z + C * C ) :: @@ -1672,7 +1671,7 @@ We begin by redefining the complete binary trees; to do so, we stipulate the recurrence relation directly on the sets:: - sage: BT = CombinatorialSpecies() + sage: BT = CombinatorialSpecies(min=1) sage: Leaf = SingletonSpecies() sage: BT.define( Leaf + (BT*BT) ) @@ -1697,7 +1696,7 @@ We recover the generating function for the Catalan numbers:: sage: g = BT.isotype_generating_series(); g - x + x^2 + 2*x^3 + 5*x^4 + 14*x^5 + O(x^6) + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) which is returned in the form of a lazy power series:: @@ -1715,7 +1714,7 @@ The Fibonacci sequence is easily recognized here, hence the name:: - sage: L = FW.isotype_generating_series().coefficients(15); L + sage: L = FW.isotype_generating_series()[:15]; L [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987] :: diff --git a/src/sage/combinat/vector_partition.py b/src/sage/combinat/vector_partition.py index 953e450d8a1..ba1a51c29d4 100644 --- a/src/sage/combinat/vector_partition.py +++ b/src/sage/combinat/vector_partition.py @@ -4,9 +4,11 @@ AUTHORS: - Amritanshu Prasad (2013): Initial version +- Shriya M (2022): Added new parameters such as ``distinct``, ``parts`` and ``is_repeatable`` """ #***************************************************************************** # Copyright (C) 2013 Amritanshu Prasad <amri@imsc.res.in> +# 2022 Shriya M <25shriya@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # @@ -26,10 +28,11 @@ from sage.combinat.combinat import CombinatorialElement from sage.combinat.partition import Partition + def find_min(vect): """ Return a string of ``0``'s with one ``1`` at the location where the list - vect has its last entry which is not equal to ``0``. + ``vect`` has its last entry which is not equal to ``0``. INPUT: @@ -56,6 +59,7 @@ def find_min(vect): min[i-1]=1 return min + def IntegerVectorsIterator(vect, min = None): """ Return an iterator over the list of integer vectors which are componentwise @@ -82,6 +86,7 @@ def IntegerVectorsIterator(vect, min = None): sage: list(IntegerVectorsIterator([1, 1], min = [1, 0])) [[1, 0], [1, 1]] """ + vect = list(vect) if not vect: yield [] else: @@ -97,6 +102,7 @@ def IntegerVectorsIterator(vect, min = None): yield [j] + vec + class VectorPartition(CombinatorialElement): r""" A vector partition is a multiset of integer vectors. @@ -158,17 +164,22 @@ def partition_at_vertex(self, i): """ return Partition(sorted([vec[i] for vec in self._list], reverse = True)) + class VectorPartitions(UniqueRepresentation, Parent): r""" Class of all vector partitions of ``vec`` with all parts greater than - or equal to ``min`` in lexicographic order. + or equal to ``min`` in lexicographic order, with parts from ``parts``. A vector partition of ``vec`` is a list of vectors with non-negative integer entries whose sum is ``vec``. INPUT: - - ``vec`` -- a list of non-negative integers. + - ``vec`` - Integer vector + - ``min`` - Integer vector dominated elementwise by ``vec`` + - ``parts`` - Finite list of possible parts + - ``distinct`` - Boolean, set to ``True`` if only vector partitions with distinct parts are enumerated + - ``is_repeatable`` - Boolean function on ``parts`` which gives ``True`` in parts that can be repeated EXAMPLES: @@ -188,6 +199,16 @@ class VectorPartitions(UniqueRepresentation, Parent): [[1, 1], [1, 1]] [[2, 2]] + If ``distinct`` is set to be True, then distinct part partitions are created:: + + sage: VP = VectorPartitions([2,2], distinct = True) + sage: list(VP) + [[[0, 1], [1, 0], [1, 1]], + [[0, 1], [2, 1]], + [[0, 2], [2, 0]], + [[1, 0], [1, 2]], + [[2, 2]]] + If ``min`` is specified, then the class consists of only those vector partitions whose parts are all greater than or equal to ``min`` in lexicographic order:: @@ -198,9 +219,30 @@ class VectorPartitions(UniqueRepresentation, Parent): [[1, 0], [1, 2]] [[1, 1], [1, 1]] [[2, 2]] + sage: VP = VectorPartitions([2, 2], min = [1, 0], distinct = True) + sage: for vecpar in VP: + ....: print(vecpar) + [[1, 0], [1, 2]] + [[2, 2]] + + If ``parts`` is specified, then the class consists only of those vector partitions + whose parts are from ``parts``:: + + sage: Vec_Par = VectorPartitions([2,2], parts=[[0,1],[1,0],[1,1]]) + sage: list(Vec_Par) + [[[0, 1], [0, 1], [1, 0], [1, 0]], [[0, 1], [1, 0], [1, 1]], [[1, 1], [1, 1]]] + + If ``is_repeatable`` is specified, then the parts which satisfy the boolean function + ``is_repeatable`` are allowed to be repeated:: + + + sage: Vector_Partitions = VectorPartitions([2,2], parts=[[0,1],[1,0],[1,1]], is_repeatable=lambda vec: sum(vec)%2!=0) + sage: list(Vector_Partitions) + [[[0, 1], [0, 1], [1, 0], [1, 0]], [[0, 1], [1, 0], [1, 1]]] + """ @staticmethod - def __classcall_private__(cls, vec, min=None): + def __classcall_private__(cls, vec, min=None, parts=None, distinct=False, is_repeatable=None): r""" Create the class of vector partitions of ``vec`` where all parts are greater than or equal to the vector ``min``. @@ -214,11 +256,19 @@ def __classcall_private__(cls, vec, min=None): """ if min is None: min = find_min(vec) # tuple([0 for v in vec[:-1]]+[1]) - min = tuple(min) - vec = tuple(vec) - return super().__classcall__(cls, tuple(vec), min) - - def __init__(self, vec, min): + if parts is None: + parts = list(IntegerVectorsIterator(vec, min=min)) + if [0]*len(vec) in parts: + parts.remove([0]*len(vec)) + if min in parts: + min_index = parts.index(min) + parts = parts[min_index:] + parts = list(parts) + for part_index in range(len(parts)): + parts[part_index] = tuple(parts[part_index]) + return super().__classcall__(cls, tuple(vec), tuple(min), tuple(parts), distinct, is_repeatable) + + def __init__(self, vec, min=None, parts=None, distinct=False, is_repeatable=None): r""" Initialize ``self``. @@ -230,6 +280,9 @@ def __init__(self, vec, min): Parent.__init__(self, category=FiniteEnumeratedSets()) self._vec = vec self._min = min + self._parts = parts + self._distinct = distinct + self._is_repeatable = is_repeatable def _element_constructor_(self, vecpar): """ @@ -260,9 +313,23 @@ def __iter__(self): if all(coord == 0 for coord in self._vec): yield self.element_class(self, []) # the zero vector has only the empty partition else: - for vec in IntegerVectorsIterator(list(self._vec), min = list(self._min)): # choose the first part - if tuple(vec) == self._vec: - yield self.element_class(self, [vec]) + for part in self._parts: # choose the first part + if tuple(part) == self._vec: + yield self.element_class(self, [list(part)]) + elif any(part[i]>self._vec[i] for i in range(len(self._vec))): + pass else:# recursively find all possibilities for the rest of the vector partition - for smaller_partition in VectorPartitions([x-vec[i] for i,x in enumerate(self._vec)], min = vec): - yield self.element_class(self, [vec] + list(smaller_partition)) + new_vec = tuple(self._vec[i]-part[i] for i in range(len(self._vec))) + i = self._parts.index(part) + if self._is_repeatable is None: + if self._distinct: + new_parts = self._parts[i+1:] + else: + new_parts = self._parts[i:] + else: + if self._is_repeatable(part): + new_parts = self._parts[i:] + else: + new_parts = self._parts[i+1:] + for vecpar in VectorPartitions(new_vec, min=self._min, parts=new_parts, distinct=self._distinct, is_repeatable=self._is_repeatable): + yield self.element_class(self, [list(part)] + list(vecpar)) diff --git a/src/sage/combinat/words/abstract_word.py b/src/sage/combinat/words/abstract_word.py index 80dab5e552b..ff3be9930ad 100644 --- a/src/sage/combinat/words/abstract_word.py +++ b/src/sage/combinat/words/abstract_word.py @@ -833,7 +833,7 @@ def delta(self): word: 1211222112112112221122211222112112112221... """ from sage.combinat.words.word import Word - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return Word(self._delta_iterator(), alphabet=NN) def _iterated_right_palindromic_closure_iterator(self, f=None): diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 5773491e025..7fc8d3b4b18 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -217,6 +217,7 @@ from itertools import repeat from collections import defaultdict from itertools import islice, cycle + from sage.combinat.words.abstract_word import Word_class from sage.combinat.words.words import Words from sage.misc.cachefunc import cached_method @@ -2622,7 +2623,7 @@ def palindromic_lacunas_study(self, f=None): and lacunas of ``self`` (see [BMBL2008]_ and [BMBFLR2008]_). Note that a word `w` has at most `|w| + 1` different palindromic factors - (see [DJP2001]_). For `f`-palindromes (or pseudopalidromes or theta-palindromes), + (see [DJP2001]_). For `f`-palindromes (or pseudopalindromes or theta-palindromes), the maximum number of `f`-palindromic factors is `|w|+1-g_f(w)`, where `g_f(w)` is the number of pairs `\{a, f(a)\}` such that `a` is a letter, `a` is not equal to `f(a)`, and `a` or `f(a)` occurs in `w`, see [Star2011]_. @@ -5949,7 +5950,6 @@ def sturmian_desubstitute_as_possible(self): desubstitued_word = desubstitued_word + w_running ** (current_run_length - min_run) return desubstitued_word.sturmian_desubstitute_as_possible() - def is_sturmian_factor(self): r""" Tell whether ``self`` is a factor of a Sturmian word. @@ -6010,7 +6010,6 @@ def is_sturmian_factor(self): """ return self.sturmian_desubstitute_as_possible().is_empty() - def is_tangent(self): r""" Tell whether ``self`` is a tangent word. @@ -6080,7 +6079,6 @@ def is_tangent(self): mini = min(mini , height) return (maxi - mini <= 2) - # TODO. # 1. Those three swap functions should use the cmp of python. # 2. The actual code should then be copied as is in the Word_over_Alphabet @@ -7175,7 +7173,6 @@ def minimal_conjugate(self): if end >= p.length(): return factor ** q -####################################################################### class CallableFromListOfWords(tuple): r""" @@ -7222,6 +7219,7 @@ def __call__(self, i): j -= c.length() raise IndexError("index (=%s) out of range" % i) + class Factorization(list): r""" A list subclass having a nicer representation for factorization of words. @@ -7245,6 +7243,7 @@ def __repr__(self): """ return '(%s)' % ', '.join(w.string_rep() for w in self) + ####################################################################### def evaluation_dict(w): @@ -7270,13 +7269,13 @@ def evaluation_dict(w): sage: evaluation_dict('1213121') # keys appear in random order {'1': 4, '2': 2, '3': 1} - """ d = defaultdict(int) for a in w: d[a] += 1 return dict(d) + def word_to_ordered_set_partition(w): r""" Return the ordered set partition corresponding to a finite diff --git a/src/sage/combinat/words/lyndon_word.py b/src/sage/combinat/words/lyndon_word.py index c006bb9588c..ca271c5a114 100644 --- a/src/sage/combinat/words/lyndon_word.py +++ b/src/sage/combinat/words/lyndon_word.py @@ -613,6 +613,7 @@ def standard_bracketing(lw): if lw[i:] in LyndonWords(): return [standard_bracketing(lw[:i]), standard_bracketing(lw[i:])] + def standard_unbracketing(sblw): """ Return flattened ``sblw`` if it is a standard bracketing of a Lyndon word, diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 38f238aa945..3d0ee41a4c4 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -202,6 +202,7 @@ WordDatatype_callable) from sage.matrix.constructor import vector_on_axis_rotation_matrix + ####################################################################### # # # WordPaths function # @@ -348,10 +349,11 @@ def WordPaths(alphabet, steps=None): elif steps == 'dyck': return WordPaths_dyck(alphabet=alphabet) else: - raise TypeError("Unknown type of steps : %s"%steps) + raise TypeError("Unknown type of steps : %s" % steps) else: return WordPaths_all(alphabet=alphabet, steps=steps) + ####################################################################### # # # Combinatorial classes of word paths # @@ -608,6 +610,7 @@ def vector_space(self): """ return self._vector_space + class WordPaths_square_grid(WordPaths_all): r""" The combinatorial class of all paths on the square grid. @@ -676,6 +679,7 @@ def __repr__(self): """ return "Word Paths on the square grid" + class WordPaths_triangle_grid(WordPaths_all): r""" The combinatorial class of all paths on the triangle grid. @@ -755,6 +759,7 @@ def __repr__(self): """ return "Word Paths on the triangle grid" + class WordPaths_hexagonal_grid(WordPaths_triangle_grid): r""" The combinatorial class of all paths on the hexagonal grid. @@ -823,6 +828,7 @@ def __repr__(self): """ return "Word Paths on the hexagonal grid" + class WordPaths_cube_grid(WordPaths_all): r""" The combinatorial class of all paths on the cube grid. @@ -890,6 +896,7 @@ def __repr__(self): """ return "Word Paths on the cube grid" + class WordPaths_dyck(WordPaths_all): r""" The combinatorial class of all Dyck paths. @@ -957,6 +964,7 @@ def __repr__(self): """ return "Finite Dyck paths" + class WordPaths_north_east(WordPaths_all): r""" The combinatorial class of all paths using North and East directions. @@ -1024,6 +1032,7 @@ def __repr__(self): """ return "Word Paths in North and East steps" + ####################################################################### # # # Abstract word path classes # @@ -1476,6 +1485,7 @@ def is_tangent(self): """ raise NotImplementedError + class FiniteWordPath_2d(FiniteWordPath_all): def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), fill=True, filloptions=dict(rgbcolor='red',alpha=0.2), @@ -2023,6 +2033,7 @@ def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), G += line(pts, **pathoptions) return G + ####################################################################### # # # Abstract word path classes # @@ -2208,7 +2219,8 @@ def tikz_trajectory(self): sage: f.tikz_trajectory() '(0, 0) -- (0, -1) -- (-1, -1) -- (-1, -2) -- (0, -2) -- (0, -3) -- (1, -3) -- (1, -2) -- (2, -2) -- (2, -1) -- (1, -1) -- (1, 0) -- (0, 0)' """ - return ' -- '.join(map(str,self.points())) + return ' -- '.join(map(str, self.points())) + class FiniteWordPath_triangle_grid(FiniteWordPath_2d): # Triangle grid paths are implemented with quadratic fields, @@ -2285,7 +2297,7 @@ def ymax(self): return max(RR(y) for (_,y) in self.points()) -#TODO: faire une verification du mot pour etre sur hexagonal grid +# TODO: faire une verification du mot pour etre sur hexagonal grid class FiniteWordPath_hexagonal_grid(FiniteWordPath_triangle_grid): def __init__(self, parent, *args, **kwds): r""" @@ -2310,15 +2322,19 @@ def __init__(self, parent, *args, **kwds): """ super().__init__(parent, *args, **kwds) + class FiniteWordPath_cube_grid(FiniteWordPath_3d): pass + class FiniteWordPath_north_east(FiniteWordPath_2d): pass + class FiniteWordPath_dyck(FiniteWordPath_2d): pass + ####################################################################### # # # Concrete word path classes # @@ -2345,6 +2361,7 @@ class FiniteWordPath_all_list(WordDatatype_list, FiniteWordPath_all, FiniteWord_ """ pass + class FiniteWordPath_all_str(WordDatatype_str, FiniteWordPath_all, FiniteWord_class): r""" TESTS:: @@ -2359,6 +2376,7 @@ class FiniteWordPath_all_str(WordDatatype_str, FiniteWordPath_all, FiniteWord_cl """ pass + class FiniteWordPath_all_tuple(WordDatatype_tuple, FiniteWordPath_all, FiniteWord_class): r""" TESTS:: @@ -2373,18 +2391,23 @@ class FiniteWordPath_all_tuple(WordDatatype_tuple, FiniteWordPath_all, FiniteWor """ pass + class FiniteWordPath_all_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_all, FiniteWord_class): pass + class FiniteWordPath_all_iter(WordDatatype_iter, FiniteWordPath_all, FiniteWord_class): pass + class FiniteWordPath_all_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_all, FiniteWord_class): pass + class FiniteWordPath_all_callable(WordDatatype_callable, FiniteWordPath_all, FiniteWord_class): pass + ##### Finite paths on 2d ##### class FiniteWordPath_2d_list(WordDatatype_list, FiniteWordPath_2d, FiniteWord_class): @@ -2401,6 +2424,7 @@ class FiniteWordPath_2d_list(WordDatatype_list, FiniteWordPath_2d, FiniteWord_cl """ pass + class FiniteWordPath_2d_str(WordDatatype_str, FiniteWordPath_2d, FiniteWord_class): r""" TESTS:: @@ -2415,6 +2439,7 @@ class FiniteWordPath_2d_str(WordDatatype_str, FiniteWordPath_2d, FiniteWord_clas """ pass + class FiniteWordPath_2d_tuple(WordDatatype_tuple, FiniteWordPath_2d, FiniteWord_class): r""" TESTS:: @@ -2429,18 +2454,23 @@ class FiniteWordPath_2d_tuple(WordDatatype_tuple, FiniteWordPath_2d, FiniteWord_ """ pass + class FiniteWordPath_2d_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_2d, FiniteWord_class): pass + class FiniteWordPath_2d_iter(WordDatatype_iter, FiniteWordPath_2d, FiniteWord_class): pass + class FiniteWordPath_2d_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_2d, FiniteWord_class): pass + class FiniteWordPath_2d_callable(WordDatatype_callable, FiniteWordPath_2d, FiniteWord_class): pass + ##### Finite paths on 3d ##### class FiniteWordPath_3d_list(WordDatatype_list, FiniteWordPath_3d, FiniteWord_class): @@ -2457,6 +2487,7 @@ class FiniteWordPath_3d_list(WordDatatype_list, FiniteWordPath_3d, FiniteWord_cl """ pass + class FiniteWordPath_3d_str(WordDatatype_str, FiniteWordPath_3d, FiniteWord_class): r""" TESTS:: @@ -2471,6 +2502,7 @@ class FiniteWordPath_3d_str(WordDatatype_str, FiniteWordPath_3d, FiniteWord_clas """ pass + class FiniteWordPath_3d_tuple(WordDatatype_tuple, FiniteWordPath_3d, FiniteWord_class): r""" TESTS:: @@ -2485,18 +2517,23 @@ class FiniteWordPath_3d_tuple(WordDatatype_tuple, FiniteWordPath_3d, FiniteWord_ """ pass + class FiniteWordPath_3d_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_3d, FiniteWord_class): pass + class FiniteWordPath_3d_iter(WordDatatype_iter, FiniteWordPath_3d, FiniteWord_class): pass + class FiniteWordPath_3d_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_3d, FiniteWord_class): pass + class FiniteWordPath_3d_callable(WordDatatype_callable, FiniteWordPath_3d, FiniteWord_class): pass + ##### Finite paths on square grid ##### class FiniteWordPath_square_grid_list(WordDatatype_list, FiniteWordPath_square_grid, FiniteWord_class): @@ -2513,6 +2550,7 @@ class FiniteWordPath_square_grid_list(WordDatatype_list, FiniteWordPath_square_g """ pass + class FiniteWordPath_square_grid_str(WordDatatype_str, FiniteWordPath_square_grid, FiniteWord_class): r""" TESTS:: @@ -2527,6 +2565,7 @@ class FiniteWordPath_square_grid_str(WordDatatype_str, FiniteWordPath_square_gri """ pass + class FiniteWordPath_square_grid_tuple(WordDatatype_tuple, FiniteWordPath_square_grid, FiniteWord_class): r""" TESTS:: @@ -2541,18 +2580,23 @@ class FiniteWordPath_square_grid_tuple(WordDatatype_tuple, FiniteWordPath_square """ pass + class FiniteWordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, FiniteWord_class): pass + class FiniteWordPath_square_grid_iter(WordDatatype_iter, FiniteWordPath_square_grid, FiniteWord_class): pass + class FiniteWordPath_square_grid_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_square_grid, FiniteWord_class): pass + class FiniteWordPath_square_grid_callable(WordDatatype_callable, FiniteWordPath_square_grid, FiniteWord_class): pass + ##### Unknown length paths on square grid (experimental) ##### #class WordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, Word_class): @@ -2574,6 +2618,7 @@ class FiniteWordPath_triangle_grid_list(WordDatatype_list, FiniteWordPath_triang """ pass + class FiniteWordPath_triangle_grid_str(WordDatatype_str, FiniteWordPath_triangle_grid, FiniteWord_class): r""" TESTS:: @@ -2588,6 +2633,7 @@ class FiniteWordPath_triangle_grid_str(WordDatatype_str, FiniteWordPath_triangle """ pass + class FiniteWordPath_triangle_grid_tuple(WordDatatype_tuple, FiniteWordPath_triangle_grid, FiniteWord_class): r""" TESTS:: @@ -2602,18 +2648,23 @@ class FiniteWordPath_triangle_grid_tuple(WordDatatype_tuple, FiniteWordPath_tria """ pass + class FiniteWordPath_triangle_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_triangle_grid, FiniteWord_class): pass + class FiniteWordPath_triangle_grid_iter(WordDatatype_iter, FiniteWordPath_triangle_grid, FiniteWord_class): pass + class FiniteWordPath_triangle_grid_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_triangle_grid, FiniteWord_class): pass + class FiniteWordPath_triangle_grid_callable(WordDatatype_callable, FiniteWordPath_triangle_grid, FiniteWord_class): pass + ##### Finite paths on hexagonal grid ##### class FiniteWordPath_hexagonal_grid_list(WordDatatype_list, FiniteWordPath_hexagonal_grid, FiniteWord_class): @@ -2630,6 +2681,7 @@ class FiniteWordPath_hexagonal_grid_list(WordDatatype_list, FiniteWordPath_hexag """ pass + class FiniteWordPath_hexagonal_grid_str(WordDatatype_str, FiniteWordPath_hexagonal_grid, FiniteWord_class): r""" TESTS:: @@ -2644,6 +2696,7 @@ class FiniteWordPath_hexagonal_grid_str(WordDatatype_str, FiniteWordPath_hexagon """ pass + class FiniteWordPath_hexagonal_grid_tuple(WordDatatype_tuple, FiniteWordPath_hexagonal_grid, FiniteWord_class): r""" TESTS:: @@ -2658,18 +2711,23 @@ class FiniteWordPath_hexagonal_grid_tuple(WordDatatype_tuple, FiniteWordPath_hex """ pass + class FiniteWordPath_hexagonal_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_hexagonal_grid, FiniteWord_class): pass + class FiniteWordPath_hexagonal_grid_iter(WordDatatype_iter, FiniteWordPath_hexagonal_grid, FiniteWord_class): pass + class FiniteWordPath_hexagonal_grid_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_hexagonal_grid, FiniteWord_class): pass + class FiniteWordPath_hexagonal_grid_callable(WordDatatype_callable, FiniteWordPath_hexagonal_grid, FiniteWord_class): pass + ##### Finite paths on cube grid ##### class FiniteWordPath_cube_grid_list(WordDatatype_list, FiniteWordPath_cube_grid, FiniteWord_class): @@ -2686,6 +2744,7 @@ class FiniteWordPath_cube_grid_list(WordDatatype_list, FiniteWordPath_cube_grid, """ pass + class FiniteWordPath_cube_grid_str(WordDatatype_str, FiniteWordPath_cube_grid, FiniteWord_class): r""" TESTS:: @@ -2700,6 +2759,7 @@ class FiniteWordPath_cube_grid_str(WordDatatype_str, FiniteWordPath_cube_grid, F """ pass + class FiniteWordPath_cube_grid_tuple(WordDatatype_tuple, FiniteWordPath_cube_grid, FiniteWord_class): r""" TESTS:: @@ -2714,18 +2774,23 @@ class FiniteWordPath_cube_grid_tuple(WordDatatype_tuple, FiniteWordPath_cube_gri """ pass + class FiniteWordPath_cube_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_cube_grid, FiniteWord_class): pass + class FiniteWordPath_cube_grid_iter(WordDatatype_iter, FiniteWordPath_cube_grid, FiniteWord_class): pass + class FiniteWordPath_cube_grid_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_cube_grid, FiniteWord_class): pass + class FiniteWordPath_cube_grid_callable(WordDatatype_callable, FiniteWordPath_cube_grid, FiniteWord_class): pass + ##### Finite paths on north_east ##### class FiniteWordPath_north_east_list(WordDatatype_list, FiniteWordPath_north_east, FiniteWord_class): @@ -2742,6 +2807,7 @@ class FiniteWordPath_north_east_list(WordDatatype_list, FiniteWordPath_north_eas """ pass + class FiniteWordPath_north_east_str(WordDatatype_str, FiniteWordPath_north_east, FiniteWord_class): r""" TESTS:: @@ -2756,6 +2822,7 @@ class FiniteWordPath_north_east_str(WordDatatype_str, FiniteWordPath_north_east, """ pass + class FiniteWordPath_north_east_tuple(WordDatatype_tuple, FiniteWordPath_north_east, FiniteWord_class): r""" TESTS:: @@ -2770,18 +2837,23 @@ class FiniteWordPath_north_east_tuple(WordDatatype_tuple, FiniteWordPath_north_e """ pass + class FiniteWordPath_north_east_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_north_east, FiniteWord_class): pass + class FiniteWordPath_north_east_iter(WordDatatype_iter, FiniteWordPath_north_east, FiniteWord_class): pass + class FiniteWordPath_north_east_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_north_east, FiniteWord_class): pass + class FiniteWordPath_north_east_callable(WordDatatype_callable, FiniteWordPath_north_east, FiniteWord_class): pass + ##### Finite paths on dyck ##### class FiniteWordPath_dyck_list(WordDatatype_list, FiniteWordPath_dyck, FiniteWord_class): @@ -2798,6 +2870,7 @@ class FiniteWordPath_dyck_list(WordDatatype_list, FiniteWordPath_dyck, FiniteWor """ pass + class FiniteWordPath_dyck_str(WordDatatype_str, FiniteWordPath_dyck, FiniteWord_class): r""" TESTS:: @@ -2812,6 +2885,7 @@ class FiniteWordPath_dyck_str(WordDatatype_str, FiniteWordPath_dyck, FiniteWord_ """ pass + class FiniteWordPath_dyck_tuple(WordDatatype_tuple, FiniteWordPath_dyck, FiniteWord_class): r""" TESTS:: @@ -2826,14 +2900,18 @@ class FiniteWordPath_dyck_tuple(WordDatatype_tuple, FiniteWordPath_dyck, FiniteW """ pass + class FiniteWordPath_dyck_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_dyck, FiniteWord_class): pass + class FiniteWordPath_dyck_iter(WordDatatype_iter, FiniteWordPath_dyck, FiniteWord_class): pass + class FiniteWordPath_dyck_callable_with_caching(WordDatatype_callable_with_caching, FiniteWordPath_dyck, FiniteWord_class): pass + class FiniteWordPath_dyck_callable(WordDatatype_callable, FiniteWordPath_dyck, FiniteWord_class): pass diff --git a/src/sage/combinat/words/word.py b/src/sage/combinat/words/word.py index 85974ec33e7..051d0a1a702 100644 --- a/src/sage/combinat/words/word.py +++ b/src/sage/combinat/words/word.py @@ -41,6 +41,7 @@ # TODO. Word needs to be replaced by Word. Consider renaming # Word_class to Word and imbedding Word as its __call__ method. + def Word(data=None, alphabet=None, length=None, datatype=None, caching=True, RSK_data=None): r""" Construct a word. @@ -218,6 +219,7 @@ def Word(data=None, alphabet=None, length=None, datatype=None, caching=True, RSK ##### Finite Words ##### + class FiniteWord_char(WordDatatype_char, FiniteWord_class): r""" Finite word represented by an array ``unsigned char *`` (i.e. integers @@ -279,11 +281,12 @@ class FiniteWord_char(WordDatatype_char, FiniteWord_class): """ pass + class FiniteWord_list(WordDatatype_list, FiniteWord_class): r""" Finite word represented by a Python list. - For any word `w`, type ``w.`` and hit TAB key to see the list of + For any word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -300,11 +303,12 @@ class FiniteWord_list(WordDatatype_list, FiniteWord_class): """ pass + class FiniteWord_str(WordDatatype_str, FiniteWord_class): r""" Finite word represented by a Python str. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -321,11 +325,12 @@ class FiniteWord_str(WordDatatype_str, FiniteWord_class): """ pass + class FiniteWord_tuple(WordDatatype_tuple, FiniteWord_class): r""" Finite word represented by a Python tuple. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -342,11 +347,12 @@ class FiniteWord_tuple(WordDatatype_tuple, FiniteWord_class): """ pass + class FiniteWord_iter_with_caching(WordDatatype_iter_with_caching, FiniteWord_class): r""" Finite word represented by an iterator (with caching). - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -368,11 +374,12 @@ class FiniteWord_iter_with_caching(WordDatatype_iter_with_caching, FiniteWord_cl """ pass + class FiniteWord_iter(WordDatatype_iter, FiniteWord_class): r""" Finite word represented by an iterator. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -396,11 +403,12 @@ class FiniteWord_iter(WordDatatype_iter, FiniteWord_class): """ pass + class FiniteWord_callable_with_caching(WordDatatype_callable_with_caching, FiniteWord_class): r""" Finite word represented by a callable (with caching). - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -447,11 +455,12 @@ class FiniteWord_callable_with_caching(WordDatatype_callable_with_caching, Finit """ pass + class FiniteWord_callable(WordDatatype_callable, FiniteWord_class): r""" Finite word represented by a callable. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -476,13 +485,14 @@ class FiniteWord_callable(WordDatatype_callable, FiniteWord_class): """ pass + ##### Infinite Words ##### class InfiniteWord_iter_with_caching(WordDatatype_iter_with_caching, InfiniteWord_class): r""" Infinite word represented by an iterable (with caching). - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Infinite words behave like a Python list : they can be sliced using @@ -516,11 +526,12 @@ class InfiniteWord_iter_with_caching(WordDatatype_iter_with_caching, InfiniteWor """ pass + class InfiniteWord_iter(WordDatatype_iter, InfiniteWord_class): r""" Infinite word represented by an iterable. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Infinite words behave like a Python list : they can be sliced using @@ -554,11 +565,12 @@ class InfiniteWord_iter(WordDatatype_iter, InfiniteWord_class): """ pass + class InfiniteWord_callable_with_caching(WordDatatype_callable_with_caching, InfiniteWord_class): r""" Infinite word represented by a callable (with caching). - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Infinite words behave like a Python list : they can be sliced using @@ -584,11 +596,12 @@ class InfiniteWord_callable_with_caching(WordDatatype_callable_with_caching, Inf """ pass + class InfiniteWord_callable(WordDatatype_callable, InfiniteWord_class): r""" Infinite word represented by a callable. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Infinite words behave like a Python list : they can be sliced using @@ -615,6 +628,7 @@ class InfiniteWord_callable(WordDatatype_callable, InfiniteWord_class): """ pass + ##### Words of unknown length ##### class Word_iter_with_caching(WordDatatype_iter_with_caching, Word_class): @@ -622,7 +636,7 @@ class Word_iter_with_caching(WordDatatype_iter_with_caching, Word_class): Word of unknown length (finite or infinite) represented by an iterable (with caching). - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Words behave like a Python list : they can be sliced using @@ -653,12 +667,13 @@ class Word_iter_with_caching(WordDatatype_iter_with_caching, Word_class): """ pass + class Word_iter(WordDatatype_iter, Word_class): r""" Word of unknown length (finite or infinite) represented by an iterable. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Words behave like a Python list : they can be sliced using @@ -689,12 +704,14 @@ class Word_iter(WordDatatype_iter, Word_class): """ pass + ##### Morphic Words ##### + class FiniteWord_morphic(WordDatatype_morphic, FiniteWord_class): r""" Finite morphic word. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. EXAMPLES:: @@ -713,15 +730,15 @@ class FiniteWord_morphic(WordDatatype_morphic, FiniteWord_class): <class 'sage.combinat.words.word.FiniteWord_morphic'> sage: loads(dumps(w)) word: ab - """ pass + class InfiniteWord_morphic(WordDatatype_morphic, InfiniteWord_class): r""" Morphic word of infinite length. - For such word `w`, type ``w.`` and hit TAB key to see the list of + For such word `w`, type ``w.`` and hit :kbd:`Tab` key to see the list of functions defined on `w`. Infinite words behave like a Python list : they can be sliced using @@ -740,6 +757,5 @@ class InfiniteWord_morphic(WordDatatype_morphic, InfiniteWord_class): sage: loads(dumps(w)) word: abaababaabaababaababaabaababaabaababaaba... - """ pass diff --git a/src/sage/combinat/words/word_generators.py b/src/sage/combinat/words/word_generators.py index e7110609d63..b485205878f 100644 --- a/src/sage/combinat/words/word_generators.py +++ b/src/sage/combinat/words/word_generators.py @@ -5,16 +5,16 @@ AUTHORS: - Franco Saliola (2008-12-17): merged into sage -- Sebastien Labbe (2008-12-17): merged into sage +- Sébastien Labbé (2008-12-17): merged into sage - Arnaud Bergeron (2008-12-17): merged into sage - Amy Glen (2008-12-17): merged into sage - Sébastien Labbé (2009-12-19): Added S-adic words (:trac:`7543`) USE: -To see a list of all word constructors, type ``words.`` and then press the tab -key. The documentation for each constructor includes information about each -word, which provides a useful reference. +To see a list of all word constructors, type ``words.`` and then press +the :kbd:`Tab` key. The documentation for each constructor includes +information about each word, which provides a useful reference. REFERENCES: @@ -22,11 +22,11 @@ numbers with a regular expansion, J. Number Theory 103 (2003) 27--37. -.. [BmBGL07] \A. Blondin-Masse, S. Brlek, A. Glen, and S. Labbe. On the +.. [BmBGL07] \A. Blondin-Massé, S. Brlek, A. Glen, and S. Labbé. On the critical exponent of generalized Thue-Morse words. *Discrete Math. Theor. Comput. Sci.* 9 (1):293--304, 2007. -.. [BmBGL09] \A. Blondin-Masse, S. Brlek, A. Garon, and S. Labbe. Christoffel +.. [BmBGL09] \A. Blondin-Massé, S. Brlek, A. Garon, and S. Labbé. Christoffel and Fibonacci Tiles, DGCI 2009, Montreal, to appear in LNCS. .. [Loth02] \M. Lothaire, Algebraic Combinatorics On Words, vol. 90 of @@ -104,6 +104,7 @@ def _build_tab(sym, tab, W): res.append((w[-1] % c) + 1) return res + class LowerChristoffelWord(FiniteWord_list): r""" Returns the lower Christoffel word of slope `p/q`, where `p` and @@ -310,6 +311,7 @@ def __reduce__(self): """ return self.__class__, (self.__p, self.__q, self.parent().alphabet()) + class WordGenerator(): r""" Constructor of several famous words. @@ -351,7 +353,7 @@ class WordGenerator(): .. NOTE:: To see a list of all word constructors, type ``words.`` and then - hit the TAB key. The documentation for each constructor + hit the :kbd:`Tab` key. The documentation for each constructor includes information about each word, which provides a useful reference. @@ -1275,7 +1277,7 @@ def StandardEpisturmianWord(self, directive_word): if not isinstance(directive_word, Word_class): raise TypeError("directive_word is not a word, so it cannot be used to build an episturmian word") epistandard = directive_word.parent()(\ - self._StandardEpisturmianWord_LetterIterator(directive_word), \ + self._StandardEpisturmianWord_LetterIterator(directive_word), datatype='iter') return epistandard @@ -2042,4 +2044,5 @@ def BaumSweetWord(self): inner = WordMorphism('a->aa,b->cb,c->ba,d->db') return outer(inner.fixed_point('d')) + words = WordGenerator() diff --git a/src/sage/combinat/words/word_infinite_datatypes.py b/src/sage/combinat/words/word_infinite_datatypes.py index 90ab35efd84..defef1e84d1 100644 --- a/src/sage/combinat/words/word_infinite_datatypes.py +++ b/src/sage/combinat/words/word_infinite_datatypes.py @@ -244,13 +244,13 @@ def __getitem__(self, key): if step > 0: start = 0 if key.start is None else key.start length = self._len if key.stop is None else \ - int(max(0,ceil((key.stop-start)/float(step)))) + int(max(0, ceil((key.stop-start)/float(step)))) else: if key.start is None or key.start < 0: raise ValueError("start value must be nonnegative for negative step values") start = key.start stop = 0 if key.stop is None else key.stop - length = int(max(0,ceil((key.stop-start)/float(step)))) + length = int(max(0, ceil((key.stop-start)/float(step)))) fcn = lambda x: self._func(start + x*step) if length is None: return self._parent(fcn, length=length) @@ -270,7 +270,7 @@ def __getitem__(self, key): else: start, stop, step = slice(key.start, key.stop, step).indices(self._len) - length = int(max(0,ceil((stop-start)/float(step)))) + length = int(max(0, ceil((stop-start)/float(step)))) fcn = lambda x: self._func(start + x*step) return self._parent(fcn, length=length) else: @@ -313,6 +313,7 @@ def __reduce__(self): else: return self._parent, (s, 'pickled_function', False) + class WordDatatype_callable_with_caching(WordDatatype_callable): r""" Datatype for a word defined by a callable. @@ -581,6 +582,7 @@ def flush(self): """ self._letter_cache = {} + class WordDatatype_iter(WordDatatype): # NOTE: The constructor callable should do all the slicing (see islice) def __init__(self, parent, iter, length=None): @@ -839,7 +841,7 @@ def __getitem__(self, key): length = Infinity stop = None else: # key.stop > 0 - length = int(max(0,ceil((key.stop-start)/float(step)))) + length = int(max(0, ceil((key.stop-start)/float(step)))) stop = int(key.stop) data = itertools.islice(self, start, stop, step) else: @@ -847,7 +849,7 @@ def __getitem__(self, key): raise ValueError("start value must be nonnegative for negative step values") start = int(key.start) stop = 0 if key.stop is None else int(key.stop) - length = int(max(0,ceil((stop-start)/float(step)))) + length = int(max(0, ceil((stop-start)/float(step)))) data = list(itertools.islice(self, start+1))[key] if length is None or length is Infinity: @@ -872,7 +874,7 @@ def __getitem__(self, key): length = None else: # start >= 0, step >= 1, stop >= 0 or None data = itertools.islice(self, start, stop, step) - length = "unknown" if stop is None else int(max(0,((stop-start)/float(step)))) + length = "unknown" if stop is None else int(max(0, ((stop-start)/float(step)))) return self._parent.factors()(data, length=length) else: @@ -912,8 +914,8 @@ def __reduce__(self): """ if self.is_finite(): return self._parent, (list(self),) - else: - return self._parent, (iter(self), 'iter', False) + return self._parent, (iter(self), 'iter', False) + class WordDatatype_iter_with_caching(WordDatatype_iter): def __init__(self, parent, iter, length=None): diff --git a/src/sage/combinat/words/word_options.py b/src/sage/combinat/words/word_options.py index e49946a9deb..2d2bf3bc7ff 100644 --- a/src/sage/combinat/words/word_options.py +++ b/src/sage/combinat/words/word_options.py @@ -1,27 +1,26 @@ r""" User-customizable options for words """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Franco Saliola <saliola@gmail.com> # # 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 copy from sage.rings.integer import Integer -word_options = {\ - 'identifier':'word: ', \ - 'display':'string', \ - 'truncate':True, \ - 'truncate_length':40, \ - 'letter_separator':',', \ - 'cache':True, \ - 'old_repr':False \ - } +word_options = {'identifier': 'word: ', + 'display': 'string', + 'truncate': True, + 'truncate_length': 40, + 'letter_separator': ',', + 'cache': True, + 'old_repr': False} + def WordOptions(**kwargs): """ @@ -73,7 +72,7 @@ def WordOptions(**kwargs): else: word_options['truncate'] = kwargs['truncate'] elif 'truncate_length' in kwargs: - if not isinstance(kwargs['truncate_length'], (int,Integer)) or kwargs['truncate_length'] <= 0: + if not isinstance(kwargs['truncate_length'], (int, Integer)) or kwargs['truncate_length'] <= 0: raise ValueError("truncate_length must be a positive integer") else: word_options['truncate_length'] = kwargs['truncate_length'] diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index d642a9c3361..c0a4457df85 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -965,11 +965,12 @@ def __iter__(self): def __contains__(self, x): """ - Tests whether self contains x. + Tests whether ``self`` contains ``x``. OUTPUT: - This method returns True if x is a word of the appropriate - length and the alphabets of the parents match. Returns False + + This method returns ``True`` if ``x`` is a word of the appropriate + length and the alphabets of the parents match. It returns ``False`` otherwise. EXAMPLES:: diff --git a/src/sage/cpython/_py2_random.py b/src/sage/cpython/_py2_random.py index 745834f0cd1..795820c6cc7 100644 --- a/src/sage/cpython/_py2_random.py +++ b/src/sage/cpython/_py2_random.py @@ -149,7 +149,7 @@ def __reduce__(self): ## -------------------- integer methods ------------------- - def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1<<BPF): + def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1 << BPF): """Choose a random item from range(start, stop[, step]). This fixes the problem with randint() which includes the @@ -216,10 +216,9 @@ def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1<<BPF): def randint(self, a, b): """Return random integer in range [a, b], including both end points. """ + return self.randrange(a, b + 1) - return self.randrange(a, b+1) - - def _randbelow(self, n, _log=_log, _int=int, _maxwidth=1<<BPF, + def _randbelow(self, n, _log=_log, _int=int, _maxwidth=1 << BPF, _Method=_MethodType, _BuiltinMethod=_BuiltinMethodType): """Return a random int in the range [0,n) diff --git a/src/sage/cpython/cython_metaclass.h b/src/sage/cpython/cython_metaclass.h index cc620a4dac7..da06ab75a6b 100644 --- a/src/sage/cpython/cython_metaclass.h +++ b/src/sage/cpython/cython_metaclass.h @@ -8,6 +8,13 @@ * http://www.gnu.org/licenses/ *****************************************************************************/ +/* Compatibility for python 3.8, can be removed later */ +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ ob->ob_type = type; } +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type) +#endif + /* Tuple (None, None, None), initialized as needed */ static PyObject* NoneNoneNone; @@ -66,7 +73,7 @@ static CYTHON_INLINE int Sage_PyType_Ready(PyTypeObject* t) } /* Now, set t.__class__ to metaclass */ - Py_TYPE(t) = metaclass; + Py_SET_TYPE(t, metaclass); PyType_Modified(t); } else diff --git a/src/sage/cpython/dict_del_by_value.pyx b/src/sage/cpython/dict_del_by_value.pyx index 488bf9c84cc..3894554c13d 100644 --- a/src/sage/cpython/dict_del_by_value.pyx +++ b/src/sage/cpython/dict_del_by_value.pyx @@ -19,13 +19,8 @@ AUTHORS: # https://www.gnu.org/licenses/ # **************************************************************************** -import weakref -from weakref import KeyedRef - from cpython.list cimport PyList_New -from cpython cimport Py_XINCREF, Py_XDECREF -from libc.stdint cimport int8_t, int16_t, int32_t, int64_t cdef extern from "Python.h": ctypedef struct PyDictKeysObject @@ -34,99 +29,47 @@ cdef extern from "Python.h": PyDictKeysObject * ma_keys PyObject ** ma_values - #we need this redefinition because we want to be able to call - #PyWeakref_GetObject with borrowed references. This is the recommended - #strategy according to Cython/Includes/cpython/__init__.pxd - PyObject* PyWeakref_GetObject(PyObject * wr) int PyList_SetItem(object list, Py_ssize_t index, PyObject * item) except -1 - int PyWeakref_Check(PyObject * ob) -#### -#definitions replicated from CPython's Objects/dict-common.h -#(this file is not exported from CPython, so we need to be -#careful the definitions are in step with what happens there. - -ctypedef void* dict_lookup_func # Precise definition not needed - -ctypedef union IndexBlock: - int8_t as_1[8] - int16_t as_2[4] - int32_t as_4[2] - int64_t as_8[1] - -ctypedef struct MyPyDictKeysObject: - Py_ssize_t dk_refcnt - Py_ssize_t dk_size - dict_lookup_func dk_lookup - Py_ssize_t dk_usable - Py_ssize_t dk_nentries - IndexBlock dk_indices - -ctypedef struct PyDictKeyEntry: - Py_hash_t me_hash - PyObject * me_key - PyObject * me_value - -cdef Py_ssize_t DKIX_EMPTY = -1 -cdef Py_ssize_t DKIX_DUMMY = -2 -cdef Py_ssize_t DKIX_ERROR = -3 - -##### -#These routines are copied from CPython's Object/dictobject.c -#in order to access PyDictKeysObject fields - -cdef inline int DK_IXSIZE(MyPyDictKeysObject *keys): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: - return 1 - elif s <= 0xffff: - return 2 - elif s <= 0xffffffff: - return 4 - else: - return 8 - -cdef inline PyDictKeyEntry * DK_ENTRIES(MyPyDictKeysObject *keys): - return <PyDictKeyEntry*> &(keys.dk_indices.as_1[keys.dk_size * DK_IXSIZE(keys)]) - -cdef inline Py_ssize_t dk_get_index(MyPyDictKeysObject *keys, Py_ssize_t i): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: - return keys.dk_indices.as_1[i] - elif s <= 0xffff: - return keys.dk_indices.as_2[i] - elif s <= 0xffffffff: - return keys.dk_indices.as_4[i] - else: - return keys.dk_indices.as_8[i] - -cdef inline void dk_set_index(MyPyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: - keys.dk_indices.as_1[i] = ix - elif s <= 0xffff: - keys.dk_indices.as_2[i] = ix - elif s <= 0xffffffff: - keys.dk_indices.as_4[i] = ix - else: - keys.dk_indices.as_8[i] = ix - -#End of replication of Object/dictobject.c -###### - -cdef dict_lookup_func lookdict - -cdef dict_lookup_func DK_LOOKUP(PyDictObject *mp): - return (<MyPyDictKeysObject *>(mp.ma_keys)).dk_lookup - -def init_lookdict(): - global lookdict - # A dict which a non-string key uses the generic "lookdict" - # as lookup function - cdef object D = {} - D[0] = 0 - lookdict = DK_LOOKUP(<PyDictObject *>D) - -init_lookdict() + +cdef extern from "dict_internal.h": + Py_ssize_t DK_MASK(PyDictKeysObject *) + PyDictKeyEntry * DK_ENTRIES(PyDictKeysObject *keys) + + Py_ssize_t dictkeys_get_index (PyDictKeysObject *keys, Py_ssize_t i) + void dictkeys_set_index (PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) + + Py_ssize_t DKIX_EMPTY, DKIX_DUMMY + int PERTURB_SHIFT + + ctypedef struct PyDictKeyEntry: + Py_hash_t me_hash + PyObject * me_key + PyObject * me_value + + +# dk_lookup was removed in python 3.11 +DEF HAS_DK_LOOKUP = PY_VERSION_HEX < 0x30b0000 + +IF HAS_DK_LOOKUP: + + cdef extern from *: + """ + #define DK_LOOKUP(dk) ((dk)->dk_lookup) + """ + ctypedef void * dict_lookup_func # Precise definition not needed + dict_lookup_func DK_LOOKUP(PyDictKeysObject *mp) + + cdef dict_lookup_func lookdict + + def init_lookdict(): + global lookdict + # A dict which a non-string key uses the generic "lookdict" + # as lookup function + cdef object D = {} + D[0] = 0 + lookdict = DK_LOOKUP((<PyDictObject *>D).ma_keys) + + init_lookdict() cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hash) except -1: """ @@ -177,9 +120,9 @@ cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_ sage: for i in range(10^3+10): newA = A(); M[newA] = prev; prev = newA sage: del a """ - keys = <MyPyDictKeysObject *>(mp.ma_keys) + keys = mp.ma_keys cdef size_t perturb - cdef size_t mask = <size_t> keys.dk_size-1 + cdef size_t mask = DK_MASK(keys) cdef PyDictKeyEntry *entries = DK_ENTRIES(keys) cdef PyDictKeyEntry *ep @@ -187,7 +130,7 @@ cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_ raise TypeError("del_dictitem_by_exact_value cannot be applied to a shared key dict") cdef size_t i = <size_t>hash & mask - ix = dk_get_index(keys, i) + ix = dictkeys_get_index(keys, i) if ix == DKIX_EMPTY: # key not found @@ -196,9 +139,9 @@ cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_ ep = &(entries[ix]) perturb = hash while (ep.me_value != value or ep.me_hash != hash): - perturb = perturb >> 5 #this is the value of PERTURB_SHIFT + perturb = perturb >> PERTURB_SHIFT i = mask & (i * 5 + perturb + 1) - ix = dk_get_index(keys, i) + ix = dictkeys_get_index(keys, i) if ix == DKIX_EMPTY: # key not found return 0 @@ -206,7 +149,9 @@ cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_ # We need the lookup function to be the generic lookdict, otherwise # deletions may not work correctly - keys.dk_lookup = lookdict + IF HAS_DK_LOOKUP: + # Can this fail? In any case dk_lookup was removed in python 3.11 + assert DK_LOOKUP(keys) is lookdict T = PyList_New(2) PyList_SetItem(T, 0, ep.me_key) @@ -214,7 +159,7 @@ cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_ ep.me_key = NULL ep.me_value = NULL mp.ma_used -= 1 - dk_set_index(keys, i, DKIX_DUMMY) + dictkeys_set_index(keys, i, DKIX_DUMMY) #We have transferred the to-be-deleted references to the list T #we now delete the list so that the actual decref happens through a #deallocation routine that uses the Python Trashcan macros to diff --git a/src/sage/cpython/dict_internal.h b/src/sage/cpython/dict_internal.h new file mode 100644 index 00000000000..42a57bcb468 --- /dev/null +++ b/src/sage/cpython/dict_internal.h @@ -0,0 +1,242 @@ +/* This contains internal definitions for python dictionaries, + * mostly copied from cpython sourcecode. + * + * Moved here to make it easier to maintain in the face of python + * changes. + * */ + +#if PY_VERSION_HEX < 0x30b0000 + +/************************************************************/ +/* Copied verbatim from cpython 3.8 (Objects/dict-common.h) */ +/************************************************************/ + +#ifndef Py_DICT_COMMON_H +#define Py_DICT_COMMON_H + +typedef struct { + /* Cached hash code of me_key. */ + Py_hash_t me_hash; + PyObject *me_key; + PyObject *me_value; /* This field is only meaningful for combined tables */ +} PyDictKeyEntry; + +/* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. + * -1 when no entry found, -3 when compare raises error. + */ +typedef Py_ssize_t (*dict_lookup_func) + (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); + +#define DKIX_EMPTY (-1) +#define DKIX_DUMMY (-2) /* Used internally */ +#define DKIX_ERROR (-3) + +/* See dictobject.c for actual layout of DictKeysObject */ +struct _dictkeysobject { + Py_ssize_t dk_refcnt; + + /* Size of the hash table (dk_indices). It must be a power of 2. */ + Py_ssize_t dk_size; + + /* Function to lookup in the hash table (dk_indices): + + - lookdict(): general-purpose, and may return DKIX_ERROR if (and + only if) a comparison raises an exception. + + - lookdict_unicode(): specialized to Unicode string keys, comparison of + which can never raise an exception; that function can never return + DKIX_ERROR. + + - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further + specialized for Unicode string keys that cannot be the <dummy> value. + + - lookdict_split(): Version of lookdict() for split tables. */ + dict_lookup_func dk_lookup; + + /* Number of usable entries in dk_entries. */ + Py_ssize_t dk_usable; + + /* Number of used entries in dk_entries. */ + Py_ssize_t dk_nentries; + + /* Actual hash table of dk_size entries. It holds indices in dk_entries, + or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). + + Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). + + The size in bytes of an indice depends on dk_size: + + - 1 byte if dk_size <= 0xff (char*) + - 2 bytes if dk_size <= 0xffff (int16_t*) + - 4 bytes if dk_size <= 0xffffffff (int32_t*) + - 8 bytes otherwise (int64_t*) + + Dynamically sized, SIZEOF_VOID_P is minimum. */ + char dk_indices[]; /* char is required to avoid strict aliasing. */ + + /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: + see the DK_ENTRIES() macro */ +}; + +#endif + + +/***********************************************************/ +/* Copied verbatim from cpython 3.8 (Objects/dictobject.c) */ +/***********************************************************/ + +#define PERTURB_SHIFT 5 +#define DK_SIZE(dk) ((dk)->dk_size) +#if SIZEOF_VOID_P > 4 +#define DK_IXSIZE(dk) \ + (DK_SIZE(dk) <= 0xff ? \ + 1 : DK_SIZE(dk) <= 0xffff ? \ + 2 : DK_SIZE(dk) <= 0xffffffff ? \ + 4 : sizeof(int64_t)) +#else +#define DK_IXSIZE(dk) \ + (DK_SIZE(dk) <= 0xff ? \ + 1 : DK_SIZE(dk) <= 0xffff ? \ + 2 : sizeof(int32_t)) +#endif +#define DK_ENTRIES(dk) \ + ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) + +#define DK_MASK(dk) (((dk)->dk_size)-1) + +/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ +static inline Py_ssize_t +dictkeys_get_index(PyDictKeysObject *keys, Py_ssize_t i) +{ + Py_ssize_t s = DK_SIZE(keys); + Py_ssize_t ix; + + if (s <= 0xff) { + int8_t *indices = (int8_t*)(keys->dk_indices); + ix = indices[i]; + } + else if (s <= 0xffff) { + int16_t *indices = (int16_t*)(keys->dk_indices); + ix = indices[i]; + } +#if SIZEOF_VOID_P > 4 + else if (s > 0xffffffff) { + int64_t *indices = (int64_t*)(keys->dk_indices); + ix = indices[i]; + } +#endif + else { + int32_t *indices = (int32_t*)(keys->dk_indices); + ix = indices[i]; + } + assert(ix >= DKIX_DUMMY); + return ix; +} + +/* write to indices. */ +static inline void +dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) +{ + Py_ssize_t s = DK_SIZE(keys); + + assert(ix >= DKIX_DUMMY); + + if (s <= 0xff) { + int8_t *indices = (int8_t*)(keys->dk_indices); + assert(ix <= 0x7f); + indices[i] = (char)ix; + } + else if (s <= 0xffff) { + int16_t *indices = (int16_t*)(keys->dk_indices); + assert(ix <= 0x7fff); + indices[i] = (int16_t)ix; + } +#if SIZEOF_VOID_P > 4 + else if (s > 0xffffffff) { + int64_t *indices = (int64_t*)(keys->dk_indices); + indices[i] = ix; + } +#endif + else { + int32_t *indices = (int32_t*)(keys->dk_indices); + assert(ix <= 0x7fffffff); + indices[i] = (int32_t)ix; + } +} + +/************************************************************/ + +#else /* Python >= 3.11 */ + +#define Py_BUILD_CORE +#include <internal/pycore_dict.h> + +/************************************************************/ +/* Copied verbatim from cpython 3.11 (Objects/dictobject.c) */ +/************************************************************/ + +#define PERTURB_SHIFT 5 +#define DK_MASK(dk) (DK_SIZE(dk)-1) + +/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ +static inline Py_ssize_t +dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) +{ + int log2size = DK_LOG_SIZE(keys); + Py_ssize_t ix; + + if (log2size < 8) { + const int8_t *indices = (const int8_t*)(keys->dk_indices); + ix = indices[i]; + } + else if (log2size < 16) { + const int16_t *indices = (const int16_t*)(keys->dk_indices); + ix = indices[i]; + } +#if SIZEOF_VOID_P > 4 + else if (log2size >= 32) { + const int64_t *indices = (const int64_t*)(keys->dk_indices); + ix = indices[i]; + } +#endif + else { + const int32_t *indices = (const int32_t*)(keys->dk_indices); + ix = indices[i]; + } + assert(ix >= DKIX_DUMMY); + return ix; +} + +/* write to indices. */ +static inline void +dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) +{ + int log2size = DK_LOG_SIZE(keys); + + assert(ix >= DKIX_DUMMY); + assert(keys->dk_version == 0); + + if (log2size < 8) { + int8_t *indices = (int8_t*)(keys->dk_indices); + assert(ix <= 0x7f); + indices[i] = (char)ix; + } + else if (log2size < 16) { + int16_t *indices = (int16_t*)(keys->dk_indices); + assert(ix <= 0x7fff); + indices[i] = (int16_t)ix; + } +#if SIZEOF_VOID_P > 4 + else if (log2size >= 32) { + int64_t *indices = (int64_t*)(keys->dk_indices); + indices[i] = ix; + } +#endif + else { + int32_t *indices = (int32_t*)(keys->dk_indices); + assert(ix <= 0x7fffffff); + indices[i] = (int32_t)ix; + } +} + +#endif diff --git a/src/sage/crypto/block_cipher/present.py b/src/sage/crypto/block_cipher/present.py index 4cbb74fc241..1774e6d5977 100644 --- a/src/sage/crypto/block_cipher/present.py +++ b/src/sage/crypto/block_cipher/present.py @@ -838,7 +838,7 @@ def __getitem__(self, r): Computes the sub key for round ``r`` derived from initial master key. The key schedule object has to have been initialised with the - `master_key` argument. + ``master_key`` argument. INPUT: @@ -860,7 +860,7 @@ def __getitem__(self, r): def __iter__(self): """ Iterate over the ``self._rounds + 1`` PRESENT round keys, derived from - `master_key` + ``master_key``. EXAMPLES:: diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 887afd683fd..a9ea665475c 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -321,7 +321,7 @@ cdef class BooleanFunction(SageObject): bitset_set(self._truth_table, i) reed_muller(self._truth_table.bits, ZZ(self._truth_table.limbs).exact_log(2) ) - elif isinstance(x, (int,long,Integer) ): + elif isinstance(x, (int, Integer)): # initialisation to the zero function self._nvariables = ZZ(x) bitset_init(self._truth_table, <mp_bitcnt_t> (1<<self._nvariables)) @@ -651,7 +651,7 @@ cdef class BooleanFunction(SageObject): ... IndexError: index out of bound """ - if isinstance(x, (int,long,Integer)): + if isinstance(x, (int, Integer)): if x >= self._truth_table.size: raise IndexError("index out of bound") return bitset_in(self._truth_table, <mp_bitcnt_t> x) @@ -1464,4 +1464,3 @@ def random_boolean_function(n): sig_check() T.bits[i] = r.randrange(0,Integer(1)<<(sizeof(unsigned long)*8)) return B - diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index b593bac9b03..cf72c2660c4 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -60,8 +60,10 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. - OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, - dual: lower_right) basis of row vectors for the lattice in question. + OUTPUT: + + ``B`` a unique size-reduced triangular (primal: lower_left, + dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index 619026bfe96..154c84d146c 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -785,7 +785,7 @@ def _GF_to_hex(self, GF): not GF.parent().order() == 2**8: msg = "keyword 'GF' must be in" raise TypeError(msg.format(self._F)) - return hex(GF.integer_representation())[2:].zfill(2) + return hex(GF.to_integer())[2:].zfill(2) def _bin_to_GF(self, B, matrix=True): r""" @@ -911,7 +911,7 @@ def _GF_to_bin(self, GF): not GF.parent().order() == 2**8: msg = "keyword 'GF' must be in" raise TypeError(msg.format(self._F)) - return bin(GF.integer_representation())[2:].zfill(8) + return bin(GF.to_integer())[2:].zfill(8) def encrypt(self, plain, key, format='hex'): r""" diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 1b4a94824dc..81ae146e204 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -762,9 +762,9 @@ def sub_byte(self, b): # constant addition if e == 4: - b = b + k.fetch_int(6) + b = b + k.from_integer(6) elif e == 8: - b = b + k.fetch_int(99) + b = b + k.from_integer(99) return b @@ -782,9 +782,9 @@ def sbox_constant(self): """ k = self.k if self.e == 4: - return k.fetch_int(6) + return k.from_integer(6) elif self.e == 8: - return k.fetch_int(99) + return k.from_integer(99) else: raise TypeError("sbox constant only defined for e in (4, 8)") @@ -1006,7 +1006,7 @@ def state_array(self, d=None): sage: sr = mq.SR(2, 2, 2, 4) sage: k = sr.base_ring() - sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1 + sage: e1 = [k.from_integer(e) for e in range(2*2)]; e1 [0, 1, a, a + 1] sage: e2 = sr.phi( Matrix(k, 2*2, 1, e1) ) sage: sr.state_array(e1) # note the column major ordering @@ -1229,8 +1229,8 @@ def __call__(self, P, K): sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True) sage: k = sr.base_ring() - sage: plaintext = sr.state_array([k.fetch_int(e) for e in range(16)]) - sage: key = sr.state_array([k.fetch_int(e) for e in range(16)]) + sage: plaintext = sr.state_array([k.from_integer(e) for e in range(16)]) + sage: key = sr.state_array([k.from_integer(e) for e in range(16)]) sage: print(sr.hex_str_matrix( sr(plaintext, key) )) 0A 41 F1 C6 94 6E C3 53 @@ -1301,9 +1301,9 @@ def __call__(self, P, K): F = self.base_ring() if isinstance(P, str): - P = self.state_array([F.fetch_int(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) + P = self.state_array([F.from_integer(ZZ(P[i: i + 2], 16)) for i in range(0, len(P), 2)]) if isinstance(K, str): - K = self.state_array([F.fetch_int(ZZ(K[i:i+2], 16)) for i in range(0, len(K), 2)]) + K = self.state_array([F.from_integer(ZZ(K[i: i + 2], 16)) for i in range(0, len(K), 2)]) if self.is_state_array(P) and self.is_state_array(K): _type = self.state_array @@ -1433,9 +1433,9 @@ def hex_str_matrix(self, M): for x in range(M.nrows()): for y in range(M.ncols()): if e == 8: - st.append("%02X" % M[x, y].integer_representation()) + st.append("%02X" % M[x, y].to_integer()) else: - st.append("%X" % M[x, y].integer_representation()) + st.append("%X" % M[x, y].to_integer()) st.append("\n") return " ".join(st) @@ -1464,9 +1464,9 @@ def hex_str_vector(self, M): for y in range(M.ncols()): for x in range(M.nrows()): if e == 8: - st.append("%02X" % M[x, y].integer_representation()) + st.append("%02X" % M[x, y].to_integer()) else: - st.append("%X" % M[x, y].integer_representation()) + st.append("%X" % M[x, y].to_integer()) #st.append("\n") return "".join(st) @@ -2321,13 +2321,13 @@ def lin_matrix(self, length = None): lin = Matrix(self.base_ring(), length*e, length*e) if e == 4: - l = [ k.fetch_int(x) for x in (5, 1, 12, 5) ] + l = [k.from_integer(x) for x in (5, 1, 12, 5)] for k in range( 0, length ): for i in range(0, 4): for j in range(0, 4): lin[k*4+j, k*4+i] = l[(i-j)%4] ** (2**j) elif e == 8: - l = [ k.fetch_int(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ] + l = [k.from_integer(x) for x in (5, 9, 249, 37, 244, 1, 181, 143)] for k in range( 0, length ): for i in range(0, 8): for j in range(0, 8): diff --git a/src/sage/crypto/sbox.pyx b/src/sage/crypto/sbox.pyx index 6e08ba3501c..77b7a904cc2 100644 --- a/src/sage/crypto/sbox.pyx +++ b/src/sage/crypto/sbox.pyx @@ -2030,4 +2030,3 @@ def misty_construction(*args): 64 """ return sbox_construction(misty_substitute, list(args)) - diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index f0554d7a6cc..728e73ca59b 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -288,7 +288,7 @@ def blum_blum_shub(length, seed=None, p=None, q=None, is the period. :: sage: from sage.crypto.stream import blum_blum_shub - sage: from sage.crypto.util import carmichael_lambda + sage: from sage.arith.misc import carmichael_lambda sage: carmichael_lambda(carmichael_lambda(7*11)) 4 sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=7, q=11, seed=13)] diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index 42b262c9858..aa013f9aca4 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -25,6 +25,9 @@ from sage.rings.integer import Integer from sage.rings.finite_rings.integer_mod import Mod as mod +from sage.misc.lazy_import import lazy_import +lazy_import('sage.arith.misc', ('carmichael_lambda'), deprecation=34719) + def ascii_integer(B): r""" Return the ASCII integer corresponding to the binary string ``B``. @@ -255,158 +258,6 @@ def bin_to_ascii(B): A.append(chr(ascii_integer(b[8*i: 8*(i+1)]))) return "".join(A) -def carmichael_lambda(n): - r""" - Return the Carmichael function of a positive integer ``n``. - - The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest - positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all - `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` - is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. - - INPUT: - - - ``n`` -- a positive integer. - - OUTPUT: - - - The Carmichael function of ``n``. - - ALGORITHM: - - If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd - prime and let `k` be a positive integer. Then - `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then - `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is - composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the - prime factorization of `n`. Then - - .. MATH:: - - \lambda(n) - = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) - = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) - - EXAMPLES: - - The Carmichael function of all positive integers up to and including 10:: - - sage: from sage.crypto.util import carmichael_lambda - sage: list(map(carmichael_lambda, [1..10])) - [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] - - The Carmichael function of the first ten primes:: - - sage: list(map(carmichael_lambda, primes_first_n(10))) - [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] - - Cases where the Carmichael function is equivalent to the Euler phi - function:: - - sage: carmichael_lambda(2) == euler_phi(2) - True - sage: carmichael_lambda(4) == euler_phi(4) - True - sage: p = random_prime(1000, lbound=3, proof=True) - sage: k = randint(1, 1000) - sage: carmichael_lambda(p^k) == euler_phi(p^k) - True - - A case where `\lambda(n) \neq \varphi(n)`:: - - sage: k = randint(3, 1000) - sage: carmichael_lambda(2^k) == 2^(k - 2) - True - sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) - False - - Verifying the current implementation of the Carmichael function using - another implementation. The other implementation that we use for - verification is an exhaustive search for the exponent of the - multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: - - sage: from sage.crypto.util import carmichael_lambda - sage: n = randint(1, 500) - sage: c = carmichael_lambda(n) - sage: def coprime(n): - ....: return [i for i in range(n) if gcd(i, n) == 1] - sage: def znpower(n, k): - ....: L = coprime(n) - ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) - sage: def my_carmichael(n): - ....: if n == 1: - ....: return 1 - ....: for k in range(1, n): - ....: L = znpower(n, k) - ....: ones = [1] * len(L) - ....: T = [L[i] == ones[i] for i in range(len(L))] - ....: if all(T): - ....: return k - sage: c == my_carmichael(n) - True - - Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` - for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. - Here, we verify Carmichael's theorem. :: - - sage: from sage.crypto.util import carmichael_lambda - sage: n = randint(2, 1000) - sage: c = carmichael_lambda(n) - sage: ZnZ = IntegerModRing(n) - sage: M = ZnZ.list_of_elements_of_multiplicative_group() - sage: ones = [1] * len(M) - sage: P = [power_mod(a, c, n) for a in M] - sage: P == ones - True - - TESTS: - - The input ``n`` must be a positive integer:: - - sage: from sage.crypto.util import carmichael_lambda - sage: carmichael_lambda(0) - Traceback (most recent call last): - ... - ValueError: Input n must be a positive integer. - sage: carmichael_lambda(randint(-10, 0)) - Traceback (most recent call last): - ... - ValueError: Input n must be a positive integer. - - Bug reported in :trac:`8283`:: - - sage: from sage.crypto.util import carmichael_lambda - sage: type(carmichael_lambda(16)) - <class 'sage.rings.integer.Integer'> - - REFERENCES: - - - :wikipedia:`Carmichael_function` - """ - n = Integer(n) - # sanity check - if n < 1: - raise ValueError("Input n must be a positive integer.") - - L = n.factor() - t = [] - - # first get rid of the prime factor 2 - if n & 1 == 0: - e = L[0][1] - L = L[1:] # now, n = 2**e * L.value() - if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) - e = e - 1 - else: # for k >= 3, lambda(2**k) = 2**(k - 2) - e = e - 2 - t.append(1 << e) # 2**e - - # then other prime factors - t += [p**(k - 1) * (p - 1) for p, k in L] - - # finish the job - return lcm(t) - def has_blum_prime(lbound, ubound): r""" diff --git a/src/sage/data_structures/binary_search.pyx b/src/sage/data_structures/binary_search.pyx index 030d50266e5..a53061c3efb 100644 --- a/src/sage/data_structures/binary_search.pyx +++ b/src/sage/data_structures/binary_search.pyx @@ -63,4 +63,3 @@ cdef Py_ssize_t binary_search0(Py_ssize_t* v, Py_ssize_t n, Py_ssize_t x): else: # only possibility is that v[k] == x return k return -1 - diff --git a/src/sage/data_structures/bitset_base.pxd b/src/sage/data_structures/bitset_base.pxd index 5a89b84fa3e..f932e894efe 100644 --- a/src/sage/data_structures/bitset_base.pxd +++ b/src/sage/data_structures/bitset_base.pxd @@ -37,7 +37,7 @@ from memory_allocator cimport MemoryAllocator from memory_allocator.memory_allocator cimport align from cython.operator import preincrement as preinc -from sage.cpython.string cimport char_to_str, str_to_bytes, bytes_to_str +from sage.cpython.string cimport str_to_bytes, bytes_to_str from sage.libs.gmp.mpn cimport * from sage.libs.gmp.types cimport * from sage.data_structures.sparse_bitset cimport sparse_bitset_t diff --git a/src/sage/data_structures/blas_dict.pxd b/src/sage/data_structures/blas_dict.pxd index f55b77975b4..7464c0daba8 100644 --- a/src/sage/data_structures/blas_dict.pxd +++ b/src/sage/data_structures/blas_dict.pxd @@ -7,6 +7,5 @@ cpdef dict sum(dict_iter) cpdef dict linear_combination(dict_factor_iter, bint factor_on_left=*) cpdef dict sum_of_monomials(monomials, scalar) cpdef dict sum_of_terms(index_coeff_pairs) -cdef inline dict remove_zeros(dict D) +cdef dict remove_zeros(dict D) cpdef dict convert_remove_zeroes(dict D, R) - diff --git a/src/sage/data_structures/blas_dict.pyx b/src/sage/data_structures/blas_dict.pyx index 55364880150..df6bf29641e 100644 --- a/src/sage/data_structures/blas_dict.pyx +++ b/src/sage/data_structures/blas_dict.pyx @@ -448,4 +448,3 @@ cpdef dict convert_remove_zeroes(dict D, R): for index in for_removal: del D[index] return D - diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index f307ebd01d1..724f296eae2 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -77,7 +77,7 @@ cimported in Cython modules: - ``cdef size_t biseq_getitem_py(biseq_t S, mp_size_t index)`` - Return ``S[index]`` as Python ``int`` or ``long``, without checking margins. + Return ``S[index]`` as Python ``int``, without checking margins. - ``cdef biseq_inititem(biseq_t S, mp_size_t index, size_t item)`` @@ -291,7 +291,7 @@ cdef inline size_t biseq_getitem(biseq_t S, mp_size_t index): cdef biseq_getitem_py(biseq_t S, mp_size_t index): """ - Get item ``S[index]`` as a Python ``int`` or ``long``, without + Get item ``S[index]`` as a Python ``int``, without checking margins. """ diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index e349708c1ed..f8f6dc6a186 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -21,57 +21,57 @@ example, we can add two streams:: sage: from sage.data_structures.stream import * - sage: f = Stream_function(lambda n: n, QQ, True, 0) + sage: f = Stream_function(lambda n: n, True, 0) sage: [f[i] for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - sage: g = Stream_function(lambda n: 1, QQ, True, 0) + sage: g = Stream_function(lambda n: 1, True, 0) sage: [g[i] for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] We can subtract one stream from another:: - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] There is a Cauchy product on streams:: - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] We can compute the inverse corresponding to the Cauchy product:: sage: ginv = Stream_cauchy_invert(g) - sage: h = Stream_cauchy_mul(f, ginv) + sage: h = Stream_cauchy_mul(f, ginv, True) sage: [h[i] for i in range(10)] [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] Two streams can be composed:: - sage: g = Stream_function(lambda n: n, QQ, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: g = Stream_function(lambda n: n, True, 1) + sage: h = Stream_cauchy_compose(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 4, 14, 46, 145, 444, 1331, 3926, 11434] There is a unary negation operator:: - sage: h = Stream_neg(f) + sage: h = Stream_neg(f, True) sage: [h[i] for i in range(10)] [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] More generally, we can multiply by a scalar:: - sage: h = Stream_lmul(f, 2) + sage: h = Stream_lmul(f, 2, True) sage: [h[i] for i in range(10)] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] Finally, we can apply an arbitrary functions to the elements of a stream:: - sage: h = Stream_map_coefficients(f, lambda n: n^2, QQ) + sage: h = Stream_map_coefficients(f, lambda n: n^2, True) sage: [h[i] for i in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] @@ -80,7 +80,6 @@ - Kwankyu Lee (2019-02-24): initial version - Tejasvi Chebrolu, Martin Rubey, Travis Scrimshaw (2021-08): refactored and expanded functionality - """ # **************************************************************************** @@ -98,7 +97,12 @@ from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity from sage.arith.misc import divisors +from sage.misc.misc_c import prod +from sage.misc.lazy_attribute import lazy_attribute from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter +from sage.combinat.sf.sfa import _variables_recursive, _raise_variables +from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis + class Stream(): """ @@ -106,21 +110,52 @@ class Stream(): INPUT: - - ``sparse`` -- boolean; whether the implementation of the stream is sparse - - ``approximate_order`` -- integer; a lower bound for the order - of the stream + - ``true_order`` -- boolean; if the approximate order is the actual order + + .. NOTE:: + + An implementation of a stream class depending on other stream + classes must not access coefficients or the approximate order + of these, in order not to interfere with lazy definitions for + :class:`Stream_uninitialized`. + + If an approximate order or even the true order is known, it + must be set after calling ``super().__init__``. + + Otherwise, a lazy attribute `_approximate_order` has to be + defined. Any initialization code depending on the + approximate orders of input streams can be put into this + definition. + + However, keep in mind that (trivially) this initialization + code is not executed if `_approximate_order` is set to a + value before it is accessed. + """ - def __init__(self, sparse, approximate_order): + def __init__(self, true_order): """ Initialize ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) """ - self._is_sparse = sparse - self._approximate_order = approximate_order + self._true_order = true_order + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_exact + sage: f = Stream_exact([0,3]) + sage: f._approximate_order + 1 + """ + raise NotImplementedError def __ne__(self, other): """ @@ -132,12 +167,11 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) sage: CS != CS False - sage: CS != Stream(False, -2) + sage: CS != Stream(-2) False - """ return False @@ -151,7 +185,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) sage: CS.is_nonzero() False """ @@ -163,34 +197,65 @@ class Stream_inexact(Stream): An abstract base class for the stream when we do not know it is eventually constant. + In particular, a cache is provided. + INPUT: - - ``sparse`` -- boolean; whether the implementation of the stream is sparse - - ``approximate_order`` -- integer; a lower bound for the order - of the stream + - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse + - ``true_order`` -- boolean; if the approximate order is the actual order + + .. TODO:: + + The ``approximate_order`` is currently only updated when + invoking :meth:`order`. It might make sense to update it + whenever the coefficient one larger than the current + ``approximate_order`` is computed, since in some methods this + will allow shortcuts. + """ - def __init__(self, is_sparse, approximate_order): + def __init__(self, is_sparse, true_order): """ - Initialize the stream class for a Stream when it is not - or it cannot be determined if it is eventually geometric. + Initialize the stream class for a stream whose + coefficients are not necessarily eventually constant. TESTS:: sage: from sage.data_structures.stream import Stream_inexact sage: from sage.data_structures.stream import Stream_function - sage: g = Stream_function(lambda n: n, QQ, False, 0) + sage: g = Stream_function(lambda n: n, False, 0) sage: isinstance(g, Stream_inexact) True - """ - super().__init__(is_sparse, approximate_order) + """ + super().__init__(true_order) + self._is_sparse = is_sparse if self._is_sparse: self._cache = dict() # cache of known coefficients else: self._cache = list() - self._offset = approximate_order self._iter = self.iterate_coefficients() + @lazy_attribute + def _offset(self): + """ + Return the offset of a stream with a dense cache. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function + sage: f = Stream_function(lambda n: n, False, -3) + sage: f._offset + -3 + sage: [f[i] for i in range(-3, 5)] + [-3, -2, -1, 0, 1, 2, 3, 4] + sage: f._cache + [-3, -2, -1, 0, 1, 2, 3, 4] + """ + # self[n] = self._cache[n-self._offset] + if self._is_sparse: + raise ValueError("_offset is only for dense streams") + return self._approximate_order + def is_nonzero(self): r""" Return ``True`` if and only if the cache contains a nonzero element. @@ -198,7 +263,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function - sage: CS = Stream_function(lambda n: 1/n, ZZ, False, 1) + sage: CS = Stream_function(lambda n: 1/n, False, 1) sage: CS.is_nonzero() False sage: CS[1] @@ -221,9 +286,9 @@ def __getstate__(self): sage: from sage.data_structures.stream import Stream_exact sage: from sage.data_structures.stream import Stream_cauchy_mul - sage: h = Stream_exact([1], True) - sage: g = Stream_exact([1, -1, -1], True) - sage: u = Stream_cauchy_mul(h, g) + sage: h = Stream_exact([1]) + sage: g = Stream_exact([1, -1, -1]) + sage: u = Stream_cauchy_mul(h, g, True) sage: [u[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] sage: u._cache @@ -234,9 +299,9 @@ def __getstate__(self): sage: [m[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] - sage: h = Stream_exact([1], False) - sage: g = Stream_exact([1, -1, -1], False) - sage: u = Stream_cauchy_mul(h, g) + sage: h = Stream_exact([1]) + sage: g = Stream_exact([1, -1, -1]) + sage: u = Stream_cauchy_mul(h, g, False) sage: [u[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] sage: u._cache @@ -266,10 +331,10 @@ def __setstate__(self, d): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([-1], True) - sage: g = Stream_exact([1, -1], True) + sage: h = Stream_exact([-1]) + sage: g = Stream_exact([1, -1]) sage: from sage.data_structures.stream import Stream_cauchy_mul - sage: u = Stream_cauchy_mul(h, g) + sage: u = Stream_cauchy_mul(h, g, True) sage: [u[i] for i in range(10)] [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0] sage: loads(dumps(u)) == u @@ -291,7 +356,7 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n^2, QQ, True, 0) + sage: f = Stream_function(lambda n: n^2, True, 0) sage: f[3] 9 sage: f._cache @@ -301,7 +366,7 @@ def __getitem__(self, n): sage: f._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} - sage: f = Stream_function(lambda n: n^2, QQ, False, 0) + sage: f = Stream_function(lambda n: n^2, False, 0) sage: f[3] 9 sage: f._cache @@ -339,9 +404,9 @@ def iterate_coefficients(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose - sage: f = Stream_function(lambda n: 1, ZZ, False, 1) - sage: g = Stream_function(lambda n: n^3, ZZ, False, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: f = Stream_function(lambda n: 1, False, 1) + sage: g = Stream_function(lambda n: n^3, False, 1) + sage: h = Stream_cauchy_compose(f, g, True) sage: n = h.iterate_coefficients() sage: [next(n) for i in range(10)] [1, 9, 44, 207, 991, 4752, 22769, 109089, 522676, 2504295] @@ -359,10 +424,12 @@ def order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n, QQ, True, 0) + sage: f = Stream_function(lambda n: n, True, 0) sage: f.order() 1 """ + if self._true_order: + return self._approximate_order if self._is_sparse: n = self._approximate_order cache = self._cache @@ -370,11 +437,13 @@ def order(self): if n in cache: if cache[n]: self._approximate_order = n + self._true_order = True return n n += 1 else: if self[n]: self._approximate_order = n + self._true_order = True return n n += 1 else: @@ -384,11 +453,13 @@ def order(self): if n - self._offset < len(cache): if cache[n - self._offset]: self._approximate_order = n + self._true_order = True return n n += 1 else: if self[n]: self._approximate_order = n + self._true_order = True return n n += 1 @@ -402,8 +473,8 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n, QQ, True, 0) - sage: g = Stream_function(lambda n: n^2, QQ, True, 0) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: n^2, True, 0) sage: f != g False sage: f[1], g[1] @@ -421,8 +492,8 @@ def __ne__(self, other): Checking the dense implementation:: - sage: f = Stream_function(lambda n: n if n > 0 else 0, QQ, False, -3) - sage: g = Stream_function(lambda n: n^2, QQ, False, 0) + sage: f = Stream_function(lambda n: n if n > 0 else 0, False, -3) + sage: g = Stream_function(lambda n: n^2, False, 0) sage: f != g False sage: g != f @@ -438,8 +509,8 @@ def __ne__(self, other): sage: g != f True - sage: f = Stream_function(lambda n: n if n > 0 else 0, QQ, False, -3) - sage: g = Stream_function(lambda n: n^2, QQ, False, 0) + sage: f = Stream_function(lambda n: n if n > 0 else 0, False, -3) + sage: g = Stream_function(lambda n: n^2, False, 0) sage: _ = f[5], g[1] sage: f != g False @@ -451,8 +522,8 @@ def __ne__(self, other): sage: g != f True - sage: f = Stream_function(lambda n: n if n > 0 else 0, QQ, False, -3) - sage: g = Stream_function(lambda n: n^2, QQ, False, 0) + sage: f = Stream_function(lambda n: n if n > 0 else 0, False, -3) + sage: g = Stream_function(lambda n: n^2, False, 0) sage: _ = g[5], f[1] sage: f != g False @@ -471,7 +542,7 @@ def __ne__(self, other): for i in self._cache: if i in other._cache and other._cache[i] != self._cache[i]: return True - else: # they are dense + else: # they are dense # Make ``self`` have the smaller approximate order. if self._approximate_order > other._approximate_order: self, other = other, self @@ -491,6 +562,7 @@ def __ne__(self, other): return False + class Stream_exact(Stream): r""" A stream of eventually constant coefficients. @@ -505,40 +577,98 @@ class Stream_exact(Stream): of the first element which is known to be equal to ``constant`` - ``constant`` -- integer (default: 0); the coefficient of every index larger than or equal to ``degree`` + + .. WARNING:: + + The convention for ``order`` is different to the one in + :class:`sage.rings.lazy_series_ring.LazySeriesRing`, where + the input is shifted to have the prescribed order. + """ - def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, order=None): + def __init__(self, initial_coefficients, constant=None, degree=None, order=None): """ Initialize a stream with eventually constant coefficients. TESTS:: sage: from sage.data_structures.stream import Stream_exact - sage: Stream_exact([], False) + sage: Stream_exact([]) Traceback (most recent call last): ... AssertionError: Stream_exact should only be used for non-zero streams + + sage: s = Stream_exact([0, 0, 1, 0, 0]) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1,), 2, 3, True) + + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=0) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1,), 2, 3, True) + + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=0, degree=10) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1,), 2, 3, True) + + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=1) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1,), 2, 5, True) + + sage: s = Stream_exact([0, 0, 1, 0, 1], constant=1, degree=10) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1, 0, 1), 2, 10, True) + + sage: s = Stream_exact([0, 0, 1, 0, 1], constant=1, degree=5) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1,), 2, 4, True) + + sage: s = Stream_exact([0, 0, 1, 2, 0, 1], constant=1) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1, 2), 2, 5, True) + + sage: s = Stream_exact([0, 0, 1, 2, 1, 1], constant=1) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1, 2), 2, 4, True) + + sage: s = Stream_exact([0, 0, 1, 2, 1, 1], constant=1, order=-2) + sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order + ((1, 2), 0, 2, True) """ if constant is None: self._constant = ZZ.zero() else: self._constant = constant + if order is None: order = 0 - if degree is None: + if (degree is None + or (not self._constant + and degree > order + len(initial_coefficients))): self._degree = order + len(initial_coefficients) else: self._degree = degree - assert order + len(initial_coefficients) <= self._degree - # We do not insist that the last entry of - # initial_coefficients is different from constant in case - # comparisons can be expensive such as in the symbolic ring + # we remove leading and trailing zeros from + # initial_coefficients + + # if the degree is order + len(initial_coefficients), we also + # insist that the last entry of initial_coefficients is + # different from constant, because __eq__ below would become + # complicated otherwise for i, v in enumerate(initial_coefficients): if v: + # We have found the first nonzero coefficient order += i initial_coefficients = initial_coefficients[i:] - for j, w in enumerate(reversed(initial_coefficients)): + if order + len(initial_coefficients) == self._degree: + # Strip off the constant values at the end + for w in reversed(initial_coefficients): + if w != self._constant: + break + initial_coefficients.pop() + self._degree -= 1 + # Strip off all remaining zeros at the end + for w in reversed(initial_coefficients): if w: break initial_coefficients.pop() @@ -550,7 +680,8 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, assert self._initial_coefficients or self._constant, "Stream_exact should only be used for non-zero streams" - super().__init__(is_sparse, order) + super().__init__(True) + self._approximate_order = order def __getitem__(self, n): """ @@ -563,29 +694,37 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: [s[i] for i in range(-2, 5)] [0, 0, 1, 0, 0, 0, 0] - sage: s = Stream_exact([], False, constant=1) + sage: s = Stream_exact([], constant=1) sage: [s[i] for i in range(-2, 5)] [0, 0, 1, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, constant=1) + sage: s = Stream_exact([2], constant=1) sage: [s[i] for i in range(-2, 5)] [0, 0, 2, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], order=-1, constant=1) sage: [s[i] for i in range(-2, 5)] [0, 2, 1, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: [s[i] for i in range(-2, 5)] [0, 2, 0, 0, 1, 1, 1] - sage: t = Stream_exact([0, 2, 0], False, order=-2, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], order=-2, degree=2, constant=1) sage: t == s True + + sage: s = Stream_exact([0,1,2,1,0,0,1,1], constant=1) + sage: [s[i] for i in range(10)] + [0, 1, 2, 1, 0, 0, 1, 1, 1, 1] + + sage: t = Stream_exact([0,1,2,1,0,0], constant=1) + sage: s == t + True """ if n >= self._degree: return self._constant @@ -596,15 +735,16 @@ def __getitem__(self, n): def order(self): r""" - Return the order of ``self``, which is the minimum index ``n`` such - that ``self[n]`` is nonzero. + Return the order of ``self``, which is the minimum index + ``n`` such that ``self[n]`` is nonzero. EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: s.order() 0 + """ return self._approximate_order @@ -615,7 +755,7 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: hash(s) == hash(s) True """ @@ -632,16 +772,16 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) - sage: t = Stream_exact([0, 2, 0], False, 1, 2, -2) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], 1, 2, -2) sage: [s[i] for i in range(10)] [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] sage: [t[i] for i in range(10)] [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] sage: s == t True - sage: s = Stream_exact([2], False, constant=1) - sage: t = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], constant=1) + sage: t = Stream_exact([2], order=-1, constant=1) sage: [s[i] for i in range(10)] [2, 1, 1, 1, 1, 1, 1, 1, 1, 1] sage: [t[i] for i in range(10)] @@ -651,8 +791,8 @@ def __eq__(self, other): sage: t == t True - sage: s = Stream_exact([2], False, order=0, degree=5, constant=1) - sage: t = Stream_exact([2], False, order=-1, degree=5, constant=1) + sage: s = Stream_exact([2], order=0, degree=5, constant=1) + sage: t = Stream_exact([2], order=-1, degree=5, constant=1) sage: s == t False """ @@ -664,7 +804,8 @@ def __eq__(self, other): def __ne__(self, other): """ - Test inequality between ``self`` and ``other``. + Test inequality between ``self`` and ``other``, where + other is exact or inexact, but not zero. INPUT: @@ -673,12 +814,12 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) - sage: t = Stream_exact([0, 2, 0], False, 1, 2, -2) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], 1, 2, -2) sage: s != t False - sage: s = Stream_exact([2], False, constant=1) - sage: t = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], constant=1) + sage: t = Stream_exact([2], order=-1, constant=1) sage: s != t True @@ -686,7 +827,7 @@ def __ne__(self, other): return ``False``:: sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: 2 if n == 0 else 1, ZZ, False, 0) + sage: f = Stream_function(lambda n: 2 if n == 0 else 1, False, 0) sage: s == f False sage: s != f @@ -695,12 +836,24 @@ def __ne__(self, other): [0, 0, 0, 2, 1, 1, 1, 1] sage: [f[i] for i in range(-3, 5)] [0, 0, 0, 2, 1, 1, 1, 1] + """ if isinstance(other, type(self)): return (self._degree != other._degree or self._approximate_order != other._approximate_order or self._initial_coefficients != other._initial_coefficients or self._constant != other._constant) + # if other is not exact, we can at least compare with the + # elements in its cache + if other._is_sparse: + for i in other._cache: + if self[i] != other._cache[i]: + return True + else: + if other._offset > self._approximate_order: + return False + return any(self[i] != c for i, c in enumerate(other._cache, other._offset)) + return False def is_nonzero(self): @@ -713,7 +866,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: s.is_nonzero() True """ @@ -726,7 +879,7 @@ def _polynomial_part(self, R): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: L.<z> = LazyLaurentSeriesRing(ZZ) sage: s._polynomial_part(L._laurent_poly_ring) 2*z^-1 @@ -735,77 +888,89 @@ def _polynomial_part(self, R): return R(self._initial_coefficients).shift(v) -class Stream_function(Stream_inexact): +class Stream_iterator(Stream_inexact): r""" - Class that creates a stream from a function on the integers. + Class that creates a stream from an iterator. INPUT: - - ``function`` -- a function that generates the - coefficients of the stream - - ``ring`` -- the base ring - - ``is_sparse`` -- boolean; specifies whether the stream is sparse + - ``iter`` -- a function that generates the coefficients of the + stream - ``approximate_order`` -- integer; a lower bound for the order of the stream + Instances of this class are always dense. + EXAMPLES:: - sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n^2, ZZ, False, 1) - sage: f[3] - 9 + sage: from sage.data_structures.stream import Stream_iterator + sage: f = Stream_iterator(iter(NonNegativeIntegers()), 0) sage: [f[i] for i in range(10)] - [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] - """ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + sage: f = Stream_iterator(iter(NonNegativeIntegers()), 1) + sage: [f[i] for i in range(10)] + [0, 0, 1, 2, 3, 4, 5, 6, 7, 8] - def __init__(self, function, ring, is_sparse, approximate_order): + """ + def __init__(self, iter, approximate_order, true_order=False): """ Initialize. TESTS:: - sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: 1, ZZ, False, 1) + sage: from sage.data_structures.stream import Stream_iterator + sage: f = Stream_iterator(iter(NonNegativeIntegers()), 0) sage: TestSuite(f).run(skip="_test_pickling") """ - self._function = function - self._ring = ring - super().__init__(is_sparse, approximate_order) + self.iterate_coefficients = lambda: iter + super().__init__(False, true_order) + self._approximate_order = approximate_order - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of ``self``. - INPUT: +class Stream_function(Stream_inexact): + r""" + Class that creates a stream from a function on the integers. - - ``n`` -- integer; the degree for the coefficient + INPUT: - EXAMPLES:: + - ``function`` -- a function that generates the + coefficients of the stream + - ``is_sparse`` -- boolean; specifies whether the stream is sparse + - ``approximate_order`` -- integer; a lower bound for the order + of the stream - sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n, QQ, True, 0) - sage: f.get_coefficient(4) - 4 - """ - return self._ring(self._function(n)) + EXAMPLES:: - def iterate_coefficients(self): + sage: from sage.data_structures.stream import Stream_function + sage: f = Stream_function(lambda n: n^2, False, 1) + sage: f[3] + 9 + sage: [f[i] for i in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + sage: f = Stream_function(lambda n: 1, False, 0) + sage: n = f.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + + sage: f = Stream_function(lambda n: n, True, 0) + sage: f[4] + 4 + """ + def __init__(self, function, is_sparse, approximate_order, true_order=False): """ - A generator for the coefficients of ``self``. + Initialize. - EXAMPLES:: + TESTS:: sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: 1, QQ, False, 0) - sage: n = f.iterate_coefficients() - sage: [next(n) for _ in range(10)] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + sage: f = Stream_function(lambda n: 1, False, 1) + sage: TestSuite(f).run(skip="_test_pickling") """ - n = self._offset - ring = self._ring - while True: - yield ring(self._function(n)) - n += 1 + self.get_coefficient = function + super().__init__(is_sparse, true_order) + self._approximate_order = approximate_order class Stream_uninitialized(Stream_inexact): @@ -814,55 +979,43 @@ class Stream_uninitialized(Stream_inexact): INPUT: - - ``is_sparse`` -- boolean; which specifies whether the stream is sparse - ``approximate_order`` -- integer; a lower bound for the order of the stream + Instances of this class are always dense. + + .. TODO:: + + shouldn't instances of this class share the cache with its + ``_target``? + EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact - sage: one = Stream_exact([1], True) - sage: C = Stream_uninitialized(True, 0) + sage: one = Stream_exact([1]) + sage: C = Stream_uninitialized(0) sage: C._target sage: C._target = one - sage: C.get_coefficient(4) + sage: C[4] 0 + """ - def __init__(self, is_sparse, approximate_order): + def __init__(self, approximate_order, true_order=False): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import Stream_uninitialized - sage: C = Stream_uninitialized(False, 0) + sage: C = Stream_uninitialized(0) sage: TestSuite(C).run(skip="_test_pickling") """ self._target = None - assert approximate_order is not None, "calling Stream_uninitialized with None as approximate order" - super().__init__(is_sparse, approximate_order) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of ``self``. - - INPUT: - - - ``n`` -- integer; the degree for the coefficient - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_exact - sage: one = Stream_exact([1], True) - sage: C = Stream_uninitialized(True, 0) - sage: C._target - sage: C._target = one - sage: C.get_coefficient(0) - 1 - """ - return self._target[n] + if approximate_order is None: + raise ValueError("the valuation must be specified for undefined series") + super().__init__(False, true_order) + self._approximate_order = approximate_order def iterate_coefficients(self): """ @@ -872,8 +1025,8 @@ def iterate_coefficients(self): sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact - sage: z = Stream_exact([1], True, order=1) - sage: C = Stream_uninitialized(True, 0) + sage: z = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(0) sage: C._target sage: C._target = z sage: n = C.iterate_coefficients() @@ -897,16 +1050,16 @@ class Stream_unary(Stream_inexact): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_invert, Stream_lmul) - sage: f = Stream_function(lambda n: 2*n, ZZ, False, 1) + sage: f = Stream_function(lambda n: 2*n, False, 1) sage: g = Stream_cauchy_invert(f) sage: [g[i] for i in range(10)] [-1, 1/2, 0, 0, 0, 0, 0, 0, 0, 0] - sage: g = Stream_lmul(f, 2) + sage: g = Stream_lmul(f, 2, True) sage: [g[i] for i in range(10)] [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] """ - def __init__(self, series, *args, **kwargs): + def __init__(self, series, is_sparse): """ Initialize ``self``. @@ -914,14 +1067,14 @@ def __init__(self, series, *args, **kwargs): sage: from sage.data_structures.stream import Stream_unary sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) - sage: f = Stream_exact([1, -1], False) + sage: f = Stream_exact([1, -1]) sage: g = Stream_cauchy_invert(f) sage: isinstance(g, Stream_unary) True sage: TestSuite(g).run() """ self._series = series - super().__init__(*args, **kwargs) + super().__init__(is_sparse, False) def __hash__(self): """ @@ -931,7 +1084,7 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_unary sage: from sage.data_structures.stream import Stream_function - sage: M = Stream_unary(Stream_function(lambda n: 1, ZZ, False, 1), True, 0) + sage: M = Stream_unary(Stream_function(lambda n: 1, False, 1), True) sage: hash(M) == hash(M) True """ @@ -948,10 +1101,10 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_rmul) - sage: f = Stream_function(lambda n: 2*n, ZZ, False, 1) - sage: g = Stream_function(lambda n: n, ZZ, False, 1) - sage: h = Stream_rmul(f, 2) - sage: n = Stream_rmul(g, 2) + sage: f = Stream_function(lambda n: 2*n, False, 1) + sage: g = Stream_function(lambda n: n, False, 1) + sage: h = Stream_rmul(f, 2, True) + sage: n = Stream_rmul(g, 2, True) sage: h == n False sage: n == n @@ -974,17 +1127,17 @@ class Stream_binary(Stream_inexact): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_add, Stream_sub) - sage: f = Stream_function(lambda n: 2*n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n, ZZ, True, 1) - sage: h = Stream_add(f, g) + sage: f = Stream_function(lambda n: 2*n, True, 0) + sage: g = Stream_function(lambda n: n, True, 1) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] """ - def __init__(self, left, right, *args, **kwargs): + def __init__(self, left, right, is_sparse): """ Initialize ``self``. @@ -992,18 +1145,18 @@ def __init__(self, left, right, *args, **kwargs): sage: from sage.data_structures.stream import Stream_binary sage: from sage.data_structures.stream import (Stream_add, Stream_cauchy_invert, Stream_exact) - sage: f1 = Stream_exact([1, -1], False) + sage: f1 = Stream_exact([1, -1]) sage: g1 = Stream_cauchy_invert(f1) - sage: f2 = Stream_exact([1, 1], False) + sage: f2 = Stream_exact([1, 1]) sage: g2 = Stream_cauchy_invert(f2) - sage: O = Stream_add(g1, g2) + sage: O = Stream_add(g1, g2, True) sage: isinstance(O, Stream_binary) True sage: TestSuite(O).run() """ self._left = left self._right = right - super().__init__(*args, **kwargs) + super().__init__(is_sparse, False) def __hash__(self): """ @@ -1013,9 +1166,9 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_binary sage: from sage.data_structures.stream import Stream_function - sage: M = Stream_function(lambda n: n, ZZ, True, 0) - sage: N = Stream_function(lambda n: -2*n, ZZ, True, 0) - sage: O = Stream_binary(M, N, True, 0) + sage: M = Stream_function(lambda n: n, True, 0) + sage: N = Stream_function(lambda n: -2*n, True, 0) + sage: O = Stream_binary(M, N, True) sage: hash(O) == hash(O) True """ @@ -1032,12 +1185,12 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) - sage: f = Stream_function(lambda n: 2*n, ZZ, False, 1) - sage: g = Stream_function(lambda n: n, ZZ, False, 1) - sage: h = Stream_function(lambda n: 1, ZZ, False, 1) - sage: t = Stream_cauchy_mul(f, g) - sage: u = Stream_cauchy_mul(g, h) - sage: v = Stream_cauchy_mul(h, f) + sage: f = Stream_function(lambda n: 2*n, False, 1) + sage: g = Stream_function(lambda n: n, False, 1) + sage: h = Stream_function(lambda n: 1, False, 1) + sage: t = Stream_cauchy_mul(f, g, True) + sage: u = Stream_cauchy_mul(g, h, True) + sage: v = Stream_cauchy_mul(h, f, True) sage: t == u False sage: t == t @@ -1057,12 +1210,12 @@ class Stream_binaryCommutative(Stream_binary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: 2*n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n, ZZ, True, 1) - sage: h = Stream_add(f, g) + sage: f = Stream_function(lambda n: 2*n, True, 0) + sage: g = Stream_function(lambda n: n, True, 1) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] sage: h == u @@ -1075,10 +1228,10 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: 2*n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n, ZZ, True, 1) - sage: h = Stream_add(f, g) - sage: u = Stream_add(g, f) + sage: f = Stream_function(lambda n: 2*n, True, 0) + sage: g = Stream_function(lambda n: n, True, 1) + sage: h = Stream_add(f, g, True) + sage: u = Stream_add(g, f, True) sage: hash(h) == hash(u) True """ @@ -1095,12 +1248,12 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: 2*n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n, ZZ, True, 1) - sage: h = Stream_add(f, g) + sage: f = Stream_function(lambda n: 2*n, True, 0) + sage: g = Stream_function(lambda n: n, True, 1) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] sage: h == u @@ -1119,29 +1272,27 @@ class Stream_zero(Stream): """ A coefficient stream that is exactly equal to zero. - INPUT: - - - ``sparse`` -- boolean; whether the coefficient stream is sparse or not - EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s[5] 0 """ - def __init__(self, sparse): + def __init__(self): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(False) + sage: s = Stream_zero() sage: TestSuite(s).run() + """ - return super().__init__(sparse, 0) + super().__init__(True) + self._approximate_order = infinity def __getitem__(self, n): """ @@ -1154,7 +1305,7 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s[1] 0 sage: sum([s[i] for i in range(10)]) @@ -1169,20 +1320,20 @@ def order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s.order() +Infinity """ - return infinity + return self._approximate_order # == infinity def __eq__(self, other): """ - Check equality of ``self`` and ``other`` ignoring sparsity. + Check equality of ``self`` and ``other``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: Stream_zero(True) == Stream_zero(False) + sage: Stream_zero() == Stream_zero() True """ return self is other or isinstance(other, Stream_zero) @@ -1194,14 +1345,9 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(False) - sage: a = hash(s); a - 0 - sage: t = Stream_zero(False) - sage: b = hash(t); b + sage: s = Stream_zero() + sage: hash(s) 0 - sage: b == a - True """ return 0 @@ -1221,31 +1367,29 @@ class Stream_add(Stream_binaryCommutative): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_add, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: 1, ZZ, True, 0) - sage: h = Stream_add(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: 1, True, 0) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] """ - def __init__(self, left, right): + @lazy_attribute + def _approximate_order(self): """ - Initialize ``self``. + Compute and return the approximate order of ``self``. - TESTS:: + EXAMPLES:: - sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: 1, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_add(f, g) + sage: from sage.data_structures.stream import Stream_exact + sage: h = Stream_exact([0,3]) + sage: h._approximate_order + 1 """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_order, right._approximate_order) - super().__init__(left, right, left._is_sparse, a) + # this is not the true order, because we may have cancellation + return min(self._left._approximate_order, self._right._approximate_order) def get_coefficient(self, n): """ @@ -1258,9 +1402,9 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_add(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: n^2, True, 0) + sage: h = Stream_add(f, g, True) sage: h.get_coefficient(5) 30 sage: [h.get_coefficient(i) for i in range(10)] @@ -1281,32 +1425,33 @@ class Stream_sub(Stream_binary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_sub, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: 1, ZZ, True, 0) - sage: h = Stream_sub(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: 1, True, 0) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] - sage: u = Stream_sub(g, f) + sage: u = Stream_sub(g, f, True) sage: [u[i] for i in range(10)] [1, 0, -1, -2, -3, -4, -5, -6, -7, -8] """ - - def __init__(self, left, right): + @lazy_attribute + def _approximate_order(self): """ - initialize ``self``. + Compute and return the approximate order of ``self``. - TESTS:: + EXAMPLES:: - sage: from sage.data_structures.stream import (Stream_function, Stream_sub) - sage: f = Stream_function(lambda n: 1, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_sub(f, g) + sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_add + sage: f = Stream_exact([0,3]) + sage: g = Stream_function(lambda n: -3*n, True, 1) + sage: h = Stream_add(f, g, True) + sage: h._approximate_order + 1 + sage: [h[i] for i in range(5)] + [0, 0, -6, -9, -12] """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_order, right._approximate_order) - super().__init__(left, right, left._is_sparse, a) + # this is not the true order, because we may have cancellation + return min(self._left._approximate_order, self._right._approximate_order) def get_coefficient(self, n): """ @@ -1319,9 +1464,9 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_sub) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_sub(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: n^2, True, 0) + sage: h = Stream_sub(f, g, True) sage: h.get_coefficient(5) -20 sage: [h.get_coefficient(i) for i in range(10)] @@ -1346,31 +1491,33 @@ class Stream_cauchy_mul(Stream_binary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_mul, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: 1, ZZ, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: 1, True, 0) + sage: h = Stream_cauchy_mul(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] - sage: u = Stream_cauchy_mul(g, f) + sage: u = Stream_cauchy_mul(g, f, True) sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ - def __init__(self, left, right): + @lazy_attribute + def _approximate_order(self): """ - initialize ``self``. + Compute and return the approximate order of ``self``. - TESTS:: + EXAMPLES:: - sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) - sage: f = Stream_function(lambda n: 1, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_cauchy_mul + sage: f = Stream_exact([0, Zmod(6)(2)]) + sage: g = Stream_function(lambda n: Zmod(6)(3*n), True, 1) + sage: h = Stream_cauchy_mul(f, g, True) + sage: h._approximate_order + 2 + sage: [h[i] for i in range(5)] + [0, 0, 0, 0, 0] """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = left._approximate_order + right._approximate_order - super().__init__(left, right, left._is_sparse, a) + # this is not the true order, unless we have an integral domain + return self._left._approximate_order + self._right._approximate_order def get_coefficient(self, n): """ @@ -1383,9 +1530,9 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: f = Stream_function(lambda n: n, True, 0) + sage: g = Stream_function(lambda n: n^2, True, 0) + sage: h = Stream_cauchy_mul(f, g, True) sage: h.get_coefficient(5) 50 sage: [h.get_coefficient(i) for i in range(10)] @@ -1408,12 +1555,12 @@ def is_nonzero(self): sage: from sage.data_structures.stream import (Stream_function, ....: Stream_cauchy_mul, Stream_cauchy_invert) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_cauchy_mul(f, f) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_cauchy_mul(f, f, True) sage: g.is_nonzero() False sage: fi = Stream_cauchy_invert(f) - sage: h = Stream_cauchy_mul(fi, fi) + sage: h = Stream_cauchy_mul(fi, fi, True) sage: h.is_nonzero() True """ @@ -1435,43 +1582,41 @@ class Stream_dirichlet_convolve(Stream_binary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_exact([0], True, constant=1) - sage: h = Stream_dirichlet_convolve(f, g) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_exact([0], constant=1) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: [h[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] sage: [sigma(n) for n in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] - sage: u = Stream_dirichlet_convolve(g, f) + sage: u = Stream_dirichlet_convolve(g, f, True) sage: [u[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] """ - def __init__(self, left, right): - """ - Initalize ``self``. - - sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_exact([1], True, constant=0) - sage: Stream_dirichlet_convolve(f, g) - Traceback (most recent call last): - ... - AssertionError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 - sage: Stream_dirichlet_convolve(g, f) - Traceback (most recent call last): - ... - AssertionError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 + @lazy_attribute + def _approximate_order(self): """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError + Compute and return the approximate order of ``self``. - assert left._approximate_order > 0 and right._approximate_order > 0, "Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1" + EXAMPLES:: - vl = left._approximate_order - vr = right._approximate_order - a = vl * vr - super().__init__(left, right, left._is_sparse, a) + sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_dirichlet_convolve + sage: f = Stream_exact([0, 2]) + sage: g = Stream_function(lambda n: 3*n, True, 1) + sage: h = Stream_dirichlet_convolve(f, g, True) + sage: h._approximate_order + 1 + sage: [h[i] for i in range(5)] + [0, 6, 12, 18, 24] + """ + # this is not the true order, unless we have an integral domain + if (self._left._approximate_order <= 0 + or self._right._approximate_order <= 0): + raise ValueError("Dirichlet convolution is only defined for " + "coefficient streams with minimal index of " + "nonzero coefficient at least 1") + return self._left._approximate_order * self._right._approximate_order def get_coefficient(self, n): """ @@ -1484,9 +1629,9 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_exact([0], True, constant=1) - sage: h = Stream_dirichlet_convolve(f, g) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_exact([0], constant=1) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: h.get_coefficient(7) 8 sage: [h[i] for i in range(1, 10)] @@ -1513,32 +1658,75 @@ class Stream_dirichlet_invert(Stream_unary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_dirichlet_invert, Stream_function) - sage: f = Stream_function(lambda n: 1, ZZ, True, 1) - sage: g = Stream_dirichlet_invert(f) + sage: f = Stream_function(lambda n: 1, True, 1) + sage: g = Stream_dirichlet_invert(f, True) sage: [g[i] for i in range(10)] [0, 1, -1, -1, 0, -1, 1, -1, 0, 0] sage: [moebius(i) for i in range(10)] [0, 1, -1, -1, 0, -1, 1, -1, 0, 0] """ - def __init__(self, series): + def __init__(self, series, is_sparse): """ Initialize. TESTS:: sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) - sage: f = Stream_exact([0, 0], True, constant=1) - sage: g = Stream_dirichlet_invert(f) + sage: f = Stream_exact([0, 0], constant=1) + sage: g = Stream_dirichlet_invert(f, True) + sage: g[1] Traceback (most recent call last): ... - AssertionError: the Dirichlet inverse only exists if the coefficient with index 1 is non-zero + ZeroDivisionError: the Dirichlet inverse only exists if the coefficient with index 1 is non-zero """ - assert series[1], "the Dirichlet inverse only exists if the coefficient with index 1 is non-zero" - super().__init__(series, series._is_sparse, 1) - - self._ainv = ~series[1] + super().__init__(series, is_sparse) self._zero = ZZ.zero() + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_dirichlet_invert + sage: f = Stream_function(lambda n: n, True, 1) + sage: h = Stream_dirichlet_invert(f, True) + sage: h._approximate_order + 1 + sage: [h[i] for i in range(5)] + [0, -2, -8, -12, -48] + """ + # this is the true order, but we want to check first + if self._series._approximate_order > 1: + raise ZeroDivisionError("the Dirichlet inverse only exists if the " + "coefficient with index 1 is non-zero") + self._true_order = True + return 1 + + @lazy_attribute + def _ainv(self): + """ + The inverse of the leading coefficient. + + EXAMPLES:: + + sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) + sage: f = Stream_exact([0, 3], constant=2) + sage: g = Stream_dirichlet_invert(f, True) + sage: g._ainv + 1/3 + + sage: f = Stream_exact([Zmod(6)(5)], constant=2, order=1) + sage: g = Stream_dirichlet_invert(f, True) + sage: g._ainv + 5 + """ + try: + return ~self._series[1] + except TypeError: + return self._series[1].inverse_of_unit() + def get_coefficient(self, n): """ Return the ``n``-th coefficient of ``self``. @@ -1550,8 +1738,8 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) - sage: f = Stream_exact([0, 3], True, constant=2) - sage: g = Stream_dirichlet_invert(f) + sage: f = Stream_exact([0, 3], constant=2) + sage: g = Stream_dirichlet_invert(f, True) sage: g.get_coefficient(6) 2/27 sage: [g[i] for i in range(8)] @@ -1582,40 +1770,66 @@ class Stream_cauchy_compose(Stream_binary): EXAMPLES:: sage: from sage.data_structures.stream import Stream_cauchy_compose, Stream_function - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_function(lambda n: 1, ZZ, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_function(lambda n: 1, True, 1) + sage: h = Stream_cauchy_compose(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 8, 20, 48, 112, 256, 576, 1280] - sage: u = Stream_cauchy_compose(g, f) + sage: u = Stream_cauchy_compose(g, f, True) sage: [u[i] for i in range(10)] [0, 1, 3, 8, 21, 55, 144, 377, 987, 2584] """ - def __init__(self, f, g): + def __init__(self, f, g, is_sparse): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose - sage: f = Stream_function(lambda n: 1, ZZ, True, 1) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 1) - sage: h = Stream_cauchy_compose(f, g) - """ - #assert g._approximate_order > 0 - self._fv = f._approximate_order - self._gv = g._approximate_order - if self._fv < 0: - ginv = Stream_cauchy_invert(g) + sage: f = Stream_function(lambda n: 1, True, 1) + sage: g = Stream_function(lambda n: n^2, True, 1) + sage: h = Stream_cauchy_compose(f, g, True) + """ + if g._true_order and g._approximate_order <= 0: + raise ValueError("can only compose with a series of positive valuation") + super().__init__(f, g, is_sparse) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_function(lambda n: n^2, True, 1) + sage: h = Stream_cauchy_compose(f, g, True) + sage: h._approximate_order + 1 + sage: [h[i] for i in range(5)] + [0, 1, 6, 28, 124] + + .. TODO:: + + check similarities with :class:`Stream_plethysm` + """ + # this is very likely not the true order + if self._right._approximate_order <= 0: + raise ValueError("can only compose with a series of positive valuation") + + if self._left._approximate_order < 0: + ginv = Stream_cauchy_invert(self._right) # The constant part makes no contribution to the negative. # We need this for the case so self._neg_powers[0][n] => 0. - self._neg_powers = [Stream_zero(f._is_sparse), ginv] - for i in range(1, -self._fv): - self._neg_powers.append(Stream_cauchy_mul(self._neg_powers[-1], ginv)) - # Placeholder None to make this 1-based. - self._pos_powers = [None, g] - val = self._fv * self._gv - super().__init__(f, g, f._is_sparse, val) + self._neg_powers = [Stream_zero(), ginv] + for i in range(1, -self._left._approximate_order): + # TODO: possibly we always want a dense cache here? + self._neg_powers.append(Stream_cauchy_mul(self._neg_powers[-1], ginv, self._is_sparse)) + # placeholder None to make this 1-based. + self._pos_powers = [None, self._right] + + return self._left._approximate_order * self._right._approximate_order def get_coefficient(self, n): """ @@ -1628,23 +1842,27 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_function(lambda n: n^2, ZZ, True, 1) - sage: h = Stream_cauchy_compose(f, g) - sage: h.get_coefficient(5) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_function(lambda n: n^2, True, 1) + sage: h = Stream_cauchy_compose(f, g, True) + sage: h[5] # indirect doctest 527 - sage: [h.get_coefficient(i) for i in range(10)] + sage: [h[i] for i in range(10)] # indirect doctest [0, 1, 6, 28, 124, 527, 2172, 8755, 34704, 135772] """ + fv = self._left._approximate_order + gv = self._right._approximate_order if n < 0: - return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) + return sum(self._left[i] * self._neg_powers[-i][n] + for i in range(fv, n // gv + 1)) # n > 0 - while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right)) - ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) + while len(self._pos_powers) <= n // gv: + # TODO: possibly we always want a dense cache here? + self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right, self._is_sparse)) + ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(fv, 0)) if n == 0: ret += self._left[0] - return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) + return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // gv+1)) class Stream_plethysm(Stream_binary): @@ -1652,37 +1870,89 @@ class Stream_plethysm(Stream_binary): Return the plethysm of ``f`` composed by ``g``. This is the plethysm `f \circ g = f(g)` when `g` is an element of - the ring of symmetric functions. + a ring of symmetric functions. INPUT: - ``f`` -- a :class:`Stream` - - ``g`` -- a :class:`Stream` with positive order - - ``p`` -- the powersum symmetric functions + - ``g`` -- a :class:`Stream` with positive order, unless ``f`` is + of :class:`Stream_exact`. + - ``p`` -- the ring of powersum symmetric functions containing ``g`` + - ``ring`` (optional, default ``None``) -- the ring the result + should be in, by default ``p`` + - ``include`` -- a list of variables to be treated as degree one + elements instead of the default degree one elements + - ``exclude`` -- a list of variables to be excluded from the + default degree one elements EXAMPLES:: - sage: from sage.data_structures.stream import Stream_function, Stream_plethysm + sage: from sage.data_structures.stream import Stream_function, Stream_plethysm sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_function(lambda n: s[n], s, True, 1) - sage: g = Stream_function(lambda n: s[[1]*n], s, True, 1) - sage: h = Stream_plethysm(f, g, p) - sage: [s(h[i]) for i in range(5)] + sage: f = Stream_function(lambda n: s[n], True, 1) + sage: g = Stream_function(lambda n: s[[1]*n], True, 1) + sage: h = Stream_plethysm(f, g, True, p, s) + sage: [h[i] for i in range(5)] [0, s[1], s[1, 1] + s[2], 2*s[1, 1, 1] + s[2, 1] + s[3], 3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4]] - sage: u = Stream_plethysm(g, f, p) - sage: [s(u[i]) for i in range(5)] + sage: u = Stream_plethysm(g, f, True, p, s) + sage: [u[i] for i in range(5)] [0, s[1], s[1, 1] + s[2], s[1, 1, 1] + s[2, 1] + 2*s[3], s[1, 1, 1, 1] + s[2, 1, 1] + 3*s[3, 1] + 2*s[4]] + + This class also handles the plethysm of an exact stream with a + stream of order `0`:: + + sage: from sage.data_structures.stream import Stream_exact + sage: f = Stream_exact([s[1]], order=1) + sage: g = Stream_function(lambda n: s[n], True, 0) + sage: r = Stream_plethysm(f, g, True, p, s) + sage: [r[n] for n in range(3)] + [s[], s[1], s[2]] + + TESTS: + + Check corner cases:: + + sage: f0 = Stream_exact([p([])]) + sage: f1 = Stream_exact([p[1]], order=1) + sage: f2 = Stream_exact([p[2]], order=2 ) + sage: f11 = Stream_exact([p[1,1]], order=2 ) + sage: r = Stream_plethysm(f0, f1, True, p); [r[n] for n in range(3)] + [p[], 0, 0] + sage: r = Stream_plethysm(f0, f2, True, p); [r[n] for n in range(3)] + [p[], 0, 0] + sage: r = Stream_plethysm(f0, f11, True, p); [r[n] for n in range(3)] + [p[], 0, 0] + + Check that degree one elements are treated in the correct way:: + + sage: R.<a1,a2,a11,b1,b21,b111> = QQ[]; p = SymmetricFunctions(R).p() + sage: f_s = a1*p[1] + a2*p[2] + a11*p[1,1] + sage: g_s = b1*p[1] + b21*p[2,1] + b111*p[1,1,1] + sage: r_s = f_s(g_s) + sage: f = Stream_exact([f_s.restrict_degree(k) for k in range(f_s.degree()+1)]) + sage: g = Stream_exact([g_s.restrict_degree(k) for k in range(g_s.degree()+1)]) + sage: r = Stream_plethysm(f, g, True, p) + sage: r_s == sum(r[n] for n in range(2*(r_s.degree()+1))) + True + + sage: r_s - f_s(g_s, include=[]) + (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] + + sage: r2 = Stream_plethysm(f, g, True, p, include=[]) + sage: r_s - sum(r2[n] for n in range(2*(r_s.degree()+1))) + (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] + """ - def __init__(self, f, g, p): + def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): r""" Initialize ``self``. @@ -1691,16 +1961,60 @@ def __init__(self, f, g, p): sage: from sage.data_structures.stream import Stream_function, Stream_plethysm sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_function(lambda n: s[n], s, True, 1) - sage: g = Stream_function(lambda n: s[n-1,1], s, True, 2) - sage: h = Stream_plethysm(f, g, p) + sage: f = Stream_function(lambda n: s[n], True, 1) + sage: g = Stream_function(lambda n: s[n-1,1], True, 2) + sage: h = Stream_plethysm(f, g, True, p) """ - #assert g._approximate_order > 0 - self._fv = f._approximate_order - self._gv = g._approximate_order + if isinstance(f, Stream_exact): + self._degree_f = f._degree + else: + self._degree_f = None + + if g._true_order and g._approximate_order == 0 and self._degree_f is None: + raise ValueError("can only compute plethysm with a series of valuation 0 for symmetric functions of finite support") + + if ring is None: + self._basis = p + else: + self._basis = ring self._p = p - val = self._fv * self._gv - super().__init__(f, g, f._is_sparse, val) + g = Stream_map_coefficients(g, lambda x: p(x), is_sparse) + self._powers = [g] # a cache for the powers of g + R = self._basis.base_ring() + self._degree_one = _variables_recursive(R, include=include, exclude=exclude) + + if HopfAlgebrasWithBasis(R).TensorProducts() in p.categories(): + self._tensor_power = len(p._sets) + p_f = p._sets[0] + f = Stream_map_coefficients(f, lambda x: p_f(x), is_sparse) + else: + self._tensor_power = None + f = Stream_map_coefficients(f, lambda x: p(x), is_sparse) + super().__init__(f, g, is_sparse) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_plethysm + sage: p = SymmetricFunctions(QQ).p() + sage: f = Stream_function(lambda n: p[n], True, 1) + sage: h = Stream_plethysm(f, f, True, p) + sage: h._approximate_order + 1 + sage: [h[i] for i in range(5)] + [0, p[1], 2*p[2], 2*p[3], 3*p[4]] + """ + # this is very likely not the true order +# if self._right._approximate_order == 0 and self._degree_f is None: +# raise ValueError("can only compute plethysm with a series of " +# " valuation 0 for symmetric functions of finite " +# " support") + return self._left._approximate_order * self._right._approximate_order + def get_coefficient(self, n): r""" @@ -1715,9 +2029,9 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import Stream_function, Stream_plethysm sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_function(lambda n: s[n], s, True, 1) - sage: g = Stream_function(lambda n: s[[1]*n], s, True, 1) - sage: h = Stream_plethysm(f, g, p) + sage: f = Stream_function(lambda n: s[n], True, 1) + sage: g = Stream_function(lambda n: s[[1]*n], True, 1) + sage: h = Stream_plethysm(f, g, True, p) sage: s(h.get_coefficient(5)) 4*s[1, 1, 1, 1, 1] + 4*s[2, 1, 1, 1] + 2*s[2, 2, 1] + 2*s[3, 1, 1] + s[3, 2] + s[4, 1] + s[5] sage: [s(h.get_coefficient(i)) for i in range(6)] @@ -1728,72 +2042,176 @@ def get_coefficient(self, n): 3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4], 4*s[1, 1, 1, 1, 1] + 4*s[2, 1, 1, 1] + 2*s[2, 2, 1] + 2*s[3, 1, 1] + s[3, 2] + s[4, 1] + s[5]] """ - if not n: # special case of 0 - return self._left[0] - - # We assume n > 0 - p = self._p - ret = p.zero() - for k in range(n+1): - temp = p(self._left[k]) - for la, c in temp: - inner = self._compute_product(n, la, c) - if inner is not None: - ret += inner - return ret + if not n: # special case of 0 + if self._right[0]: + assert self._degree_f is not None, "the plethysm with a lazy symmetric function of valuation 0 is defined only for symmetric functions of finite support" - def _compute_product(self, n, la, c): - """ + return sum((c * self.compute_product(n, la) + for k in range(self._left._approximate_order, self._degree_f) + if self._left[k] + for la, c in self._left[k]), + self._basis.zero()) + + res = sum((c * self.compute_product(n, la) + for k in range(self._left._approximate_order, n+1) + if self._left[k] + for la, c in self._left[k]), + self._basis.zero()) + return res + + def compute_product(self, n, la): + r""" Compute the product ``c * p[la](self._right)`` in degree ``n``. EXAMPLES:: - sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function + sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function, Stream_zero sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_function(lambda n: s[n], s, True, 1) - sage: g = Stream_exact([s[2], s[3]], False, 0, 4, 2) - sage: h = Stream_plethysm(f, g, p) - sage: ret = h._compute_product(7, [2, 1], 1); ret + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_exact([s[2], s[3]], 0, 4, 2) + sage: h = Stream_plethysm(f, g, True, p) + sage: A = h.compute_product(7, Partition([2, 1])); A 1/12*p[2, 2, 1, 1, 1] + 1/4*p[2, 2, 2, 1] + 1/6*p[3, 2, 2] + 1/12*p[4, 1, 1, 1] + 1/4*p[4, 2, 1] + 1/6*p[4, 3] - sage: ret == p[2,1](s[2] + s[3]).homogeneous_component(7) + sage: A == p[2, 1](s[2] + s[3]).homogeneous_component(7) + True + + sage: p2 = tensor([p, p]) + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_function(lambda n: sum(tensor([p[k], p[n-k]]) for k in range(n+1)), True, 1) + sage: h = Stream_plethysm(f, g, True, p2) + sage: A = h.compute_product(7, Partition([2, 1])) + sage: B = p[2, 1](sum(g[n] for n in range(7))) + sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 7}) + sage: A == B + True + + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_function(lambda n: s[n], True, 0) + sage: h = Stream_plethysm(f, g, True, p) + sage: B = p[2, 2, 1](sum(s[i] for i in range(7))) + sage: all(h.compute_product(k, Partition([2, 2, 1])) == B.restrict_degree(k) for k in range(7)) True """ - p = self._p - ret = p.zero() - for mu in wt_int_vec_iter(n, la): - temp = c - for i, j in zip(la, mu): - gs = self._right[j] - if not gs: - temp = p.zero() - break - temp *= p[i](gs) - ret += temp + # This is the approximate order of the result + rao = self._right._approximate_order + ret_approx_order = rao * sum(la) + ret = self._basis.zero() + if n < ret_approx_order: + return ret + + la_exp = la.to_exp() + wgt = [i for i, m in enumerate(la_exp, 1) if m] + exp = [m for m in la_exp if m] + # the docstring of wt_int_vec_iter, i.e., iterator_fast, + # states that the weights should be weakly decreasing + wgt.reverse() + exp.reverse() + for k in wt_int_vec_iter(n - ret_approx_order, wgt): + # TODO: it may make a big difference here if the + # approximate order would be updated. + # The test below is based on not removing the fixed block + #if any(d < self._right._approximate_order * m + # for m, d in zip(exp, k)): + # continue + ret += prod(self.stretched_power_restrict_degree(i, m, rao * m + d) + for i, m, d in zip(wgt, exp, k)) return ret + def stretched_power_restrict_degree(self, i, m, d): + r""" + Return the degree ``d*i`` part of ``p([i]*m)(g)``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function, Stream_zero + sage: s = SymmetricFunctions(QQ).s() + sage: p = SymmetricFunctions(QQ).p() + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_exact([s[2], s[3]], 0, 4, 2) + sage: h = Stream_plethysm(f, g, True, p) + sage: A = h.stretched_power_restrict_degree(2, 3, 6) + sage: A == p[2,2,2](s[2] + s[3]).homogeneous_component(12) + True + + sage: p2 = tensor([p, p]) + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_function(lambda n: sum(tensor([p[k], p[n-k]]) for k in range(n+1)), True, 1) + sage: h = Stream_plethysm(f, g, True, p2) + sage: A = h.stretched_power_restrict_degree(2, 3, 6) + sage: B = p[2,2,2](sum(g[n] for n in range(7))) # long time + sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 12}) # long time + sage: A == B # long time + True + """ + while len(self._powers) < m: + # TODO: possibly we always want a dense cache here? + self._powers.append(Stream_cauchy_mul(self._powers[-1], self._powers[0], self._is_sparse)) + power_d = self._powers[m-1][d] + # we have to check power_d for zero because it might be an + # integer and not a symmetric function + if power_d: + if self._tensor_power is None: + terms = {mon.stretch(i): raised_c for mon, c in power_d + if (raised_c := _raise_variables(c, i, self._degree_one))} + else: + terms = {tuple((mu.stretch(i) for mu in mon)): raised_c + for mon, c in power_d + if (raised_c := _raise_variables(c, i, self._degree_one))} + return self._p.element_class(self._p, terms) + + return self._p.zero() + + ##################################################################### # Unary operations class Stream_scalar(Stream_inexact): """ - Base class for operators multiplying a coefficient stream by a scalar. + Base class for operators multiplying a coefficient stream by a + scalar. + + .. TODO:: + + This does not inherit from :class:`Stream_unary`, because of + the extra argument ``scalar``. However, we could also + override :meth:`Stream_unary.hash`, + :meth:`Stream_unary.__eq__`. Would this be any better? + """ - def __init__(self, series, scalar): + def __init__(self, series, scalar, is_sparse): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) - sage: f = Stream_function(lambda n: -1, ZZ, True, 0) - sage: g = Stream_rmul(f, 3) + sage: f = Stream_function(lambda n: -1, True, 0) + sage: g = Stream_rmul(f, 3, True) """ self._series = series self._scalar = scalar assert scalar, "the scalar must not be equal to 0" - super().__init__(series._is_sparse, series._approximate_order) + super().__init__(is_sparse, series._true_order) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_rmul + sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) + sage: h = Stream_rmul(f, 3, True) # indirect doctest + sage: h._approximate_order + 2 + sage: [h[i] for i in range(5)] + [0, 0, 0, 3, 0] + """ + # this is not the true order, unless we have an integral domain + return self._series._approximate_order def __hash__(self): """ @@ -1803,8 +2221,8 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_function sage: from sage.data_structures.stream import Stream_rmul - sage: a = Stream_function(lambda n: 2*n, ZZ, False, 1) - sage: f = Stream_rmul(a, 2) + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: f = Stream_rmul(a, 2, True) sage: hash(f) == hash(f) True """ @@ -1822,16 +2240,16 @@ def __eq__(self, other): sage: from sage.data_structures.stream import Stream_function sage: from sage.data_structures.stream import Stream_rmul, Stream_lmul - sage: a = Stream_function(lambda n: 2*n, ZZ, False, 1) - sage: b = Stream_function(lambda n: n, ZZ, False, 1) - sage: f = Stream_rmul(a, 2) - sage: f == Stream_rmul(b, 2) + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: b = Stream_function(lambda n: n, False, 1) + sage: f = Stream_rmul(a, 2, True) + sage: f == Stream_rmul(b, 2, True) False - sage: f == Stream_rmul(a, 2) + sage: f == Stream_rmul(a, 2, False) True - sage: f == Stream_rmul(a, 3) + sage: f == Stream_rmul(a, 3, True) False - sage: f == Stream_lmul(a, 3) + sage: f == Stream_lmul(a, 3, True) False """ return (isinstance(other, type(self)) and self._series == other._series @@ -1845,14 +2263,14 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_rmul(f, 2) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_rmul(f, 2, True) sage: g.is_nonzero() False sage: from sage.data_structures.stream import Stream_cauchy_invert sage: fi = Stream_cauchy_invert(f) - sage: g = Stream_rmul(fi, 2) + sage: g = Stream_rmul(fi, 2, True) sage: g.is_nonzero() True """ @@ -1874,8 +2292,8 @@ class Stream_rmul(Stream_scalar): sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) sage: W = algebras.DifferentialWeyl(QQ, names=('x',)) sage: x, dx = W.gens() - sage: f = Stream_function(lambda n: x^n, W, True, 1) - sage: g = Stream_rmul(f, dx) + sage: f = Stream_function(lambda n: x^n, True, 1) + sage: g = Stream_rmul(f, dx, True) sage: [g[i] for i in range(5)] [0, x*dx + 1, x^2*dx + 2*x, x^3*dx + 3*x^2, x^4*dx + 4*x^3] """ @@ -1890,8 +2308,8 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_rmul(f, 3) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_rmul(f, 3, True) sage: g.get_coefficient(5) 15 sage: [g.get_coefficient(i) for i in range(10)] @@ -1915,8 +2333,8 @@ class Stream_lmul(Stream_scalar): sage: from sage.data_structures.stream import (Stream_lmul, Stream_function) sage: W = algebras.DifferentialWeyl(QQ, names=('x',)) sage: x, dx = W.gens() - sage: f = Stream_function(lambda n: x^n, W, True, 1) - sage: g = Stream_lmul(f, dx) + sage: f = Stream_function(lambda n: x^n, True, 1) + sage: g = Stream_lmul(f, dx, True) sage: [g[i] for i in range(5)] [0, x*dx, x^2*dx, x^3*dx, x^4*dx] """ @@ -1931,8 +2349,8 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_lmul, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_lmul(f, 3) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_lmul(f, 3, True) sage: g.get_coefficient(5) 15 sage: [g.get_coefficient(i) for i in range(10)] @@ -1952,22 +2370,44 @@ class Stream_neg(Stream_unary): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_neg, Stream_function) - sage: f = Stream_function(lambda n: 1, ZZ, True, 1) - sage: g = Stream_neg(f) + sage: f = Stream_function(lambda n: 1, True, 1) + sage: g = Stream_neg(f, True) sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ - def __init__(self, series): + # TODO: maybe we should just inherit from `Stream` instead of + # inheriting from `Stream_unary` and do not create a copy of the + # cache + def __init__(self, series, is_sparse): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import (Stream_neg, Stream_function) - sage: f = Stream_function(lambda n: -1, ZZ, True, 0) - sage: g = Stream_neg(f) + sage: f = Stream_function(lambda n: -1, True, 0) + sage: g = Stream_neg(f, True) """ - super().__init__(series, series._is_sparse, series._approximate_order) + super().__init__(series, is_sparse) + self._true_order = self._series._true_order + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_neg + sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) + sage: h = Stream_neg(f, True) + sage: h._approximate_order + 2 + sage: [h[i] for i in range(5)] + [0, 0, 4, 3, 2] + """ + # this is the true order, if self._series._true_order + return self._series._approximate_order def get_coefficient(self, n): """ @@ -1980,8 +2420,8 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_neg, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_neg(f) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_neg(f, True) sage: g.get_coefficient(5) -5 sage: [g.get_coefficient(i) for i in range(10)] @@ -1997,19 +2437,20 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_neg, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) - sage: g = Stream_neg(f) + sage: f = Stream_function(lambda n: n, True, 1) + sage: g = Stream_neg(f, True) sage: g.is_nonzero() False sage: from sage.data_structures.stream import Stream_cauchy_invert sage: fi = Stream_cauchy_invert(f) - sage: g = Stream_neg(fi) + sage: g = Stream_neg(fi, True) sage: g.is_nonzero() True """ return self._series.is_nonzero() + class Stream_cauchy_invert(Stream_unary): """ Operator for multiplicative inverse of the stream. @@ -2017,58 +2458,80 @@ class Stream_cauchy_invert(Stream_unary): INPUT: - ``series`` -- a :class:`Stream` + - ``approximate_order`` -- ``None``, or a lower bound on the + order of the resulting stream + + Instances of this class are always dense. EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: 1, ZZ, True, 1) + sage: f = Stream_function(lambda n: 1, True, 1) sage: g = Stream_cauchy_invert(f) sage: [g[i] for i in range(10)] [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + """ - def __init__(self, series): + def __init__(self, series, approximate_order=None): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) - sage: f = Stream_exact([1, -1], False) + sage: f = Stream_exact([1, -1]) sage: g = Stream_cauchy_invert(f) """ - v = series.order() - super().__init__(series, series._is_sparse, -v) - - self._ainv = ~series[v] + super().__init__(series, False) + if approximate_order is not None: + self._approximate_order = approximate_order self._zero = ZZ.zero() - def get_coefficient(self, n): + @lazy_attribute + def _approximate_order(self): """ - Return the ``n``-th coefficient of ``self``. + Compute and return the approximate order of ``self``. - INPUT: + EXAMPLES:: - - ``n`` -- integer; the degree for the coefficient + sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_invert + sage: f = Stream_function(lambda n: GF(7)(n), True, 0) + sage: [f[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: h = Stream_cauchy_invert(f) + sage: h._approximate_order + -1 + sage: [h[i] for i in range(-2, 5)] + [0, 1, 5, 1, 0, 0, 0] + """ + try: + return -self._series.order() + except (ValueError, RecursionError): + raise ValueError("inverse does not exist") + + @lazy_attribute + def _ainv(self): + r""" + The inverse of the leading coefficient. EXAMPLES:: - sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, 1) + sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) + sage: f = Stream_exact([2, -3]) sage: g = Stream_cauchy_invert(f) - sage: g.get_coefficient(5) - 0 - sage: [g.get_coefficient(i) for i in range(10)] - [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] + sage: g._ainv + 1/2 + + sage: f = Stream_exact([Zmod(6)(5)], constant=2) + sage: g = Stream_cauchy_invert(f) + sage: g._ainv + 5 """ - v = self._approximate_order - if n == v: - return self._ainv - c = self._zero - for k in range(v, n): - l = self[k] - if l: - c += l * self._series[n - v - k] - return -c * self._ainv + v = self._series.order() + try: + return ~self._series[v] + except TypeError: + return self._series[v].inverse_of_unit() def iterate_coefficients(self): """ @@ -2077,17 +2540,19 @@ def iterate_coefficients(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: n^2, ZZ, False, 1) + sage: f = Stream_function(lambda n: n^2, False, 1) sage: g = Stream_cauchy_invert(f) sage: n = g.iterate_coefficients() sage: [next(n) for i in range(10)] [1, -4, 7, -8, 8, -8, 8, -8, 8, -8] """ + yield self._ainv + # This is the true order, which is computed in self._ainv v = self._approximate_order n = 0 # Counts the number of places from v. - yield self._ainv # Note that the first entry of the cache will correspond to # z^v, when the stream corresponds to a Laurent series. + while True: n += 1 c = self._zero @@ -2112,47 +2577,67 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: n^2, ZZ, False, 1) + sage: f = Stream_function(lambda n: n^2, False, 1) sage: g = Stream_cauchy_invert(f) sage: g.is_nonzero() True """ return True + class Stream_map_coefficients(Stream_inexact): r""" - The stream with ``function`` applied to each nonzero - coefficient of ``series``. + The stream with ``function`` applied to each nonzero coefficient + of ``series``. INPUT: - ``series`` -- a :class:`Stream` - ``function`` -- a function that modifies the elements of the stream - - ``ring`` -- the base ring of the stream EXAMPLES:: sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) - sage: f = Stream_function(lambda n: 1, ZZ, True, 1) - sage: g = Stream_map_coefficients(f, lambda n: -n, ZZ) + sage: f = Stream_function(lambda n: 1, True, 1) + sage: g = Stream_map_coefficients(f, lambda n: -n, True) sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] + """ - def __init__(self, series, function, ring): + def __init__(self, series, function, is_sparse, approximate_order=None, true_order=False): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) - sage: f = Stream_function(lambda n: -1, ZZ, True, 0) - sage: g = Stream_map_coefficients(f, lambda n: n + 1, ZZ) + sage: f = Stream_function(lambda n: -1, True, 0) + sage: g = Stream_map_coefficients(f, lambda n: n + 1, True) sage: TestSuite(g).run(skip="_test_pickling") """ self._function = function - self._ring = ring self._series = series - super().__init__(series._is_sparse, series._approximate_order) + super().__init__(is_sparse, true_order) + if approximate_order is not None: + self._approximate_order = approximate_order + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_map_coefficients + sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) + sage: h = Stream_map_coefficients(f, lambda c: 3*c, True) + sage: h._approximate_order + 2 + sage: [h[i] for i in range(5)] + [0, 0, 0, 3, 0] + """ + # this is not the true order + return self._series._approximate_order def get_coefficient(self, n): """ @@ -2165,25 +2650,20 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) - sage: f = Stream_function(lambda n: n, ZZ, True, -1) - sage: g = Stream_map_coefficients(f, lambda n: n^2 + 1, ZZ) + sage: f = Stream_function(lambda n: n, True, -1) + sage: g = Stream_map_coefficients(f, lambda n: n^2 + 1, True) sage: g.get_coefficient(5) 26 sage: [g.get_coefficient(i) for i in range(-1, 10)] [2, 0, 2, 5, 10, 17, 26, 37, 50, 65, 82] sage: R.<x,y> = ZZ[] - sage: f = Stream_function(lambda n: n, ZZ, True, -1) - sage: g = Stream_map_coefficients(f, lambda n: n.degree() + 1, R) + sage: f = Stream_function(lambda n: n, True, -1) + sage: g = Stream_map_coefficients(f, lambda n: R(n).degree() + 1, True) sage: [g.get_coefficient(i) for i in range(-1, 3)] [1, 0, 1, 1] - - sage: f = Stream_function(lambda n: n, ZZ, True, 0) - sage: g = Stream_map_coefficients(f, lambda n: 5, GF(3)) - sage: [g.get_coefficient(i) for i in range(10)] - [0, 5, 5, 0, 5, 5, 0, 5, 5, 0] """ - c = self._ring(self._series[n]) + c = self._series[n] if c: return self._function(c) return c @@ -2195,13 +2675,13 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) - sage: f = Stream_function(lambda n: -1, ZZ, True, 0) - sage: g = Stream_map_coefficients(f, lambda n: n + 1, ZZ) + sage: f = Stream_function(lambda n: -1, True, 0) + sage: g = Stream_map_coefficients(f, lambda n: n + 1, True) sage: hash(g) == hash(g) True """ # We don't hash the function as it might not be hashable. - return hash((type(self), self._series, self._ring)) + return hash((type(self), self._series)) def __eq__(self, other): """ @@ -2214,22 +2694,23 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) - sage: f = Stream_function(lambda n: -1, ZZ, True, 0) + sage: f = Stream_function(lambda n: -1, True, 0) sage: def plus_one(n): return n + 1 - sage: g = Stream_map_coefficients(f, plus_one, ZZ) + sage: g = Stream_map_coefficients(f, plus_one, True) sage: g == f False - sage: g == Stream_map_coefficients(f, plus_one, QQ) + sage: g == Stream_map_coefficients(f, lambda n: n + 1, True) False - sage: g == Stream_map_coefficients(f, plus_one, ZZ) - True """ return (isinstance(other, type(self)) and self._series == other._series - and self._ring == other._ring and self._function == other._function) + and self._function == other._function) + -class Stream_shift(Stream_inexact): +class Stream_shift(Stream): """ - Operator for shifting the stream. + Operator for shifting a nonzero, nonexact stream. + + Instances of this class share the cache with its input stream. INPUT: @@ -2243,14 +2724,47 @@ def __init__(self, series, shift): EXAMPLES:: sage: from sage.data_structures.stream import Stream_shift - sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([1], False, constant=3) + sage: from sage.data_structures.stream import Stream_function + sage: h = Stream_function(lambda n: n, True, -5) sage: M = Stream_shift(h, 2) - sage: TestSuite(M).run() + sage: TestSuite(M).run(skip="_test_pickling") """ self._series = series self._shift = shift - super().__init__(series._is_sparse, series._approximate_order + shift) + super().__init__(series._true_order) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_shift + sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) + sage: h = Stream_shift(f, -2) + sage: h._approximate_order + 0 + sage: [h[i] for i in range(5)] + [2, 3, 4, 5, 0] + """ + # this is the true order, if self._series._true_order + return self._series._approximate_order + self._shift + + def order(self): + r""" + Return the order of ``self``, which is the minimum index + ``n`` such that ``self[n]`` is nonzero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_shift + sage: s = Stream_shift(Stream_function(lambda n: n, True, 0), 2) + sage: s.order() + 3 + """ + return self._series.order() + self._shift + def __getitem__(self, n): """ @@ -2260,7 +2774,7 @@ def __getitem__(self, n): sage: from sage.data_structures.stream import Stream_shift sage: from sage.data_structures.stream import Stream_function - sage: F = Stream_function(lambda n: n, ZZ, False, 1) + sage: F = Stream_function(lambda n: n, False, 1) sage: M = Stream_shift(F, 2) sage: [F[i] for i in range(6)] [0, 1, 2, 3, 4, 5] @@ -2277,7 +2791,7 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_shift sage: from sage.data_structures.stream import Stream_function - sage: F = Stream_function(lambda n: n, ZZ, False, 1) + sage: F = Stream_function(lambda n: n, False, 1) sage: M = Stream_shift(F, 2) sage: hash(M) == hash(M) True @@ -2296,7 +2810,7 @@ def __eq__(self, other): sage: from sage.data_structures.stream import Stream_shift sage: from sage.data_structures.stream import Stream_function - sage: F = Stream_function(lambda n: 1, ZZ, False, 1) + sage: F = Stream_function(lambda n: 1, False, 1) sage: M2 = Stream_shift(F, 2) sage: M3 = Stream_shift(F, 3) sage: M2 == M3 @@ -2317,10 +2831,137 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: n^2, ZZ, False, 1) + sage: f = Stream_function(lambda n: n^2, False, 1) sage: g = Stream_cauchy_invert(f) sage: g.is_nonzero() True """ return self._series.is_nonzero() + +class Stream_derivative(Stream_inexact): + """ + Operator for taking derivatives of a stream. + + INPUT: + + - ``series`` -- a :class:`Stream` + - ``shift`` -- a positive integer + """ + def __init__(self, series, shift, is_sparse): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_exact, Stream_derivative + sage: f = Stream_exact([1,2,3]) + sage: f2 = Stream_derivative(f, 2, True) + sage: TestSuite(f2).run() + """ + self._series = series + self._shift = shift + super().__init__(is_sparse, False) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_derivative + sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) + sage: h = Stream_derivative(f, 3, True) + sage: h._approximate_order + 0 + sage: [h[i] for i in range(5)] + [0, 0, 0, 0, 0] + """ + # this is not the true order, unless multiplying by an + # integer cannot give 0 + if 0 <= self._series._approximate_order <= self._shift: + return 0 + return self._series._approximate_order - self._shift + + def __getitem__(self, n): + """ + Return the ``n``-th coefficient of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_derivative + sage: f = Stream_function(lambda n: 1/n if n else 0, True, -2) + sage: [f[i] for i in range(-5, 3)] + [0, 0, 0, -1/2, -1, 0, 1, 1/2] + sage: f2 = Stream_derivative(f, 2, True) + sage: [f2[i] for i in range(-5, 3)] + [0, -3, -2, 0, 0, 1, 2, 3] + + sage: f = Stream_function(lambda n: 1/n, True, 2) + sage: [f[i] for i in range(-1, 4)] + [0, 0, 0, 1/2, 1/3] + sage: f2 = Stream_derivative(f, 3, True) + sage: [f2[i] for i in range(-1, 4)] + [0, 2, 6, 12, 20] + """ + return (prod(n+k for k in range(1, self._shift + 1)) + * self._series[n + self._shift]) + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function + sage: from sage.data_structures.stream import Stream_derivative + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: f = Stream_derivative(a, 1, True) + sage: g = Stream_derivative(a, 2, True) + sage: hash(f) == hash(f) + True + sage: hash(f) == hash(g) + False + + """ + return hash((type(self), self._series, self._shift)) + + def __eq__(self, other): + """ + Test equality. + + INPUT: + + - ``other`` -- a stream of coefficients + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function + sage: from sage.data_structures.stream import Stream_derivative + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: f = Stream_derivative(a, 1, True) + sage: g = Stream_derivative(a, 2, True) + sage: f == g + False + sage: f == Stream_derivative(a, 1, True) + True + """ + return (isinstance(other, type(self)) and self._shift == other._shift + and self._series == other._series) + + def is_nonzero(self): + r""" + Return ``True`` if and only if this stream is known + to be nonzero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_exact, Stream_derivative + sage: f = Stream_exact([1,2]) + sage: Stream_derivative(f, 1, True).is_nonzero() + True + sage: Stream_derivative(f, 2, True).is_nonzero() # it might be nice if this gave False + True + """ + return self._series.is_nonzero() diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index fd0068f8793..99e96a9b816 100644 --- a/src/sage/databases/cubic_hecke_db.py +++ b/src/sage/databases/cubic_hecke_db.py @@ -1514,4 +1514,3 @@ def read_markov(bas_ele, variables, num_strands=4): 0, 1]} return data[num_strands][bas_ele] - diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index d21f1a250b3..218a2dff3ff 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -118,13 +118,14 @@ advertise yet another way to pass values to FindStat:: sage: r = findstat(Permutations, lambda pi: pi.saliances()[0], depth=2); r # optional -- internet - 0: St000740oMp00087 with offset 1 (quality [100, 100]) + 0: St000740oMp00066 with offset 1 (quality [100, 100]) + 1: St000740oMp00087 with offset 1 (quality [100, 100]) ... Note that some of the matches are up to a global offset. For example, we have:: - sage: r[0].info() # optional -- internet + sage: r[1].info() # optional -- internet after adding 1 to every value and applying Mp00087: inverse first fundamental transformation: Permutations -> Permutations @@ -221,7 +222,6 @@ def mapping(sigma): from sage.databases.oeis import FancyTuple from ast import literal_eval -from collections import OrderedDict from copy import deepcopy import re import webbrowser @@ -256,7 +256,7 @@ def mapping(sigma): from sage.combinat.words.word import Word from sage.combinat.words.words import Words from sage.combinat.words.abstract_word import Word_class -from sage.combinat.colored_permutations import SignedPermutations +from sage.combinat.colored_permutations import SignedPermutation, SignedPermutations from sage.combinat.plane_partition import PlanePartition from sage.combinat.decorated_permutation import DecoratedPermutation, DecoratedPermutations @@ -431,10 +431,10 @@ def _get_json(url, **kwargs): EXAMPLES:: sage: from sage.databases.findstat import _get_json, FINDSTAT_API_MAPS - sage: _get_json(FINDSTAT_API_MAPS + "?xxx=yyy") # optional -- internet + sage: _get_json(FINDSTAT_API_MAPS + "?fields=yyy") # optional -- internet Traceback (most recent call last): ... - ValueError: E005: On filtering maps, the following parameters are not allowed: [u'xxx']. + ValueError: E018: Unknown fields in Map, Combinatorial map: to semistandard tableau via monotone triangles: ['yyy'] """ response = requests.get(url) if response.ok: @@ -454,10 +454,10 @@ def _post_json(url, data, **kwargs): EXAMPLES:: sage: from sage.databases.findstat import _post_json, FINDSTAT_API_STATISTICS - sage: _post_json(FINDSTAT_API_STATISTICS, {"xxx": "yyy"}) # optional -- internet + sage: _post_json(FINDSTAT_API_STATISTICS, {"fields": "yyy"}) # optional -- internet Traceback (most recent call last): ... - ValueError: E005: On filtering statistics, the following parameters are not allowed: ['xxx']. + ValueError: E018: Unknown fields in Statistic, Combinatorial statistic: St000001: ['yyy'] """ response = requests.post(url, data=data) if response.ok: @@ -1007,7 +1007,9 @@ def findstat(query=None, values=None, distribution=None, domain=None, sage: findstat((PM, [1 for m in PM])) # optional -- internet Traceback (most recent call last): ... - ValueError: E016: You passed too few elements (0 < 3) to FindStat! + ValueError: E016: The statistic finder was unable to perform a search on your data. The following errors have occured: + <BLANKLINE> + You passed too few elements (0 < 3) to FindStat! Finally, we can also retrieve all statistics with a given domain:: @@ -1797,7 +1799,7 @@ def set_sage_code(self, value): EXAMPLES:: sage: q = findstat([(d, randint(1,1000)) for d in DyckWords(4)]) # optional -- internet - sage: q.set_sage_code("def statistic(x):\r\n return randint(1,1000)") # optional -- internet + sage: q.set_sage_code("def statistic(x):\n return randint(1,1000)") # optional -- internet sage: print(q.sage_code()) # optional -- internet def statistic(x): return randint(1,1000) @@ -1852,7 +1854,7 @@ def first_terms(self): if self._first_terms_cache is None: self._first_terms_cache = self._fetch_first_terms() # a shallow copy suffices - tuples are immutable - return OrderedDict(self._first_terms_cache) + return dict(self._first_terms_cache) def _first_terms_raw(self, max_values): """ @@ -1916,8 +1918,8 @@ def first_terms_str(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES): sage: len(st.cache) # optional -- internet 100 """ - return "\r\n".join(key + " => " + str(val) - for key, val in self._first_terms_raw(max_values=max_values)) + return "\n".join(key + " => " + str(val) + for key, val in self._first_terms_raw(max_values=max_values)) def _fetch_first_terms(self): r""" @@ -2211,10 +2213,10 @@ def _fetch_data(self): 'MathSciNet:1418763': {'Author': 'Simion, R., Stanton, D.', 'Title': 'Octabasic Laguerre polynomials and permutation statistics'}}, 'Code': 'def statistic(x):\r\n return len(x.nestings())', - 'Description': 'The number of nestings of a perfect matching. \r\n\r\n\r\nThis is the number of pairs of edges $((a,b), (c,d))$ such that $a\\le c\\le d\\le b$. i.e., the edge $(c,d)$ is nested inside $(a,b)$.', + 'Description': 'The number of nestings of a perfect matching.\r\n\r\nThis is the number of pairs of edges $((a,b), (c,d))$ such that $a\\le c\\le d\\le b$. i.e., the edge $(c,d)$ is nested inside $(a,b)$.', 'Domain': 'Cc0012', 'Name': 'The number of nestings of a perfect matching.', - 'References': '[1] [[MathSciNet:1288802]]\n[2] [[MathSciNet:1418763]]', + 'References': '[1] [[MathSciNet:1288802]]\r\n[2] [[MathSciNet:1418763]]', 'SageCode': 'def statistic(x):\r\n return len(x.nestings())'} """ fields = "Bibliography,Code,Description,Domain,Name,References,SageCode" @@ -2335,7 +2337,7 @@ def statistic(x): EXAMPLES:: sage: q = findstat([(d, randint(1,1000)) for d in DyckWords(4)]) # optional -- internet - sage: q.set_code("def statistic(x):\r\n return randint(1,1000)") # optional -- internet + sage: q.set_code("def statistic(x):\n return randint(1,1000)") # optional -- internet sage: print(q.code()) # optional -- internet def statistic(x): return randint(1,1000) @@ -2519,6 +2521,21 @@ def __iter__(self): for st in self._identifiers: yield FindStatStatistic(st) + def _an_element_(self): + """ + Return a FindStat statistic. + + EXAMPLES:: + + sage: findstat(domain="Permutations").an_element() # optional -- internet + St000001: The number of reduced words for a permutation. + """ + try: + return next(iter(self)) + except StopIteration: + from sage.categories.sets_cat import EmptySetError + raise EmptySetError + Element = FindStatStatistic @@ -2655,11 +2672,11 @@ def first_terms(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES): 0: St000041 (quality [99, 100]) 1: St000042 (quality [99, 100]) sage: r.first_terms() # optional -- internet - OrderedDict([([], 0), ([(1, 2)], 0)]) + {[]: 0, [(1, 2)]: 0} """ - return OrderedDict(itertools.islice(((objs[0], vals[0]) - for objs, vals in self._known_terms - if len(vals) == 1), max_values)) + return dict(itertools.islice(((objs[0], vals[0]) + for objs, vals in self._known_terms + if len(vals) == 1), max_values)) def _first_terms_raw(self, max_values): """ @@ -2693,7 +2710,7 @@ def _generating_functions_dict(self, sage: q = findstat(data, depth=0); q # optional -- internet 0: St000054 (quality [100, 100]) sage: q.first_terms() # optional -- internet - OrderedDict([([1, 2], 1)]) + {[1, 2]: 1} sage: q.generating_functions() # optional -- internet, indirect doctest {3: 2*q^3 + 2*q^2 + 2*q} """ @@ -3452,6 +3469,21 @@ def __iter__(self): for mp in self._identifiers: yield FindStatMap(mp) + def _an_element_(self): + """ + Return a FindStat map. + + EXAMPLES:: + + sage: findmap(domain="Dyck paths", codomain="Posets").an_element() # optional -- internet + Mp00232: parallelogram poset + """ + try: + return next(iter(self)) + except StopIteration: + from sage.categories.sets_cat import EmptySetError + raise EmptySetError + Element = FindStatMap @@ -4232,7 +4264,7 @@ def levels_with_sizes(self): sage: from sage.databases.findstat import FindStatCollection sage: cc = FindStatCollection("Perfect Matchings") # optional -- internet sage: cc.levels_with_sizes() # optional -- internet - OrderedDict([(2, 1), (4, 3), (6, 15), (8, 105), (10, 945)]) + {2: 1, 4: 3, 6: 15, 8: 105, 10: 945} """ return self._data["LevelsWithSizes"] @@ -4486,8 +4518,10 @@ def name(self, style="singular"): "element_to_string", "elements_on_level", # return all elements on given level "element_level", # return level of a given element - "is_element"]) # return whether element is member of this collection (and, ideally, of no other collection) + "is_element"]) # return whether element is member of this collection +# this dictionary must be sorted so that subclasses come before +# superclasses, eg., "StandardTableaux" before "SemistandardTableaux" _SupportedFindStatCollections = { "Permutations": _SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)), @@ -4501,7 +4535,6 @@ def name(self, style="singular"): lambda x: Words([0,1], length=x), lambda x: x.length(), lambda x: isinstance(x, Word_class)), - "AlternatingSignMatrices": _SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)), lambda x: str(list(map(list, x.to_matrix().rows()))), @@ -4545,22 +4578,22 @@ def name(self, style="singular"): _SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)), lambda i,j: (i,j) in E or (j,i) in E], immutable=True))(*literal_eval(x)), - lambda X: str((sorted(X.edges(labels=False)), X.num_verts())), + lambda X: str((X.edges(labels=False, sort=True), X.num_verts())), lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)), lambda x: x.num_verts(), lambda x: isinstance(x, Graph)), - "IntegerCompositions": - _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), - str, - Compositions, - lambda x: x.size(), - lambda x: isinstance(x, Composition)), "IntegerPartitions": _SupportedFindStatCollection(lambda x: Partition(literal_eval(x)), str, Partitions, lambda x: x.size(), lambda x: isinstance(x, Partition)), + "IntegerCompositions": + _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), + str, + Compositions, + lambda x: x.size(), + lambda x: isinstance(x, Composition)), "OrderedTrees": _SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)), str, @@ -4573,12 +4606,13 @@ def name(self, style="singular"): ParkingFunctions, len, lambda x: isinstance(x, ParkingFunction)), - "PerfectMatchings": - _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), - str, - PerfectMatchings, - lambda x: x.size(), - lambda x: isinstance(x, PerfectMatching)), + "Lattices": + _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), + lambda X: str((sorted(X._hasse_diagram.cover_relations()), + len(X._hasse_diagram.vertices(sort=False)))), + _finite_lattices, + lambda x: x.cardinality(), + lambda x: isinstance(x, FiniteLatticePoset)), "Posets": _SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)), lambda X: str((sorted(X._hasse_diagram.cover_relations()), @@ -4592,13 +4626,19 @@ def name(self, style="singular"): StandardTableaux, lambda x: x.size(), lambda x: isinstance(x, StandardTableau)), - "SemistandardTableaux": # apparently, isinstance(x, SemistandardTableau) is True for StandardTableaux x + "SemistandardTableaux": _SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)), str, lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1]) if max(T.entries()) == x[1]), lambda x: (x.size(), max(x.entries())), - lambda x: isinstance(x, SemistandardTableau) and not isinstance(x, StandardTableau)), + lambda x: isinstance(x, SemistandardTableau)), + "PerfectMatchings": + _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), + str, + PerfectMatchings, + lambda x: x.size(), + lambda x: isinstance(x, PerfectMatching)), "SetPartitions": _SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))), str, @@ -4616,7 +4656,7 @@ def name(self, style="singular"): str, SignedPermutations, lambda x: len(list(x)), - lambda x: isinstance(x, SignedPermutations.Element)), + lambda x: isinstance(x, SignedPermutation)), "PlanePartitions": _SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)), lambda X: str(list(X)).replace(" ",""), @@ -4630,14 +4670,7 @@ def name(self, style="singular"): for i, v in enumerate(x, 1))) + "]", DecoratedPermutations, lambda x: x.size(), - lambda x: isinstance(x, DecoratedPermutation)), - "Lattices": - _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), - lambda X: str((sorted(X._hasse_diagram.cover_relations()), - len(X._hasse_diagram.vertices(sort=False)))), - _finite_lattices, - lambda x: x.cardinality(), - lambda x: isinstance(x, FiniteLatticePoset))} + lambda x: isinstance(x, DecoratedPermutation))} class FindStatCollections(UniqueRepresentation, Parent): @@ -4689,11 +4722,10 @@ def __init__(self): """ fields = "LevelsWithSizes,Name,NamePlural,NameWiki" url = FINDSTAT_API_COLLECTIONS + "?fields=" + fields - d = _get_json(url, object_pairs_hook=OrderedDict) - self._findstat_collections = d["included"]["Collections"] - for id, data in self._findstat_collections.items(): - data["LevelsWithSizes"] = OrderedDict((literal_eval(level), size) - for level, size in data["LevelsWithSizes"].items()) + d = _get_json(url, object_pairs_hook=dict)["included"]["Collections"] + for id, data in d.items(): + data["LevelsWithSizes"] = {literal_eval(level): size + for level, size in data["LevelsWithSizes"].items()} if data["NameWiki"] in _SupportedFindStatCollections: data["Code"] = _SupportedFindStatCollections[data["NameWiki"]] else: @@ -4706,7 +4738,13 @@ def __init__(self): # fields = "SageCodeElementToString,SageCodeElementsOnLevel,SageCodeStringToElement" # url = FINDSTAT_API_COLLECTIONS + id + "?fields=" + fields # print(json.load(urlopen(url))["included"]["Collections"][id]) + def position(item): + try: + return tuple(_SupportedFindStatCollections).index(item[1]["NameWiki"]) + except ValueError: + return len(_SupportedFindStatCollections) + self._findstat_collections = dict(sorted(d.items(), key=position)) Parent.__init__(self, category=Sets()) def _element_constructor_(self, entry): @@ -4733,7 +4771,6 @@ def _element_constructor_(self, entry): Cc0012: Perfect matchings, Cc0013: Cores, Cc0014: Posets, - Cc0014: Posets, Cc0017: Alternating sign matrices, Cc0018: Gelfand-Tsetlin patterns, Cc0019: Semistandard tableaux, @@ -4745,7 +4782,8 @@ def _element_constructor_(self, entry): Cc0025: Plane partitions, Cc0026: Decorated permutations, Cc0027: Signed permutations, - Cc0028: Skew partitions] + Cc0028: Skew partitions, + Cc0029: Lattices] sage: FindStatCollection(Permutation([1,2,3])) # optional -- internet Cc0001: Permutations @@ -4767,14 +4805,21 @@ def _element_constructor_(self, entry): sage: cc = FindStatCollection(graphs(3)); cc # optional -- internet a subset of Cc0020: Graphs - sage: cc.first_terms(lambda x: x.edges(labels=False)).list() # optional -- internet + sage: cc.first_terms(lambda x: x.edges(labels=False, sort=True)).list() # optional -- internet [(Graph on 3 vertices, []), (Graph on 3 vertices, [(0, 2)]), (Graph on 3 vertices, [(0, 2), (1, 2)]), (Graph on 3 vertices, [(0, 1), (0, 2), (1, 2)])] - sage: len(cc.first_terms(lambda x: x.edges(labels=False)).list()) # optional -- internet + sage: len(cc.first_terms(lambda x: x.edges(labels=False, sort=False)).list()) # optional -- internet 4 + + Check that we can override the automatic detection:: + + sage: l = [(T, len(T)) for n in range(1,5) for T in StandardTableaux(n)] # optional -- internet + sage: qu = findstat("Semistandardtableaux", l, depth=1) # optional -- internet + sage: qu[0] # optional -- internet + St000010oMp00077 (quality [100, 100]) """ if isinstance(entry, self.Element): return entry diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index bd066b73809..d70e6be6bac 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -75,6 +75,7 @@ class KnotInfoColumns(Enum): 'PD Notation (vector)', 'PD Notation (KnotTheory)', 'Braid Notation', + 'Quasipositive Braid', 'Multivariable Alexander Polynomial', 'HOMFLYPT Polynomial', 'Unoriented', @@ -602,16 +603,16 @@ def _create_data_sobj(self, sobj_path=None): val_list.append(knot_list[i][col.name]) - if col.column_type() != col.types.OnlyKnots: - for i in range(1 , len_links): + if col.column_type() != col.types.OnlyKnots: + for i in range(1, len_links): if col.name == self._names_column: link_name = link_list[i][col.name] link_name = link_name.replace('{', '_') link_name = link_name.replace(',', '_') link_name = link_name.replace('}', '') - + num_comp = int(link_list[i][self._components_column]) - row_dict[link_name] = [i + len_knots - 2 , num_comp] + row_dict[link_name] = [i + len_knots - 2, num_comp] val_list.append(link_list[i][col.name]) @@ -822,6 +823,8 @@ def _test_database(self, **options): 'homfly_polynomial': ['HOMFLY', KnotInfoColumnTypes.OnlyKnots], 'homflypt_polynomial': ['HOMFLYPT Polynomial', KnotInfoColumnTypes.OnlyLinks], 'kauffman_polynomial': ['Kauffman', KnotInfoColumnTypes.KnotsAndLinks], + 'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.KnotsAndLinks], + 'khovanov_torsion_polynomial': ['Khovanov Torsion', KnotInfoColumnTypes.OnlyKnots], 'determinant': ['Determinant', KnotInfoColumnTypes.KnotsAndLinks], 'positive': ['Positive', KnotInfoColumnTypes.OnlyKnots], 'fibered': ['Fibered', KnotInfoColumnTypes.OnlyKnots], @@ -1089,5 +1092,60 @@ def _test_database(self, **options): '1-3*t+ 3*t^2-3*t^3+ t^4', '1-3*t+ 5*t^2-3*t^3+ t^4', '1-t+ t^2-t^3+ t^4-t^5+ t^6', - '3-5*t+ 3*t^2'] + '3-5*t+ 3*t^2'], + dc.conway_polynomial: [ + '1', + '1+z^2', + '1-z^2', + '1+3*z^2+z^4', + '1+2*z^2', + '1-2*z^2', + '1-z^2-z^4', + '1+z^2+z^4', + '1+6*z^2+5*z^4+z^6', + '1+3*z^2', + '-z', + 'z', + '-2*z', + '2*z + z^3', + 'z^3', + 'z^3', + '-2*z + z^3', + '2*z + 2*z^3', + '-3*z-2*z^3', + '3*z + 2*z^3', + '-3*z-4*z^3-z^5'], + dc.khovanov_polynomial: [ + '', + 'q^(-9)t^(-3)+q^(-5)t^(-2)+q^(-3)+q^(-1)', + 'q^(-5)t^(-2)+q^(-1)t^(-1)+q+q^(-1)+qt+q^5t^2', + 'q^(-15)t^(-5)+q^(-11)t^(-4)+q^(-11)t^(-3)+q^(-7)t^(-2)+q^(-5)+q^(-3)', + 'q^(-13)t^(-5)+q^(-9)t^(-4)+q^(-9)t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', + 'q^(-9)t^(-4)+q^(-5)t^(-3)+q^(-5)t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+q^(-1)+qt+q^5t^2', + 'q^(-11)t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+(q^(-5)+q^(-3))t^(-1)+q^(-3)+2q^(-1)+tq^(-1)+q^3t^2', + 'q^(-7)t^(-3)+(q^(-5)+q^(-3))t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+2q^(-1)+t(q+q^3)+(q^3+q^5)t^2+q^7t^3', + 'q^(-21)t^(-7)+q^(-17)t^(-6)+q^(-17)t^(-5)+q^(-13)t^(-4)+q^(-13)t^(-3)+q^(-9)t^(-2)+q^(-7)+q^(-5)', + 'q^(-17)t^(-7)+q^(-13)t^(-6)+q^(-13)t^(-5)+(q^(-11)+q^(-9))t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', + '1 + q^(-2) + 1/(q^6*t^2) + 1/(q^4*t^2)', + '1 + q^2 + q^4*t^2 + q^6*t^2', + '1 + q^(-2) + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^6*t^2) + 1/(q^2*t)', + 'q^2 + q^4 + q^6*t^2 + q^10*t^3 + q^10*t^4 + q^12*t^4', + '2 + 2/q^2 + 1/(q^8*t^3) + 1/(q^6*t^2) + 1/(q^4*t^2) + 1/(q^2*t) + t + q^4*t^2', + '2 + 2/q^2 + 1/(q^8*t^3) + 1/(q^6*t^2) + 1/(q^4*t^2) + 1/(q^2*t) + t + q^4*t^2', + '1 + 2/q^2 + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^8*t^3) + 2/(q^6*t^2) + 1/(q^4*t^2) + 2/(q^2*t) + t + q^2*t + q^4*t^2', + 'q^2 + q^4 + q^4*t + 2*q^6*t^2 + q^8*t^2 + 2*q^10*t^3 + 2*q^10*t^4 + q^12*t^4 + q^12*t^5 + q^14*t^5 + q^16*t^6', + 'q^(-4) + q^(-2) + 1/(q^16*t^6) + 1/(q^14*t^6) + 1/(q^14*t^5) + 1/(q^12*t^4) + 1/(q^10*t^4) + 1/(q^10*t^3) + 1/(q^8*t^3) + 1/(q^8*t^2) + 1/(q^6*t^2) + 1/(q^4*t)', + 'q^2 + q^4 + q^4*t + q^6*t^2 + q^8*t^2 + q^8*t^3 + q^10*t^3 + q^10*t^4 + q^12*t^4 + q^14*t^5 + q^14*t^6 + q^16*t^6', + 'q^(-6) + q^(-4) + 1/(q^18*t^6) + 1/(q^16*t^6) + 1/(q^16*t^5) + 1/(q^12*t^4) + 1/(q^12*t^3) + 1/(q^8*t^2)'], + dc.khovanov_torsion_polynomial: [ + '', + 'Q^(-7)t^(-2)', + 'Q^(-3)t^(-1)+Q^3t^2', + 'Q^(-13)t^(-4)+Q^(-9)t^(-2)', + 'Q^(-11)t^(-4)+Q^(-7)t^(-2)+Q^(-5)t^(-1)', + 'Q^(-7)t^(-3)+Q^(-3)t^(-1)+Q^(-1)+Q^3t^2', + 'Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)+Q^(-3)+Qt^2', + 'Q^(-5)t^(-2)+Q^(-3)t^(-1)+Q^(-1)+Qt+Q^3t^2+Q^5t^3', + 'Q^(-19)t^(-6)+Q^(-15)t^(-4)+Q^(-11)t^(-2)', + 'Q^(-15)t^(-6)+Q^(-11)t^(-4)+Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)'] } diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index ca5b3e65290..a800db53df2 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -28,13 +28,13 @@ :: - sage: search = oeis([3, 7, 15, 1], max_results=4) ; search # optional -- internet + sage: search = oeis([3, 7, 15, 1], max_results=4) ; search # optional -- internet # random 0: A001203: Simple continued fraction expansion of Pi. 1: A240698: Partial sums of divisors of n, cf. A027750. 2: A082495: a(n) = (2^n - 1) mod n. 3: A165416: Irregular array read by rows: The n-th row contains those distinct positive integers that each, when written in binary, occurs as a substring in binary n. - sage: [u.id() for u in search] # optional -- internet + sage: [u.id() for u in search] # optional -- internet # random ['A001203', 'A240698', 'A082495', 'A165416'] sage: c = search[0] ; c # optional -- internet A001203: Simple continued fraction expansion of Pi. @@ -349,7 +349,7 @@ class OEIS: The database can be searched by description:: - sage: oeis('prime gap factorization', max_results=4) # optional --internet + sage: oeis('prime gap factorization', max_results=4) # optional --internet # random 0: A073491: Numbers having no prime gaps in their factorization. 1: A073485: Product of any number of consecutive primes; squarefree numbers with no gaps in their prime factorization. 2: A073490: Number of prime gaps in factorization of n. @@ -491,7 +491,7 @@ def find_by_description(self, description, max_results=3, first_result=0): 2: A...: ... sage: prime_gaps = _[2] ; prime_gaps # optional -- internet - A073490: Number of prime gaps in factorization of n. + A... sage: oeis('beaver') # optional -- internet 0: A...: ...eaver... @@ -535,7 +535,7 @@ def find_by_subsequence(self, subsequence, max_results=3, first_result=0): EXAMPLES:: - sage: oeis.find_by_subsequence([2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]) # optional -- internet + sage: oeis.find_by_subsequence([2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]) # optional -- internet # random 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. 1: A212804: Expansion of (1 - x)/(1 - x - x^2). 2: A020695: Pisot sequence E(2,3). diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 5e29cad2685..087c494ef39 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -1259,8 +1259,10 @@ def get_skeleton(self, check=False): def query(self, *args, **kwds): """ - Create a ``SQLQuery`` on this database. For full class details, - type ``SQLQuery?`` and press shift+enter. + Create a ``SQLQuery`` on this database. + + For full class details, + type ``SQLQuery?`` and press :kbd:`Shift` + :kbd:`Enter`. EXAMPLES:: diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 24d0bf9ae01..e6f8fc45f8e 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -340,7 +340,6 @@ def trace_method(obj, meth, **kwds): - ``reads`` -- (default: ``True``) whether to trace read access as well. - EXAMPLES:: diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 07ed880ad7f..adbf012ab2e 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1316,8 +1316,6 @@ def report_failure(self, out, test, example, got, globs): If debugging is turned on this function starts an IPython prompt when a test returns an incorrect answer:: - sage: import os - sage: os.environ['SAGE_PEXPECT_LOG'] = "1" sage: sage0.quit() sage: _ = sage0.eval("import doctest, sys, os, multiprocessing, subprocess") sage: _ = sage0.eval("from sage.doctest.parsing import SageOutputChecker") @@ -1473,8 +1471,6 @@ def report_unexpected_exception(self, out, test, example, exc_info): EXAMPLES:: - sage: import os - sage: os.environ['SAGE_PEXPECT_LOG'] = "1" sage: sage0.quit() sage: _ = sage0.eval("import doctest, sys, os, multiprocessing, subprocess") sage: _ = sage0.eval("from sage.doctest.parsing import SageOutputChecker") diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 1752caa851a..26d793b96bf 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -29,35 +29,8 @@ from sage.repl.preparse import preparse, strip_string_literals from functools import reduce - from .external import available_software -float_regex = re.compile(r'\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') -optional_regex = re.compile(r'(arb216|arb218|py2|long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w|[.])*))') -# Version 4.65 of glpk prints the warning "Long-step dual simplex will -# be used" frequently. When Sage uses a system installation of glpk -# which has not been patched, we need to ignore that message. -# See :trac:`29317`. -glpk_simplex_warning_regex = re.compile(r'(Long-step dual simplex will be used)') -# :trac:`31204` -- suppress warning about ld and OS version for dylib files. -ld_warning_regex = re.compile(r'^.*dylib.*was built for newer macOS version.*than being linked.*') -# :trac:`30845` -- suppress warning on conda about ld -ld_pie_warning_regex = re.compile(r'ld: warning: -pie being ignored. It is only used when linking a main executable') -# :trac:`34533` -- suppress warning on OS X 12.6 about chained fixups -chained_fixup_warning_regex = re.compile(r'ld: warning: -undefined dynamic_lookup may not work with chained fixups') -sympow_cache_warning_regex = re.compile(r'\*\*WARNING\*\* /var/cache/sympow/datafiles/le64 yields insufficient permissions') -find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) -find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) -find_python_continuation = re.compile(r"^(\s*)\.\.\.([^\.])", re.M) -python_prompt = re.compile(r"^(\s*)>>>", re.M) -# The following are used to allow ... at the beginning of output -ellipsis_tag = "<TEMP_ELLIPSIS_TAG>" -continuation_tag = "<TEMP_CONTINUATION_TAG>" -random_marker = re.compile('.*random', re.I) -tolerance_pattern = re.compile(r'\b((?:abs(?:olute)?)|(?:rel(?:ative)?))? *?tol(?:erance)?\b( +[0-9.e+-]+)?') -backslash_replacer = re.compile(r"""(\s*)sage:(.*)\\\ * -\ *(((\.){4}:)|((\.){3}))?\ *""") - _RIFtol = None @@ -107,32 +80,6 @@ def fake_RIFtol(*args): ansi_escape_sequence = re.compile(r'(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])') -# Collection of fixups applied in the SageOutputChecker. Each element in this -# this list a pair of functions applied to the actual test output ('g' for -# "got") and the expected test output ('w' for "wanted"). The first function -# should be a simple fast test on the expected and/or actual output to -# determine if a fixup should be applied. The second function is the actual -# fixup, which is applied if the test function passes. In most fixups only one -# of the expected or received outputs are normalized, depending on the -# application. -_repr_fixups = [ - (lambda g, w: "Long-step" in g, - lambda g, w: (glpk_simplex_warning_regex.sub('', g), w)), - - (lambda g, w: "chained fixups" in g, - lambda g, w: (chained_fixup_warning_regex.sub('', g), w)), - - (lambda g, w: "insufficient permissions" in g, - lambda g, w: (sympow_cache_warning_regex.sub('', g), w)), - - (lambda g, w: "dylib" in g, - lambda g, w: (ld_warning_regex.sub('', g), w)), - - (lambda g, w: "pie being ignored" in g, - lambda g, w: (ld_pie_warning_regex.sub('', g), w)) -] - - def parse_optional_tags(string): """ Return a set consisting of the optional tags from the following @@ -190,6 +137,8 @@ def parse_optional_tags(string): # strip_string_literals replaces comments comment = "#" + (literals[comment]).lower() + optional_regex = re.compile(r'(arb216|arb218|py2|long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w|[.])*))') + tags = [] for m in optional_regex.finditer(comment): cmd = m.group(1) @@ -231,6 +180,10 @@ def parse_tolerance(source, want): sage: marked.abs_tol 0.010000000000000000000...? """ + # regular expressions + random_marker = re.compile('.*random', re.I) + tolerance_pattern = re.compile(r'\b((?:abs(?:olute)?)|(?:rel(?:ative)?))? *?tol(?:erance)?\b( +[0-9.e+-]+)?') + safe, literals, state = strip_string_literals(source) first_line = safe.split('\n', 1)[0] if '#' not in first_line: @@ -644,6 +597,17 @@ def parse(self, string, *args): sage: dte.want '...00010\n' """ + # Regular expressions + find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) + find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) + find_python_continuation = re.compile(r"^(\s*)\.\.\.([^\.])", re.M) + python_prompt = re.compile(r"^(\s*)>>>", re.M) + backslash_replacer = re.compile(r"""(\s*)sage:(.*)\\\ * +\ *(((\.){4}:)|((\.){3}))?\ *""") + + # The following are used to allow ... at the beginning of output + ellipsis_tag = "<TEMP_ELLIPSIS_TAG>" + # Hack for non-standard backslash line escapes accepted by the current # doctest system. m = backslash_replacer.search(string) @@ -942,13 +906,16 @@ def check_output(self, want, got, optionflags): sage: OC.check_output(ex.want, 'Long-step dual simplex will be used\n1.3090169943749475', optflag) True """ + # Regular expression for floats + float_regex = re.compile(r'\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') + got = self.human_readable_escape_sequences(got) if isinstance(want, MarkedOutput): if want.random: return True elif want.tol or want.rel_tol or want.abs_tol: - # First check that the number of float appearing match + # First check that the number of occurrences of floats appearing match want_str = [g[0] for g in float_regex.findall(want)] got_str = [g[0] for g in float_regex.findall(got)] if len(want_str) != len(got_str): @@ -993,10 +960,18 @@ def do_fixup(self, want, got): A tuple: - - bool, True when some fixup were performed - - string, (unchanged) wanted string + - bool, ``True`` when some fixup were performed and ``False`` otherwise + - string, edited wanted string - string, edited got string + .. NOTE:: + + Currently, the code only possibly changes the string ``got`` + while keeping ``want`` invariant. We keep open the possibility + of adding a regular expression which would also change the + ``want`` string. This is why ``want`` is an input and an output + of the method even if currently kept invariant. + EXAMPLES:: sage: from sage.doctest.parsing import SageOutputChecker @@ -1029,11 +1004,48 @@ def do_fixup(self, want, got): """ did_fixup = False - for quick_check, fixup in _repr_fixups: - do_fixup = quick_check(got, want) - if do_fixup: - got, want = fixup(got, want) - did_fixup = True + # The conditions in the below `if` are simple fast test on the expected + # and/or actual output to determine if a fixup should be applied. + + if "Long-step" in got: + # Version 4.65 of glpk prints the warning "Long-step dual + # simplex will be used" frequently. When Sage uses a system + # installation of glpk which has not been patched, we need to + # ignore that message. See :trac:`29317`. + glpk_simplex_warning_regex = re.compile(r'(Long-step dual simplex will be used)') + got = glpk_simplex_warning_regex.sub('', got) + did_fixup = True + + if "chained fixups" in got: + # :trac:`34533` -- suppress warning on OS X 12.6 about chained fixups + chained_fixup_warning_regex = re.compile(r'ld: warning: -undefined dynamic_lookup may not work with chained fixups') + got = chained_fixup_warning_regex.sub('', got) + did_fixup = True + + if "newer macOS version" in got: + # :trac:`34741` -- suppress warning arising after + # upgrading from macOS 12.X to 13.X. + newer_macOS_version_regex = re.compile(r'.*dylib \(.*\) was built for newer macOS version \(.*\) than being linked \(.*\)') + got = newer_macOS_version_regex.sub('', got) + did_fixup = True + + if "insufficient permissions" in got: + sympow_cache_warning_regex = re.compile(r'\*\*WARNING\*\* /var/cache/sympow/datafiles/le64 yields insufficient permissions') + got = sympow_cache_warning_regex.sub('', got) + did_fixup = True + + if "dylib" in got: + # :trac:`31204` -- suppress warning about ld and OS version for + # dylib files. + ld_warning_regex = re.compile(r'^.*dylib.*was built for newer macOS version.*than being linked.*') + got = ld_warning_regex.sub('', got) + did_fixup = True + + if "pie being ignored" in got: + # :trac:`30845` -- suppress warning on conda about ld + ld_pie_warning_regex = re.compile(r'ld: warning: -pie being ignored. It is only used when linking a main executable') + got = ld_pie_warning_regex.sub('', got) + did_fixup = True return did_fixup, want, got @@ -1166,6 +1178,9 @@ def output_difference(self, example, got, optionflags): Tolerance exceeded: 0.0 vs 10.05, tolerance +infinity > 1e-1 """ + # Regular expression for floats + float_regex = re.compile(r'\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') + got = self.human_readable_escape_sequences(got) want = example.want diff = doctest.OutputChecker.output_difference(self, example, got, optionflags) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 92f10c05a9a..96cc0d12d39 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -1,5 +1,5 @@ r""" -Dynamical systmes on Berkovich space over `\CC_p`. +Dynamical systems on Berkovich space over `\CC_p`. A dynamical system on Berkovich space over `\CC_p` is determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, @@ -239,14 +239,14 @@ def __classcall_private__(cls, dynamical_system, domain=None, ideal=None): try: dynamical_system = DynamicalSystem_affine(dynamical_system) except (TypeError, ValueError): - raise TypeError('domain was affine Berkovich space, but dynamical_system did not ' + \ + raise TypeError('domain was affine Berkovich space, but dynamical_system did not ' 'convert to an affine dynamical system') if isinstance(domain, Berkovich_Cp_Projective): if not isinstance(dynamical_system, DynamicalSystem_projective): try: dynamical_system = DynamicalSystem_projective(dynamical_system) except (TypeError, ValueError): - raise TypeError('domain was projective Berkovich space, but dynamical_system did not convert ' + \ + raise TypeError('domain was projective Berkovich space, but dynamical_system did not convert ' 'to a projective dynamical system') if not isinstance(dynamical_system, DynamicalSystem): @@ -268,7 +268,7 @@ def __classcall_private__(cls, dynamical_system, domain=None, ideal=None): if ideal != domain.ideal(): raise ValueError('conflicting inputs for ideal and domain') else: - raise ValueError('base ring of domain of dynamical_system must be p-adic or a number field ' + \ + raise ValueError('base ring of domain of dynamical_system must be p-adic or a number field ' 'not %s' %morphism_domain.base_ring()) if is_AffineSpace(morphism_domain): @@ -446,6 +446,7 @@ def _repr_(self): return "Dynamical system of " + domain_str + " induced by the map" + \ "\n Defn: %s"%('\n '.join(self._system._repr_defn().split('\n'))) + class DynamicalSystem_Berkovich_projective(DynamicalSystem_Berkovich): r""" A dynamical system on projective Berkovich space over `\CC_p`. @@ -528,13 +529,13 @@ def __classcall_private__(cls, dynamical_system, domain=None): raise ValueError('domain was not relative dimension 1') if not isinstance(R, pAdicBaseGeneric): if domain is None: - raise TypeError('dynamical system defined over %s, not p-adic, ' %morphism_domain.base_ring() + \ + raise TypeError('dynamical system defined over %s, not p-adic, ' %morphism_domain.base_ring() + 'and domain is None') if not isinstance(domain, Berkovich_Cp_Projective): raise TypeError('domain was %s, not a projective Berkovich space over Cp' %domain) if domain.base() != morphism_domain: - raise ValueError('base of domain was %s, with coordinate ring %s ' %(domain.base(), \ - domain.base().coordinate_ring())+ 'while dynamical_system acts on %s, ' %morphism_domain + \ + raise ValueError('base of domain was %s, with coordinate ring %s ' %(domain.base(), + domain.base().coordinate_ring())+ 'while dynamical_system acts on %s, ' %morphism_domain + 'with coordinate ring %s' %morphism_domain.coordinate_ring()) else: domain = Berkovich_Cp_Projective(morphism_domain) @@ -1007,7 +1008,7 @@ def __classcall_private__(cls, dynamical_system, domain=None): raise ValueError('domain not relative dimension 1') if not isinstance(R, pAdicBaseGeneric): if domain is None: - raise TypeError('dynamical system defined over %s, not padic, ' %morphism_domain.base_ring() + \ + raise TypeError('dynamical system defined over %s, not padic, ' %morphism_domain.base_ring() + 'and domain was not specified') if not isinstance(domain, Berkovich_Cp_Affine): raise TypeError('domain was %s, not an affine Berkovich space over Cp' %domain) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 8424b2fc505..b1e5240e90f 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -754,7 +754,7 @@ def automorphism_group_QQ_CRT(rational_function, prime_lower_bound=4, return_fun # and the gcd of orders over Fp and 24 is 24 # or if the gcd is equal to the number of automorphisms we have if (len(elements) == gcd(orderaut + [24])) or \ - (gcd(orderaut + [24]) == 24 and \ + (gcd(orderaut + [24]) == 24 and (len(elements) == 12 or len(elements) == 8)): if iso_type: return elements, which_group(elements) @@ -1907,7 +1907,7 @@ def conjugating_set_initializer(f, g): repeated_mult_L[repeated] += [mult_to_point_L[mult_L]] more = True - # the n+2 points to be used to specificy PGL conjugations + # the n+2 points to be used to specify PGL conjugations source = [] # a list of tuples of the form ((multiplier, level), repeat) where the @@ -1997,9 +1997,10 @@ def conjugating_set_initializer(f, g): for r in sorted(repeated_mult_L.keys()): for point_lst in repeated_mult_L[r]: all_points += point_lst - # this loop is quite long, so we break after finding the first subset - # with the desired property. There is, however, no guarentee that the - # subset we found minimizes the combinatorics when checking conjugations + # this loop is quite long, so we break after finding the + # first subset with the desired property. There is, + # however, no guarantee that the subset we found minimizes + # the combinatorics when checking conjugations for subset in Subsets(range(len(all_points)), n+2): source = [] for i in subset: @@ -2166,10 +2167,10 @@ def conjugating_set_helper(f, g, num_cpus, source, possible_targets): subset_iterators.append(Subsets(range(len(lst[0])), lst[1])) # helper function for parallelization - # given a list of tuples which specify indicies of possible target points - # in possible_targets, check all arragements of those possible target points - # and if any of them define a conjugation which sends f to g, return - # those conjugations as a list + # given a list of tuples which specify indices of possible target + # points in possible_targets, check all arrangements of those + # possible target points and if any of them define a conjugation + # which sends f to g, return those conjugations as a list def find_conjugations_subset(tuples): conj = [] for tup in tuples: @@ -2195,9 +2196,9 @@ def find_conjugations_subset(tuples): return conj # helper function for parallelization - # given a list of tuples which specify indicies of possible target points - # in possible_targets, check all possible target points - # and if any of them define a conjugation which sends f to g, return + # given a list of tuples which specify indices of possible target + # points in possible_targets, check all possible target points and + # if any of them define a conjugation which sends f to g, return # those conjugations as a list def find_conjugations_arrangement(tuples): conj = [] @@ -2229,7 +2230,7 @@ def find_conjugations_arrangement(tuples): if ret[1]: Conj += ret[1] # otherwise, we need to first check linear independence of the subsets - # and then build a big list of all the arrangemenets to split among + # and then build a big list of all the arrangements to split among # the threads else: good_targets = [] @@ -2307,9 +2308,10 @@ def is_conjugate_helper(f, g, num_cpus, source, possible_targets): subset_iterators.append(Subsets(range(len(lst[0])), lst[1])) # helper function for parallelization - # given a list of tuples which specify indicies of possible target points - # in possible_targets, check all arragements of those possible target points - # and if any of them define a conjugation which sends f to g, return True + # given a list of tuples which specify indices of possible target + # points in possible_targets, check all arrangements of those + # possible target points and if any of them define a conjugation + # which sends f to g, return True def find_conjugations_subset(tuples): for tup in tuples: target_set = [] @@ -2334,7 +2336,7 @@ def find_conjugations_subset(tuples): return False # helper function for parallelization - # given a list of tuples which specify indicies of possible target points + # given a list of tuples which specify indices of possible target points # in possible_targets, check all possible target points # and if any of them define a conjugation which sends f to g, return True def find_conjugations_arrangement(tuples): @@ -2367,7 +2369,7 @@ def find_conjugations_arrangement(tuples): is_conj = True break # otherwise, we need to first check linear independence of the subsets - # and then build a big list of all the arrangemenets to split among + # and then build a big list of all the arrangements to split among # the threads else: good_targets = [] diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index cdfc11a9e52..385ccad1a91 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -70,6 +70,7 @@ class initialization directly. from sage.rings.integer import Integer from sage.arith.all import gcd, lcm, next_prime, binomial, primes, moebius from sage.categories.finite_fields import FiniteFields +from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField_generic from sage.rings.complex_mpfr import ComplexField from sage.rings.finite_rings.finite_field_constructor import (is_FiniteField, GF, is_PrimeFiniteField) @@ -1103,6 +1104,303 @@ def nth_iterate(self, P, n, **kwds): raise TypeError("must be a forward orbit") return self.orbit(P, [n,n+1], **kwds)[0] + def arakelov_zhang_pairing(self, g, **kwds): + r""" + Return an estimate of the Arakelov-Zhang pairing of the rational + maps ``self`` and ``g`` on `\mathbb{P}^1` over a number field. + + The Arakelov-Zhang pairing was introduced by Petsche, Szpiro, and + Tucker in 2012, which measures the dynamical closeness of two rational + maps. They prove inter alia that if one takes a sequence of small points + for one map (for example, preperiodic points for ``self``) and measure + their dynamical height with respect to the other map (say, ``g``), then + the values of the height will tend to the value of the Arakelov-Zhang pairing. + + The Arakelov-Zhang pairing involves mutual energy integrals between dynamical + measures, which are in the case of polynomials, the equilibrium measures + of the associated Julia sets at each place. As a result, these pairings + are very difficult to compute exactly via analytic methods. We use a + discrete approximation to these energy integrals. + + ALGORITHM: + + We select periodic points of order `n`, or ``n``-th preimages of a + specified starting value given by ``f_starting_point`` and ``g_starting_point``. + At the archimedean places and the places of bad reduction of the two maps, + we compute the discrete approximations to the energy integrals involved + using these points. + + INPUT: + + - ``g`` - a rational map of `\mathbb{P}^1` given as a projective morphism. + ``g`` and ``self`` should have the same field of definition. + + kwds: + + - ``n`` - (default: 5) a positive integer + Order of periodic points to use or preimages to take if starting points are specified. + + - ``f_starting_point`` - (optional, default: ``None``) value in the base number field or None. + If ``f_starting_point`` is None, we solve for points of period ``n`` for ``self``. + Otherwise, we take ``n``-th preimages of the point given by ``f_starting_point`` + under ``f`` on the affine line. + + - ``g_starting_point`` - (optional, default: ``None``) value in the base number field or None. + If ``g_starting_point`` is None, we solve for points of period ``n`` for ``g``. + Otherwise, we take ``n``-th preimages of the point given by ``g_starting_point`` + under ``g`` on the affine line. + + - ``check_primes_of_bad_reduction`` - (optional, default: ``False``) boolean. + Passed to the ``primes_of_bad_reduction`` function for ``self`` and ``g``. + + - ``prec`` - (optional, default: ``RealField`` default) + default precision for RealField values which are returned. + + - ``noise_multiplier`` - (default: 2) a real number. + Discriminant terms involved in the computation at the archimedean places + are often not needed, particularly if the capacity of the Julia sets is 1, + and introduce a lot of error. By a well-known result of Mahler (see + also M. Baker, ""A lower bound for averages of dynamical Green's + functions") such error (for a set of `N` points) is on the order of + `\log(N)/N` after our normalization. We check if the value of the + archimedean discriminant terms is within ``2*noise_multiplier`` of + `\log(N)/N`. If so, we discard it. In practice this greatly improves + the accuracy of the estimate of the pairing. If desired, + ``noise_multiplier`` can be set to 0, and no terms will be ignored. + + OUTPUT: + + - a real number estimating the Arakelov-Zhang pairing of the two rational maps. + + EXAMPLES:: + + sage: K.<k> = CyclotomicField(3) + sage: P.<x,y> = ProjectiveSpace(K, 1) + sage: f = DynamicalSystem_projective([x^2 + (2*k + 2)*y^2, y^2]) + sage: g = DynamicalSystem_projective([x^2, y^2]) + sage: pairingval = f.arakelov_zhang_pairing(g, n=5); pairingval + 0.409598197761958 + + :: + + sage: P.<x,y> = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([x^2 + 4*y^2, y^2]) + sage: g = DynamicalSystem_projective([x^2, y^2]) + sage: pairingval = f.arakelov_zhang_pairing(g, n=6); pairingval + 0.750178391443644 + sage: # Compare to the exact value: + sage: dynheight = f.canonical_height(P(0, 1)); dynheight + 0.75017839144364417318023000563 + sage: dynheight - pairingval + 0.000000000000000 + + Notice that if we set the noise_multiplier to 0, the accuracy is diminished:: + + sage: P.<x,y> = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([x^2 + 4*y^2, y^2]) + sage: g = DynamicalSystem_projective([x^2, y^2]) + sage: pairingval = f.arakelov_zhang_pairing(g, n=6, noise_multiplier=0) + sage: pairingval + 0.650660018921632 + sage: dynheight = f.canonical_height(P(0, 1)); dynheight + 0.75017839144364417318023000563 + sage: pairingval - dynheight + -0.0995183725220122 + + We compute the example of Prop. 18(d) from Petsche, Szpiro and Tucker:: + + sage: P.<x,y> = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([y^2 - (y - x)^2, y^2]) + sage: g = DynamicalSystem_projective([x^2, y^2]) + sage: f.arakelov_zhang_pairing(g) + 0.326954667248466 + sage: # Correct value should be = 0.323067... + sage: f.arakelov_zhang_pairing(g, n=9) + 0.323091061918965 + sage: _ - 0.323067 + 0.0000240619189654789 + + Also from Prop. 18 of Petsche, Szpiro and Tucker, includes places of bad reduction:: + + sage: R.<z> = PolynomialRing(ZZ) + sage: K.<b> = NumberField(z^3 - 11) + sage: P.<x,y> = ProjectiveSpace(K,1) + sage: a = 7/(b - 1) + sage: f = DynamicalSystem_projective([a*y^2 - (a*y - x)^2, y^2]) + sage: g = DynamicalSystem_projective([x^2, y^2]) + sage: # If all archimedean absolute values of a have modulus > 2, + sage: # then the pairing should be h(a). + sage: f.arakelov_zhang_pairing(g, n=6) + 1.93846423207664 + sage: _ - a.global_height() + -0.00744591697867292 + """ + n = kwds.pop('n', 5) + f_starting_point = kwds.pop('f_starting_point', None) + g_starting_point = kwds.pop('g_starting_point', None) + check_primes_of_bad_reduction = kwds.pop('check_primes_of_bad_reduction', False) + prec = kwds.pop('prec', None) + noise_multiplier = kwds.pop('noise_multiplier', 2) + + f_domain = self.domain() + R = f_domain.base_ring() + g_domain = g.domain() + + if f_domain != g_domain: + raise TypeError("Implemented only for rational maps of the same projective line.") + + if n <= 0: + raise ValueError("Period must be a positive integer.") + + if not (is_ProjectiveSpace(f_domain) and is_ProjectiveSpace(g_domain)): + raise NotImplementedError("Not implemented for subschemes.") + + if f_domain.dimension_relative() > 1: + raise NotImplementedError("Only implemented for dimension 1.") + + if not self.is_endomorphism(): + raise TypeError("Self must be an endomorphism.") + + if R not in NumberFields() and R is not QQbar: + raise NotImplementedError("Only implemented for number fields.") + + f_iterate_map = self.nth_iterate_map(n) + f_iter_map_poly = f_iterate_map.defining_polynomials() + if f_starting_point is None: + f_poly_hom = f_iter_map_poly[0] * f_domain.gens()[1] - f_iter_map_poly[1] * f_domain.gens()[0] + else: + f_poly_hom = f_iter_map_poly[0] - f_starting_point * f_iter_map_poly[1] + + g_iterate_map = g.nth_iterate_map(n) + g_iter_map_poly = g_iterate_map.defining_polynomials() + if g_starting_point is None: + g_poly_hom = g_iter_map_poly[0] * g_domain.gens()[1] - g_iter_map_poly[1] * g_domain.gens()[0] + else: + g_poly_hom = g_iter_map_poly[0] - g_starting_point * g_iter_map_poly[1] + + f_poly = f_poly_hom([(f_domain.gens()[0]), 1]).univariate_polynomial().monic() + g_poly = g_poly_hom([(g_domain.gens()[0]), 1]).univariate_polynomial().monic() + + # If f_poly and g_poly are not square-free, make them square-free. + if not f_poly.is_squarefree(): + f_poly = f_poly.quo_rem(gcd(f_poly, f_poly.derivative()))[0] + if not g_poly.is_squarefree(): + g_poly = g_poly.quo_rem(gcd(g_poly, g_poly.derivative()))[0] + + if f_poly.degree() <= 2 or g_poly.degree() <= 2: + # f_point or g_point is exceptional + raise ValueError("One of the starting points is exceptional. \ + Please specify a non-exceptional initial point.") + + if gcd(f_poly, g_poly).degree() > 0: + if f_poly.degree() > g_poly.degree(): + f_poly = f_poly.quo_rem(gcd(f_poly, g_poly))[0] + else: + g_poly = g_poly.quo_rem(gcd(f_poly, g_poly))[0] + + if f_poly.degree() <= 2 or g_poly.degree() <= 2: + raise ValueError("After removing common factors, the n-th \ + iterates of 'self' and 'g' have too many \ + roots in common. Try another 'n' or starting \ + values.") + + # We want higher precision here temporarily, since resultants are + # usually very large. This is not to say that the computation is + # very accurate, merely that we want to keep track of potentially + # very large height integers/rationals. + old_prec = prec + if prec is None: + Real = RealField(512) + elif prec < 512: + prec = 512 + Real = RealField(prec) + + bad_primes = list(set(self.primes_of_bad_reduction(check=check_primes_of_bad_reduction)) + .union(g.primes_of_bad_reduction(check=check_primes_of_bad_reduction))) + + f_deg = f_poly.degree() + g_deg = g_poly.degree() + + f_disc = f_poly.discriminant() + g_disc = g_poly.discriminant() + + res = f_poly.resultant(g_poly) + + # The code below actually computes -( mu_f - mu_g, mu_f - mu_g ), + # so flip the sign at the end. + AZ_pairing = Real(0) + if R is QQ: + for p in bad_primes: + temp = (ZZ(1)/2) * (-f_disc.ord(p)) * Real(p).log() / (f_deg**2) + if abs(temp) > noise_multiplier * Real(f_deg).log() / Real(f_deg): + AZ_pairing += temp + + temp = (ZZ(1)/2) * (-g_disc.ord(p)) * Real(p).log() / (g_deg**2) + if abs(temp) > noise_multiplier * Real(g_deg).log() / Real(g_deg): + AZ_pairing += temp + + AZ_pairing -= (-res.ord(p)) * Real(p).log() / (f_deg * g_deg) + + temp = (ZZ(1)/2) * (Real(f_disc).abs().log()) / (f_deg**2) + if abs(temp) > noise_multiplier * Real(f_deg).log() / Real(f_deg): + AZ_pairing += temp + + temp = (ZZ(1)/2) * (Real(g_disc).abs().log()) / (g_deg**2) + if abs(temp) > noise_multiplier * Real(g_deg).log() / Real(g_deg): + AZ_pairing += temp + + AZ_pairing -= Real(res).abs().log() / (f_deg * g_deg) + + # For number fields + else: + K = self.base_ring() + d = K.absolute_degree() + + for v in bad_primes: + Nv = v.absolute_ramification_index() * v.residue_class_degree() / d + + temp = Nv * ((ZZ(1)/2) * K(f_disc).abs_non_arch(v, prec=prec).log() / (f_deg**2)) + if abs(temp) > noise_multiplier * Real(f_deg).log() / Real(f_deg): + AZ_pairing += temp + + temp = Nv * ((ZZ(1)/2) * K(g_disc).abs_non_arch(v, prec=prec).log() / (g_deg**2)) + if abs(temp) > noise_multiplier * Real(g_deg).log() / Real(g_deg): + AZ_pairing += temp + + AZ_pairing -= Nv * (K(res).abs_non_arch(v, prec=prec).log() / (f_deg * g_deg)) + + if f_disc.is_rational(): + f_disc = QQ(f_disc) + temp = (ZZ(1)/2) * (Real(f_disc).abs().log()) / (f_deg**2) + if abs(temp) > noise_multiplier * Real(f_deg).log() / Real(f_deg): + AZ_pairing += temp + else: + temp = (ZZ(1)/d) * (ZZ(1)/2) * (Real(K(f_disc).norm()).abs().log()) / (f_deg**2) + if abs(temp) > noise_multiplier * Real(f_deg).log() / Real(f_deg): + AZ_pairing += temp + + if g_disc.is_rational(): + g_disc = QQ(g_disc) + temp = (ZZ(1)/2) * (Real(g_disc).abs().log()) / (g_deg**2) + if abs(temp) > noise_multiplier * Real(g_deg).log() / Real(g_deg): + AZ_pairing += temp + else: + temp = (ZZ(1)/d) * (ZZ(1)/2) * (Real(K(g_disc).norm()).abs().log()) / (g_deg**2) + if abs(temp) > noise_multiplier * Real(g_deg).log() / Real(g_deg): + AZ_pairing += temp + + if res.is_rational(): + AZ_pairing -= (Real(res).abs().log()) / (f_deg * g_deg) + else: + AZ_pairing -= (ZZ(1)/d) * (Real(K(res).norm()).abs().log()) / (f_deg * g_deg) + + if old_prec is None: + Real = RealField() + else: + Real = RealField(old_prec) + + return Real(-AZ_pairing) + def degree_sequence(self, iterates=2): r""" Return sequence of degrees of normalized iterates starting with @@ -3072,13 +3370,18 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): sage: g = f.affine_preperiodic_model(0, 1); g Dynamical System of Projective Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to - (-x^2 : 2*x^2 + 2*x*y + y^2 : 2*x^2 + 2*x*y + 2*y^2 - 2*y*z + z^2) + (-x^2 : -2*x^2 + 2*x*y - y^2 : 2*x^2 - 2*x*y + 2*y^2 + 2*y*z + z^2) We can check that ``g`` has affine fixed points:: sage: g.periodic_points(1) - [(-1 : 1 : 1), (-1/2 : 1/2 : 1), (-1/2 : 1 : 1), (-1/3 : 2/3 : 1), (0 : 0 : 1), - (0 : 1/2 : 1), (0 : 1 : 1)] + [(-1 : -1 : 1), + (-1/2 : -1 : 1), + (-1/2 : -1/2 : 1), + (-1/3 : -2/3 : 1), + (0 : -1 : 1), + (0 : -1/2 : 1), + (0 : 0 : 1)] :: @@ -3087,8 +3390,8 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): sage: f.affine_preperiodic_model(0, 1) Dynamical System of Projective Space of dimension 2 over Finite Field in z2 of size 3^2 Defn: Defined on coordinates by sending (x : y : z) to - ((z2 + 1)*x^2 : (z2 + 1)*x^2 + (z2 + 1)*x*y + (-z2 - 1)*y^2 : - (z2 - 1)*x^2 + (z2 - 1)*x*y - y^2 + (-z2)*y*z + z^2) + ((-z2)*x^2 : z2*x^2 + (-z2)*x*y + (-z2)*y^2 : + (-z2)*x^2 + z2*x*y + (z2 + 1)*y^2 - y*z + z^2) :: @@ -3099,9 +3402,8 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Projective Space of dimension 2 over Univariate Polynomial Ring in c over Finite Field of size 3 Defn: Defined on coordinates by sending (x : y : z) to - ((2*c^4 + c^3)*x^2 : (2*c^4 + c^3)*x^2 + (2*c^4 + c^3)*x*y + (c^4 + 2*c^3)*y^2 : - c^3*x^2 + c^3*x*y + (2*c^3 + 2*c^2)*y^2 + (c^3 + 2*c^2)*y*z + (2*c^4 + 2*c^3 + - 2*c^2)*z^2) + (2*c^3*x^2 : c^3*x^2 + 2*c^3*x*y + 2*c^3*y^2 : + 2*c^3*x^2 + c^3*x*y + (c^3 + c^2)*y^2 + 2*c^2*y*z + c^2*z^2) :: @@ -3112,8 +3414,7 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Projective Space of dimension 2 over Cyclotomic Field of order 3 and degree 2 Defn: Defined on coordinates by sending (x : y : z) to - (x^2 + y^2 + (-k + 2)*x*z - 2*y*z + (-k + 3)*z^2 : - -2*x^2 + (k - 4)*x*z + (k - 3)*z^2 : -x^2 + (k - 2)*x*z + (k - 2)*z^2) + (-y^2 : x^2 : x^2 + (-k)*x*z + z^2) :: @@ -3132,8 +3433,7 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: 2*y - z Defn: Defined on coordinates by sending (x : y : z) to - (2*x^2 + y^2 + 4*x*z - 2*y*z + 4*z^2 : -x^2 - y^2 - 2*x*z + 2*y*z - 3*z^2 : - -x^2 - 2*x*z - 2*z^2) + (-x^2 - y^2 : y^2 : x^2 + z^2) TESTS:: @@ -4219,7 +4519,7 @@ def preperiodic_points(self, m, n, **kwds): f_deformed = DynamicalSystem(deformed_polys) # after deforming by the parameter, the preperiodic points with multiplicity - # will seperate into different points. we can now calculate the minimal preperiodic + # will separate into different points. we can now calculate the minimal preperiodic # points with the parameter, and then specialize to get the formal preperiodic points ideal = f_deformed.preperiodic_points(m, n, return_scheme=True).defining_ideal() L = [poly.specialization({t:0}) for poly in ideal.gens()] @@ -4569,7 +4869,7 @@ def periodic_points(self, n, minimal=True, formal=False, R=None, algorithm='vari f_deformed = DynamicalSystem(deformed_polys) # after deforming by the parameter, the preperiodic points with multiplicity - # will seperate into different points. we can now calculate the minimal preperiodic + # will separate into different points. we can now calculate the minimal preperiodic # points with the parameter, and then specialize to get the formal periodic points ideal = f_deformed.periodic_points(n, return_scheme=True).defining_ideal() L = [poly.specialization({t:0}) for poly in ideal.gens()] @@ -4626,20 +4926,22 @@ def periodic_points(self, n, minimal=True, formal=False, R=None, algorithm='vari else: raise ValueError("algorithm must be either 'variety' or 'cyclegraph'") - def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closure=True): + def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closure=True, check=True): r""" Computes the ``n`` multiplier spectra of this dynamical system. - This is the set of multipliers of the periodic points of formal + This is the set of multipliers of all peroidic points of period ``n`` included with the appropriate multiplicity. - User can also specify to compute the ``n`` multiplier spectra - instead which includes the multipliers of all periodic points - of period ``n``. The map must be defined over - projective space of dimension 1 over a number field or finite field. + User can also specify to compute the formal ``n`` multiplier spectra + instead which includes the multipliers of all formal periodic points + of period ``n`` with appropriate multiplicity. The map must be defined over + projective space over a number field or finite field. - The computations can be done either over the algebraic closure of the - base field or over the minimal extension of the base field that - contains the critical points. + By default, the computations are done over the algebraic closure of the + base field. If the map is defined over projective space of dimension 1, + the computation can be done over the minimal extension of the base field that + contains the periodic points. Otherwise, it will be done over the base ring + of the map. INPUT: @@ -4653,37 +4955,67 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur or ``'cycle'`` depending on whether you compute one multiplier per point or one per cycle - - ``use_algebraic_closure`` -- boolean (default: True) -- If True uses the - algebraic closure. If False, uses the smallest extension of the base field - containing all the critical points. + - ``use_algebraic_closure`` -- boolean (default: ``True``) -- If ``True`` uses the + algebraic closure. Using the algebraic closure can sometimes lead to numerical instability + and extraneous errors. For most accurate results in dimension 1, set to ``False``. + If ``False``, and the map is defined over projective space of + dimension 1, uses the smallest extension of the base field + containing all the periodic points. If the map is defined over projective space + of dimension greater than 1, then the base ring of the map is used. - OUTPUT: a list of field elements + - ``check`` -- (defualt: ``True``) whether to check if the + full multiplier spectra was computed. If ``False``, can lead to + mathematically incorrect answers in dimension greater than 1. Ignored + if ``use_algebraic_closure`` is ``True`` or if this dynamical system is defined + over projective space of dimension 1. + + OUTPUT: + + A list of field elements if the domain of the map is projective space of + dimension 1. If the domain of the map is projective space of dimension + greater than 1, a list of matrices EXAMPLES:: - sage: P.<x,y> = ProjectiveSpace(QQ,1) - sage: f = DynamicalSystem_projective([4608*x^10 - 2910096*x^9*y + 325988068*x^8*y^2 + 31825198932*x^7*y^3 - 4139806626613*x^6*y^4\ - - 44439736715486*x^5*y^5 + 2317935971590902*x^4*y^6 - 15344764859590852*x^3*y^7 + 2561851642765275*x^2*y^8\ - + 113578270285012470*x*y^9 - 150049940203963800*y^10, 4608*y^10]) - sage: sorted(f.multiplier_spectra(1)) - [-119820502365680843999, - -7198147681176255644585/256, - -3086380435599991/9, - -3323781962860268721722583135/35184372088832, - -4290991994944936653/2097152, - 0, - 529278480109921/256, - 1061953534167447403/19683, - 848446157556848459363/19683, - 82911372672808161930567/8192, - 3553497751559301575157261317/8192] + sage: P.<x,y> = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([x^2 - 3/4*y^2, y^2]) + sage: sorted(f.multiplier_spectra(2, type='point')) + [0, 1, 1, 1, 9] + sage: sorted(f.multiplier_spectra(2, type='cycle')) + [0, 1, 1, 9] + + :: + + sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) + sage: f = DynamicalSystem_projective([x^2, z^2, y^2]) + sage: f.multiplier_spectra(1) + [ + [ 2 1 - 1.732050807568878?*I] + [ 0 -2], + [ 2 1 + 1.732050807568878?*I] [ 0 0] [ 0 0] + [ 0 -2], [ 0 -2], [ 0 -2], + [ 0 0] [0 0] [ 2 -2] + [ 0 -2], [0 0], [ 0 -2] + ] + + :: + + sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) + sage: f = DynamicalSystem_projective([x^2, z^2, y^2]) + sage: f.multiplier_spectra(2, formal=True) + [ + [4 0] [4 0] [4 0] [4 0] [4 0] [4 0] [4 0] [4 0] [0 0] [0 0] + [0 4], [0 0], [0 0], [0 4], [0 4], [0 0], [0 0], [0 4], [0 0], [0 0], + [4 0] [4 0] [4 0] [4 0] + [0 4], [0 4], [0 0], [0 0] + ] :: sage: set_verbose(None) sage: z = QQ['z'].0 sage: K.<w> = NumberField(z^4 - 4*z^2 + 1,'z') - sage: P.<x,y> = ProjectiveSpace(K,1) + sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x^2 - w/4*y^2, y^2]) sage: sorted(f.multiplier_spectra(2, formal=False, type='cycle')) [0, @@ -4693,16 +5025,26 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur :: - sage: P.<x,y> = ProjectiveSpace(QQ,1) - sage: f = DynamicalSystem_projective([x^2 - 3/4*y^2, y^2]) - sage: sorted(f.multiplier_spectra(2, formal=False, type='cycle')) - [0, 1, 1, 9] - sage: sorted(f.multiplier_spectra(2, formal=False, type='point')) - [0, 1, 1, 1, 9] + sage: P.<x,y> = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([4608*x^10 - 2910096*x^9*y + 325988068*x^8*y^2 + 31825198932*x^7*y^3 - 4139806626613*x^6*y^4\ + - 44439736715486*x^5*y^5 + 2317935971590902*x^4*y^6 - 15344764859590852*x^3*y^7 + 2561851642765275*x^2*y^8\ + + 113578270285012470*x*y^9 - 150049940203963800*y^10, 4608*y^10]) + sage: sorted(f.multiplier_spectra(1)) + [-119820502365680843999, + -7198147681176255644585/256, + -3086380435599991/9, + -3323781962860268721722583135/35184372088832, + -4290991994944936653/2097152, + 0, + 529278480109921/256, + 1061953534167447403/19683, + 848446157556848459363/19683, + 82911372672808161930567/8192, + 3553497751559301575157261317/8192] :: - sage: P.<x,y> = ProjectiveSpace(QQ,1) + sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 - 7/4*y^2, y^2]) sage: f.multiplier_spectra(3, formal=True, type='cycle') [1, 1] @@ -4711,7 +5053,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur :: - sage: P.<x,y> = ProjectiveSpace(QQ,1) + sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^4 + 3*y^4, 4*x^2*y^2]) sage: f.multiplier_spectra(1, use_algebraic_closure=False) [0, @@ -4728,7 +5070,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur :: - sage: P.<x,y> = ProjectiveSpace(GF(5),1) + sage: P.<x,y> = ProjectiveSpace(GF(5), 1) sage: f = DynamicalSystem_projective([x^4 + 2*y^4, 4*x^2*y^2]) sage: f.multiplier_spectra(1, use_algebraic_closure=False) [0, 3*a + 3, 2*a + 1, 1, 1] @@ -4737,7 +5079,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur :: - sage: P.<x,y> = ProjectiveSpace(QQbar,1) + sage: P.<x,y> = ProjectiveSpace(QQbar, 1) sage: f = DynamicalSystem_projective([x^5 + 3*y^5, 4*x^3*y^2]) sage: f.multiplier_spectra(1) [0, @@ -4750,25 +5092,73 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur :: sage: K = GF(3).algebraic_closure() - sage: P.<x,y> = ProjectiveSpace(K,1) + sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x^5 + 2*y^5, 4*x^3*y^2]) sage: f.multiplier_spectra(1) [0, z3 + 2, z3 + 1, z3, 1, 1] TESTS:: - sage: P.<x,y> = ProjectiveSpace(QQ,1) + sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: f.multiplier_spectra(1) [1, 1, 1] + :: + + sage: K = GF(3).algebraic_closure() + sage: P.<x,y,z> = ProjectiveSpace(K, 2) + sage: f = DynamicalSystem_projective([x^2 + 2*y^2, 4*x*y, z^2]) + sage: f.multiplier_spectra(1) + [ + [0 0] [1 0] [1 0] [1 0] [2 0] [2 0] [2 0] + [0 0], [0 0], [0 0], [0 0], [0 1], [0 1], [0 1] + ] + :: sage: F.<a> = GF(7) - sage: P.<x,y>=ProjectiveSpace(F,1) + sage: P.<x,y>=ProjectiveSpace(F, 1) sage: f = DynamicalSystem_projective([x^2 + y^2, y^2]) sage: sorted(f.multiplier_spectra(1)) [0, 3, 6] + + :: + + sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) + sage: f = DynamicalSystem_projective([x^2, z^2, y^2]) + sage: g = f.change_ring(QQbar) + sage: f.multiplier_spectra(1) == g.multiplier_spectra(1) + True + + :: + + sage: K.<w> = QuadraticField(5) + sage: P.<x,y,z> = ProjectiveSpace(K, 2) + sage: f = DynamicalSystem_projective([x^2 + w*x*y + y^2, y^2, z^2]) + sage: f.multiplier_spectra(1) + [ + [1.000000000000000? - 1.572302755514847?*I 0] + [1.000000000000000? - 1.572302755514847?*I 0.618033988749895? - 1.757887921270715?*I] + [1.000000000000000? + 1.572302755514847?*I 0] + [1.000000000000000? + 1.572302755514847?*I 0.618033988749895? + 1.757887921270715?*I] + [ 0 0], + [ 0 2], + [ 0 0], + [ 0 2], + [0 0] [0 0] [ 2 2.236067977499790?] + [0 0], [0 0], [ 0 0] + ] + + :: + + sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) + sage: f = DynamicalSystem_projective([x^2, z^2, y^2]) + sage: f.multiplier_spectra(1, use_algebraic_closure=False) + Traceback (most recent call last): + ... + ValueError: failed to compute the full multiplier spectra. Try use_algebraic_closure=True + or extend the base ring of this dynamical system """ PS = self.domain() n = Integer(n) @@ -4777,43 +5167,138 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur raise ValueError("period must be a positive integer") if not is_ProjectiveSpace(PS): raise NotImplementedError("not implemented for subschemes") - if (PS.dimension_relative() > 1): - raise NotImplementedError("only implemented for dimension 1") - K = FractionField(self.codomain().base_ring()) - if use_algebraic_closure: - Kbar = K.algebraic_closure() - if Kbar.has_coerce_map_from(K): - f = self.change_ring(Kbar) + if PS.dimension_relative() > 1: + K = self.domain().base_ring() + + # if we are already using an algebraic closure, we move the + # map into a finite extension and set use_algebraic_closure to True + # in order to get a scheme defined over a finite extension + if K is QQbar or isinstance(K, AlgebraicClosureFiniteField_generic): + f = self.reduce_base_field() + K = f.base_ring() + use_algebraic_closure = True else: - embeds = K.embeddings(Kbar) - if embeds: - f = self.change_ring(embeds[0]) + f = self + + # in order to calculate multiplicity, we need to have a scheme defined + # over a finite extension, not an algebraic closure + X = f.periodic_points(n, minimal=False, formal=formal, return_scheme=True) + if use_algebraic_closure: + number_field = False + finite_field = False + if K in NumberFields(): + number_field = True + if K in FiniteFields(): + finite_field = True + if not (number_field or finite_field): + raise NotImplementedError('Only implemented for number fields, QQbar, finite fields, and algebraic closures of finite fields') + Kbar = K.algebraic_closure() + if Kbar.has_coerce_map_from(K): + f = f.change_ring(Kbar) + rat_points = X.rational_points(F=Kbar) else: - raise ValueError("no embeddings of base field to algebraic closure") + embeds = K.embeddings(Kbar) + if embeds: + X2 = X.change_ring(embeds[0]) + rat_points = X2.rational_points() + f = self.change_ring(embeds[0]) + else: + raise ValueError("no embeddings of base field to algebraic closure") + else: + rat_points = X.rational_points() + f = self + PS = f.domain() + points = [] + for point in rat_points: + if use_algebraic_closure: + if number_field: + # in order to calculate multiplicity, the point must be defined over a finite extension + K2, pnt_lst, _ = number_field_elements_from_algebraics(list(point)) + # we coerce if we can + if K.has_coerce_map_from(K2): + for i in range(X.multiplicity(pnt_lst)): + points.append(PS(point)) + elif K2.has_coerce_map_from(K): + X_k = X.change_ring(K2) + for i in range(X_k.multiplicity(pnt_lst)): + points.append(PS(point)) + # otherwise, we need to calculate a composite field + else: + _, K_embed, K2_embed, _ = K.composite_fields(K2, both_maps=True)[0] + X_k = X.change_ring(K_embed) + pnt_lst = [K2_embed(pnt) for pnt in pnt_lst] + new_point = X_k.ambient_space()(pnt_lst) + for i in range(X_k.multiplicity(new_point)): + points.append(PS(point)) + else: + # we find a finite extension which the current point + # and X coerce into + final_degree = K.degree() + new_point = [] + for num in list(point): + ff_num = num.as_finite_field_element()[1] + new_point.append(ff_num) + degree = ff_num.parent().degree() + final_degree = final_degree.lcm(degree) + K_prime = GF(K.characteristic()**final_degree) + X_k = X.change_ring(K_prime) + for i in range(X_k.multiplicity(new_point)): + points.append(PS(point)) + else: + for i in range(X.multiplicity(point)): + points.append(point) + if not use_algebraic_closure: + if check: + # we check if we computed the full multiplier spectra + d = self.degree() + N = self.domain().ambient_space().dimension_relative() + if not formal: + expected_number = sum(d**(n*i) for i in range(N+1)) + else: + expected_number = 0 + for D in n.divisors(): + u = moebius(n/D) + inner_sum = sum(d**(D*j) for j in range(N+1)) + expected_number += u*inner_sum + if len(points) != expected_number: + raise ValueError('failed to compute the full multiplier spectra. Try use_algebraic_closure=True' + + ' or extend the base ring of this dynamical system') else: - embedding = self.field_of_definition_periodic(n, formal=formal, return_embedding=True)[1] - f = self.change_ring(embedding) + K = FractionField(self.codomain().base_ring()) + if use_algebraic_closure: + Kbar = K.algebraic_closure() + if Kbar.has_coerce_map_from(K): + f = self.change_ring(Kbar) + else: + embeds = K.embeddings(Kbar) + if embeds: + f = self.change_ring(embeds[0]) + else: + raise ValueError("no embeddings of base field to algebraic closure") + else: + embedding = self.field_of_definition_periodic(n, formal=formal, return_embedding=True)[1] + f = self.change_ring(embedding) - PS = f.domain() - if not formal: - G = f.nth_iterate_map(n) - F = G[0]*PS.gens()[1] - G[1]*PS.gens()[0] - else: - # periodic points of formal period n are the roots of the nth dynatomic polynomial - F = f.dynatomic_polynomial(n) + PS = f.domain() + if not formal: + G = f.nth_iterate_map(n) + F = G[0]*PS.gens()[1] - G[1]*PS.gens()[0] + else: + # periodic points of formal period n are the roots of the nth dynatomic polynomial + F = f.dynatomic_polynomial(n) - other_roots = F.parent()(F([(f.domain().gens()[0]),1])).univariate_polynomial().roots(ring=f.base_ring()) + other_roots = F.parent()(F([(f.domain().gens()[0]),1])).univariate_polynomial().roots(ring=f.base_ring()) - points = [] + points = [] - minfty = min(ex[1] for ex in F.exponents()) # include the point at infinity with the right multiplicity - for i in range(minfty): - points.append(PS([1,0])) + minfty = min(ex[1] for ex in F.exponents()) # include the point at infinity with the right multiplicity + for i in range(minfty): + points.append(PS([1,0])) - for R in other_roots: - for i in range(R[1]): - points.append(PS([R[0],1])) # include copies of higher multiplicity roots + for R in other_roots: + for i in range(R[1]): + points.append(PS([R[0],1])) # include copies of higher multiplicity roots if type == 'cycle': # should include one representative point per cycle, included with the right multiplicity @@ -4832,7 +5317,10 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur Q = f(Q) points = newpoints - multipliers = [f.multiplier(pt,n)[0,0] for pt in points] + if PS.dimension_relative() > 1: + multipliers = [f.multiplier(pt,n) for pt in points] + else: + multipliers = [f.multiplier(pt,n)[0,0] for pt in points] return multipliers @@ -4901,7 +5389,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point', - ``n`` periodic points are repeated, multipliers are all distinct -- to deal with this case, we deform the map by a formal parameter `k`. The deformation - seperates the ``n`` periodic points, making them distinct, and we can recover + separates the ``n`` periodic points, making them distinct, and we can recover the ``n`` periodic points of the original map by specializing `k` to 0. This corresponds to ``deform=True``. @@ -4952,7 +5440,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point', - ``check`` -- (default: ``True``) boolean; when ``True`` the degree of the sigma polynomial is checked against the expected degree. This is - done as the sigma polynomial may drop degree if multiplicites of periodic + done as the sigma polynomial may drop degree if multiplicities of periodic points or multipliers are not correctly accounted for using ``chow`` or ``deform``. @@ -5236,8 +5724,8 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point', # create polynomial ring for result R2 = PolynomialRing(F, var[:N] + var[-2:]) psi = R2.hom(N*[0]+list(newR.gens()), newR) - # create substition to set extra variables to 0 - R_zero = {R.gen(N):1} + # create substitution to set extra variables to 0 + R_zero = {R.gen(N): 1} for j in range(N+1, 2*N+1): R_zero[R.gen(j)] = 0 t = var.pop() @@ -5251,7 +5739,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point', w = var.pop() sigma_polynomial = 1 # go through each affine patch to avoid repeating periodic points - # setting the visited coordiantes to 0 as we go + # setting the visited coordinates to 0 as we go for j in range(N,-1,-1): Xa = X.affine_patch(j) fa = Fn.dehomogenize(j) @@ -5308,7 +5796,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point', 'try setting chow=True and/or deform=True') if return_polynomial: return sigma_polynomial - # if we are returing a numerical list, read off the coefficients + # if we are returning a numerical list, read off the coefficients # in order of degree adjusting sign appropriately sigmas = [] sigma_dictionary = dict([list(reversed(i)) for i in list(sigma_polynomial)]) @@ -5679,7 +6167,7 @@ def reduced_form(self, **kwds): raise NotImplementedError("smallest coeff only over ZZ or QQ") check_min = kwds.get('check_minimal', True) from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import smallest_dynamical - sm_f, m = smallest_dynamical(self, dynatomic=dynatomic, start_n=start_n,\ + sm_f, m = smallest_dynamical(self, dynatomic=dynatomic, start_n=start_n, prec=prec, emb=emb, algorithm=algorithm, check_minimal=check_min) else: #reduce via covariant @@ -6364,7 +6852,7 @@ def all_periodic_points(self, **kwds): if alg != 'lifting': for i in periods[:]: - if (alg == 'dynatomic') or ((N == 1) \ + if (alg == 'dynatomic') or ((N == 1) and i <= pd_bounds[0] and DS.degree() <= pd_bounds[1]): periodic.update(DS.periodic_points(i)) periods.remove(i) @@ -6833,7 +7321,7 @@ def conjugating_set(self, other, R=None, num_cpus=2): by taking the fixed points of one map and mapping them to permutations of the fixed points of the other map. As conjugacy preserves the multipliers as a set, fixed points - are only maped to fixed points with the same multiplier. + are only mapped to fixed points with the same multiplier. If there are not enough fixed points the function compares the mapping between rational preimages of fixed points and the rational preimages of the preimages of @@ -7055,7 +7543,7 @@ def conjugating_set(self, other, R=None, num_cpus=2): #else is_similar went to algebraic closure if base in NumberFields(): from sage.rings.qqbar import number_field_elements_from_algebraics - K,mK,phi = number_field_elements_from_algebraics([u for t in list(m) for u in t],\ + K,mK,phi = number_field_elements_from_algebraics([u for t in list(m) for u in t], minimal=True) if K == base: return [matrix(K, M, M, mK)] @@ -7736,15 +8224,15 @@ def potential_good_reduction(self, prime, return_conjugation=False): hom = old_parent.hom([new_parent.gens()[0]]) L = field_of_definition_periodic if hom(K.defining_polynomial()) != L.defining_polynomial(): - raise ValueError('prime ideal of %s ' % K + \ - 'but field of definition of fixed points is %s. ' % L + \ + raise ValueError('prime ideal of %s ' % K + + 'but field of definition of fixed points is %s. ' % L + 'see documentation for examples') embedding = K.embeddings(field_of_definition_periodic)[0] prime = embedding(prime) else: if field_of_definition_periodic is not QQ: - raise ValueError('field of definition of fixed ' + \ - 'points is %s but prime is in QQ. ' %field_of_definition_periodic) + raise ValueError('field of definition of fixed ' + + 'points is %s but prime is in QQ. ' % field_of_definition_periodic) system = self.change_ring(field_of_definition_periodic) fixed_points = system.periodic_points(1) @@ -7825,9 +8313,9 @@ def reduce_base_field(self): sage: f = DynamicalSystem_projective([x^2 + QQbar(sqrt(3))*y^2, y^2, QQbar(sqrt(2))*z^2]) sage: f.reduce_base_field() Dynamical System of Projective Space of dimension 2 over Number Field in a with - defining polynomial y^4 - 4*y^2 + 1 with a = 1.931851652578137? + defining polynomial y^4 - 4*y^2 + 1 with a = -0.5176380902050415? Defn: Defined on coordinates by sending (x : y : z) to - (x^2 + (a^2 - 2)*y^2 : y^2 : (a^3 - 3*a)*z^2) + (x^2 + (-a^2 + 2)*y^2 : y^2 : (a^3 - 3*a)*z^2) :: diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 8f5f1f6a690..442bb514561 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -27,13 +27,12 @@ from sage.calculus.functions import jacobian from sage.categories.fields import Fields +from sage.categories.commutative_rings import CommutativeRings from sage.categories.number_fields import NumberFields from sage.misc.functional import sqrt from sage.misc.cachefunc import cached_method from sage.misc.mrange import xmrange -from sage.rings.all import CommutativeRing from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.fraction_field import FractionField from sage.rings.integer_ring import ZZ from sage.rings.number_field.order import is_NumberFieldOrder @@ -48,6 +47,7 @@ _NumberFields = NumberFields() _Fields = Fields() +_CommutativeRings = CommutativeRings() def WehlerK3Surface(polys): @@ -75,14 +75,12 @@ def WehlerK3Surface(polys): R = polys[0].parent().base_ring() if R in _Fields: - if is_FiniteField(R): + if R in _Fields.Finite(): return WehlerK3Surface_finite_field(polys) - else: - return WehlerK3Surface_field(polys) - elif isinstance(R, CommutativeRing): + return WehlerK3Surface_field(polys) + if R in _CommutativeRings: return WehlerK3Surface_ring(polys) - else: - raise TypeError("R (= %s) must be a commutative ring" % R) + raise TypeError("R (= %s) must be a commutative ring" % R) def random_WehlerK3Surface(PP): @@ -115,7 +113,6 @@ def random_WehlerK3Surface(PP): class WehlerK3Surface_ring(AlgebraicScheme_subscheme_product_projective): r""" - A K3 surface in `\mathbb{P}^2 \times \mathbb{P}^2` defined as the intersection of a bilinear and biquadratic form. [Weh1998]_ @@ -131,7 +128,7 @@ class WehlerK3Surface_ring(AlgebraicScheme_subscheme_product_projective): x*y*v^2 + z^2*u*w """ def __init__(self, polys): - if not isinstance(polys, (list,tuple)): + if not isinstance(polys, (list, tuple)): raise TypeError("polys must be a list or tuple of polynomials") R = polys[0].parent() vars = R.variable_names() @@ -809,7 +806,7 @@ def degenerate_fibers(self): vars = list(PSX.gens()) K = FractionField(PSX.base_ring()) R0 = PolynomialRing(K, 3, vars) - I = R.ideal(self.Gpoly(1, 0), self.Gpoly(1, 1), self.Gpoly(1, 2), self.Hpoly(1, 0,1 ), \ + I = R.ideal(self.Gpoly(1, 0), self.Gpoly(1, 1), self.Gpoly(1, 2), self.Hpoly(1, 0,1 ), self.Hpoly(1, 0, 2), self.Hpoly(1, 1, 2)) phi = R.hom(vars + [0, 0, 0], R0) I = phi(I) @@ -840,7 +837,7 @@ def degenerate_fibers(self): vars = list(PSY.gens()) K = FractionField(PSY.base_ring()) R0 = PolynomialRing(K, 3, vars) - I = R.ideal(self.Gpoly(0, 0), self.Gpoly(0, 1), self.Gpoly(0, 2), self.Hpoly(0, 0, 1), \ + I = R.ideal(self.Gpoly(0, 0), self.Gpoly(0, 1), self.Gpoly(0, 2), self.Hpoly(0, 0, 1), self.Hpoly(0, 0, 2), self.Hpoly(0, 1, 2)) phi = PP.coordinate_ring().hom([0, 0, 0] + vars, R0) I = phi(I) @@ -1110,16 +1107,16 @@ def sigmaX(self, P, **kwds): raise TypeError("%s fails to convert into the map's domain %s, but a `pushforward` method is not properly implemented"%(P, self)) pt = list(P[0]) + [0, 0, 0] if P[1][0] != 0: - [a,b,c] = [P[1][0]*self.Gpoly(1, 0)(*pt),\ - -1*P[1][0]*self.Hpoly(1, 0, 1)(*pt) - P[1][1]*self.Gpoly(1, 0)(*pt),\ + [a,b,c] = [P[1][0]*self.Gpoly(1, 0)(*pt), + -1*P[1][0]*self.Hpoly(1, 0, 1)(*pt) - P[1][1]*self.Gpoly(1, 0)(*pt), -P[1][0]*self.Hpoly(1, 0, 2)(*pt) - P[1][2]*self.Gpoly(1, 0)(*pt)] elif P[1][1] != 0: - [a,b,c] = [-1*P[1][1]*self.Hpoly(1, 0, 1)(*pt)-P[1][0]*self.Gpoly(1, 1)(*pt),\ - P[1][1]*self.Gpoly(1, 1)(*pt),\ + [a,b,c] = [-1*P[1][1]*self.Hpoly(1, 0, 1)(*pt)-P[1][0]*self.Gpoly(1, 1)(*pt), + P[1][1]*self.Gpoly(1, 1)(*pt), -P[1][1]*self.Hpoly(1, 1, 2)(*pt)-P[1][2]*self.Gpoly(1, 1)(*pt)] else: - [a,b,c] = [-1*P[1][2]*self.Hpoly(1, 0, 2)(*pt) - P[1][0]*self.Gpoly(1, 2)(*pt),\ - -P[1][2]*self.Hpoly(1, 1, 2)(*pt) - P[1][1]*self.Gpoly(1, 2)(*pt),\ + [a,b,c] = [-1*P[1][2]*self.Hpoly(1, 0, 2)(*pt) - P[1][0]*self.Gpoly(1, 2)(*pt), + -P[1][2]*self.Hpoly(1, 1, 2)(*pt) - P[1][1]*self.Gpoly(1, 2)(*pt), P[1][2]*self.Gpoly(1, 2)(*pt)] Point = [P[0][0], P[0][1], P[0][2], a, b, c] @@ -1150,13 +1147,13 @@ def sigmaX(self, P, **kwds): t = w1 - t1 phi = R.hom([s1*(t) + s0*P[0][0]/P[0][2], s0*w1, s0, z0, z1, z2], S) - #Blow-up the fiber - T = [phi(self.L),phi(self.Q),\ - phi(self.Gpoly(1, 0)),\ - phi(self.Gpoly(1, 1)),\ - phi(self.Gpoly(1, 2)),\ - -phi(self.Hpoly(1, 0, 1)),\ - -phi(self.Hpoly(1, 0, 2)),\ + # Blow-up the fiber + T = [phi(self.L),phi(self.Q), + phi(self.Gpoly(1, 0)), + phi(self.Gpoly(1, 1)), + phi(self.Gpoly(1, 2)), + -phi(self.Hpoly(1, 0, 1)), + -phi(self.Hpoly(1, 0, 2)), -phi(self.Hpoly(1, 1, 2))] maxexp = [] @@ -1184,11 +1181,11 @@ def sigmaX(self, P, **kwds): #on the fiber RR = PolynomialRing(BR, 5,'s0, s1, z0, z1, z2',order = 'lex') s0, s1, z0, z1, z2 = RR.gens() - I = RR.ideal([RR(T[0]), \ - RR(T[1]), \ - RR(T[2]) - P[1][0]*z0, RR(T[3]) - P[1][1]*z1, RR(T[4])-P[1][2]*z2, \ - RR(T[5]) - (P[1][0]*z1 + P[1][1]*z0), \ - RR(T[6]) - (P[1][0]*z2 + P[1][2]*z0), \ + I = RR.ideal([RR(T[0]), + RR(T[1]), + RR(T[2]) - P[1][0]*z0, RR(T[3]) - P[1][1]*z1, RR(T[4])-P[1][2]*z2, + RR(T[5]) - (P[1][0]*z1 + P[1][1]*z0), + RR(T[6]) - (P[1][0]*z2 + P[1][2]*z0), RR(T[7]) - (P[1][1]*z2 + P[1][2]*z1)]) #Find the points @@ -1227,13 +1224,13 @@ def sigmaX(self, P, **kwds): for i in range(2,len(T)): newT[i] = newT[i]/s**e #Create the new ideal - II = SS.ideal([SS(newT[0]), \ - SS(newT[1]), \ - SS(newT[2]) - P[1][0]*z0, \ - SS(newT[3]) - P[1][1]*z1, \ - SS(newT[4]) - P[1][2]*z2, \ - SS(newT[5]) - (P[1][0]*z1 + P[1][1]*z0), \ - SS(newT[6]) - (P[1][0]*z2 + P[1][2]*z0), \ + II = SS.ideal([SS(newT[0]), + SS(newT[1]), + SS(newT[2]) - P[1][0]*z0, + SS(newT[3]) - P[1][1]*z1, + SS(newT[4]) - P[1][2]*z2, + SS(newT[5]) - (P[1][0]*z1 + P[1][1]*z0), + SS(newT[6]) - (P[1][0]*z2 + P[1][2]*z0), SS(newT[7]) - (P[1][1]*z2 + P[1][2]*z1)]) #Find the points @@ -1352,16 +1349,16 @@ def sigmaY(self,P, **kwds): raise TypeError("%s fails to convert into the map's domain %s, but a `pushforward` method is not properly implemented"%(P, self)) pt = [0, 0, 0] + list(P[1]) if P[0][0] != 0: - [a, b, c] = [P[0][0]*self.Gpoly(0, 0)(*pt), \ - -1*P[0][0]*self.Hpoly(0, 0, 1)(*pt) - P[0][1]*self.Gpoly(0, 0)(*pt), \ + [a, b, c] = [P[0][0]*self.Gpoly(0, 0)(*pt), + -1*P[0][0]*self.Hpoly(0, 0, 1)(*pt) - P[0][1]*self.Gpoly(0, 0)(*pt), -P[0][0]*self.Hpoly(0, 0, 2)(*pt) - P[0][2]*self.Gpoly(0, 0)(*pt)] elif P[0][1] != 0: - [a, b, c] = [-1*P[0][1]*self.Hpoly(0, 0, 1)(*pt) - P[0][0]*self.Gpoly(0, 1)(*pt),\ - P[0][1]*self.Gpoly(0, 1)(*pt), \ + [a, b, c] = [-1*P[0][1]*self.Hpoly(0, 0, 1)(*pt) - P[0][0]*self.Gpoly(0, 1)(*pt), + P[0][1]*self.Gpoly(0, 1)(*pt), -P[0][1]*self.Hpoly(0, 1, 2)(*pt) - P[0][2]*self.Gpoly(0, 1)(*pt)] else: - [a, b, c] = [-1*P[0][2]*self.Hpoly(0, 0, 2)(*pt) - P[0][0]*self.Gpoly(0, 2)(*pt), \ - - P[0][2]*self.Hpoly(0, 1, 2)(*pt) - P[0][1]*self.Gpoly(0, 2)(*pt), \ + [a, b, c] = [-1*P[0][2]*self.Hpoly(0, 0, 2)(*pt) - P[0][0]*self.Gpoly(0, 2)(*pt), + - P[0][2]*self.Hpoly(0, 1, 2)(*pt) - P[0][1]*self.Gpoly(0, 2)(*pt), P[0][2]*self.Gpoly(0, 2)(*pt)] Point = [a, b, c, P[1][0], P[1][1], P[1][2]] if [a, b, c] != [0, 0, 0]: @@ -1393,13 +1390,13 @@ def sigmaY(self,P, **kwds): phi = R.hom([z0, z1, z2, s1*(t) + s0*P[1][0]/P[1][2], s0*w1, s0], S) #Blow-up the fiber - T = [phi(self.L),\ - phi(self.Q),\ - phi(self.Gpoly(0, 0)), \ - phi(self.Gpoly(0, 1)), \ - phi(self.Gpoly(0, 2)), \ - -phi(self.Hpoly(0, 0, 1)), \ - -phi(self.Hpoly(0, 0, 2)), \ + T = [phi(self.L), + phi(self.Q), + phi(self.Gpoly(0, 0)), + phi(self.Gpoly(0, 1)), + phi(self.Gpoly(0, 2)), + -phi(self.Hpoly(0, 0, 1)), + -phi(self.Hpoly(0, 0, 2)), -phi(self.Hpoly(0, 1, 2))] maxexp = [] @@ -1424,13 +1421,13 @@ def sigmaY(self,P, **kwds): # on the fiber RR = PolynomialRing(BR, 5, 's0, s1, z0, z1, z2', order='lex') s0, s1, z0, z1, z2 = RR.gens() - I = RR.ideal([RR(T[0]), \ - RR(T[1]), \ - RR(T[2]) - P[0][0]*z0, \ - RR(T[3]) - P[0][1]*z1, \ - RR(T[4]) - P[0][2]*z2, \ - RR(T[5]) - (P[0][0]*z1 + P[0][1]*z0), \ - RR(T[6]) - (P[0][0]*z2 + P[0][2]*z0), \ + I = RR.ideal([RR(T[0]), + RR(T[1]), + RR(T[2]) - P[0][0]*z0, + RR(T[3]) - P[0][1]*z1, + RR(T[4]) - P[0][2]*z2, + RR(T[5]) - (P[0][0]*z1 + P[0][1]*z0), + RR(T[6]) - (P[0][0]*z2 + P[0][2]*z0), RR(T[7]) - (P[0][1]*z2 + P[0][2]*z1)]) #Find the points SS = PolynomialRing(BR, 4, 's, z0, z1, z2', order = 'lex') @@ -1467,14 +1464,14 @@ def sigmaY(self,P, **kwds): for i in range(2,len(T)): newT[i] = newT[i]/s**e #Create the new ideal - II = SS.ideal([SS(newT[0]), \ - SS(newT[1]), \ - SS(newT[2]) - P[0][0]*z0, \ - SS(newT[3]) - P[0][1]*z1, \ - SS(newT[4]) - P[0][2]*z2, \ - SS(newT[5]) - (P[0][0]*z1 + P[0][1]*z0), \ - SS(newT[6]) - (P[0][0]*z2 + P[0][2]*z0), \ - SS(newT[7]) - (P[0][1]*z2 + P[0][2]*z1)]) + II = SS.ideal([SS(newT[0]), + SS(newT[1]), + SS(newT[2]) - P[0][0]*z0, + SS(newT[3]) - P[0][1]*z1, + SS(newT[4]) - P[0][2]*z2, + SS(newT[5]) - (P[0][0]*z1 + P[0][1]*z0), + SS(newT[6]) - (P[0][0]*z2 + P[0][2]*z0), + SS(newT[7]) - (P[0][1]*z2 + P[0][2]*z1)]) # Find the points SSS = PolynomialRing(BR, 3, 'z0, z1, z2', order='lex') z0, z1, z2 = SSS.gens() @@ -1635,11 +1632,11 @@ def lambda_plus(self, P, v, N, m, n, prec = 100): K = Qp(v, prec) PK = P.change_ring(K) W = self.change_ring(K) - Rx = W.ambient_space().coordinate_ring().hom(\ - list(W.ambient_space()[0].coordinate_ring().gens())+[0, 0, 0], \ + Rx = W.ambient_space().coordinate_ring().hom( + list(W.ambient_space()[0].coordinate_ring().gens()) + [0, 0, 0], W.ambient_space()[0].coordinate_ring()) - Ry = W.ambient_space().coordinate_ring().hom(\ - [0, 0, 0] + list(W.ambient_space()[1].coordinate_ring().gens()), \ + Ry = W.ambient_space().coordinate_ring().hom( + [0, 0, 0] + list(W.ambient_space()[1].coordinate_ring().gens()), W.ambient_space()[1].coordinate_ring()) beta = R(2 + sqrt(3)) L = [x.abs() for x in list(PK[0])] @@ -1732,10 +1729,10 @@ def lambda_minus(self, P, v, N, m, n, prec=100): K = Qp(v, prec) PK = P.change_ring(K) W = self.change_ring(K) - Rx = W.ambient_space().coordinate_ring().hom(list(W.ambient_space()[0].coordinate_ring().gens())\ - +[0, 0, 0],W.ambient_space()[0].coordinate_ring()) - Ry = W.ambient_space().coordinate_ring().hom([0, 0, 0] + \ - list(W.ambient_space()[1].coordinate_ring().gens()), \ + Rx = W.ambient_space().coordinate_ring().hom(list(W.ambient_space()[0].coordinate_ring().gens()) + + [0, 0, 0], W.ambient_space()[0].coordinate_ring()) + Ry = W.ambient_space().coordinate_ring().hom([0, 0, 0] + + list(W.ambient_space()[1].coordinate_ring().gens()), W.ambient_space()[1].coordinate_ring()) beta = R(2 + sqrt(3)) L = [x.abs() for x in list(PK[0])] @@ -2295,7 +2292,6 @@ def orbit_phi(self, P, N, **kwds): : 3992260691327218828582255586014718568398539828275296031491644987908/18550615454277582153932951051931712107449915856862264913424670784695 : 1 , -117756062505511/54767410965117 : -23134047983794359/37466994368025041 : 1)] """ - if not isinstance(N, (list, tuple)): N = [0, N] try: diff --git a/src/sage/dynamics/cellular_automata/catalog.py b/src/sage/dynamics/cellular_automata/catalog.py index 480f073bf7a..44c28f07cb5 100644 --- a/src/sage/dynamics/cellular_automata/catalog.py +++ b/src/sage/dynamics/cellular_automata/catalog.py @@ -6,7 +6,7 @@ this object is an easy way to discover and quickly create the cellular automata that are available (as listed here). -Let ``<tab>`` indicate pressing the tab key. So begin by typing +Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing ``cellular_automata.<tab>`` to the see the currently implemented named cellular automata. diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia.py b/src/sage/dynamics/complex_dynamics/mandel_julia.py index 224a484c7bb..765cb9559d6 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia.py +++ b/src/sage/dynamics/complex_dynamics/mandel_julia.py @@ -130,7 +130,7 @@ def mandelbrot_plot(f=None, **kwds): ``interact`` to ``True``. (This is only implemented for ``z^2 + c``):: sage: mandelbrot_plot(interact=True) - interactive(children=(FloatSlider(value=0.0, description='Real center', max=1.0, min=-1.0, step=1e-05), + ...interactive(children=(FloatSlider(value=0.0, description='Real center', max=1.0, min=-1.0, step=1e-05), FloatSlider(value=0.0, description='Imag center', max=1.0, min=-1.0, step=1e-05), FloatSlider(value=4.0, description='Width', max=4.0, min=1e-05, step=1e-05), IntSlider(value=500, description='Iterations', max=1000), @@ -144,7 +144,7 @@ def mandelbrot_plot(f=None, **kwds): sage: mandelbrot_plot(interact=True, x_center=-0.75, y_center=0.25, ....: image_width=1/2, number_of_colors=75) - interactive(children=(FloatSlider(value=-0.75, description='Real center', max=1.0, min=-1.0, step=1e-05), + ...interactive(children=(FloatSlider(value=-0.75, description='Real center', max=1.0, min=-1.0, step=1e-05), FloatSlider(value=0.25, description='Imag center', max=1.0, min=-1.0, step=1e-05), FloatSlider(value=0.5, description='Width', max=4.0, min=1e-05, step=1e-05), IntSlider(value=500, description='Iterations', max=1000), @@ -283,10 +283,11 @@ def mandelbrot_plot(f=None, **kwds): max_iteration = 50 # Mandelbrot of General Polynomial Map - return polynomial_mandelbrot(f, parameter, x_center, y_center, \ - image_width, max_iteration, pixel_count, level_sep, \ + return polynomial_mandelbrot(f, parameter, x_center, y_center, + image_width, max_iteration, pixel_count, level_sep, number_of_colors, base_color) + def external_ray(theta, **kwds): r""" Draws the external ray(s) of a given angle (or list of angles) @@ -600,14 +601,14 @@ def julia_plot(f=None, **kwds): the form ``f = z^2 + c``):: sage: julia_plot(interact=True) - interactive(children=(FloatSlider(value=-1.0, description='Real c'... + ...interactive(children=(FloatSlider(value=-1.0, description='Real c'... :: sage: R.<z> = CC[] sage: f = z^2 + 1/2 sage: julia_plot(f,interact=True) - interactive(children=(FloatSlider(value=0.5, description='Real c'... + ...interactive(children=(FloatSlider(value=0.5, description='Real c'... To return the Julia set of a random `c` value with (formal) cycle structure `(2,3)`, set ``period = [2,3]``:: diff --git a/src/sage/dynamics/finite_dynamical_system_catalog.py b/src/sage/dynamics/finite_dynamical_system_catalog.py index 3ba09eb9948..74217178a29 100755 --- a/src/sage/dynamics/finite_dynamical_system_catalog.py +++ b/src/sage/dynamics/finite_dynamical_system_catalog.py @@ -5,7 +5,7 @@ dynamical systems. These are accessible through :mod:`sage.dynamics.finite_dynamical_system_catalog. <sage.dynamics.finite_dynamical_system_catalog>` -or just through `finite_dynamical_systems.` +or just through ``finite_dynamical_systems.`` (type either of these in Sage and hit ``tab`` for a list). AUTHORS: diff --git a/src/sage/env.py b/src/sage/env.py index e71e625308b..d4708e61f16 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -11,7 +11,8 @@ sage: env = {k:v for (k,v) in os.environ.items() if not k.startswith("SAGE_")} sage: from subprocess import check_output - sage: cmd = "from sage.all import SAGE_ROOT, SAGE_LOCAL; print((SAGE_ROOT, SAGE_LOCAL))" + sage: environment = "sage.all" + sage: cmd = f"from {environment} import SAGE_ROOT, SAGE_LOCAL; print((SAGE_ROOT, SAGE_LOCAL))" sage: out = check_output([sys.executable, "-c", cmd], env=env).decode().strip() # long time sage: out == repr((SAGE_ROOT, SAGE_LOCAL)) # long time True diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 9cd023601e8..d21bddd5deb 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -983,7 +983,7 @@ cdef class Expression: # flag.) cdef Expression es - if isinstance(o, (int, long, Integer)): + if isinstance(o, (int, Integer)): es = s return ExpressionIPow(es._etb, s, o) else: diff --git a/src/sage/ext_data/pari/simon/ell.gp b/src/sage/ext_data/pari/simon/ell.gp index 4e9870eb13e..663df10a7c3 100644 --- a/src/sage/ext_data/pari/simon/ell.gp +++ b/src/sage/ext_data/pari/simon/ell.gp @@ -1503,7 +1503,7 @@ if( DEBUGLEVEL_ell >= 4, print(" end of bnfredquartique")); \\ si bigflag !=0 alors on applique bnfredquartique. \\ si flag3 ==1 alors on utilise bnfqfsolve2 (equation aux normes) pour resoudre Legendre \\ aut est une liste d'automorphismes connus de bnf -\\ (ca peut aider a factoriser certains discriminiants). +\\ (ca peut aider a factoriser certains discriminants). \\ ell est de la forme y^2=x^3+A*x^2+B*x+C \\ ie ell=[0,A,0,B,C], avec A,B et C entiers. \\ diff --git a/src/sage/ext_data/pari/simon/ellQ.gp b/src/sage/ext_data/pari/simon/ellQ.gp index 420af8f6a29..65e8386779b 100644 --- a/src/sage/ext_data/pari/simon/ellQ.gp +++ b/src/sage/ext_data/pari/simon/ellQ.gp @@ -40,7 +40,7 @@ gp > \r ellcommon.gp gp > \r ellQ.gp - The main function is ellrank(), which takes as an argument + The main function is ellQ_ellrank(), which takes as an argument any elliptic curve in the form [a1,a2,a3,a4,a6] the result is a vector [r,s,v], where r is a lower bound for the rank, @@ -50,7 +50,7 @@ Example: gp > ell = [1,2,3,4,5]; - gp > ellrank(ell) + gp > ellQ_ellrank(ell) %1 = [1, 1, [[1,2]] In this example, the rank is exactly 1, and [1,2] has infinite order. @@ -92,7 +92,7 @@ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Explications succintes : - La fonction ellrank() accepte toutes les courbes sous la forme + La fonction ellQ_ellrank() accepte toutes les courbes sous la forme [a1,a2,a3,a4,a6] Les coefficients peuvent etre entiers ou non. L'algorithme utilise est celui de la 2-descente. @@ -100,7 +100,7 @@ Il suffit de taper : gp > ell = [a1,a2,a3,a4,a6]; - gp > ellrank(ell) + gp > ellQ_ellrank(ell) Retourne un vecteur [r,s,v] ou r est le rang probable (c'est toujours une minoration du rang), @@ -110,7 +110,7 @@ Exemple : gp > ell = [1,2,3,4,5]; - gp > ellrank(ell) + gp > ellQ_ellrank(ell) %1 = [1, 1, [[1,2]] Ici, le rang est exactement 1, et le point [1,2] est d'ordre infini. @@ -1571,12 +1571,12 @@ if( DEBUGLEVEL_ell >= 4, print(" end of ell2descent_gen")); print("rank(E/Q) >= ",m1) ); } -{ellrank(ell,help=[]) = +{ellQ_ellrank(ell,help=[]) = \\ Algorithm of 2-descent on the elliptic curve ell. \\ help is a list of known points on ell. my(urst,urst1,den,eqell,tors2,bnf,rang,time1); -if( DEBUGLEVEL_ell >= 3, print(" starting ellrank")); +if( DEBUGLEVEL_ell >= 3, print(" starting ellQ_ellrank")); if( #ell < 13, ell = ellinit(ell)); \\ kill the coefficients a1 and a3 @@ -1630,7 +1630,7 @@ if( DEBUGLEVEL_ell >= 1, print(" Elliptic curve: Y^2 = ",eqell)); )); rang[3] = ellchangepoint(rang[3],ellinverturst(urst)); -if( DEBUGLEVEL_ell >= 3, print(" end of ellrank")); +if( DEBUGLEVEL_ell >= 3, print(" end of ellQ_ellrank")); return(rang); } @@ -2106,13 +2106,13 @@ if( DEBUGLEVEL_ell >= 3, print(" end of ell2descent_viaisog")); { \\ functions for elliptic curves addhelp(ell2descent_complete, - "ell2descent_complete(e1,e2,e3): Performs a complete 2-descent on the elliptic curve y^2 = (x-e1)*(x-e2)*(x-e3). See ?ellrank for the format of the output."); + "ell2descent_complete(e1,e2,e3): Performs a complete 2-descent on the elliptic curve y^2 = (x-e1)*(x-e2)*(x-e3). See ?ellQ_ellrank for the format of the output."); addhelp(ell2descent_gen, - "ell2descent_gen((E,bnf,k=1,help=[]): E is a vector of the form [0,A,0,B,C], (or the result of ellinit of such a vector) A,B,C integers such that x^3+A*x^2+B*x+C; bnf is the corresponding bnfinit(,1); Performs 2-descent on the elliptic curve Ek: k*y^2=x^3+A*x^2+B*x+C. See ?ellrank for the format of the output."); + "ell2descent_gen((E,bnf,k=1,help=[]): E is a vector of the form [0,A,0,B,C], (or the result of ellinit of such a vector) A,B,C integers such that x^3+A*x^2+B*x+C; bnf is the corresponding bnfinit(,1); Performs 2-descent on the elliptic curve Ek: k*y^2=x^3+A*x^2+B*x+C. See ?ellQ_ellrank for the format of the output."); addhelp(ell2descent_viaisog, - "ell2descent_viaisog(E,help=[]): E is an elliptic curve of the form [0,a,0,b,0], with a, b integers. Performs a 2-descent via isogeny on E. See ?ellrank for the format of the output."); - addhelp(ellrank, - "ellrank(E,help=[]): E is any elliptic curve defined over Q. Returns a vector [r,s,v], where r is a lower bound for the rank of E, s is the rank of its 2-Selmer group and v is a list of independant points in E(Q)/2E(Q). If help is a vector of nontrivial points on E, the result might be faster. This function might be used in conjunction with elltors2(E). See also ?default_ellQ"); + "ell2descent_viaisog(E,help=[]): E is an elliptic curve of the form [0,a,0,b,0], with a, b integers. Performs a 2-descent via isogeny on E. See ?ellQ_ellrank for the format of the output."); + addhelp(ellQ_ellrank, + "ellQ_ellrank(E,help=[]): E is any elliptic curve defined over Q. Returns a vector [r,s,v], where r is a lower bound for the rank of E, s is the rank of its 2-Selmer group and v is a list of independant points in E(Q)/2E(Q). If help is a vector of nontrivial points on E, the result might be faster. This function might be used in conjunction with elltors2(E). See also ?default_ellQ"); addhelp(ellhalf, "ellhalf(E,P): returns the vector of all points Q on the elliptic curve E such that 2Q = P"); addhelp(ellredgen, @@ -2143,7 +2143,7 @@ if( DEBUGLEVEL_ell >= 3, print(" end of ell2descent_viaisog")); \\ others addhelp(default_ellQ, - "default_ellQ(DEBUGLEVEL_ell, LIM1, LIM3, LIMTRIV, ELLREDGENFLAG, COMPLETE, MAXPROB, LIMBIGPRIME): set the value of the global variables used for ellrank() and other related functions. DEBUGLEVEL_ell: 0-5: choose the quantity of information printed during the computation (default=0: print nothing); LIM1 (resp LIM3): search limit for easy (resp hard) points on quartics; LIMTRIV: search limit for trivial points on elliptic curves; ELLREDGENFLAG: if != 0, try to reduce the generators at the end; COMPLETE: if != 0 and full 2-torsion, use complete 2-descent, otherwise via 2-isogeny; MAXPROB, LIMBIGPRIME: technical."); + "default_ellQ(DEBUGLEVEL_ell, LIM1, LIM3, LIMTRIV, ELLREDGENFLAG, COMPLETE, MAXPROB, LIMBIGPRIME): set the value of the global variables used for ellQ_ellrank() and other related functions. DEBUGLEVEL_ell: 0-5: choose the quantity of information printed during the computation (default=0: print nothing); LIM1 (resp LIM3): search limit for easy (resp hard) points on quartics; LIMTRIV: search limit for trivial points on elliptic curves; ELLREDGENFLAG: if != 0, try to reduce the generators at the end; COMPLETE: if != 0 and full 2-torsion, use complete 2-descent, otherwise via 2-isogeny; MAXPROB, LIMBIGPRIME: technical."); /* addhelp(DEBUGLEVEL_ell, "DEBUGLEVEL_ell: Choose a higher value of this global variable to have more details of the computations printed during the 2-descent algorithm. 0 = don't print anything; 1 = (default) just print the result; 2 = print more details including the Selmer group and the nontrivial quartics."); */ diff --git a/src/sage/ext_data/pari/simon/qfsolve.gp b/src/sage/ext_data/pari/simon/qfsolve.gp index 501fb50828d..2107288c1d9 100644 --- a/src/sage/ext_data/pari/simon/qfsolve.gp +++ b/src/sage/ext_data/pari/simon/qfsolve.gp @@ -434,146 +434,6 @@ my(cc); return([U3~*G3*U3,red[2]*U1*U2*U3]); } -\\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -\\ QUADRATIC FORMS MINIMIZATION \\ -\\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - -\\ Minimization of the quadratic form G, with nonzero determinant. -\\ of dimension n>=2. -\\ G must by symmetric and have integral coefficients. -\\ Returns [G',U,factd] with U in GLn(Q) such that G'=U~*G*U*constant -\\ is integral and has minimal determinant. -\\ In dimension 3 or 4, may return a prime p -\\ if the reduction at p is impossible because of the local non solvability. -\\ If given, factdetG must be equal to factor(abs(det(G))). -{qfminimize(G,factdetG) = -my(factd,U,Ker,Ker2,sol,aux,di); -my(p); -my(n,lf,i,vp,dimKer,dimKer2,m); - - n = length(G); - factd = matrix(0,2); - if( !factdetG, factdetG = factor(matdet(G))); - - lf = length(factdetG[,1]); - i = 1; U = matid(n); - - while(i <= lf, - vp = factdetG[i,2]; - if( vp == 0, i++; next); - p = factdetG[i,1]; - if( p == -1, i++; next); -if( DEBUGLEVEL_qfsolve >= 4, print(" p = ",p,"^",vp)); - -\\ The case vp = 1 can be minimized only if n is odd. - if( vp == 1 && n%2 == 0, - factd = concat(factd~, Mat([p,1])~)~; - i++; next - ); - Ker = kermodp(G,p); dimKer = Ker[1]; Ker = Ker[2]; - -\\ Rem: we must have dimKer <= vp -if( DEBUGLEVEL_qfsolve >= 4, print(" dimKer = ",dimKer)); -\\ trivial case: dimKer = n - if( dimKer == n, -if( DEBUGLEVEL_qfsolve >= 4, print(" case 0: dimKer = n")); - G /= p; - factdetG[i,2] -= n; - next - ); - G = Ker~*G*Ker; - U = U*Ker; - -\\ 1st case: dimKer < vp -\\ then the kernel mod p contains a kernel mod p^2 - if( dimKer < vp, -if( DEBUGLEVEL_qfsolve >= 4, print(" case 1: dimker < vp")); - if( dimKer == 1, -\\ G[,1] /= p; G[1,] /= p; - G[,1] /= p; G[1,] = G[1,]/p; - U[,1] /= p; - factdetG[i,2] -= 2 - , - Ker2 = kermodp(matrix(dimKer,dimKer,j,k,G[j,k]/p),p); - dimKer2 = Ker2[1]; Ker2 = Ker2[2]; - for( j = 1, dimKer2, Ker2[,j] /= p); - Ker2 = matdiagonalblock([Ker2,matid(n-dimKer)]); - G = Ker2~*G*Ker2; - U = U*Ker2; - factdetG[i,2] -= 2*dimKer2 -); - -if( DEBUGLEVEL_qfsolve >= 4, print(" end of case 1")); - next - ); - -\\ Now, we have vp = dimKer -\\ 2nd case: the dimension of the kernel is >=2 -\\ and contains an element of norm 0 mod p^2 - -\\ search for an element of norm p^2... in the kernel - if( dimKer > 2 || - (dimKer == 2 && issquare( di = Mod((G[1,2]^2-G[1,1]*G[2,2])/p^2,p))), - if( dimKer > 2, -if( DEBUGLEVEL_qfsolve >= 4, print(" case 2.1")); - dimKer = 3; - sol = qfsolvemodp(matrix(3,3,j,k,G[j,k]/p),p) - , -if( DEBUGLEVEL_qfsolve >= 4, print(" case 2.2")); - if( G[1,1]%p^2 == 0, - sol = [1,0]~ - , sol = [-G[1,2]/p+sqrt(di),Mod(G[1,1]/p,p)]~ - ) - ); - sol = centerlift(sol); - sol /= content(sol); -if( DEBUGLEVEL_qfsolve >= 4, print(" sol = ",sol)); - Ker = vectorv(n, j, if( j<= dimKer, sol[j], 0)); \\ fill with 0's - Ker = completebasis(Ker,1); - Ker[,n] /= p; - G = Ker~*G*Ker; - U = U*Ker; - factdetG[i,2] -= 2; -if( DEBUGLEVEL_qfsolve >= 4, print(" end of case 2")); - next - ); - -\\ Now, we have vp = dimKer <= 2 -\\ and the kernel contains no vector with norm p^2... - -\\ In some cases, exchanging the kernel and the image -\\ makes the minimization easy. - - m = (n-1)\2-1; - if( ( vp == 1 && issquare(Mod(-(-1)^m*matdet(G)/G[1,1],p))) - || ( vp == 2 && n%2 == 1 && n >= 5) - || ( vp == 2 && n%2 == 0 && !issquare(Mod((-1)^m*matdet(G)/p^2,p))) - , -if( DEBUGLEVEL_qfsolve >= 4, print(" case 3")); - Ker = matid(n); - for( j = dimKer+1, n, Ker[j,j] = p); - G = Ker~*G*Ker/p; - U = U*Ker; - factdetG[i,2] -= 2*dimKer-n; -if( DEBUGLEVEL_qfsolve >= 4, print(" end of case 3")); - next - ); - -\\ Minimization was not possible se far. -\\ If n == 3 or 4, this proves the local non-solubility at p. - if( n == 3 || n == 4, -if( DEBUGLEVEL_qfsolve >= 1, print(" no local solution at ",p)); - return(p)); - -if( DEBUGLEVEL_qfsolve >= 4, print(" prime ",p," finished")); - factd = concat(factd~,Mat([p,vp])~)~; - i++ - ); -\\ apply LLL to avoid coefficients explosion - aux = qflll(U/content(U)); -return([aux~*G*aux,U*aux,factd]); -} - \\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\ CLASS GROUP COMPUTATIONS \\ \\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index fd899aa4770..1fe5d00b669 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -371,8 +371,6 @@ def __bool__(self): """ return bool(self.is_present) - - def __repr__(self): r""" TESTS:: @@ -386,6 +384,7 @@ def __repr__(self): _cache_package_systems = None + def package_systems(): """ Return a list of :class:`~sage.features.pkg_systems.PackageSystem` objects @@ -742,8 +741,8 @@ def __init__(self, name, test_code, **kwds): TESTS:: sage: from sage.features import CythonFeature - sage: from sage.features.fes import LibFESLibrary - sage: isinstance(LibFESLibrary(), CythonFeature) # indirect doctest + sage: from sage.features.bliss import BlissLibrary + sage: isinstance(BlissLibrary(), CythonFeature) # indirect doctest True """ Feature.__init__(self, name, **kwds) @@ -768,8 +767,8 @@ def _is_present(self): from distutils.errors import CCompilerError with open(tmp_filename(ext=".pyx"), 'w') as pyx: pyx.write(self.test_code) - from sage.misc.cython import cython_import try: + from sage.misc.cython import cython_import cython_import(pyx.name, verbose=-1) except CCompilerError: return FeatureTestResult(self, False, reason="Failed to compile test code.") diff --git a/src/sage/features/fes.py b/src/sage/features/fes.py deleted file mode 100644 index 50a2eba97c3..00000000000 --- a/src/sage/features/fes.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -r""" -Features for testing the presence of ``fes`` -""" - -from . import CythonFeature, PythonModule -from .join_feature import JoinFeature - - -TEST_CODE = """ -# distutils: libraries=fes - -from libc.stdint cimport uint64_t -cdef extern from "<fes_interface.h>": - ctypedef int (*solution_callback_t)(void *, uint64_t) - void exhaustive_search_wrapper(int n, int n_eqs, int degree, int ***coeffs, solution_callback_t callback, void* callback_state, int verbose) - -solutions = 0 - -class InternalState: - verbose = False - sols = [] - max_sols = 0 - -cdef int report_solution(void *_state, uint64_t i): - global solutions - solutions += 1 - return 0 - -sig_on() -cdef int ***coeffs = <int ***> sig_calloc(1, sizeof(int **)) -coeffs[0] = <int **> sig_calloc(3, sizeof(int *)) -coeffs[0][0] = <int *> sig_calloc(1, sizeof(int)) -coeffs[0][1] = <int *> sig_calloc(2, sizeof(int)) -coeffs[0][2] = <int *> sig_calloc(1, sizeof(int)) -coeffs[0][2][0] = 1 # x*y = 0 -internal_state = InternalState() - -exhaustive_search_wrapper(2, 1, 2, coeffs, report_solution, <void *>internal_state, 0) - -sig_free(coeffs[0][2]) -sig_free(coeffs[0][1]) -sig_free(coeffs[0][0]) -sig_free(coeffs[0]) -sig_free(coeffs) -sig_off() - -if solutions != 3: - raise AssertionError("libFES did not find three solutions for x*y = 0") -""" - - -class LibFESLibrary(CythonFeature): - r""" - A :class:`~sage.features.Feature` which describes whether the FES library - is present and functional. - - EXAMPLES:: - - sage: from sage.features.fes import LibFESLibrary - sage: LibFESLibrary().require() # optional - fes - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.fes import LibFESLibrary - sage: isinstance(LibFESLibrary(), LibFESLibrary) - True - """ - CythonFeature.__init__(self, "LibFES", test_code=TEST_CODE, spkg="fes", - url="http://www.lifl.fr/~bouillag/fes/") - - -class LibFES(JoinFeature): - r""" - A :class:`~sage.features.Feature` which describes whether the :mod:`sage.libs.fes` - module has been enabled for this build of Sage and is functional. - - EXAMPLES:: - - sage: from sage.features.fes import LibFES - sage: LibFES().require() # optional - fes - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.fes import LibFES - sage: isinstance(LibFES(), LibFES) - True - """ - JoinFeature.__init__(self, 'fes', - [PythonModule("sage.libs.fes")], - spkg="fes", - url="http://www.lifl.fr/~bouillag/fes/") diff --git a/src/sage/features/join_feature.py b/src/sage/features/join_feature.py index b29241eb462..92e9851d635 100644 --- a/src/sage/features/join_feature.py +++ b/src/sage/features/join_feature.py @@ -9,6 +9,17 @@ class JoinFeature(Feature): r""" Join of several :class:`~sage.features.Feature` instances. + This creates a new feature as the union of the given features. Typically + these are executables of an SPKG. For an example, see + :class:`~sage.features.rubiks.Rubiks`. + + Furthermore, this can be the union of a single feature. This is used to map + the given feature to a more convenient name to be used in ``optional`` tags + of doctests. Thus you can equip a feature such as a + :class:`~sage.features.PythonModule` with a tag name that differs from the + systematic tag name. As an example for this use case, see + :class:`~sage.features.meataxe.Meataxe`. + EXAMPLES:: sage: from sage.features import Executable diff --git a/src/sage/features/mip_backends.py b/src/sage/features/mip_backends.py index 70f6ffb8d74..477d88efabf 100644 --- a/src/sage/features/mip_backends.py +++ b/src/sage/features/mip_backends.py @@ -2,7 +2,7 @@ Features for testing the presence of :class:`MixedIntegerLinearProgram` backends """ -from . import Feature, FeatureTestResult +from . import Feature, PythonModule, FeatureTestResult from .join_feature import JoinFeature @@ -77,7 +77,26 @@ def __init__(self): spkg='sage_numerical_backends_coin') +class CVXOPT(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing whether the :class:`MixedIntegerLinearProgram` backend ``CVXOPT`` is available. + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.mip_backends import CVXOPT + sage: CVXOPT()._is_present() # optional - cvxopt + FeatureTestResult('cvxopt', True) + """ + JoinFeature.__init__(self, 'cvxopt', + [MIPBackend('CVXOPT'), + PythonModule('cvxopt')], + spkg='cvxopt') + + def all_features(): return [CPLEX(), Gurobi(), - COIN()] + COIN(), + CVXOPT()] diff --git a/src/sage/functions/airy.py b/src/sage/functions/airy.py index cf32fb8f1c8..eceb9e6eafc 100644 --- a/src/sage/functions/airy.py +++ b/src/sage/functions/airy.py @@ -1,5 +1,5 @@ r""" -Airy Functions +Airy functions This module implements Airy functions and their generalized derivatives. It supports symbolic functionality through Maxima and numeric evaluation through diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index de1401dde68..95405c3d72f 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1,5 +1,5 @@ r""" -Bessel Functions +Bessel functions This module provides symbolic Bessel and Hankel functions, and their spherical versions. These functions use the `mpmath library`_ for numerical @@ -1434,6 +1434,8 @@ def _derivative_(self, a, z, diff_param=None): def _print_latex_(self, a, z): """ + EXAMPLES:: + sage: latex(struve_L(2,x)) L_{{2}}({x}) """ diff --git a/src/sage/functions/error.py b/src/sage/functions/error.py index 06f0b244736..ca665622f20 100644 --- a/src/sage/functions/error.py +++ b/src/sage/functions/error.py @@ -1,5 +1,5 @@ r""" -Error Functions +Error functions This module provides symbolic error functions. These functions use the `mpmath library` for numerical evaluation and Maxima, Pynac for diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index 7ef74d9ed49..3f193cf5f1b 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -1,5 +1,5 @@ r""" -Exponential Integrals +Exponential integrals AUTHORS: diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index a24268c9b6b..d912ea23719 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -1,5 +1,5 @@ r""" -Generalized Functions +Generalized functions Sage implements several generalized functions (also known as distributions) such as Dirac delta, Heaviside step functions. These diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index 4487a3b3641..9a362b8882f 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -1,5 +1,5 @@ r""" -Hyperbolic Functions +Hyperbolic functions The full set of hyperbolic and inverse hyperbolic functions is available: diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 795a1ab7228..50c60b25638 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -1,5 +1,5 @@ r""" -Hypergeometric Functions +Hypergeometric functions This module implements manipulation of infinite hypergeometric series represented in standard parametric form (as `\,_pF_q` functions). diff --git a/src/sage/functions/jacobi.py b/src/sage/functions/jacobi.py index af67d857f27..6fe3b2ade89 100644 --- a/src/sage/functions/jacobi.py +++ b/src/sage/functions/jacobi.py @@ -1,5 +1,5 @@ r""" -Jacobi Elliptic Functions +Jacobi elliptic functions This module implements the 12 Jacobi elliptic functions, along with their inverses and the Jacobi amplitude function. diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 46cc279a287..6f9133841a3 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -1,5 +1,5 @@ """ -Logarithmic Functions +Logarithmic functions AUTHORS: diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 9b7d6d99f62..28419136e52 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -1,9 +1,9 @@ r""" -Symbolic Minimum and Maximum +Symbolic minimum and maximum -Sage provides a symbolic maximum and minimum due to the fact that the Python -builtin max and min are not able to deal with variables as users might expect. -These functions wait to evaluate if there are variables. +Sage provides a symbolic maximum and minimum due to the fact that the +Python builtin :func:`max` and :func:`min` are not able to deal with variables +as users might expect. These functions wait to evaluate if there are variables. Here you can see some differences:: @@ -24,14 +24,13 @@ max(x, 5) sage: min_symbolic(3, 5, x) min(x, 3) - """ ############################################################################### # Sage: Open Source Mathematical Software # Copyright (C) 2010 Burcin Erocal <burcin@erocal.org> # Distributed under the terms of the GNU General Public License (GPL), # version 2 or any later version. The full text of the GPL is available at: -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################### from sage.symbolic.function import BuiltinFunction @@ -40,6 +39,7 @@ from builtins import max as builtin_max, min as builtin_min + class MinMax_base(BuiltinFunction): def eval_helper(self, this_f, builtin_f, initial_val, args): """ @@ -53,7 +53,6 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): min(x, 3) sage: min_symbolic([5.0r]) # indirect doctest 5.0 - """ # __call__ ensures that if args is a singleton, the element is iterable arg_is_iter = False @@ -151,12 +150,13 @@ def __call__(self, *args, **kwds): except ValueError: pass + class MaxSymbolic(MinMax_base): def __init__(self): r""" Symbolic `\max` function. - The Python builtin `\max` function doesn't work as expected when symbolic + The Python builtin :func:`max` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. @@ -236,7 +236,6 @@ def _evalf_(self, *args, **kwds): ... sage: r.n() # abs tol 1e-8 0.873911256504955 - """ return max_symbolic(args) @@ -249,7 +248,7 @@ def __init__(self): r""" Symbolic `\min` function. - The Python builtin `\min` function doesn't work as expected when symbolic + The Python builtin :func:`min` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. @@ -323,4 +322,5 @@ def _evalf_(self, *args, **kwds): """ return min_symbolic(args) + min_symbolic = MinSymbolic() diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 77b7e06b909..7398c763971 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -1,5 +1,5 @@ r""" -Orthogonal Polynomials +Orthogonal polynomials Chebyshev polynomials --------------------- diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 1ce7569919f..eff27f95bc6 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1518,7 +1518,7 @@ def _eval_(self, x): sage: type(factorial(float(3.2))) <class 'float'> """ - if isinstance(x, Integer): + if isinstance(x, (int, Integer)): try: return x.factorial() except OverflowError: diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 688db13a3b8..10d82e3709a 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Piecewise-defined Functions +Piecewise functions This module implement piecewise functions in a single variable. See :mod:`sage.sets.real_set` for more information about how to construct diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index 0a576734777..38c68a2d4b4 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -1,5 +1,13 @@ r""" -Counting Primes +Counting primes + +EXAMPLES:: + + sage: z = sage.functions.prime_pi.PrimePi() + sage: loads(dumps(z)) + prime_pi + sage: loads(dumps(z)) == z + True AUTHORS: @@ -12,14 +20,6 @@ AUTHORS: - Dima Pasechnik (2021): removed buggy cython code, replaced it with calls to primecount/primecountpy spkg - -EXAMPLES:: - - sage: z = sage.functions.prime_pi.PrimePi() - sage: loads(dumps(z)) - prime_pi - sage: loads(dumps(z)) == z - True """ # **************************************************************************** diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index 02596e49620..56f96f2ef53 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -1,20 +1,5 @@ r""" -Miscellaneous Special Functions - -AUTHORS: - -- David Joyner (2006-13-06): initial version - -- David Joyner (2006-30-10): bug fixes to pari wrappers of Bessel - functions, hypergeometric_U - -- William Stein (2008-02): Impose some sanity checks. - -- David Joyner (2008-04-23): addition of elliptic integrals - -- Eviatar Bach (2013): making elliptic integrals symbolic - -- Eric Gourgoulhon (2022): add Condon-Shortley phase to spherical harmonics +Miscellaneous special functions This module provides easy access to many of Maxima and PARI's special functions. @@ -104,6 +89,11 @@ and the complete ones are obtained by taking `\phi =\pi/2`. +.. WARNING:: + + SciPy's versions are poorly documented and seem less accurate than the + Maxima and PARI versions. Typically they are limited by hardware floats + precision. REFERENCES: @@ -118,16 +108,20 @@ AUTHORS: -- David Joyner and William Stein +- David Joyner (2006-13-06): initial version + +- David Joyner (2006-30-10): bug fixes to pari wrappers of Bessel + functions, hypergeometric_U + +- William Stein (2008-02): Impose some sanity checks. + +- David Joyner (2008-02-16): optional calls to scipy and replace all ``#random`` by ``...`` -Added 16-02-2008 (wdj): optional calls to scipy and replace all -'#random' by '...' (both at the request of William Stein) +- David Joyner (2008-04-23): addition of elliptic integrals -.. warning:: +- Eviatar Bach (2013): making elliptic integrals symbolic - SciPy's versions are poorly documented and seem less - accurate than the Maxima and PARI versions; typically they are limited - by hardware floats precision. +- Eric Gourgoulhon (2022): add Condon-Shortley phase to spherical harmonics """ # **************************************************************************** @@ -849,7 +843,7 @@ class EllipticF(BuiltinFunction): - :wikipedia:`Elliptic_integral#Incomplete_elliptic_integral_of_the_first_kind` """ def __init__(self): - """ + r""" EXAMPLES:: sage: loads(dumps(elliptic_f)) diff --git a/src/sage/functions/spike_function.py b/src/sage/functions/spike_function.py index 2118c1b2dcc..83fde81b0ca 100644 --- a/src/sage/functions/spike_function.py +++ b/src/sage/functions/spike_function.py @@ -1,5 +1,5 @@ r""" -Spike Functions +Spike functions AUTHORS: diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index a470e9ed67d..b76e3a1bbbe 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -1,5 +1,5 @@ """ -Number-Theoretic Functions +Number-theoretic functions """ # **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index fb97a5e8c58..16aeeae43ab 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -1,5 +1,5 @@ r""" -Trigonometric Functions +Trigonometric functions """ from sage.symbolic.function import GinacFunction import math diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 373c586daca..40b3365d5e3 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1674,17 +1674,17 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): if not self._is_complete(): raise ValueError("utilities have not been populated") - from sage.features.lrs import Lrs + from sage.features.lrs import LrsNash if not algorithm: if self.is_constant_sum(): algorithm = "lp" - elif Lrs().is_present(): + elif LrsNash().is_present(): algorithm = "lrs" else: algorithm = "enumeration" if algorithm == "lrs": - Lrs().require() + LrsNash().require() return self._solve_lrs(maximization) if algorithm == "LCP": diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index e3d84cac696..c8a59a8b2a8 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -205,7 +205,8 @@ from copy import copy from warnings import warn -from sage.arith.all import gcd, lcm +from sage.arith.misc import GCD as gcd +from sage.arith.functions import lcm from sage.combinat.posets.posets import FinitePoset from sage.geometry.point_collection import PointCollection from sage.geometry.polyhedron.constructor import Polyhedron @@ -3017,6 +3018,13 @@ def intersection(self, other): N( 2, 5) in 2-d lattice N + The intersection can also be expressed using the operator ``&``:: + + sage: (cone1 & cone2).rays() + N(-1, 3), + N( 2, 5) + in 2-d lattice N + It is OK to intersect cones living in sublattices of the same ambient lattice:: @@ -3042,7 +3050,18 @@ def intersection(self, other): N(3, 1), N(0, 1) in 2-d lattice N + + An intersection with a polyhedron returns a polyhedron:: + + sage: cone = Cone([(1,0), (-1,0), (0,1)]) + sage: p = polytopes.hypercube(2) + sage: cone & p + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices + sage: sorted(_.vertices_list()) + [[-1, 0], [-1, 1], [1, 0], [1, 1]] """ + if not isinstance(other, ConvexRationalPolyhedralCone): + return self.polyhedron().intersection(other) if self._ambient is other._ambient: # Cones of the same ambient cone or fan intersect nicely/quickly. # Can we maybe even return an element of the cone lattice?.. @@ -3058,6 +3077,8 @@ def intersection(self, other): p.add_constraints(other._PPL_cone().constraints()) return _Cone_from_PPL(p, self.lattice().intersection(other.lattice())) + __and__ = intersection + def is_equivalent(self, other): r""" Check if ``self`` is "mathematically" the same as ``other``. @@ -3495,7 +3516,7 @@ def plot(self, **options): result += tp.plot_walls(walls) return result - def polyhedron(self): + def polyhedron(self, **kwds): r""" Return the polyhedron associated to ``self``. @@ -3523,7 +3544,7 @@ def polyhedron(self): A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex """ - return Polyhedron(rays=self.rays(), vertices=[self.lattice()(0)]) + return Polyhedron(rays=self.rays(), vertices=[self.lattice()(0)], **kwds) def an_affine_basis(self): r""" diff --git a/src/sage/geometry/fan_morphism.py b/src/sage/geometry/fan_morphism.py index d7ba7ac7ea6..e94e2cce8c0 100644 --- a/src/sage/geometry/fan_morphism.py +++ b/src/sage/geometry/fan_morphism.py @@ -1477,7 +1477,7 @@ def is_dominant(self): If the fan morphism is dominant, then the associated morphism of toric varieties is dominant in the algebraic-geometric sense (that is, surjective onto a dense subset). - + OUTPUT: Boolean. @@ -1878,13 +1878,13 @@ def factor(self): def relative_star_generators(self, domain_cone): """ Return the relative star generators of ``domain_cone``. - + INPUT: - + - ``domain_cone`` -- a cone of the :meth:`domain_fan` of ``self``. - + OUTPUT: - + - :meth:`~RationalPolyhedralFan.star_generators` of ``domain_cone`` viewed as a cone of :meth:`preimage_fan` of :meth:`image_cone` of ``domain_cone``. diff --git a/src/sage/geometry/hyperplane_arrangement/affine_subspace.py b/src/sage/geometry/hyperplane_arrangement/affine_subspace.py index 83f7463d4b9..7c1065553c4 100644 --- a/src/sage/geometry/hyperplane_arrangement/affine_subspace.py +++ b/src/sage/geometry/hyperplane_arrangement/affine_subspace.py @@ -131,7 +131,7 @@ def __hash__(self): """ # note that the point is not canonically chosen, but the linear part is return hash(self._linear_part) - + def _repr_(self): r""" String representation for an :class:`AffineSubspace`. diff --git a/src/sage/geometry/hyperplane_arrangement/check_freeness.py b/src/sage/geometry/hyperplane_arrangement/check_freeness.py index cbf766c8f7e..30f963612ec 100644 --- a/src/sage/geometry/hyperplane_arrangement/check_freeness.py +++ b/src/sage/geometry/hyperplane_arrangement/check_freeness.py @@ -120,7 +120,7 @@ def next_step(indices, prev, T): ret = next_step(I, Y, U) if ret is not None: return [prev] + ret - return None + return None T = matrix.identity(S, r) for i in indices: diff --git a/src/sage/geometry/hyperplane_arrangement/hyperplane.py b/src/sage/geometry/hyperplane_arrangement/hyperplane.py index 491c88d176d..c431291a3a6 100644 --- a/src/sage/geometry/hyperplane_arrangement/hyperplane.py +++ b/src/sage/geometry/hyperplane_arrangement/hyperplane.py @@ -282,7 +282,7 @@ def __contains__(self, q): return self.A() * q + self._const == 0 @cached_method - def polyhedron(self): + def polyhedron(self, **kwds): """ Return the hyperplane as a polyhedron. @@ -304,8 +304,10 @@ def polyhedron(self): A vertex at (0, 0, 4/3)) """ from sage.geometry.polyhedron.constructor import Polyhedron - R = self.parent().base_ring() - return Polyhedron(eqns=[self.coefficients()], base_ring=R) + R = kwds.pop('base_ring', None) + if R is None: + R = self.parent().base_ring() + return Polyhedron(eqns=[self.coefficients()], base_ring=R, **kwds) @cached_method def linear_part(self): diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index cfa2fbeb083..0334593a889 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -22,7 +22,7 @@ from sage.combinat.combinat import stirling_number2 from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.arith.all import binomial +from sage.arith.misc import binomial from sage.rings.polynomial.polynomial_ring import polygen from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements diff --git a/src/sage/geometry/hyperplane_arrangement/plot.py b/src/sage/geometry/hyperplane_arrangement/plot.py index bf2455a0ceb..dc407a40ad3 100644 --- a/src/sage/geometry/hyperplane_arrangement/plot.py +++ b/src/sage/geometry/hyperplane_arrangement/plot.py @@ -103,9 +103,9 @@ sage: a.plot(hyperplane_labels=True,label_colors=['red','green','black']) # optional - sage.plot Graphics3d Object """ - from copy import copy from colorsys import hsv_to_rgb + from sage.misc.lazy_import import lazy_import lazy_import("sage.plot.plot3d.parametric_plot3d", "parametric_plot3d") lazy_import("sage.plot.plot3d.shapes2", "text3d") @@ -119,7 +119,7 @@ def plot(hyperplane_arrangement, **kwds): r""" - Return a plot of the hyperplane arrangement. + Return a plot of the hyperplane arrangement. If the arrangement is in 4 dimensions but inessential, a plot of the essentialization is returned. @@ -139,7 +139,7 @@ def plot(hyperplane_arrangement, **kwds): :mod:`sage.geometry.hyperplane_arrangement.plot`. OUTPUT: - + A graphics object of the plot. EXAMPLES:: @@ -338,7 +338,6 @@ def plot_hyperplane(hyperplane, **kwds): sage: a.plot(point_size=100,hyperplane_label='hello') # optional - sage.plot Graphics object consisting of 3 graphics primitives - sage: H2.<x,y> = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() # optional - sage.plot @@ -410,7 +409,7 @@ def plot_hyperplane(hyperplane, **kwds): # the extra keywords have now been handled # now create the plot if hyperplane.dimension() == 0: # a point on a line - x, = hyperplane.A() + x, = hyperplane.A() d = hyperplane.b() p = point((d/x,0), size=pt_size, **kwds) if has_hyp_label: @@ -456,7 +455,7 @@ def plot_hyperplane(hyperplane, **kwds): s0, s1 = -3, 3 t0, t1 = -3, 3 p = parametric_plot3d(pnt+s*w[0]+t*w[1], (s,s0,s1), (t,t0,t1), **kwds) - if has_hyp_label: + if has_hyp_label: if has_offset: b0, b1, b2 = label_offset else: @@ -469,14 +468,15 @@ def plot_hyperplane(hyperplane, **kwds): def legend_3d(hyperplane_arrangement, hyperplane_colors, length): r""" - Create plot of a 3d legend for an arrangement of planes in 3-space. The - ``length`` parameter determines whether short or long labels are used in - the legend. + Create plot of a 3d legend for an arrangement of planes in 3-space. + + The ``length`` parameter determines whether short or long labels + are used in the legend. INPUT: - ``hyperplane_arrangement`` -- a hyperplane arrangement - + - ``hyperplane_colors`` -- list of colors - ``length`` -- either ``'short'`` or ``'long'`` @@ -518,7 +518,7 @@ def legend_3d(hyperplane_arrangement, hyperplane_colors, length): for i in range(N): p += line([(0,0),(0,0)], color=hyperplane_colors[i], thickness=8, legend_label=labels[i], axes=False) - p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4, + p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4, fancybox=True, font_size='x-large', ncol=2) p.legend(True) return p diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index f8325eea1ed..8b2e54b2d5d 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -22,7 +22,8 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RR from sage.rings.integer cimport Integer -from sage.arith.all import gcd, lcm +from sage.arith.misc import GCD as gcd +from sage.arith.functions import lcm from sage.combinat.permutation import Permutation from sage.misc.misc_c import prod from sage.modules.free_module import FreeModule @@ -1450,4 +1451,3 @@ cpdef print_cache(InequalityCollection inequality_collection): print('Cached next-to-inner loop: ' + str(ieq.coeff) + ' * x_0 + ' + str(ieq.coeff_next) + ' * x_1 + ' + str(ieq.cache_next) + ' >= 0') - diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index ff85c1724d8..1c3115213ca 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -103,7 +103,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.arith.all import gcd +from sage.arith.misc import GCD as gcd from sage.combinat.posets.posets import FinitePoset from sage.env import POLYTOPE_DATA_DIR from sage.geometry.cone import _ambient_space_point, integral_length @@ -887,7 +887,9 @@ def _embed(self, data): - ``data`` - point or matrix of points (as columns) in the affine subspace spanned by this polytope - OUTPUT: The same point(s) in the coordinates of the ambient space of + OUTPUT: + + The same point(s) in the coordinates of the ambient space of this polytope. TESTS:: @@ -1053,7 +1055,9 @@ def _pullback(self, data): - ``data`` -- rational point or matrix of points (as columns) in the ambient space - OUTPUT: The same point(s) in the coordinates of the affine subspace + OUTPUT: + + The same point(s) in the coordinates of the affine subspace space spanned by this polytope. TESTS:: @@ -3503,8 +3507,9 @@ def plot3d(self, By default, everything is shown with more or less pretty combination of size and color parameters. - INPUT: Most of the parameters are self-explanatory: + INPUT: + Most of the parameters are self-explanatory: - ``show_facets`` - (default:True) @@ -3649,7 +3654,7 @@ def plot3d(self, pplot += text3d(i+self.nvertices(), bc+index_shift*(p-bc), rgbcolor=pindex_color) return pplot - def polyhedron(self): + def polyhedron(self, **kwds): r""" Return the Polyhedron object determined by this polytope's vertices. @@ -3660,7 +3665,7 @@ def polyhedron(self): A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices """ from sage.geometry.polyhedron.constructor import Polyhedron - return Polyhedron(vertices=[list(v) for v in self._vertices]) + return Polyhedron(vertices=[list(v) for v in self._vertices], **kwds) def show3d(self): """ @@ -3898,7 +3903,6 @@ def poly_x(self, keys, reduce_dimension=False): INPUT: - - ``keys`` - a string of options passed to poly.x. The key "f" is added automatically. @@ -3906,7 +3910,6 @@ def poly_x(self, keys, reduce_dimension=False): polytope is not full-dimensional, poly.x will be called for the vertices of this polytope in some basis of the spanned affine space. - OUTPUT: the output of poly.x as a string. EXAMPLES: This call is used for determining if a polytope is @@ -5322,7 +5325,6 @@ def _read_poly_x_incidences(data, dim): INPUT: - - ``data`` - an opened file with incidence information. The first line will be skipped, each consecutive line contains incidence information for all faces of one dimension, the @@ -5330,8 +5332,9 @@ def _read_poly_x_incidences(data, dim): - ``dim`` - dimension of the polytope. + OUTPUT: - OUTPUT: a sequence F, such that F[d][i] is a sequence of vertices + a sequence F, such that F[d][i] is a sequence of vertices or facets corresponding to the i-th d-dimensional face. TESTS:: @@ -5549,12 +5552,12 @@ def convex_hull(points): INPUT: - - ``points`` - a list that can be converted into vectors of the same dimension over ZZ. + OUTPUT: - OUTPUT: list of vertices of the convex hull of the given points (as + list of vertices of the convex hull of the given points (as vectors). EXAMPLES: Let's compute the convex hull of several points on a line @@ -5628,12 +5631,10 @@ def minkowski_sum(points1, points2): INPUT: - - ``points1, points2`` - lists of objects that can be converted into vectors of the same dimension, treated as vertices of two polytopes. - OUTPUT: list of vertices of the Minkowski sum, given as vectors. EXAMPLES: Let's compute the Minkowski sum of two line segments:: @@ -5659,7 +5660,9 @@ def positive_integer_relations(points): - ``points`` - lattice points given as columns of a matrix - OUTPUT: matrix of relations between given points with non-negative + OUTPUT: + + matrix of relations between given points with non-negative integer coefficients EXAMPLES: This is a 3-dimensional reflexive polytope:: diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index dd3d58f58b5..4f253741ab7 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -272,7 +272,7 @@ def __pow__(self, exp, ignored=None): This Newton polygon scaled by a factor ``exp``. - NOTE:: + .. NOTE:: If ``self`` is the Newton polygon of a polynomial `f`, then ``self^exp`` is the Newton polygon of `f^{exp}`. diff --git a/src/sage/geometry/point_collection.pyx b/src/sage/geometry/point_collection.pyx index fcfb45b1d9b..3a67de459ed 100644 --- a/src/sage/geometry/point_collection.pyx +++ b/src/sage/geometry/point_collection.pyx @@ -226,7 +226,7 @@ cdef class PointCollection(SageObject): raise NotImplementedError cdef PointCollection left_pc = left cdef PointCollection right_pc = right - if not left_pc._module is right_pc._module: + if left_pc._module is not right_pc._module: raise NotImplementedError return PointCollection(left_pc._points + right_pc._points, left_pc._module) @@ -955,23 +955,23 @@ def read_palp_point_collection(f, lattice=None, permutation=False): Read and return a point collection from an opened file. Data must be in PALP format: - + * the first input line starts with two integers `m` and `n`, the number of points and the number of components of each; - + * the rest of the first line may contain a permutation; - + * the next `m` lines contain `n` numbers each. - + .. NOTE:: - + If `m` < `n`, it is assumed (for compatibility with PALP) that the matrix is transposed, i.e. that each column is a point. INPUT: - ``f`` -- an opened file with PALP output. - + - ``lattice`` -- the lattice for points. If not given, the :class:`toric lattice <sage.geometry.toric_lattice.ToricLatticeFactory>` `M` of dimension `n` will be used. diff --git a/src/sage/geometry/polyhedron/backend_field.py b/src/sage/geometry/polyhedron/backend_field.py index 6b921d23a68..996d6c1a7ac 100644 --- a/src/sage/geometry/polyhedron/backend_field.py +++ b/src/sage/geometry/polyhedron/backend_field.py @@ -162,66 +162,76 @@ def _init_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep): self._init_Hrepresentation(*Hrep) def _init_from_Vrepresentation(self, vertices, rays, lines, - minimize=True, verbose=False): + minimize=True, verbose=False, + internal_base_ring=None): """ Construct polyhedron from V-representation data. INPUT: - ``vertices`` -- list of points. Each point can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + as any iterable container of ``internal_base_ring`` elements. - ``rays`` -- list of rays. Each ray can be specified as any - iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + iterable container of ``internal_base_ring`` elements. - - ``lines`` -- list of lines. Each line can be specified as - any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + - ``lines`` -- list of lines. Each line can be specified asinternal_base_ring + any iterable container of ``internal_base_ring`` elements. - ``verbose`` -- boolean (default: ``False``). Whether to print verbose output for debugging purposes. + - ``internal_base_ring`` -- the base ring of the generators' components. + Default is ``None``, in which case, it is set to + :meth:`~sage.geometry.polyhedron.base.base_ring`. + EXAMPLES:: sage: p = Polyhedron(ambient_dim=2, backend='field') sage: from sage.geometry.polyhedron.backend_field import Polyhedron_field sage: Polyhedron_field._init_from_Vrepresentation(p, [(0,0)], [], []) """ + if internal_base_ring is None: + internal_base_ring = self.base_ring() from sage.geometry.polyhedron.double_description_inhomogeneous import Hrep2Vrep, Vrep2Hrep - H = Vrep2Hrep(self.base_ring(), self.ambient_dim(), vertices, rays, lines) - V = Hrep2Vrep(self.base_ring(), self.ambient_dim(), + H = Vrep2Hrep(internal_base_ring, self.ambient_dim(), vertices, rays, lines) + V = Hrep2Vrep(internal_base_ring, self.ambient_dim(), H.inequalities, H.equations) self._init_Vrepresentation_backend(V) self._init_Hrepresentation_backend(H) - def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): + def _init_from_Hrepresentation(self, ieqs, eqns, + minimize=True, verbose=False, + internal_base_ring=None): """ Construct polyhedron from H-representation data. INPUT: - ``ieqs`` -- list of inequalities. Each line can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + as any iterable container of ``internal_base_ring`` elements. - ``eqns`` -- list of equalities. Each line can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + as any iterable container of ``internal_base_ring`` elements. - ``verbose`` -- boolean (default: ``False``). Whether to print verbose output for debugging purposes. + - ``internal_base_ring`` -- the base ring of the generators' components. + Default is ``None``, in which case, it is set to + :meth:`~sage.geometry.polyhedron.base.base_ring`. + TESTS:: sage: p = Polyhedron(ambient_dim=2, backend='field') sage: from sage.geometry.polyhedron.backend_field import Polyhedron_field sage: Polyhedron_field._init_from_Hrepresentation(p, [(1, 2, 3)], []) """ + if internal_base_ring is None: + internal_base_ring = self.base_ring() from sage.geometry.polyhedron.double_description_inhomogeneous import Hrep2Vrep, Vrep2Hrep - V = Hrep2Vrep(self.base_ring(), self.ambient_dim(), ieqs, eqns) - H = Vrep2Hrep(self.base_ring(), self.ambient_dim(), + V = Hrep2Vrep(internal_base_ring, self.ambient_dim(), ieqs, eqns) + H = Vrep2Hrep(internal_base_ring, self.ambient_dim(), V.vertices, V.rays, V.lines) self._init_Vrepresentation_backend(V) self._init_Hrepresentation_backend(H) @@ -265,7 +275,7 @@ def _init_Vrepresentation_backend(self, Vrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() # optional - sage.rings.number_field - (A vertex at (0.?e-15, 0.707106781186548?), + (A vertex at (0.?e-16, 0.7071067811865475?), A vertex at (1.414213562373095?, 0), A vertex at (4.000000000000000?, 0.372677996249965?)) """ @@ -308,7 +318,7 @@ def _init_Hrepresentation_backend(self, Hrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() # optional - sage.rings.number_field - (A vertex at (0.?e-15, 0.707106781186548?), + (A vertex at (0.?e-16, 0.7071067811865475?), A vertex at (1.414213562373095?, 0), A vertex at (4.000000000000000?, 0.372677996249965?)) """ diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index f38498aa597..af0c2c459a7 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ The Normaliz backend for polyhedral computations @@ -13,7 +12,15 @@ - Jean-Philippe Labbé (2019-04): Expose normaliz features and added functionalities """ # **************************************************************************** -# Copyright (C) 2016 Matthias Köppe <mkoeppe at math.ucdavis.edu> +# Copyright (C) 2016-2022 Matthias Köppe <mkoeppe at math.ucdavis.edu> +# 2016-2018 Travis Scrimshaw +# 2017 Jeroen Demeyer +# 2018-2020 Jean-Philippe Labbé +# 2019 Vincent Delecroix +# 2019-2021 Jonathan Kliem +# 2019-2021 Sophia Elia +# 2020 Frédéric Chapoton +# 2022 Yuan Zhou # # 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 @@ -36,33 +43,9 @@ from sage.misc.functional import denominator from sage.matrix.constructor import vector -from .base import Polyhedron_base from .base_QQ import Polyhedron_QQ from .base_ZZ import Polyhedron_ZZ - - -def _number_field_elements_from_algebraics_list_of_lists_of_lists(listss, **kwds): - r""" - Like `number_field_elements_from_algebraics`, but for a list of lists of lists. - - EXAMPLES:: - - sage: rt2 = AA(sqrt(2)); rt2 # optional - sage.rings.number_field - 1.414213562373095? - sage: rt3 = AA(sqrt(3)); rt3 # optional - sage.rings.number_field - 1.732050807568878? - sage: from sage.geometry.polyhedron.backend_normaliz import _number_field_elements_from_algebraics_list_of_lists_of_lists - sage: K, results, hom = _number_field_elements_from_algebraics_list_of_lists_of_lists([[[rt2], [1]], [[rt3]], [[1], []]]); results # optional - sage.rings.number_field - [[[-a^3 + 3*a], [1]], [[-a^2 + 2]], [[1], []]] - """ - from sage.rings.qqbar import number_field_elements_from_algebraics - numbers = [] - for lists in listss: - for list in lists: - numbers.extend(list) - K, K_numbers, hom = number_field_elements_from_algebraics(numbers, **kwds) - g = iter(K_numbers) - return K, [ [ [ next(g) for _ in list ] for list in lists ] for lists in listss ], hom +from .base_number_field import Polyhedron_base_number_field def _format_function_call(fn_name, *v, **k): @@ -82,7 +65,7 @@ def _format_function_call(fn_name, *v, **k): ######################################################################### -class Polyhedron_normaliz(Polyhedron_base): +class Polyhedron_normaliz(Polyhedron_base_number_field): """ Polyhedra with normaliz @@ -208,7 +191,7 @@ class Polyhedron_normaliz(Polyhedron_base): (A vertex at (2^(1/3)), A vertex at (sqrt(2))) """ - def __init__(self, parent, Vrep, Hrep, normaliz_cone=None, normaliz_data=None, normaliz_field=None, **kwds): + def __init__(self, parent, Vrep, Hrep, normaliz_cone=None, normaliz_data=None, internal_base_ring=None, **kwds): """ Initializes the polyhedron. @@ -230,16 +213,16 @@ def __init__(self, parent, Vrep, Hrep, normaliz_cone=None, normaliz_data=None, n if Hrep is not None or Vrep is not None or normaliz_data is not None: raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None") Element.__init__(self, parent=parent) - self._init_from_normaliz_cone(normaliz_cone, normaliz_field) + self._init_from_normaliz_cone(normaliz_cone, internal_base_ring) elif normaliz_data: if Hrep is not None or Vrep is not None: raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None") Element.__init__(self, parent=parent) - self._init_from_normaliz_data(normaliz_data, normaliz_field) + self._init_from_normaliz_data(normaliz_data, internal_base_ring) else: - if normaliz_field: - raise ValueError("if Vrep or Hrep are given, cannot provide normaliz_field") - Polyhedron_base.__init__(self, parent, Vrep, Hrep, **kwds) + if internal_base_ring: + raise ValueError("if Vrep or Hrep are given, cannot provide internal_base_ring") + Polyhedron_base_number_field.__init__(self, parent, Vrep, Hrep, **kwds) def _nmz_result(self, normaliz_cone, property): """ @@ -277,13 +260,13 @@ def rational_handler(list): def nfelem_handler(coords): # coords might be too short which is not accepted by Sage number field - v = list(coords) + [0] * (self._normaliz_field.degree() - len(coords)) - return self._normaliz_field(v) + v = list(coords) + [0] * (self._internal_base_ring.degree() - len(coords)) + return self._internal_base_ring(v) return NmzResult(normaliz_cone, property, RationalHandler=rational_handler, NumberfieldElementHandler=nfelem_handler) - def _init_from_normaliz_cone(self, normaliz_cone, normaliz_field): + def _init_from_normaliz_cone(self, normaliz_cone, internal_base_ring): """ Construct polyhedron from a PyNormaliz wrapper of a normaliz cone. @@ -293,9 +276,9 @@ def _init_from_normaliz_cone(self, normaliz_cone, normaliz_field): sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz # optional - pynormaliz sage: Polyhedron_normaliz._init_from_Hrepresentation(p, [], []) # indirect doctest # optional - pynormaliz """ - if normaliz_field is None: - normaliz_field = QQ - self._normaliz_field = normaliz_field + if internal_base_ring is None: + internal_base_ring = QQ + self._internal_base_ring = internal_base_ring if normaliz_cone and self._nmz_result(normaliz_cone, "AffineDim") < 0: # Empty polyhedron. Special case because Normaliz defines the @@ -356,7 +339,7 @@ def _QQ_pair(x): # number field return [ _QQ_pair(c) for c in x.list() ] - def _init_from_normaliz_data(self, data, normaliz_field=None, verbose=False): + def _init_from_normaliz_data(self, data, internal_base_ring=None, verbose=False): """ Construct polyhedron from normaliz ``data`` (a dictionary). @@ -377,15 +360,15 @@ def _init_from_normaliz_data(self, data, normaliz_field=None, verbose=False): sage: from sage.geometry.polyhedron.parent import Polyhedra_normaliz # optional - pynormaliz sage: parent = Polyhedra_normaliz(AA, 2, 'normaliz') # optional - pynormaliz # optional - sage.rings.number_field sage: Polyhedron_normaliz(parent, None, None, normaliz_data=data, # indirect doctest, optional - pynormaliz # optional - sage.rings.number_field - ....: normaliz_field=QuadraticField(2)) + ....: internal_base_ring=QuadraticField(2)) A 2-dimensional polyhedron in AA^2 defined as the convex hull of 1 vertex and 2 rays sage: _.inequalities_list() # optional - pynormaliz # optional - sage.rings.number_field [[0, -1/2, 1], [0, 2, -1]] """ - if normaliz_field is None: - normaliz_field = QQ + if internal_base_ring is None: + internal_base_ring = QQ cone = self._cone_from_normaliz_data(data, verbose) - self._init_from_normaliz_cone(cone, normaliz_field) + self._init_from_normaliz_cone(cone, internal_base_ring) def _cone_from_normaliz_data(self, data, verbose=False): """ @@ -543,10 +526,9 @@ def vert_ray_line_NF(vertices, rays, lines): if lines is None: lines = [] - (nmz_vertices, nmz_rays, nmz_lines), normaliz_field \ - = self._compute_nmz_data_lists_and_field((vertices, rays, lines), - vert_ray_line_QQ, - vert_ray_line_NF) + (nmz_vertices, nmz_rays, nmz_lines), internal_base_ring \ + = self._compute_data_lists_and_internal_base_ring( + (vertices, rays, lines), vert_ray_line_QQ, vert_ray_line_NF) if not nmz_vertices and not nmz_rays and not nmz_lines: # Special case to avoid: @@ -557,10 +539,10 @@ def vert_ray_line_NF(vertices, rays, lines): data = {"vertices": nmz_vertices, "cone": nmz_rays, "subspace": nmz_lines} - number_field_data = self._number_field_triple(normaliz_field) + number_field_data = self._number_field_triple(internal_base_ring) if number_field_data: data["number_field"] = number_field_data - self._init_from_normaliz_data(data, normaliz_field=normaliz_field, verbose=verbose) + self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose) def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): r""" @@ -642,10 +624,9 @@ def nmz_ieqs_eqns_QQ(ieqs, eqns): if eqns is None: eqns = [] - (nmz_ieqs, nmz_eqns), normaliz_field \ - = self._compute_nmz_data_lists_and_field((ieqs, eqns), - nmz_ieqs_eqns_QQ, - nmz_ieqs_eqns_NF) + (nmz_ieqs, nmz_eqns), internal_base_ring \ + = self._compute_data_lists_and_internal_base_ring( + (ieqs, eqns), nmz_ieqs_eqns_QQ, nmz_ieqs_eqns_NF) if not nmz_ieqs: # If normaliz gets an empty list of inequalities, it adds # nonnegativities. So let's add a tautological inequality to work @@ -653,10 +634,10 @@ def nmz_ieqs_eqns_QQ(ieqs, eqns): nmz_ieqs.append([0] * self.ambient_dim() + [0]) data = {"inhom_equations": nmz_eqns, "inhom_inequalities": nmz_ieqs} - number_field_data = self._number_field_triple(normaliz_field) + number_field_data = self._number_field_triple(internal_base_ring) if number_field_data: data["number_field"] = number_field_data - self._init_from_normaliz_data(data, normaliz_field=normaliz_field, verbose=verbose) + self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose) def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, ieqs, eqns=None, verbose=False, homogeneous=False): r""" @@ -850,10 +831,10 @@ def rays_subspace_lattice_ieqs_NF(vertices, rays, lines, ieqs): return nmz_vertices + nmz_rays, nmz_lines, nmz_lattice, nmz_ieqs - (nmz_extreme_rays, nmz_subspace, nmz_lattice, nmz_ieqs), normaliz_field \ - = self._compute_nmz_data_lists_and_field((vertices, rays, lines, ieqs), - rays_subspace_lattice_ieqs_QQ, - rays_subspace_lattice_ieqs_NF) + (nmz_extreme_rays, nmz_subspace, nmz_lattice, nmz_ieqs), internal_base_ring \ + = self._compute_data_lists_and_internal_base_ring( + (vertices, rays, lines, ieqs), rays_subspace_lattice_ieqs_QQ, + rays_subspace_lattice_ieqs_NF) data = {"extreme_rays": nmz_extreme_rays, "maximal_subspace": nmz_subspace, @@ -864,7 +845,7 @@ def rays_subspace_lattice_ieqs_NF(vertices, rays, lines, ieqs): if not homogeneous: data["dehomogenization"] = [[0] * (ambient_dim - 1) + [1]] - number_field_data = self._number_field_triple(normaliz_field) + number_field_data = self._number_field_triple(internal_base_ring) if number_field_data: data["number_field"] = number_field_data return self._cone_from_normaliz_data(data, verbose=verbose) @@ -910,71 +891,6 @@ def _test_far_facet_condition(self, tester=None, **options): tester.assertEqual(self.n_inequalities() + 1, len(nmz_ieqs)) tester.assertTrue(any(ieq == [0] * self.ambient_dim() + [1] for ieq in nmz_ieqs)) - def _compute_nmz_data_lists_and_field(self, data_lists, convert_QQ, convert_NF): - r""" - Compute data lists in Normaliz format and the number field to use with Normaliz. - - EXAMPLES:: - - sage: p = Polyhedron(vertices=[(0,1/2),(2,0),(4,5/6)], # optional - pynormaliz - ....: base_ring=AA, backend='normaliz') - sage: def convert_QQ(ieqs, eqs): # optional - pynormaliz - ....: return [ [ 1000*x for x in ieq ] for ieq in ieqs], \ - ....: [ [ 1000*x for x in eq ] for eq in eqs] - sage: def convert_NF(ieqs, eqs): # optional - pynormaliz - ....: return ieqs, eqs - sage: p._compute_nmz_data_lists_and_field([[[1]], [[1/2]]], # optional - pynormaliz - ....: convert_QQ, convert_NF) - (([[1000]], [[500]]), Rational Field) - sage: p._compute_nmz_data_lists_and_field([[[AA(1)]], [[1/2]]], # optional - pynormaliz - ....: convert_QQ, convert_NF) - (([[1000]], [[500]]), Rational Field) - sage: p._compute_nmz_data_lists_and_field([[[AA(sqrt(2))]], [[1/2]]], # optional - pynormaliz # optional - sage.rings.number_field - ....: convert_QQ, convert_NF) - ([[[a]], [[1/2]]], - Number Field in a with defining polynomial y^2 - 2 with a = 1.414213562373095?) - - TESTS:: - - sage: K.<a> = QuadraticField(-5) # optional - sage.rings.number_field - sage: p = Polyhedron(vertices=[(a,1/2),(2,0),(4,5/6)], # indirect doctest # optional - pynormaliz # optional - sage.rings.number_field - ....: base_ring=K, backend='normaliz') - Traceback (most recent call last): - ... - ValueError: invalid base ring: Number Field in a ... is not real embedded - - Checks that :trac:`30248` is fixed:: - - sage: q = Polyhedron(backend='normaliz', base_ring=AA, # indirect doctest # optional - pynormaliz # optional - sage.rings.number_field - ....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)]); q - A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays - sage: -q # optional - pynormaliz # optional - sage.rings.number_field - A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays - """ - from sage.categories.number_fields import NumberFields - from sage.rings.real_double import RDF - - if self.base_ring() in (QQ, ZZ): - normaliz_field = QQ - nmz_data_lists = convert_QQ(*data_lists) - else: - # Allows to re-iterate if K is QQ below when data_lists contain - # iterators: - data_lists = [tuple(_) for _ in data_lists] - nmz_data_lists = convert_NF(*data_lists) - if self.base_ring() in NumberFields(): - if not RDF.has_coerce_map_from(self.base_ring()): - raise ValueError("invalid base ring: {} is a number field that is not real embedded".format(self.base_ring())) - normaliz_field = self.base_ring() - else: - K, nmz_data_lists, hom = _number_field_elements_from_algebraics_list_of_lists_of_lists(nmz_data_lists, embedded=True) - normaliz_field = K - if K is QQ: - # Compute it with Normaliz, not QNormaliz - nmz_data_lists = convert_QQ(*[ [ [ QQ(x) for x in v ] for v in l] - for l in data_lists ]) - return nmz_data_lists, normaliz_field - def _init_Vrepresentation_from_normaliz(self): r""" Create the Vrepresentation objects from the normaliz polyhedron. @@ -1057,7 +973,7 @@ def _init_empty_polyhedron(self): self._normaliz_cone = None @classmethod - def _from_normaliz_cone(cls, parent, normaliz_cone, normaliz_field=None): + def _from_normaliz_cone(cls, parent, normaliz_cone, internal_base_ring=None): r""" Initializes a polyhedron from a PyNormaliz wrapper of a normaliz cone. @@ -1067,12 +983,12 @@ def _from_normaliz_cone(cls, parent, normaliz_cone, normaliz_field=None): ....: backend='normaliz') sage: PI = P.integral_hull() # indirect doctest; optional - pynormaliz """ - return cls(parent, None, None, normaliz_cone=normaliz_cone, normaliz_field=normaliz_field) + return cls(parent, None, None, normaliz_cone=normaliz_cone, internal_base_ring=internal_base_ring) @staticmethod - def _number_field_triple(normaliz_field): + def _number_field_triple(internal_base_ring): r""" - Construct the PyNormaliz triple that describes the number field ``normaliz_field``. + Construct the PyNormaliz triple that describes ``internal_base_ring``. TESTS:: @@ -1082,7 +998,7 @@ def _number_field_triple(normaliz_field): sage: Pn._number_field_triple(QuadraticField(5)) # optional - sage.rings.number_field ['a^2 - 5', 'a', '[2.236067977499789 +/- 8.06e-16]'] """ - R = normaliz_field + R = internal_base_ring if R is QQ: return None from sage.rings.real_arb import RealBallField @@ -1263,7 +1179,7 @@ def __getstate__(self): A vertex at (0, 0, 1, 0), A vertex at (0, 1, 0, 0), A vertex at (1, 0, 0, 0)), - '_normaliz_field': Rational Field, + '_internal_base_ring': Rational Field, '_pickle_equations': [(-1, 1, 1, 1, 1)], '_pickle_inequalities': [(0, 0, 0, 0, 1), (0, 0, 0, 1, 0), @@ -1330,7 +1246,7 @@ def __setstate__(self, state): sage: P = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz # optional - sage.rings.number_field sage: P1 = loads(dumps(P)) # optional - pynormaliz # optional - sage.rings.number_field - sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone, normaliz_field=P1._normaliz_field) # optional - pynormaliz # optional - sage.rings.number_field + sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone, internal_base_ring=P1._internal_base_ring) # optional - pynormaliz # optional - sage.rings.number_field sage: P == P2 # optional - pynormaliz # optional - sage.rings.number_field True @@ -1537,7 +1453,7 @@ def _volume_normaliz(self, measure='euclidean'): if measure == 'euclidean': return self._nmz_result(cone, 'EuclideanVolume') elif measure == 'induced_lattice': - if self._normaliz_field in (ZZ, QQ): + if self._internal_base_ring in (ZZ, QQ): return self._nmz_result(cone, 'Volume') else: return self._nmz_result(cone, 'RenfVolume') @@ -2286,8 +2202,9 @@ class functions. sage: S = polytopes.simplex(3, backend = 'normaliz'); S # optional - pynormaliz A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices - sage: G = S.restricted_automorphism_group(output = 'permutation'); G # optional - pynormaliz - Permutation Group with generators [(2,3), (1,2), (0,1)] + sage: G = S.restricted_automorphism_group(output = 'permutation'); # optional - pynormaliz + sage: G.is_isomorphic(SymmetricGroup(4)) # optional - pynormaliz + True sage: len(G) # optional - pynormaliz 24 sage: Hstar = S._Hstar_function_normaliz(G); Hstar # optional - pynormaliz @@ -2307,10 +2224,9 @@ class functions. sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: K = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz - sage: G = K.subgroup(gens = [K[6]]); G # optional - pynormaliz - Subgroup generated by [(0,2)(1,3)(4,6)(5,7)] of (Permutation Group with generators [(2,4)(3,5), (1,2)(5,6), (0,1)(2,3)(4,5)(6,7), (0,7)(1,3)(2,5)(4,6)]) + sage: G = K.subgroup(gens = [K([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: conj_reps = G.conjugacy_classes_representatives() # optional - pynormaliz - sage: Dict = P.permutations_to_matrices(conj_reps, acting_group = G) # optional - pynormaliz + sage: Dict = P.permutations_to_matrices(conj_reps, acting_group = G) # optional - pynormaliz sage: list(Dict.keys())[0] # optional - pynormaliz (0,2)(1,3)(4,6)(5,7) sage: list(Dict.values())[0] # optional - pynormaliz @@ -2517,13 +2433,15 @@ class functions of the acting group. A character `\rho` is effective if The `H^*` series of the two-dimensional permutahedron under the action of the symmetric group is effective:: - sage: p2 = polytopes.permutahedron(3, backend = 'normaliz') # optional - pynormaliz - sage: G = p2.restricted_automorphism_group(output='permutation') # optional - pynormaliz - sage: H = G.subgroup(gens=[G.gens()[1],G.gens()[2]]) # optional - pynormaliz - sage: H.order() # optional - pynormaliz - 6 - sage: [Hstar, Hlin] = [p2.Hstar_function(H), p2.Hstar_function(H, output = 'Hstar_as_lin_comb')] # optional - pynormaliz - sage: p2._is_effective_normaliz(Hstar,Hlin) # optional - pynormaliz + sage: p3 = polytopes.permutahedron(3, backend = 'normaliz') # optional - pynormaliz + sage: G = p3.restricted_automorphism_group(output='permutation') # optional - pynormaliz + sage: reflection12 = G([(0,2),(1,4),(3,5)]) # optional - pynormaliz + sage: reflection23 = G([(0,1),(2,3),(4,5)]) # optional - pynormaliz + sage: S3 = G.subgroup(gens=[reflection12, reflection23]) # optional - pynormaliz + sage: S3.is_isomorphic(SymmetricGroup(3)) # optional - pynormaliz + True + sage: [Hstar, Hlin] = [p3.Hstar_function(S3), p3.Hstar_function(S3, output = 'Hstar_as_lin_comb')] # optional - pynormaliz + sage: p3._is_effective_normaliz(Hstar,Hlin) # optional - pynormaliz True If the `H^*`-series is not polynomial, then it is not effective:: @@ -2531,7 +2449,7 @@ class functions of the acting group. A character `\rho` is effective if sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: G = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz - sage: H = G.subgroup(gens = [G[6]]) # optional - pynormaliz + sage: H = G.subgroup(gens = [G([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: Hstar = P.Hstar_function(H); Hstar # optional - pynormaliz (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3 + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1) sage: Hstar_lin = P.Hstar_function(H, output = 'Hstar_as_lin_comb') # optional - pynormaliz diff --git a/src/sage/geometry/polyhedron/backend_number_field.py b/src/sage/geometry/polyhedron/backend_number_field.py new file mode 100644 index 00000000000..437550de3aa --- /dev/null +++ b/src/sage/geometry/polyhedron/backend_number_field.py @@ -0,0 +1,166 @@ +r""" +The Python backend, using number fields internally +""" + +# **************************************************************************** +# Copyright (C) 2016-2022 Matthias Köppe <mkoeppe at math.ucdavis.edu> +# 2016-2018 Travis Scrimshaw +# 2017 Jeroen Demeyer +# 2018-2020 Jean-Philippe Labbé +# 2019 Vincent Delecroix +# 2019-2021 Jonathan Kliem +# 2019-2021 Sophia Elia +# 2020 Frédéric Chapoton +# 2022 Yuan Zhou +# +# 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 .backend_field import Polyhedron_field +from .base_number_field import Polyhedron_base_number_field + + +class Polyhedron_number_field(Polyhedron_field, Polyhedron_base_number_field): + r""" + Polyhedra whose data can be converted to number field elements + + All computations are done internally using a fixed real embedded number field, + which is determined automatically. + + INPUT: + + - ``Vrep`` -- a list ``[vertices, rays, lines]`` or ``None``. + + - ``Hrep`` -- a list ``[ieqs, eqns]`` or ``None``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[[1], [sqrt(2)]], backend='number_field') # optional - sage.rings.number_field + sage: P # optional - sage.rings.number_field + A 1-dimensional polyhedron + in (Symbolic Ring)^1 defined as the convex hull of 2 vertices + sage: P.vertices() # optional - sage.rings.number_field + (A vertex at (1), A vertex at (sqrt(2))) + + sage: P = polytopes.icosahedron(exact=True, backend='number_field') # optional - sage.rings.number_field + sage: P # optional - sage.rings.number_field + A 3-dimensional polyhedron + in (Number Field in sqrt5 with defining polynomial x^2 - 5 + with sqrt5 = 2.236067977499790?)^3 + defined as the convex hull of 12 vertices + + sage: x = polygen(ZZ); P = Polyhedron( # optional - sage.rings.number_field + ....: vertices=[[sqrt(2)], [AA.polynomial_root(x^3-2, RIF(0,3))]], + ....: backend='number_field') + sage: P # optional - sage.rings.number_field + A 1-dimensional polyhedron + in (Symbolic Ring)^1 defined as the convex hull of 2 vertices + sage: P.vertices() # optional - sage.rings.number_field + (A vertex at (sqrt(2)), A vertex at (2^(1/3))) + + TESTS: + + Tests from :class:`~sage.geometry.polyhedron.backend_field.Polyhedron_field` -- + here the data are already either in a number field or in ``AA``:: + + sage: p = Polyhedron(vertices=[(0,0),(AA(2).sqrt(),0),(0,AA(3).sqrt())], # optional - sage.rings.number_field + ....: rays=[(1,1)], lines=[], backend='number_field', base_ring=AA) + sage: TestSuite(p).run() # optional - sage.rings.number_field + + sage: K.<sqrt3> = QuadraticField(3) # optional - sage.rings.number_field + sage: p = Polyhedron([(0,0), (1,0), (1/2, sqrt3/2)], backend='number_field') # optional - sage.rings.number_field + sage: TestSuite(p).run() # optional - sage.rings.number_field + + sage: K.<phi> = NumberField(x^2-x-1, embedding=1.618) # optional - sage.rings.number_field + sage: P1 = Polyhedron([[0,1], [1,1], [1,-phi+1]], backend='number_field') # optional - sage.rings.number_field + sage: P2 = Polyhedron(ieqs=[[-1,-phi,0]], backend='number_field') # optional - sage.rings.number_field + sage: P1.intersection(P2) # optional - sage.rings.number_field + The empty polyhedron + in (Number Field in phi with defining polynomial x^2 - x - 1 + with phi = 1.618033988749895?)^2 + + sage: Polyhedron(lines=[[1]], backend='number_field') + A 1-dimensional polyhedron in QQ^1 defined as the convex hull of 1 vertex and 1 line + """ + + def _init_from_Vrepresentation(self, vertices, rays, lines, + minimize=True, verbose=False): + """ + Construct polyhedron from V-representation data. + + INPUT: + + - ``vertices`` -- list of points. Each point can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``rays`` -- list of rays. Each ray can be specified as any + iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``lines`` -- list of lines. Each line can be specified as + any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``verbose`` -- boolean (default: ``False``). Whether to print + verbose output for debugging purposes. + + EXAMPLES:: + + sage: p = Polyhedron(ambient_dim=2, backend='number_field') + sage: from sage.geometry.polyhedron.backend_number_field import Polyhedron_number_field + sage: Polyhedron_number_field._init_from_Vrepresentation(p, [(0,0)], [], []) + + TESTS: + + Check that the coordinates of a vertex get simplified in the Symbolic Ring:: + + sage: p = Polyhedron(ambient_dim=2, base_ring=SR, backend='number_field') + sage: from sage.geometry.polyhedron.backend_number_field import Polyhedron_number_field + sage: Polyhedron_number_field._init_from_Vrepresentation(p, [(0,1/2),(sqrt(2),0),(4,5/6)], [], []); p + A 2-dimensional polyhedron in (Symbolic Ring)^2 defined as the convex hull of 3 vertices + sage: p.vertices()[0][0] + 0 + """ + (vertices, rays, lines), internal_base_ring \ + = self._compute_data_lists_and_internal_base_ring((vertices, rays, lines), + lambda *x: x, lambda *x: x) + self._internal_base_ring = internal_base_ring + super()._init_from_Vrepresentation(vertices, rays, lines, + minimize=minimize, verbose=verbose, + internal_base_ring=internal_base_ring) + + def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): + """ + Construct polyhedron from H-representation data. + + INPUT: + + - ``ieqs`` -- list of inequalities. Each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``eqns`` -- list of equalities. Each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``verbose`` -- boolean (default: ``False``). Whether to print + verbose output for debugging purposes. + + TESTS:: + + sage: p = Polyhedron(ambient_dim=2, backend='number_field') + sage: from sage.geometry.polyhedron.backend_number_field import Polyhedron_number_field + sage: Polyhedron_number_field._init_from_Hrepresentation(p, [(1, 2, 3)], []) + """ + (ieqs, eqns), internal_base_ring \ + = self._compute_data_lists_and_internal_base_ring((ieqs, eqns), + lambda *x: x, lambda *x: x) + self._internal_base_ring = internal_base_ring + super()._init_from_Hrepresentation(ieqs, eqns, + minimize=minimize, verbose=verbose, + internal_base_ring=internal_base_ring) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index dbf8b4370b0..77a11c53d21 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -974,11 +974,12 @@ def permutations_to_matrices(self, conj_class_reps, acting_group=None, additiona sage: aut_square = square.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: conj_reps = aut_square.conjugacy_classes_representatives() # optional - pynormaliz sage: gens_dict = square.permutations_to_matrices(conj_reps); # optional - pynormaliz - sage: conj_reps[1],gens_dict[conj_reps[1]] # optional - pynormaliz + sage: rotation_180 = aut_square([(0,3),(1,2)]) # optional - pynormaliz + sage: rotation_180,gens_dict[rotation_180] # optional - pynormaliz ( - [0 1 0] - [1 0 0] - (1,2), [0 0 1] + [-1 0 0] + [ 0 -1 0] + (0,3)(1,2), [ 0 0 1] ) This example tests the functionality for additional elements:: @@ -986,8 +987,7 @@ def permutations_to_matrices(self, conj_class_reps, acting_group=None, additiona sage: C = polytopes.cross_polytope(2) sage: G = C.restricted_automorphism_group(output = 'permutation') sage: conj_reps = G.conjugacy_classes_representatives() - sage: add_elt = G[6]; add_elt - (0,2,3,1) + sage: add_elt = G([(0,2,3,1)]) sage: dict = C.permutations_to_matrices(conj_reps,additional_elts = [add_elt]) sage: dict[add_elt] [ 0 1 0] diff --git a/src/sage/geometry/polyhedron/base2.py b/src/sage/geometry/polyhedron/base2.py index 0d7606d96d0..9afeeaaef1f 100644 --- a/src/sage/geometry/polyhedron/base2.py +++ b/src/sage/geometry/polyhedron/base2.py @@ -675,3 +675,132 @@ def random_integral_point(self, **kwds): raise EmptySetError('polyhedron does not contain any integral points') return self.get_integral_point(current_randstate().python_random().randint(0, count-1), **kwds) + + def generating_function_of_integral_points(self, **kwds): + r""" + Return the multivariate generating function of the + integral points of this polyhedron. + + To be precise, this returns + + .. MATH:: + + \sum_{(r_0,\dots,r_{d-1}) \in \mathit{polyhedron}\cap \ZZ^d} + y_0^{r_0} \dots y_{d-1}^{r_{d-1}}. + + This calls + :func:`~sage.geometry.polyhedron.generating_function.generating_function_of_integral_points`, + so have a look at the documentation and examples there. + + INPUT: + + The following keyword arguments are passed to + :func:`~sage.geometry.polyhedron.generating_function.generating_function_of_integral_points`: + + - ``split`` -- (default: ``False``) a boolean or list + + - ``split=False`` computes the generating function directly, + without any splitting. + + - When ``split`` is a list of disjoint polyhedra, then + for each of these polyhedra, this polyhedron is intersected with it, + its generating function computed and all these generating functions + are summed up. + + - ``split=True`` splits into `d!` disjoint polyhedra. + + - ``result_as_tuple`` -- (default: ``None``) a boolean or ``None`` + + This specifies whether the output is a (partial) factorization + (``result_as_tuple=False``) or a sum of such (partial) + factorizations (``result_as_tuple=True``). By default + (``result_as_tuple=None``), this is automatically determined. + If the output is a sum, it is represented as a tuple whose + entries are the summands. + + - ``indices`` -- (default: ``None``) a list or tuple + + If this + is ``None``, this is automatically determined. + + - ``name`` -- (default: ``'y'``) a string + + The variable names of the Laurent polynomial ring of the output + are this string followed by an integer. + + - ``names`` -- a list or tuple of names (strings), or a comma separated string + + ``name`` is extracted from ``names``, therefore ``names`` has to contain + exactly one variable name, and ``name`` and``names`` cannot be specified + both at the same time. + + - ``Factorization_sort`` (default: ``False``) and + ``Factorization_simplify`` (default: ``True``) -- booleans + + These are passed on to + :class:`sage.structure.factorization.Factorization` when creating + the result. + + - ``sort_factors`` -- (default: ``False``) a boolean + + If set, then + the factors of the output are sorted such that the numerator is + first and only then all factors of the denominator. It is ensured + that the sorting is always the same; use this for doctesting. + + OUTPUT: + + The generating function as a (partial) + :class:`~sage.structure.factorization.Factorization` + of the result whose factors are Laurent polynomials + + The result might be a tuple of such factorizations + (depending on the parameter ``result_as_tuple``) as well. + + .. NOTE:: + + At the moment, only polyhedra with nonnegative coordinates + (i.e. a polyhedron in the nonnegative orthant) are handled. + + EXAMPLES:: + + sage: P2 = ( + ....: Polyhedron(ieqs=[(0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, -1)]), + ....: Polyhedron(ieqs=[(0, -1, 0, 1), (0, 1, 0, 0), (0, 0, 1, 0)])) + sage: P2[0].generating_function_of_integral_points(sort_factors=True) + 1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 + sage: P2[1].generating_function_of_integral_points(sort_factors=True) + 1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1 + sage: (P2[0] & P2[1]).Hrepresentation() + (An equation (1, 0, -1) x + 0 == 0, + An inequality (1, 0, 0) x + 0 >= 0, + An inequality (0, 1, 0) x + 0 >= 0) + sage: (P2[0] & P2[1]).generating_function_of_integral_points(sort_factors=True) + 1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 + + The number of integer partitions + `1 \leq r_0 \leq r_1 \leq r_2 \leq r_3 \leq r_4`:: + + sage: P = Polyhedron(ieqs=[(-1, 1, 0, 0, 0, 0), (0, -1, 1, 0, 0, 0), + ....: (0, 0, -1, 1, 0, 0), (0, 0, 0, -1, 1, 0), + ....: (0, 0, 0, 0, -1, 1)]) + sage: f = P.generating_function_of_integral_points(sort_factors=True); f + y0*y1*y2*y3*y4 * (-y4 + 1)^-1 * (-y3*y4 + 1)^-1 * (-y2*y3*y4 + 1)^-1 * + (-y1*y2*y3*y4 + 1)^-1 * (-y0*y1*y2*y3*y4 + 1)^-1 + sage: f = f.value() + sage: P.<z> = PowerSeriesRing(ZZ) + sage: c = f.subs({y: z for y in f.parent().gens()}); c + z^5 + z^6 + 2*z^7 + 3*z^8 + 5*z^9 + 7*z^10 + 10*z^11 + 13*z^12 + 18*z^13 + + 23*z^14 + 30*z^15 + 37*z^16 + 47*z^17 + 57*z^18 + 70*z^19 + 84*z^20 + + 101*z^21 + 119*z^22 + 141*z^23 + 164*z^24 + O(z^25) + sage: [Partitions(k, length=5).cardinality() for k in range(5,20)] == \ + ....: c.truncate().coefficients(sparse=False)[5:20] + True + + .. SEEALSO:: + + More examples can be found at + :func:`~sage.geometry.polyhedron.generating_function.generating_function_of_integral_points`. + """ + from .generating_function import generating_function_of_integral_points + return generating_function_of_integral_points(self, **kwds) diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index f16bce682b9..04d1fa0314b 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -190,9 +190,9 @@ def polar(self, in_affine_span=False): sage: (P*point).polar(in_affine_span=True) == P.polar()*point True - TESTS:: + TESTS: - Check that :trac:`25081` is fixed:: + Check that :trac:`25081` is fixed:: sage: C = polytopes.hypercube(4,backend='cdd') sage: C.polar().backend() @@ -2440,7 +2440,7 @@ def _test_lawrence(self, tester=None, **options): if self.backend() == 'normaliz' and not self.base_ring() in (ZZ, QQ): # Speeds up the doctest for significantly. - self = self.change_ring(self._normaliz_field) + self = self.change_ring(self._internal_base_ring) if not self.is_compact(): with tester.assertRaises(NotImplementedError): diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index 7a1f4882440..2dd1117ad79 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -72,7 +72,7 @@ class Polyhedron_base6(Polyhedron_base5): %% opacity = 0.8 %% vertex_color = green %% axis = False - <BLANKLINE> + %% %% Coordinate of the vertices: %% \coordinate (1.00000, -1.00000, -1.00000) at (1.00000, -1.00000, -1.00000); diff --git a/src/sage/geometry/polyhedron/base_QQ.py b/src/sage/geometry/polyhedron/base_QQ.py index 95c4f4e2b1a..0efcb15f1a2 100644 --- a/src/sage/geometry/polyhedron/base_QQ.py +++ b/src/sage/geometry/polyhedron/base_QQ.py @@ -846,13 +846,9 @@ def fixed_subpolytope(self, vertex_permutation): You can obtain non-trivial examples:: - sage: fsp1 = Cube.fixed_subpolytope(reprs[8]);fsp1 # optional - pynormaliz - A 0-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex - sage: fsp1.vertices() # optional - pynormaliz - (A vertex at (0, 0, 0),) - sage: fsp2 = Cube.fixed_subpolytope(reprs[3]);fsp2 # optional - pynormaliz + sage: fsp = Cube.fixed_subpolytope(AG([(0,1),(2,3),(4,5),(6,7)]));fsp # optional - pynormaliz A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices - sage: fsp2.vertices() # optional - pynormaliz + sage: fsp.vertices() # optional - pynormaliz (A vertex at (-1, -1, 0), A vertex at (-1, 1, 0), A vertex at (1, -1, 0), @@ -860,22 +856,17 @@ def fixed_subpolytope(self, vertex_permutation): The next example shows that fixed_subpolytope works for rational polytopes:: - sage: P = Polyhedron(vertices = [[0,0],[3/2,0],[3/2,3/2],[0,3/2]], backend ='normaliz') # optional - pynormaliz - sage: P.vertices() # optional - pynormaliz - (A vertex at (0, 0), - A vertex at (0, 3/2), - A vertex at (3/2, 0), - A vertex at (3/2, 3/2)) - sage: G = P.restricted_automorphism_group(output = 'permutation');G # optional - pynormaliz - Permutation Group with generators [(1,2), (0,1)(2,3), (0,3)] - sage: len(G) # optional - pynormaliz - 8 - sage: G[2] # optional - pynormaliz - (0,1)(2,3) - sage: fixed_set = P.fixed_subpolytope(G[2]); fixed_set # optional - pynormaliz - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices - sage: fixed_set.vertices() # optional - pynormaliz - (A vertex at (0, 3/4), A vertex at (3/2, 3/4)) + sage: P = Polyhedron(vertices=[[0],[1/2]], backend='normaliz') # optional - pynormaliz + sage: P.vertices() # optional - pynormaliz + (A vertex at (0), A vertex at (1/2)) + sage: G = P.restricted_automorphism_group(output='permutation');G # optional - pynormaliz + Permutation Group with generators [(0,1)] + sage: len(G) # optional - pynormaliz + 2 + sage: fixed_set = P.fixed_subpolytope(G.gens()[0]); fixed_set # optional - pynormaliz + A 0-dimensional polyhedron in QQ^1 defined as the convex hull of 1 vertex + sage: fixed_set.vertices_list() # optional - pynormaliz + [[1/4]] """ if self.is_empty(): raise NotImplementedError('empty polyhedra are not supported') @@ -943,14 +934,10 @@ def fixed_subpolytopes(self, conj_class_reps): sage: aut_p = p.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: aut_p.order() # optional - pynormaliz 8 - sage: conj_list = aut_p.conjugacy_classes_representatives(); conj_list # optional - pynormaliz - [(), (1,2), (0,1)(2,3), (0,1,3,2), (0,3)(1,2)] - sage: p.fixed_subpolytopes(conj_list) # optional - pynormaliz - {(): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices, - (1,2): A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices, - (0,1)(2,3): A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices, - (0,1,3,2): A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, - (0,3)(1,2): A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex} + sage: conj_list = aut_p.conjugacy_classes_representatives(); # optional - pynormaliz + sage: fixedpolytopes_dictionary = p.fixed_subpolytopes(conj_list) # optional - pynormaliz + sage: fixedpolytopes_dictionary[aut_p([(0,3),(1,2)])] # optional - pynormaliz + A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex TESTS:: @@ -1025,10 +1012,9 @@ class functions. sage: S = polytopes.simplex(3, backend = 'normaliz'); S # optional - pynormaliz A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices - sage: G = S.restricted_automorphism_group(output = 'permutation'); G # optional - pynormaliz - Permutation Group with generators [(2,3), (1,2), (0,1)] - sage: len(G) # optional - pynormaliz - 24 + sage: G = S.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz + sage: G.is_isomorphic(SymmetricGroup(4)) # optional - pynormaliz + True sage: Hstar = S._Hstar_function_normaliz(G); Hstar # optional - pynormaliz chi_4 sage: G.character_table() # optional - pynormaliz @@ -1046,10 +1032,9 @@ class functions. sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: K = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz - sage: G = K.subgroup(gens = [K[6]]); G # optional - pynormaliz - Subgroup generated by [(0,2)(1,3)(4,6)(5,7)] of (Permutation Group with generators [(2,4)(3,5), (1,2)(5,6), (0,1)(2,3)(4,5)(6,7), (0,7)(1,3)(2,5)(4,6)]) + sage: G = K.subgroup(gens = [K([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: conj_reps = G.conjugacy_classes_representatives() # optional - pynormaliz - sage: Dict = P.permutations_to_matrices(conj_reps, acting_group = G) # optional - pynormaliz + sage: Dict = P.permutations_to_matrices(conj_reps, acting_group = G) # optional - pynormaliz sage: list(Dict.keys())[0] # optional - pynormaliz (0,2)(1,3)(4,6)(5,7) sage: list(Dict.values())[0] # optional - pynormaliz @@ -1166,13 +1151,15 @@ class functions of the acting group. A character `\rho` is effective if The `H^*` series of the two-dimensional permutahedron under the action of the symmetric group is effective:: - sage: p2 = polytopes.permutahedron(3, backend = 'normaliz') # optional - pynormaliz - sage: G = p2.restricted_automorphism_group(output='permutation') # optional - pynormaliz - sage: H = G.subgroup(gens=[G.gens()[1],G.gens()[2]]) # optional - pynormaliz - sage: H.order() # optional - pynormaliz - 6 - sage: [Hstar, Hlin] = [p2.Hstar_function(H), p2.Hstar_function(H, output = 'Hstar_as_lin_comb')] # optional - pynormaliz - sage: p2.is_effective(Hstar,Hlin) # optional - pynormaliz + sage: p3 = polytopes.permutahedron(3, backend = 'normaliz') # optional - pynormaliz + sage: G = p3.restricted_automorphism_group(output='permutation') # optional - pynormaliz + sage: reflection12 = G([(0,2),(1,4),(3,5)]) # optional - pynormaliz + sage: reflection23 = G([(0,1),(2,3),(4,5)]) # optional - pynormaliz + sage: S3 = G.subgroup(gens=[reflection12, reflection23]) # optional - pynormaliz + sage: S3.is_isomorphic(SymmetricGroup(3)) # optional - pynormaliz + True + sage: [Hstar, Hlin] = [p3.Hstar_function(S3), p3.Hstar_function(S3, output = 'Hstar_as_lin_comb')] # optional - pynormaliz + sage: p3.is_effective(Hstar,Hlin) # optional - pynormaliz True If the `H^*`-series is not polynomial, then it is not effective:: @@ -1180,7 +1167,7 @@ class functions of the acting group. A character `\rho` is effective if sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: G = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz - sage: H = G.subgroup(gens = [G[6]]) # optional - pynormaliz + sage: H = G.subgroup(gens = [G([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: Hstar = P.Hstar_function(H); Hstar # optional - pynormaliz (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3 + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1) sage: Hstar_lin = P.Hstar_function(H, output = 'Hstar_as_lin_comb') # optional - pynormaliz diff --git a/src/sage/geometry/polyhedron/base_number_field.py b/src/sage/geometry/polyhedron/base_number_field.py new file mode 100644 index 00000000000..c802207303f --- /dev/null +++ b/src/sage/geometry/polyhedron/base_number_field.py @@ -0,0 +1,118 @@ +r""" +Support for internal use of number fields in backends for polyhedral computations +""" + +# **************************************************************************** +# Copyright (C) 2016-2022 Matthias Köppe <mkoeppe at math.ucdavis.edu> +# 2016-2018 Travis Scrimshaw +# 2017 Jeroen Demeyer +# 2018-2020 Jean-Philippe Labbé +# 2019 Vincent Delecroix +# 2019-2021 Jonathan Kliem +# 2019-2021 Sophia Elia +# 2020 Frédéric Chapoton +# 2022 Yuan Zhou +# +# 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_ring import ZZ +from sage.rings.rational_field import QQ + +from .base import Polyhedron_base + + +def _number_field_elements_from_algebraics_list_of_lists_of_lists(listss, **kwds): + r""" + Like `number_field_elements_from_algebraics`, but for a list of lists of lists. + + EXAMPLES:: + + sage: rt2 = AA(sqrt(2)); rt2 # optional - sage.rings.number_field + 1.414213562373095? + sage: rt3 = AA(sqrt(3)); rt3 # optional - sage.rings.number_field + 1.732050807568878? + sage: from sage.geometry.polyhedron.base_number_field import _number_field_elements_from_algebraics_list_of_lists_of_lists + sage: K, results, hom = _number_field_elements_from_algebraics_list_of_lists_of_lists([[[rt2], [1]], [[rt3]], [[1], []]]); results # optional - sage.rings.number_field + [[[-a^3 + 3*a], [1]], [[a^2 - 2]], [[1], []]] + """ + from sage.rings.qqbar import number_field_elements_from_algebraics + numbers = [] + for lists in listss: + for list in lists: + numbers.extend(list) + K, K_numbers, hom = number_field_elements_from_algebraics(numbers, **kwds) + g = iter(K_numbers) + return K, [ [ [ next(g) for _ in list ] for list in lists ] for lists in listss ], hom + + +class Polyhedron_base_number_field(Polyhedron_base): + + def _compute_data_lists_and_internal_base_ring(self, data_lists, convert_QQ, convert_NF): + r""" + Compute data lists in Normaliz or ``number_field`` backend format and the internal base ring of the data. + + EXAMPLES:: + + sage: p = Polyhedron(vertices=[(0,1/2),(2,0),(4,5/6)], # optional - pynormaliz + ....: base_ring=AA, backend='normaliz') + sage: def convert_QQ(ieqs, eqs): # optional - pynormaliz + ....: return [ [ 1000*x for x in ieq ] for ieq in ieqs], \ + ....: [ [ 1000*x for x in eq ] for eq in eqs] + sage: def convert_NF(ieqs, eqs): # optional - pynormaliz + ....: return ieqs, eqs + sage: p._compute_data_lists_and_internal_base_ring([[[1]], [[1/2]]], # optional - pynormaliz + ....: convert_QQ, convert_NF) + (([[1000]], [[500]]), Rational Field) + sage: p._compute_data_lists_and_internal_base_ring([[[AA(1)]], [[1/2]]], # optional - pynormaliz + ....: convert_QQ, convert_NF) + (([[1000]], [[500]]), Rational Field) + sage: p._compute_data_lists_and_internal_base_ring([[[AA(sqrt(2))]], [[1/2]]], # optional - pynormaliz # optional - sage.rings.number_field + ....: convert_QQ, convert_NF) + ([[[a]], [[1/2]]], + Number Field in a with defining polynomial y^2 - 2 with a = 1.414213562373095?) + + TESTS:: + + sage: K.<a> = QuadraticField(-5) # optional - sage.rings.number_field + sage: p = Polyhedron(vertices=[(a,1/2),(2,0),(4,5/6)], # indirect doctest # optional - pynormaliz # optional - sage.rings.number_field + ....: base_ring=K, backend='normaliz') + Traceback (most recent call last): + ... + ValueError: invalid base ring: Number Field in a ... is not real embedded + + Checks that :trac:`30248` is fixed:: + + sage: q = Polyhedron(backend='normaliz', base_ring=AA, # indirect doctest # optional - pynormaliz # optional - sage.rings.number_field + ....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)]); q + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays + sage: -q # optional - pynormaliz # optional - sage.rings.number_field + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays + """ + from sage.categories.number_fields import NumberFields + from sage.rings.real_double import RDF + + if self.base_ring() in (QQ, ZZ): + internal_base_ring = QQ + internal_data_lists = convert_QQ(*data_lists) + else: + # Allows to re-iterate if K is QQ below when data_lists contain + # iterators: + data_lists = [tuple(_) for _ in data_lists] + internal_data_lists = convert_NF(*data_lists) + if self.base_ring() in NumberFields(): + if not RDF.has_coerce_map_from(self.base_ring()): + raise ValueError("invalid base ring: {} is a number field that is not real embedded".format(self.base_ring())) + internal_base_ring = self.base_ring() + else: + K, internal_data_lists, hom = _number_field_elements_from_algebraics_list_of_lists_of_lists(internal_data_lists, embedded=True) + internal_base_ring = K + if K is QQ: + # Compute it with Normaliz, not QNormaliz + internal_data_lists = convert_QQ(*[ [ [ QQ(x) for x in v ] for v in l] + for l in data_lists ]) + return internal_data_lists, internal_base_ring diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index dbe00570c85..7f11ac13593 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -269,7 +269,7 @@ REFERENCES: Komei Fukuda's `FAQ in Polyhedral Computation - <https://www.inf.ethz.ch/personal/fukudak/polyfaq/polyfaq.html>`_ + <https://cddlib.github.io/polyhedral_faq/>`_ AUTHORS: @@ -291,6 +291,8 @@ # https://www.gnu.org/licenses/ ######################################################################## +import sage.geometry.abc + from sage.rings.integer_ring import ZZ from sage.rings.real_double import RDF from sage.rings.real_mpfr import RR @@ -313,11 +315,16 @@ def Polyhedron(vertices=None, rays=None, lines=None, INPUT: - - ``vertices`` -- list of points. Each point can be specified as + - ``vertices`` -- iterable of points. Each point can be specified as any iterable container of ``base_ring`` elements. If ``rays`` or ``lines`` are specified but no ``vertices``, the origin is taken to be the single vertex. + Instead of vertices, the first argument can also be an object + that can be converted to a :func:`Polyhedron` via an :meth:`as_polyhedron` + or :meth:`polyhedron` method. In this case, the following 5 arguments + cannot be provided. + - ``rays`` -- list of rays. Each ray can be specified as any iterable container of ``base_ring`` elements. @@ -332,6 +339,10 @@ def Polyhedron(vertices=None, rays=None, lines=None, any iterable container of ``base_ring`` elements. An entry equal to ``[-1,7,3,4]`` represents the equality `7x_1+3x_2+4x_3= 1`. + - ``ambient_dim`` -- integer. The ambient space dimension. Usually + can be figured out automatically from the H/Vrepresentation + dimensions. + - ``base_ring`` -- a sub-field of the reals implemented in Sage. The field over which the polyhedron will be defined. For ``QQ`` and algebraic extensions, exact arithmetic will be @@ -339,10 +350,6 @@ def Polyhedron(vertices=None, rays=None, lines=None, point arithmetic is faster but might give the wrong result for degenerate input. - - ``ambient_dim`` -- integer. The ambient space dimension. Usually - can be figured out automatically from the H/Vrepresentation - dimensions. - - ``backend`` -- string or ``None`` (default). The backend to use. Valid choices are * ``'cdd'``: use cdd @@ -465,17 +472,45 @@ def Polyhedron(vertices=None, rays=None, lines=None, ... ValueError: invalid base ring - Create a mutable polyhedron:: - - sage: P = Polyhedron(vertices=[[0, 1], [1, 0]], mutable=True) - sage: P.is_mutable() - True - sage: hasattr(P, "_Vrepresentation") - False - sage: P.Vrepresentation() - (A vertex at (0, 1), A vertex at (1, 0)) - sage: hasattr(P, "_Vrepresentation") - True + Converting from a given polyhedron:: + + sage: cb = polytopes.cube(); cb + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices + sage: Polyhedron(cb, base_ring=QQ) + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices + + Converting from other objects to a polyhedron:: + + sage: quadrant = Cone([(1,0), (0,1)]) + sage: Polyhedron(quadrant) + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + sage: Polyhedron(quadrant, base_ring=QQ) + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays + + sage: o = lattice_polytope.cross_polytope(2) + sage: Polyhedron(o) + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices + sage: Polyhedron(o, base_ring=QQ) + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices + + sage: p = MixedIntegerLinearProgram(solver='PPL') + sage: x, y = p['x'], p['y'] + sage: p.add_constraint(x <= 1) + sage: p.add_constraint(x >= -1) + sage: p.add_constraint(y <= 1) + sage: p.add_constraint(y >= -1) + sage: Polyhedron(o) + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices + sage: Polyhedron(o, base_ring=QQ) + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices + + sage: H.<x,y> = HyperplaneArrangements(QQ) + sage: h = x + y - 1; h + Hyperplane x + y - 1 + sage: Polyhedron(h, base_ring=ZZ) + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: Polyhedron(h) + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line .. NOTE:: @@ -486,7 +521,6 @@ def Polyhedron(vertices=None, rays=None, lines=None, input data - the results can depend upon the tolerance setting of cdd. - TESTS: Check that giving ``float`` input gets converted to ``RDF`` (see :trac:`22605`):: @@ -569,10 +603,53 @@ def Polyhedron(vertices=None, rays=None, lines=None, sage: Polyhedron(ambient_dim=2, vertices=[], rays=[], lines=[], base_ring=QQ) The empty polyhedron in QQ^2 + Create a mutable polyhedron:: + + sage: P = Polyhedron(vertices=[[0, 1], [1, 0]], mutable=True) + sage: P.is_mutable() + True + sage: hasattr(P, "_Vrepresentation") + False + sage: P.Vrepresentation() + (A vertex at (0, 1), A vertex at (1, 0)) + sage: hasattr(P, "_Vrepresentation") + True + .. SEEALSO:: :mod:`Library of polytopes <sage.geometry.polyhedron.library>` """ + # Special handling for first argument, for coercion-like uses + constructor = None + first_arg = vertices + if isinstance(first_arg, sage.geometry.abc.Polyhedron): + constructor = first_arg.change_ring + try: + # PolyhedronFace.as_polyhedron (it also has a "polyhedron" method with a different purpose) + constructor = first_arg.as_polyhedron + except AttributeError: + try: + # ConvexRationalPolyhedralCone, LatticePolytopeClass, MixedIntegerLinearProgram, Hyperplane + constructor = first_arg.polyhedron + except AttributeError: + pass + if constructor: + if not all(x is None for x in (rays, lines, ieqs, eqns, ambient_dim)): + raise ValueError('if a polyhedron is given, cannot provide H- and V-representations objects') + # Only pass non-default arguments + kwds = {} + if base_ring is not None: + kwds['base_ring'] = base_ring + if verbose is not False: + kwds['verbose'] = verbose + if backend is not None: + kwds['backend'] = backend + if minimize is not True: + kwds['minimize'] = minimize + if mutable is not False: + kwds['mutable'] = mutable + return constructor(**kwds) + got_Vrep = not ((vertices is None) and (rays is None) and (lines is None)) got_Hrep = not ((ieqs is None) and (eqns is None)) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index d9dd5a6c4d6..2166f016ba8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -750,7 +750,7 @@ def is_compact(self): for V in self.ambient_Vrepresentation()) @cached_method - def as_polyhedron(self): + def as_polyhedron(self, **kwds): """ Return the face as an independent polyhedron. @@ -774,7 +774,12 @@ def as_polyhedron(self): P = self._polyhedron parent = P.parent() Vrep = (self.vertices(), self.rays(), self.lines()) - return P.__class__(parent, Vrep, None) + result = P.__class__(parent, Vrep, None) + if any(kwds.get(kwd) is not None + for kwd in ('base_ring', 'backend')): + from .constructor import Polyhedron + return Polyhedron(result, **kwds) + return result def _some_elements_(self): r""" @@ -1066,7 +1071,7 @@ def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): # Equations before inequalities in Hrep. H_indices = tuple(range(n_equations)) H_indices += tuple(x+n_equations for x in combinatorial_face.ambient_H_indices(add_equations=False)) - elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'polymake'): + elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'number_field', 'polymake'): # Equations after the inequalities in Hrep. n_ieqs = polyhedron.n_inequalities() H_indices = tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) diff --git a/src/sage/geometry/polyhedron/generating_function.py b/src/sage/geometry/polyhedron/generating_function.py new file mode 100644 index 00000000000..9fadd8f3e13 --- /dev/null +++ b/src/sage/geometry/polyhedron/generating_function.py @@ -0,0 +1,1620 @@ +r""" +Generating Function of Polyhedron's Integral Points + +This module provides :func:`generating_function_of_integral_points` which +computes the generating function of the integral points of a polyhedron. + +The main function is accessible via +:meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points` +as well. + +Various +======= + +AUTHORS: + +- Daniel Krenn (2016, 2021) + +ACKNOWLEDGEMENT: + +- Daniel Krenn is supported by the Austrian Science Fund (FWF): P 24644-N26 + and by the Austrian Science Fund (FWF): P 28466-N35. + + +Functions +========= +""" + +# ***************************************************************************** +# Copyright (C) 2016, 2021 Daniel Krenn <dev@danielkrenn.at> +# +# 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/ +# ***************************************************************************** + +Hrepresentation_str_options = {'prefix': 'b', 'style': 'positive'} + + +def generating_function_of_integral_points(polyhedron, split=False, + result_as_tuple=None, + name=None, names=None, + **kwds): + r""" + Return the multivariate generating function of the + integral points of the ``polyhedron``. + + To be precise, this returns + + .. MATH:: + + \sum_{(r_0,\dots,r_{d-1}) \in \mathit{polyhedron}\cap \ZZ^d} + y_0^{r_0} \dots y_{d-1}^{r_{d-1}}. + + INPUT: + + - ``polyhedron`` -- an instance of + :class:`~sage.geometry.polyhedron.base.Polyhedron_base` + (see also :mod:`sage.geometry.polyhedron.constructor`) + + - ``split`` -- (default: ``False``) a boolean or list + + - ``split=False`` computes the generating function directly, + without any splitting. + + - When ``split`` is a list of disjoint polyhedra, then + for each of these polyhedra, ``polyhedron`` is intersected with it, + its generating function computed and all these generating functions + are summed up. + + - ``split=True`` splits into `d!` disjoint polyhedra. + + - ``result_as_tuple`` -- (default: ``None``) a boolean or ``None`` + + This specifies whether the output is a (partial) factorization + (``result_as_tuple=False``) or a sum of such (partial) + factorizations (``result_as_tuple=True``). By default + (``result_as_tuple=None``), this is automatically determined. + If the output is a sum, it is represented as a tuple whose + entries are the summands. + + - ``indices`` -- (default: ``None``) a list or tuple + + If this + is ``None``, this is automatically determined. + + - ``name`` -- (default: ``'y'``) a string + + The variable names of the Laurent polynomial ring of the output + are this string followed by an integer. + + - ``names`` -- a list or tuple of names (strings), or a comma separated string + + ``name`` is extracted from ``names``, therefore ``names`` has to contain + exactly one variable name, and ``name`` and``names`` cannot be specified + both at the same time. + + - ``Factorization_sort`` (default: ``False``) and + ``Factorization_simplify`` (default: ``True``) -- booleans + + These are passed on to + :class:`sage.structure.factorization.Factorization` when creating + the result. + + - ``sort_factors`` -- (default: ``False``) a boolean + + If set, then + the factors of the output are sorted such that the numerator is + first and only then all factors of the denominator. It is ensured + that the sorting is always the same; use this for doctesting. + + OUTPUT: + + The generating function as a (partial) + :class:`~sage.structure.factorization.Factorization` + of the result whose factors are Laurent polynomials + + The result might be a tuple of such factorizations + (depending on the parameter ``result_as_tuple``) as well. + + .. NOTE:: + + At the moment, only polyhedra with nonnegative coordinates + (i.e. a polyhedron in the nonnegative orthant) are handled. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points + + :: + + sage: P2 = ( + ....: Polyhedron(ieqs=[(0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, -1)]), + ....: Polyhedron(ieqs=[(0, -1, 0, 1), (0, 1, 0, 0), (0, 0, 1, 0)])) + sage: generating_function_of_integral_points(P2[0], sort_factors=True) + 1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 + sage: generating_function_of_integral_points(P2[1], sort_factors=True) + 1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1 + sage: (P2[0] & P2[1]).Hrepresentation() + (An equation (1, 0, -1) x + 0 == 0, + An inequality (1, 0, 0) x + 0 >= 0, + An inequality (0, 1, 0) x + 0 >= 0) + sage: generating_function_of_integral_points(P2[0] & P2[1], sort_factors=True) + 1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 + + :: + + sage: P3 = ( + ....: Polyhedron( + ....: ieqs=[(0, 0, 0, 0, 1), (0, 0, 0, 1, 0), + ....: (0, 0, 1, 0, -1), (-1, 1, 0, -1, -1)]), + ....: Polyhedron( + ....: ieqs=[(0, 0, -1, 0, 1), (0, 1, 0, 0, -1), + ....: (0, 0, 0, 1, 0), (0, 0, 1, 0, 0), (-1, 1, -1, -1, 0)]), + ....: Polyhedron( + ....: ieqs=[(1, -1, 0, 1, 1), (1, -1, 1, 1, 0), + ....: (0, 0, 0, 0, 1), (0, 0, 0, 1, 0), (0, 0, 1, 0, 0), + ....: (1, 0, 1, 1, -1), (0, 1, 0, 0, 0), (1, 1, 1, 0, -1)]), + ....: Polyhedron( + ....: ieqs=[(0, 1, 0, -1, 0), (0, -1, 0, 0, 1), + ....: (-1, 0, -1, -1, 1), (0, 0, 1, 0, 0), (0, 0, 0, 1, 0)]), + ....: Polyhedron( + ....: ieqs=[(0, 1, 0, 0, 0), (0, 0, 1, 0, 0), + ....: (-1, -1, -1, 0, 1), (0, -1, 0, 1, 0)])) + sage: def intersect(I): + ....: I = iter(I) + ....: result = next(I) + ....: for i in I: + ....: result &= i + ....: return result + sage: for J in subsets(range(len(P3))): + ....: if not J: + ....: continue + ....: P = intersect([P3[j] for j in J]) + ....: print('{}: {}'.format(J, P.Hrepresentation())) + ....: print(generating_function_of_integral_points(P, sort_factors=True)) + [0]: (An inequality (0, 0, 0, 1) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, -1) x + 0 >= 0, + An inequality (1, 0, -1, -1) x - 1 >= 0) + y0 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 + [1]: (An inequality (0, -1, 0, 1) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, -1, -1, 0) x - 1 >= 0, + An inequality (1, 0, 0, -1) x + 0 >= 0) + (-y0^2*y2*y3 - y0^2*y3 + y0*y3 + y0) * + (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y3 + 1)^-1 * + (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 1]: (An equation (0, 1, 0, -1) x + 0 == 0, + An inequality (1, -1, -1, 0) x - 1 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0) + y0 * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 + [2]: (An inequality (-1, 0, 1, 1) x + 1 >= 0, + An inequality (-1, 1, 1, 0) x + 1 >= 0, + An inequality (0, 0, 0, 1) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (0, 1, 1, -1) x + 1 >= 0, + An inequality (1, 0, 0, 0) x + 0 >= 0, + An inequality (1, 1, 0, -1) x + 1 >= 0) + (y0^2*y1*y2*y3^2 + y0^2*y2^2*y3 + y0*y1^2*y3^2 - y0^2*y2*y3 + + y0*y1*y2*y3 - y0*y1*y3^2 - 2*y0*y1*y3 - 2*y0*y2*y3 - y0*y2 + + y0*y3 - y1*y3 + y0 + y3 + 1) * + (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1 * + (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 2]: (An equation (1, 0, -1, -1) x - 1 == 0, + An inequality (-1, 1, 1, 0) x + 1 >= 0, + An inequality (1, 0, -1, 0) x - 1 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0) + y0 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 + [1, 2]: (An equation (1, -1, -1, 0) x - 1 == 0, + An inequality (0, -1, 0, 1) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, 0, 0, -1) x + 0 >= 0, + An inequality (1, -1, 0, 0) x - 1 >= 0) + (-y0^2*y2*y3 + y0*y3 + y0) * + (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 1, 2]: (An equation (0, 1, 0, -1) x + 0 == 0, + An equation (1, -1, -1, 0) x - 1 == 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, -1, 0, 0) x - 1 >= 0) + y0 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 + [3]: (An inequality (-1, 0, 0, 1) x + 0 >= 0, + An inequality (0, -1, -1, 1) x - 1 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, 0, -1, 0) x + 0 >= 0) + (-y0*y1*y3^2 - y0*y3^2 + y0*y3 + y3) * + (-y3 + 1)^-1 * (-y0*y3 + 1)^-1 * + (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 3]: (An equation -1 == 0,) + 0 + [1, 3]: (An equation (1, 0, 0, -1) x + 0 == 0, + An inequality (1, -1, -1, 0) x - 1 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0) + y0*y3 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 1, 3]: (An equation -1 == 0,) + 0 + [2, 3]: (An equation (0, 1, 1, -1) x + 1 == 0, + An inequality (1, 0, -1, 0) x + 0 >= 0, + An inequality (-1, 1, 1, 0) x + 1 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0) + (-y0*y1*y3^2 + y0*y3 + y3) * + (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 2, 3]: (An equation -1 == 0,) + 0 + [1, 2, 3]: (An equation (1, 0, 0, -1) x + 0 == 0, + An equation (1, -1, -1, 0) x - 1 == 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, -1, 0, 0) x - 1 >= 0) + y0*y3 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 1, 2, 3]: (An equation -1 == 0,) + 0 + [4]: (An inequality (-1, -1, 0, 1) x - 1 >= 0, + An inequality (-1, 0, 1, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, 0, 0, 0) x + 0 >= 0) + y3 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 4]: (An equation -1 == 0,) + 0 + [1, 4]: (An equation -1 == 0,) + 0 + [0, 1, 4]: (An equation -1 == 0,) + 0 + [2, 4]: (An equation (1, 1, 0, -1) x + 1 == 0, + An inequality (-1, 0, 1, 0) x + 0 >= 0, + An inequality (1, 0, 0, 0) x + 0 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0) + y3 * (-y2 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 2, 4]: (An equation -1 == 0,) + 0 + [1, 2, 4]: (An equation -1 == 0,) + 0 + [0, 1, 2, 4]: (An equation -1 == 0,) + 0 + [3, 4]: (An equation (1, 0, -1, 0) x + 0 == 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (-1, -1, 0, 1) x - 1 >= 0, + An inequality (1, 0, 0, 0) x + 0 >= 0) + y3 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 3, 4]: (An equation -1 == 0,) + 0 + [1, 3, 4]: (An equation -1 == 0,) + 0 + [0, 1, 3, 4]: (An equation -1 == 0,) + 0 + [2, 3, 4]: (An equation (1, 1, 0, -1) x + 1 == 0, + An equation (1, 0, -1, 0) x + 0 == 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (1, 0, 0, 0) x + 0 >= 0) + y3 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 + [0, 2, 3, 4]: (An equation -1 == 0,) + 0 + [1, 2, 3, 4]: (An equation -1 == 0,) + 0 + [0, 1, 2, 3, 4]: (An equation -1 == 0,) + 0 + + :: + + sage: P = Polyhedron(vertices=[[1], [5]]) + sage: P.generating_function_of_integral_points() + y0^5 + y0^4 + y0^3 + y0^2 + y0 + + .. SEEALSO:: + + This function is accessible via + :meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points` + as well. More examples can be found there. + + TESTS:: + + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0), + ....: (0, 0, 0, 1, 0), (0, 0, 0, 0, 1)]), + ....: sort_factors=True) + y0 * (-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y0*y1 + 1)^-1 + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0), + ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0), + ....: (0, 0, 0, 1, 0)]), + ....: sort_factors=True) + (-y0^2*y3 + y0*y3 + y0) * + (-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 + + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 1, 0, -1, 0, 0), (0, 0, 0, 1, 0, 0)], + ....: eqns=[(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0), + ....: (0, 1, -1, 0, 0, 0)]), + ....: sort_factors=True) + 1 * (-y0*y1*y3 + 1)^-1 * (-y0*y1*y2*y3*y4 + 1)^-1 + + :: + + sage: G = generating_function_of_integral_points(P2[0], sort_factors=True) + sage: S = generating_function_of_integral_points(P2[0], sort_factors=True, + ....: split=True) + sage: sum(S) == G.value() + True + + sage: G = generating_function_of_integral_points(P2[1], sort_factors=True) + sage: S = generating_function_of_integral_points(P2[1], sort_factors=True, + ....: split=True) + sage: sum(S) == G.value() + True + + sage: G = generating_function_of_integral_points(P3[0], sort_factors=True) + sage: S = generating_function_of_integral_points(P3[0], sort_factors=True, + ....: split=True) + sage: sum(S) == G.value() + True + + We show the distinct polyhedra that are used when ``split=True`` and the + resulting polyhedra that are used in the individual computations:: + + sage: import logging + sage: logging.basicConfig(level=logging.INFO) + sage: sum(generating_function_of_integral_points(P2[1], sort_factors=True, + ....: split=True)) + ... + INFO:sage.geometry.polyhedron.generating_function:(1/6) split polyhedron by b0 <= b1 <= b2 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + b0 >= 0 + b1 >= b0 + b2 >= b1 + ... + INFO:sage.geometry.polyhedron.generating_function:(2/6) split polyhedron by b0 <= b2 < b1 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + b2 >= b0 + b1 >= 1 + b2 + b0 >= 0 + ... + INFO:sage.geometry.polyhedron.generating_function:(3/6) split polyhedron by b1 < b0 <= b2 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + b2 >= b0 + b1 >= 0 + b0 >= 1 + b1 + ... + INFO:sage.geometry.polyhedron.generating_function:(4/6) split polyhedron by b1 <= b2 < b0 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + 0 == 1 + INFO:sage.geometry.polyhedron.generating_function:(5/6) split polyhedron by b2 < b0 <= b1 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + 0 == 1 + INFO:sage.geometry.polyhedron.generating_function:(6/6) split polyhedron by b2 < b1 < b0 + INFO:sage.geometry.polyhedron.generating_function:using polyhedron + 0 == 1 + 1/(-y0*y1*y2^2 + y0*y1*y2 + y0*y2^2 - y0*y2 + y1*y2 - y1 - y2 + 1) + sage: logging.disable() + + :: + + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0)]), + ....: sort_factors=True) + Traceback (most recent call last): + ... + NotImplementedError: cannot compute the generating function of + polyhedra with negative coordinates + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0), + ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)]), + ....: sort_factors=True) + Traceback (most recent call last): + ... + NotImplementedError: cannot compute the generating function of + polyhedra with negative coordinates + + :: + + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1), + ....: (0, 0, 0, 1), (0, 1, 0, 0)]), + ....: name='z', + ....: sort_factors=True) + (-z0*z1*z2 - z0*z2 + z0 + z2) * + (-z0 + 1)^-1 * (-z2 + 1)^-1 * (-z0*z1 + 1)^-1 * (-z1*z2 + 1)^-1 + sage: generating_function_of_integral_points( + ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1), + ....: (0, 0, 0, 1), (0, 1, 0, 0)]), + ....: name='mu', + ....: sort_factors=True) + (-mu0*mu1*mu2 - mu0*mu2 + mu0 + mu2) * + (-mu0 + 1)^-1 * (-mu2 + 1)^-1 * (-mu0*mu1 + 1)^-1 * (-mu1*mu2 + 1)^-1 + + :: + + sage: generating_function_of_integral_points(P2[0], + ....: sort_factors=True, names=('a',)) + 1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1 + sage: generating_function_of_integral_points(P2[0], + ....: sort_factors=True, names=('a', 'b')) + Traceback (most recent call last): + ... + NotImplementedError: exactly one variable name has to be provided + sage: generating_function_of_integral_points(P2[0], + ....: sort_factors=True, name='a', names=('a',)) + 1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1 + sage: generating_function_of_integral_points(P2[0], + ....: sort_factors=True, name='a', names=('b',)) + Traceback (most recent call last): + ... + ValueError: keyword argument 'name' cannot be combined with 'names' + + :: + + sage: P = Polyhedron(rays=[(1, sqrt(2)), (0, 1)]) + Traceback (most recent call last): + ... + ValueError: no default backend for computations with Symbolic Ring + sage: P = Polyhedron(ieqs=[(RDF(pi), RDF(1))]) + sage: P.generating_function_of_integral_points() + Traceback (most recent call last): + ... + TypeError: base ring Real Double Field of the polyhedron not ZZ or QQ + """ + import logging + logger = logging.getLogger(__name__) + + from sage.combinat.permutation import Permutations + from sage.geometry.polyhedron.constructor import Polyhedron + from sage.rings.integer_ring import ZZ + from sage.rings.rational_field import QQ + from sage.structure.category_object import normalize_names + + if result_as_tuple is None: + result_as_tuple = split + + if polyhedron.is_empty(): + from sage.structure.factorization import Factorization + result = Factorization([], unit=0) + if result_as_tuple: + return (result,) + else: + return result + + if polyhedron.base_ring() not in (ZZ, QQ): + raise TypeError('base ring {} of the polyhedron not ' + 'ZZ or QQ'.format(polyhedron.base_ring())) + + d = polyhedron.ambient_dim() + nonnegative_orthant = Polyhedron(ieqs=[dd*(0,) + (1,) + (d-dd)*(0,) + for dd in range(1, d+1)]) + if polyhedron & nonnegative_orthant != polyhedron: + raise NotImplementedError('cannot compute the generating function of ' + 'polyhedra with negative coordinates') + + logger.info('%s', polyhedron) + + if names is not None: + names = normalize_names(-1, names) + if len(names) != 1: + raise NotImplementedError('exactly one variable name has to be provided') + if name is not None and name != names[0]: + raise ValueError("keyword argument 'name' cannot be combined with 'names'") + name = names[0] + if name is None: + name = 'y' + + if split is False: + result = _generating_function_of_integral_points_(polyhedron, name=name, **kwds) + if result_as_tuple: + return result + else: + if len(result) != 1: + raise ValueError("cannot unpack result " + "(set 'result_as_tuple=True')") + return result[0] + + if d <= 1: + raise ValueError('cannot do splitting with only ' + 'dimension {}'.format(d)) + + parts = None + if split is True: + + def polyhedron_from_permutation(pi): + + def ieq(a, b): + return ((0 if a < b else -1,) + + tuple(1 if i == b else (-1 if i == a else 0) + for i in range(1, d + 1))) + + def ieq_repr_rhs(a, b): + return (' <= ' if a < b else ' < ') + 'b{}'.format(b-1) + + def ieqs_repr_lhs(pi): + return 'b{}'.format(pi[0]-1) + + 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) + + split = (polyhedron_from_permutation(pi) for pi in Permutations(d)) + parts = ZZ(d).factorial() + else: + if isinstance(split, (list, tuple)): + parts = len(split) + split = ((ph, ph.Hrepresentation_str(**Hrepresentation_str_options)) + for ph in split) + + result = [] + for part, (split_polyhedron, pi_log) in enumerate(split): + if parts is None: + parts_log = str(part+1) + else: + parts_log = '{}/{}'.format(part+1, parts) + logger.info('(%s) split polyhedron by %s', parts_log, pi_log) + result.append(_generating_function_of_integral_points_( + polyhedron & split_polyhedron, name=name, **kwds)) + if not result_as_tuple: + raise ValueError("cannot unpack result" + "(unset 'result_as_tuple=False')") + return sum(result, ()) + + +def _generating_function_of_integral_points_( + polyhedron, indices=None, **kwds): + r""" + Helper function for :func:`generating_function_of_integral_points` which + does the mid-level stuff. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points + + sage: generating_function_of_integral_points( # indirect doctest + ....: Polyhedron(ieqs=[(0, 1, 0, 0), (0, -1, 1, 0)], + ....: eqns=[(0, -1, -1, 2)]), + ....: result_as_tuple=True, sort_factors=True) + (1 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1, + y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1) + """ + import logging + logger = logging.getLogger(__name__) + + logger.info('using polyhedron %s', + polyhedron.Hrepresentation_str(**Hrepresentation_str_options)) + + if polyhedron.is_empty(): + from sage.structure.factorization import Factorization + return (Factorization([], unit=0),) + + Hrepr = polyhedron.Hrepresentation() + + inequalities = tuple(tuple(entry) + for entry in Hrepr if entry.is_inequality()) + equations = tuple(tuple(entry) + for entry in Hrepr if entry.is_equation()) + if len(inequalities) + len(equations) != len(Hrepr): + raise ValueError('cannot handle {}.'.format(polyhedron)) + + if not inequalities: + raise NotImplementedError('no inequality given') + + if indices is None: + indices = range(len(inequalities[0]) - 1) + + n = len(indices) + 1 + if any(len(e) != n for e in inequalities): + raise ValueError('not all coefficient vectors of the inequalities ' + 'have the same length') + if any(len(e) != n for e in equations): + raise ValueError('not all coefficient vectors of the equations ' + 'have the same length') + + mods = _TransformMod.generate_mods(equations) + logger.debug('splitting by moduli %s', mods) + + return tuple(__generating_function_of_integral_points__( + indices, inequalities, equations, mod, **kwds) for mod in mods) + + +def __generating_function_of_integral_points__( + indices, inequalities, equations, mod, + name, + Factorization_sort=False, Factorization_simplify=False, + sort_factors=False): + r""" + Helper function for :func:`generating_function_of_integral_points` which + does the actual computation of the generating function. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import __generating_function_of_integral_points__ + + sage: __generating_function_of_integral_points__( + ....: (0, 2), [(0, 1, 0)], [(1, -1, 2)], + ....: {0: (2, 1)}, name='y', sort_factors=True) + y0 * (-y0^2*y2 + 1)^-1 + sage: __generating_function_of_integral_points__( + ....: srange(3), [(0, 1, 0, 0), (0, 0, 1, 0)], [(1, -1, 0, 2)], + ....: {0: (2, 1)}, name='y', sort_factors=True) + y0 * (-y1 + 1)^-1 * (-y0^2*y2 + 1)^-1 + sage: __generating_function_of_integral_points__( + ....: srange(3), [(0, 1, 0, 0), (0, -1, 1, 0)], [(0, -1, -1, 2)], + ....: {0: (2, 1), 1: (2, 1)}, name='y', sort_factors=True) + y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1 + """ + import logging + logger = logging.getLogger(__name__) + + from sage.rings.integer_ring import ZZ + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + from sage.structure.factorization import Factorization + + B = LaurentPolynomialRing(ZZ, + tuple(name + str(k) for k in indices), + len(indices)) + + logger.info('preprocessing %s inequalities and %s equations...', + len(inequalities), len(equations)) + + T_mod = _TransformMod(inequalities, equations, B, mod) + inequalities = T_mod.inequalities + equations = T_mod.equations + + T_equations = _EliminateByEquations(inequalities, equations, B) + inequalities = T_equations.inequalities + equations = T_equations.equations + + T_inequalities = _SplitOffSimpleInequalities(inequalities, equations, B) + inequalities = T_inequalities.inequalities + equations = T_inequalities.equations + assert not equations + + logger.info('%s inequalities left; using Omega...', len(inequalities)) + numerator, terms = _generating_function_via_Omega_( + inequalities, B, skip_indices=T_equations.indices) + + numerator, terms = T_inequalities.apply_rules(numerator, terms) + numerator, terms = T_equations.apply_rules(numerator, terms) + numerator, terms = T_mod.apply_rules(numerator, terms) + + if sort_factors: + def key(t): + D = t.dict().popitem()[0] + return (-sum(abs(d) for d in D), D) + terms = sorted(terms, key=key, reverse=True) + return Factorization([(numerator, 1)] + + list((1-t, -1) for t in terms), + sort=Factorization_sort, + simplify=Factorization_simplify) + + +def _generating_function_via_Omega_(inequalities, B, skip_indices=()): + r""" + Compute the generating function of the integral points of the + polyhedron specified by ``inequalities`` via + :func:`MacMahon's Omega operator <sage.rings.polynomial.omega.MacMahonOmega>`. + + INPUT: + + - ``inequalities`` -- a list or other iterable of tuples + of numbers. + + - ``B`` -- a Laurent polynomial ring + + - ``skip_indices`` -- a list or tuple of indices + + The variables corresponding to ``skip_indices`` are not handled + (e.g. because they are determined by an equation). + + OUTPUT: + + A pair of + + - a Laurent polynomial specifying the numerator and + + - a tuple of Laurent polynomials specifying the denominator; + each entry `t` corresponds to a factor `1 - t`. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ + sage: B = LaurentPolynomialRing(ZZ, 'y', 3) + sage: _generating_function_via_Omega_([(0, -1, -1, 1)], B) + (1, (y2, y0*y2, y1*y2)) + """ + import logging + logger = logging.getLogger(__name__) + + from .representation import repr_pretty + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + from sage.rings.polynomial.omega import _Omega_, _simplify_, partition + + numerator = B(1) + terms = B.gens() + L = B + mu = 'mu' if not repr(B.gen()).startswith('mu') else 'nu' + for i, coeffs in enumerate(inequalities): + L = LaurentPolynomialRing(L, mu + str(i), sparse=True) + l = L.gen() + logger.debug('mapping %s --> %s', l, repr_pretty(coeffs, 0)) + it_coeffs = iter(coeffs) + numerator *= l**next(it_coeffs) + assert numerator.parent() == L + terms = tuple(l**c * t for c, t in zip(it_coeffs, terms)) + assert all(y == t for y, t in + (tuple(zip(B.gens(), terms))[i] for i in skip_indices)) + terms = tuple(t for i, t in enumerate(terms) + if i not in skip_indices) + + logger.debug('terms denominator %s', terms) + + def decode_factor(factor): + D = factor.dict() + assert len(D) == 1 + exponent, coefficient = next(iter(D.items())) + return coefficient, exponent + + while repr(numerator.parent().gen()).startswith(mu): + logger.info('applying Omega[%s]...', numerator.parent().gen()) + logger.debug('...on terms denominator %s', terms) + logger.debug('...(numerator has %s terms)', numerator.number_of_terms()) + logger.debug('...numerator %s', numerator) + + decoded_factors, other_factors = \ + partition((decode_factor(factor) for factor in terms), + lambda factor: factor[1] == 0) + other_factors = tuple(factor[0] for factor in other_factors) + numerator, factors_denominator = \ + _Omega_(numerator.dict(), tuple(decoded_factors)) + terms = other_factors + factors_denominator + + return _simplify_(numerator, terms) + + +class _TransformHrepresentation(object): + r""" + An abstract base class for transformations of the + Hrepresentation of a polyhedron together with its + back-substitutions of the corresponding generating function. + + INPUT: + + - ``inequalities`` -- a list of tuples of numbers + + - ``equations`` -- a list of tuples of numbers + + - ``B`` -- a Laurent polynomial ring + + ATTRIBUTES: + + - ``inequalities``, ``equations`` -- list of tuples + + Determine the generating function of these inequalities + and equations instead of the input. + + - ``factor`` -- a Laurent polynomial + + The numerator of the generating function has to be multiplied + with ``factor`` *after* substituting ``rules``. + + - ``rules`` -- a dictionary mapping Laurent polynomial variables to + Laurent polynomials + + Substitute ``rules`` into the generating function. + + The generating function of the input ``inequalities`` and + ``equations`` is equal to the generating function of the + attributes ``inequalities`` and ``equations`` in which ``rules`` + were substituted and ``factor`` was multiplied (via + :meth:`~_TransformHrepresentation.apply_rules`). + """ + + def __init__(self, inequalities, equations, B): + r""" + See :class:`_TransformHrepresentation` for details. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B) + Traceback (most recent call last): + ... + NotImplementedError + """ + self.inequalities = inequalities + self.equations = equations + self.B = B + self._transform_() + + def _transform_(self): + r""" + Transform the input given on construction of this object. + + This is called during :meth:`__init__`. Overload this method + in derived classes. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def apply_rules(self, numerator, terms): + r""" + Substitute the generated rules. + + INPUT: + + - ``numerator`` -- a Laurent polynomial + + - ``terms`` -- a tuple or other iterable of Laurent polynomials + + The denominator is the product of factors `1 - t` for each + `t` in ``terms``. + + OUTPUT: + + A pair of a Laurent polynomial and a tuple of Laurent polynomials + representing numerator and denominator as described in the + INPUT-section. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare + sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf + sage: B = LaurentPolynomialRing(ZZ, 'y', 3) + sage: ieqs = [(0, -1, 1, 0)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([], 1, {y2: y2, y1: y1, y0: y0*y1}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (1, (y0*y1, y1, y2)) + """ + return (numerator.subs(self.rules) * self.factor, + tuple(t.subs(self.rules) for t in terms)) + + +class _SplitOffSimpleInequalities(_TransformHrepresentation): + r""" + Split off (simple) inequalities which can be handled better + without passing them to Omega. + + INPUT: + + - ``inequalities`` -- a list of tuples of numbers + + - ``equations`` -- a list of tuples of numbers + + - ``B`` -- a Laurent polynomial ring + + ATTRIBUTES: + + - ``inequalities``, ``equations`` -- list of tuples + + Determine the generating function of these inequalities + and equations instead of the input. + + - ``factor`` -- a Laurent polynomial + + The numerator of the generating function has to be multiplied + with ``factor`` *after* substituting ``rules``. + + - ``rules`` -- a dictionary mapping Laurent polynomial variables to + Laurent polynomials + + Substitute ``rules`` into the generating function. + + The generating function of the input ``inequalities`` and + ``equations`` is equal to the generating function of the + attributes ``inequalities`` and ``equations`` in which ``rules`` + were substitited and ``factor`` was multiplied (via + :meth:`~_TransformHrepresentation.apply_rules`). + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare + sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf + + sage: def eq(A, B): + ....: return (A[0] == B[0] and + ....: sorted(A[1], key=repr) == sorted(B[1], key=repr)) + + sage: B = LaurentPolynomialRing(ZZ, 'y', 3) + + sage: ieqs = [(0, -1, 1, 0), (2, -1, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(2, -2, -1, 1)], 1, {y2: y2, y1: y1, y0: y0*y1}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (y0*y1^3*y2^3 - y0*y1^3*y2^2 + y0*y1^2*y2^3 - y0*y1^2*y2^2 + - y0*y1*y2^2 - y1^2*y2 + y0*y1 + y1^2 - y1*y2 + y1 + 1, + (y2, y1*y2, y0*y1*y2^2)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-1, -1, 1, 0), (2, -1, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, -2, -1, 1)], y1, {y2: y2, y1: y1, y0: y0*y1}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (y0*y1^3*y2^3 - y0*y1^3*y2^2 - y0*y1^2*y2^2 + + y0*y1^2*y2 - y1^2*y2 + y1^2 + y1, + (y2, y1*y2, y0*y1*y2^2)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-2, -1, 1, 0), (2, -1, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(0, -2, -1, 1)], y1^2, {y2: y2, y1: y1, y0: y0*y1}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (y1^2, (y2, y1*y2, y0*y1*y2^2)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(2, -1, 1, 0), (2, -1, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(2, -1, 1, 0), (2, -1, -1, 1)], 1, {y2: y2, y1: y1, y0: y0}) + sage: eq(T.apply_rules(*gf(T.inequalities, B)), gf(ieqs, B)) + True + + TESTS:: + + sage: def eq2(A, B): + ....: a = SR(repr(A[0])) * prod(1-SR(repr(t)) for t in B[1]) + ....: b = SR(repr(B[0])) * prod(1-SR(repr(t)) for t in A[1]) + ....: return bool((a-b).full_simplify() == 0) + + sage: B = LaurentPolynomialRing(ZZ, 'y', 3) + sage: ieqs = [(-2, 1, -1, 0), (-2, -1, 0, 1), (-1, -1, -1, 3)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(9, 2, 1, 3)], y0^2*y2^4, {y2: y2, y1: y0*y1*y2, y0: y0*y2}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (y0^2*y2^4, (y2, y0*y2, y0*y1*y2)) + sage: eq2(_, gf(ieqs, B)) + True + + sage: B = LaurentPolynomialRing(ZZ, 'y', 4) + sage: ieqs = [(-1, 1, -1, 0, 0)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([], y0, {y3: y3, y2: y2, y1: y0*y1, y0: y0}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (y0, (y0, y0*y1, y2, y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(0, 0, -1, 0, 1), (0, 0, 1, 0, 0), + ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 1, 0, 0, -1)], y0, {y3: y3, y2: y2, y1: y0*y1*y3, y0: y0}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y0^2*y3 + y0*y3 + y0, (y0*y1*y3, y2, y0, y0*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-2, 1, -1, 0, 0)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([], y0^2, {y3: y3, y2: y2, y1: y0*y1, y0: y0}) + + sage: T.apply_rules(*gf(T.inequalities, B)) + (y0^2, (y0, y0*y1, y2, y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1), + ....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, -1, 1, 1)], + y3^3, + {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1*y2*y3^4 - y1*y3^4 + y1*y3^3 + y3^3, + (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1), + ....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, 1, -1, 1)], + y3^3, + {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1*y2*y3^4 - y2*y3^4 + y2*y3^3 + y3^3, + (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1), + ....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, -1, 1, 1)], + y2^3*y3^3, + {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1*y2^4*y3^4 - y1*y2^3*y3^4 + y1*y2^3*y3^3 + y2^3*y3^3, + (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1), + ....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, 1, -1, 1)], + y2^2*y3^3, + {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1*y2^3*y3^4 - y2^3*y3^4 + y2^3*y3^3 + y2^2*y3^3, + (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1), + ....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, -1, 1, 1)], + y1^2*y3^3, + {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1^3*y2*y3^4 - y1^3*y3^4 + y1^3*y3^3 + y1^2*y3^3, + (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1), + ....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, 1, -1, 1)], + y1^3*y3^3, + {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1^4*y2*y3^4 - y1^3*y2*y3^4 + y1^3*y2*y3^3 + y1^3*y3^3, + (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1), + ....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, -1, 1, 1)], + y1^2*y2^3*y3^3, + {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1^3*y2^4*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^2*y2^3*y3^3, + (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3)) + sage: eq(_, gf(ieqs, B)) + True + + sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1), + ....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)] + sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules + ([(1, 0, 1, -1, 1)], + y1^3*y2^2*y3^3, + {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3}) + sage: T.apply_rules(*gf(T.inequalities, B)) + (-y1^4*y2^3*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^3*y2^2*y3^3, + (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3)) + sage: eq(_, gf(ieqs, B)) + True + """ + + def _transform_(self): + r""" + Do the actual transformation. + + This is called during initialization. + See :class:`_SplitOffSimpleInequalities` for details. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: T = _SplitOffSimpleInequalities([(1, 1, 1)], [], B) # indirect doctest + """ + inequalities = self.inequalities + B = self.B + + import logging + logger = logging.getLogger(__name__) + + from itertools import takewhile + from .representation import repr_pretty + from sage.graphs.digraph import DiGraph + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + from sage.rings.integer_ring import ZZ + + inequalities_filtered = [] + chain_links = {} + for coeffs in inequalities: + dim = len(coeffs) + if all(c >= 0 for c in coeffs): + logger.debug('skipping %s (all coefficients >= 0)', + repr_pretty(coeffs, 0)) + continue + constant = coeffs[0] + ones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == 1) + mones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == -1) + absgetwo = tuple(i+1 for i, c in enumerate(coeffs[1:]) if abs(c) >= 2) + if len(ones) == 1 and not mones and not absgetwo: + if constant < 0: + # This case could be cleverly skipped... + inequalities_filtered.append(coeffs) + elif len(ones) == 1 and len(mones) == 1 and not absgetwo and constant <= 0: + logger.debug('handling %s', + repr_pretty(coeffs, 0)) + chain_links[(mones[0], ones[0])] = constant + else: + inequalities_filtered.append(coeffs) + + G = DiGraph(chain_links, format='list_of_edges') + potential = {} + paths = {} + D = {} + inequalities_extra = [] + for i in range(dim): + D[(i, i)] = 1 + for v in G.topological_sort(): + NP = iter(sorted(((n, potential[n] + chain_links[(n, v)]) + for n in G.neighbor_in_iterator(v)), + key=lambda k: (k[1], k[0]))) + n, p = next(NP, (None, 0)) + potential[v] = p + D[(0, v)] = -p + paths[v] = paths.get(n, ()) + (v,) + for u in paths[v]: + D[(u, v)] = 1 + + for n, p in NP: + ell = len(tuple(takewhile(lambda u: u[0] == u[1], + zip(paths[n], paths[v])))) + coeffs = dim*[0] + for u in paths[v][ell:]: + coeffs[u] = 1 + for u in paths[n][ell:]: + coeffs[u] = -1 + coeffs[0] = p - potential[v] + inequalities_extra.append(tuple(coeffs)) + T = matrix(ZZ, dim, dim, D) + + self.inequalities = (list(tuple(T*vector(ieq)) + for ieq in inequalities_filtered) + + inequalities_extra) + + rules_pre = ((y, B({tuple(row[1:]): 1})) + for y, row in zip((1,) + B.gens(), T.rows())) + self.factor = next(rules_pre)[1] + self.rules = dict(rules_pre) + + +class _EliminateByEquations(_TransformHrepresentation): + r""" + Prepare the substitutions coming from "eliminated" variables + in the given equations. + + INPUT: + + - ``inequalities`` -- a list of tuples of numbers + + - ``equations`` -- a list of tuples of numbers + + - ``B`` -- a Laurent polynomial ring + + ATTRIBUTES: + + - ``inequalities``, ``equations`` -- list of tuples + + Determine the generating function of these inequalities + and equations instead of the input. + + - ``factor`` -- a Laurent polynomial + + The numerator of the generating function has to be multiplied + with ``factor`` *after* substituting ``rules``. + + - ``rules`` -- a dictionary mapping Laurent polynomial variables to + Laurent polynomials + + Substitute ``rules`` into the generating function. + + - ``indices`` -- a sorted tuple of integers representing + indices of Laurent polynomial ring variables + + These are exactly the "eliminated" variables which we take care of + by ``factor`` and ``rules``. + + The generating function of the input ``inequalities`` and + ``equations`` is equal to the generating function of the + attributes ``inequalities`` and ``equations`` in which ``rules`` + were substitited and ``factor`` was multiplied (via + :meth:`~_TransformHrepresentation.apply_rules`). + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations + + sage: def prepare_equations(equations, B): + ....: T = _EliminateByEquations([], equations, B) + ....: return T.factor, T.rules, T.indices + + sage: B = LaurentPolynomialRing(ZZ, 'y', 4) + sage: prepare_equations([(1, 1, 1, -1, 0)], B) + (y2, {y1: y1*y2, y0: y0*y2}, (2,)) + sage: prepare_equations([(0, 1, 0, -1, 0)], B) + (1, {y0: y0*y2}, (2,)) + sage: prepare_equations([(-1, 0, 1, -1, -1), (1, 1, 0, 1, 2)], B) + (y2^-1, {y1: y1*y2^2*y3^-1, y0: y0*y2*y3^-1}, (2, 3)) + + TESTS:: + + sage: B = LaurentPolynomialRing(ZZ, 'y', 4) + sage: prepare_equations([(0, 0, 1, 0, -1), (-1, 1, -1, -1, 0)], B) + (y2^-1, {y1: y1*y2^-1*y3, y0: y0*y2}, (2, 3)) + + sage: B = LaurentPolynomialRing(ZZ, 'y', 5) + sage: prepare_equations([(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0), + ....: (0, 1, -1, 0, 0, 0)], B) + (1, {y2: y2*y4, y0: y0*y1*y3}, (1, 3, 4)) + """ + + def _transform_(self): + r""" + Do the actual transformation. + + This is called during initialization. + See :class:`_EliminateByEquations` for details. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: T = _EliminateByEquations([], [], B) # indirect doctest + """ + from sage.matrix.constructor import matrix + from sage.misc.misc_c import prod + + equations = self.equations + B = self.B + + E = matrix(equations) + if not E: + self.factor = 1 + self.rules = {} + self.indices = () + return + + TE, indices, indicesn = _EliminateByEquations.prepare_equations_transformation(E) + + gens = (1,) + B.gens() + z = tuple(gens[i] for i in indices) + gens_cols = tuple(zip(gens, TE.columns())) + rules_pre = ((y, y * prod(zz**(-c) for zz, c in zip(z, col))) + for y, col in (gens_cols[i] for i in indicesn)) + self.factor = next(rules_pre)[1] + self.rules = dict(rules_pre) + self.indices = tuple(i-1 for i in indices) + self.equations = [] + + @staticmethod + def prepare_equations_transformation(E): + r""" + Return a transformation matrix and indices which variables + in the equation to "eliminate" and deal with later. + + INPUT: + + - ``E`` -- a matrix whose rows represent equations + + OUTPUT: + + A triple ``(TE, indices, indicesn)`` with the following properties: + + - ``TE`` -- a matrix + + This matrix arises from ``E`` by multiplying a transformation matrix + on the left. + + - ``indices`` -- a sorted tuple of integers representing column indices + + The the sub-matrix of ``TE`` with columns ``indices`` + is the identity matrix. + + - ``indicesn`` -- a sorted tuple of integers representing column indices + + ``indicesn`` contains ``0`` and all indices of the columns of ``E`` + which are non-zero. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations + + sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, 0, -2)])) + ([ 0 -1/2 0 1], (3,), (0, 1)) + sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, -2, 0), (0, 2, 0, -3)])) + ( + [ 0 -1/2 1 0] + [ 0 -2/3 0 1], (2, 3), (0, 1) + ) + """ + indices_nonzero = tuple(i for i, col in enumerate(E.columns()) + if i > 0 and not col.is_zero()) + indices = [] + r = 0 + for i in reversed(indices_nonzero): + indices.append(i) + r1 = E.matrix_from_columns(indices).rank() + if r1 > r: + r = r1 + if len(indices) >= E.nrows(): + break + else: + indices = indices[:-1] + assert len(indices) == E.nrows() + indices = tuple(reversed(indices)) + indicesn = (0,) + tuple(i for i in indices_nonzero if i not in indices) + TE = E.matrix_from_columns(indices).inverse() * E + return TE, indices, indicesn + + +class _TransformMod(_TransformHrepresentation): + r""" + Prepare the substitutions coming from the moduli. + + INPUT: + + - ``inequalities`` -- a list of tuples of numbers + + - ``equations`` -- a list of tuples of numbers + + - ``B`` -- a Laurent polynomial ring + + - ``mod`` -- a dictionary mapping an index ``i`` to ``(m, r)`` + + This is one entry of the output tuple of :meth:`generate_mods`. + + ATTRIBUTES: + + - ``inequalities``, ``equations`` -- list of tuples + + Determine the generating function of these inequalities + and equations instead of the input. + + - ``factor`` -- a Laurent polynomial + + The numerator of the generating function has to be multiplied + with ``factor`` *after* substituting ``rules``. + + - ``rules`` -- a dictionary mapping Laurent polynomial variables to + Laurent polynomials + + Substitute ``rules`` into the generating function. + + The generating function of the input ``inequalities`` and + ``equations`` is equal to the generating function of the + attributes ``inequalities`` and ``equations`` in which ``rules`` + were substitited and ``factor`` was multiplied (via + :meth:`~_TransformHrepresentation.apply_rules`). + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformMod + + sage: def prepare_mod(mod, B, *vecs): + ....: inequalities, equations = vecs + ....: T = _TransformMod(inequalities, equations, B, mod) + ....: return T.factor, T.rules, T.inequalities, T.equations + + sage: B = LaurentPolynomialRing(ZZ, 'y', 3) + sage: prepare_mod({0: (2, 1)}, B, [(1, -1, 0, 2)], []) + (y0, {y2: y2, y1: y1, y0: y0^2}, [(0, -2, 0, 2)], []) + sage: prepare_mod({0: (2, 1), 1: (2, 1)}, B, + ....: [(0, -1, -1, 2)], [(0, -1, 1, 0)]) + (y0*y1, {y2: y2, y1: y1^2, y0: y0^2}, + [(-2, -2, -2, 2)], [(0, -2, 2, 0)]) + """ + + def __init__(self, inequalities, equations, B, mod): + r""" + See :class:`_TransformMod` for details. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformMod + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: _TransformMod([], [], B, {}) + <..._TransformMod object at 0x...> + """ + self.mod = mod + super(_TransformMod, self).__init__(inequalities, equations, B) + + def _transform_(self): + r""" + Do the actual transformation. + + This is called during initialization. + See :class:`_TransformMod` for details. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformMod + sage: B = LaurentPolynomialRing(ZZ, 'y', 2) + sage: T = _TransformMod([], [], B, {}) # indirect doctest + """ + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + from sage.rings.integer_ring import ZZ + + mod = self.mod + B = self.B + + if not mod: + self.factor = 1 + self.rules = {} + + n = len(B.gens()) + 1 + + D = {(i, i): 1 for i in range(n)} + for i, mr in mod.items(): + D[(i+1, i+1)] = mr[0] + D[(i+1, 0)] = mr[1] + T = matrix(ZZ, n, n, D) + + rules_pre = ((y, B({tuple(row[1:]): 1})) + for y, row in zip((1,) + B.gens(), T.columns())) + self.factor = next(rules_pre)[1] + self.rules = dict(rules_pre) + + self.inequalities = list(tuple(vector(e)*T) for e in self.inequalities) + self.equations = list(tuple(vector(e)*T) for e in self.equations) + + @staticmethod + def generate_mods(equations): + r""" + Extract the moduli and residue classes implied + by the equations. + + INPUT: + + - ``equations`` -- a list of tuples + + OUTPUT: + + A tuple where each entry represents one possible configuration. + Each entry is a dictionary mapping ``i`` to ``(m, r)`` with the following + meaning: The ``i``-th coordinate of each element of the polyhedron + has to be congruent to ``r`` modulo ``m``. + + TESTS:: + + sage: from sage.geometry.polyhedron.generating_function import _TransformMod + sage: _TransformMod.generate_mods([(0, 1, 1, -2)]) + ({0: (2, 0), 1: (2, 0)}, {0: (2, 1), 1: (2, 1)}) + """ + from sage.arith.functions import lcm + from sage.matrix.constructor import matrix + from sage.rings.integer_ring import ZZ + from sage.rings.rational_field import QQ + + TE, TEi, TEin = _EliminateByEquations.prepare_equations_transformation(matrix(equations)) + TEin = TEin[1:] + if TE.base_ring() == ZZ: + mods = [{}] + elif TE.base_ring() == QQ: + m = lcm([e.denominator() for e in TE.list()]) + if m == 1: + mods = [{}] + else: + cols = TE.columns() + assert all(cols[j][i] == 1 for i, j in enumerate(TEi)) + pre_mods = _compositions_mod((tuple(ZZ(cc*m) for cc in cols[i]) + for i in TEin), + m, r=(-cc*m for cc in cols[0]), + multidimensional=True) + mods = tuple({i-1: (aa.modulus(), ZZ(aa)) + for i, aa in zip(TEin, a) if aa.modulus() > 1} + for a in pre_mods) + else: + raise TypeError('equations over ZZ or QQ expected, but got ' + 'equations over {}.'.format(TE.base_ring())) + + return mods + + +def _compositions_mod(u, m, r=0, multidimensional=False): + r""" + Return an iterable of all tuples `a` such that `a u^T \equiv r \mod m`. + + INPUT: + + - ``m`` -- the modulus as a positive integer + + - ``multidimensional`` -- (default: ``False``) a boolean + + If ``multidimensional=False``: + + - ``u`` -- the coefficients as a tuple + + - ``r`` -- (default: `0`) + the remainder as a nonnegative integer + + If ``multidimensional=True``: + + - ``u`` -- the coefficients as a tuple of tuples (read column-wise) + + - ``r`` -- (default: the zero vector) + the remainder as a tuple of nonnegative integers + + OUTPUT: + + An iterable of tuples. All these tuples have the same size as ``u``. + The elements of the tuples are integers modulo some appropriate + modulus; this modulus divides `m` and is possibly different for each element. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.generating_function import _compositions_mod + sage: def show_cm(cm): + ....: print(', '.join('({})'.format( + ....: ', '.join('{}mod{}'.format(aa, aa.modulus()) + ....: for aa in a)) + ....: for a in cm)) + + sage: list(_compositions_mod([1, 1], 2)) + [(0, 0), (1, 1)] + sage: show_cm(_compositions_mod([1, 1], 2)) + (0mod2, 0mod2), (1mod2, 1mod2) + sage: show_cm(_compositions_mod([1, 2, 3], 6)) + (0mod6, 0mod3, 0mod2), (1mod6, 1mod3, 1mod2), (2mod6, 2mod3, 0mod2), + (3mod6, 0mod3, 1mod2), (4mod6, 1mod3, 0mod2), (5mod6, 2mod3, 1mod2) + sage: show_cm(_compositions_mod([2, 2, 2], 6)) + (0mod3, 0mod3, 0mod3), (0mod3, 1mod3, 2mod3), (0mod3, 2mod3, 1mod3), + (1mod3, 0mod3, 2mod3), (1mod3, 1mod3, 1mod3), (1mod3, 2mod3, 0mod3), + (2mod3, 0mod3, 1mod3), (2mod3, 1mod3, 0mod3), (2mod3, 2mod3, 2mod3) + + :: + + sage: show_cm(_compositions_mod([(1, 0), (0, 1)], 2, + ....: multidimensional=True)) + (0mod2, 0mod2) + sage: show_cm(_compositions_mod([(1, 2), (2, 2), (3, 2)], 6, + ....: multidimensional=True)) + (0mod6, 0mod3, 0mod6), (1mod6, 1mod3, 1mod6), (2mod6, 2mod3, 2mod6), + (3mod6, 0mod3, 3mod6), (4mod6, 1mod3, 4mod6), (5mod6, 2mod3, 5mod6) + + TESTS:: + + sage: show_cm(_compositions_mod([1, 0], 2)) + (0mod2, 0mod1) + """ + from sage.arith.functions import lcm + from sage.arith.srange import srange + from sage.modules.free_module_element import vector + from sage.rings.finite_rings.integer_mod_ring import Zmod + + Z = Zmod(m) + if not multidimensional: + u = tuple(vector([Z(uu)]) for uu in u) + r = vector([Z(r)]) + else: + u = tuple(vector(Z(uuu) for uuu in uu) for uu in u) + if r == 0: + r = vector(Z(0) for _ in range(len(u[0]))) + else: + r = vector(Z(rr) for rr in r) + + def recursively_build_compositions(u, r): + if not u: + if all(rr == 0 for rr in r): + yield () + return + + v = u[0] + m = lcm(vv.order() for vv in v) + Z = Zmod(m) + for j in srange(m): + for a in recursively_build_compositions(u[1:], r - j*v): + yield (Z(j),) + a + + yield from recursively_build_compositions(u, r) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index b71e1e29d2b..e54d8a7efd1 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -391,38 +391,28 @@ def gale_transform_to_primal(vectors, base_ring=None, backend=None): One can specify the base ring:: - sage: gale_transform_to_primal( - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)]) + sage: p = [(1,1), (-1,-1), (1,0), (-1,0), (1,-1), (-2,1)] + sage: gtpp = gale_transform_to_primal(p); gtpp [(16, -35, 54), (24, 10, 31), (-15, 50, -60), (-25, 0, 0), (0, -25, 0), (0, 0, -25)] - sage: gale_transform_to_primal( - ....: [(1,1),(-1,-1),(1,0),(-1,0),(1,-1),(-2,1)], base_ring=RDF) - [(-0.6400000000000001, 1.4, -2.1600000000000006), - (-0.9600000000000002, -0.39999999999999997, -1.2400000000000002), - (0.6000000000000001, -2.0, 2.4000000000000004), - (1.0, 0.0, 0.0), - (0.0, 1.0, 0.0), - (0.0, 0.0, 1.0)] + sage: (matrix(RDF, gtpp)/25 + + ....: matrix(gale_transform_to_primal(p, base_ring=RDF))).norm() < 1e-15 + True One can also specify the backend to be used internally:: - sage: gale_transform_to_primal( - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)], backend='field') + sage: gale_transform_to_primal(p, backend='field') [(48, -71, 88), (84, -28, 99), (-77, 154, -132), (-55, 0, 0), (0, -55, 0), (0, 0, -55)] - sage: gale_transform_to_primal( # optional - pynormaliz - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)], backend='normaliz') + sage: gale_transform_to_primal(p, backend='normaliz') # optional - pynormaliz [(16, -35, 54), (24, 10, 31), (-15, 50, -60), @@ -2575,7 +2565,6 @@ def tri(m): return parent([verts, [], []], [ieqs, eqns], Vrep_minimal=True, Hrep_minimal=True, pref_rep="Hrep") - def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regular=False, backend=None): r""" Return the generalized permutahedron of type ``coxeter_type`` as the @@ -2638,38 +2627,19 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula A vertex at (1, 0), A vertex at (1, 1)) - Setting ``regular=True`` applies a linear transformation to get - isometric vertex figures and the result is inscribed. Even though there - are traces of small numbers, the internal computations are done using - an exact embedded NumberField:: + It works also with Coxeter types that lead to non-rational coordinates:: - sage: perm_a2_reg = polytopes.generalized_permutahedron(['A',2],regular=True) - sage: V = sorted(perm_a2_reg.vertices()); V # random - [A vertex at (-1, 0), - A vertex at (-1/2, -0.866025403784439?), - A vertex at (-1/2, 0.866025403784439?), - A vertex at (1/2, -0.866025403784439?), - A vertex at (1/2, 0.866025403784439?), - A vertex at (1.000000000000000?, 0.?e-18)] - sage: for v in V: - ....: for x in v: - ....: x.exactify() - sage: V - [A vertex at (-1, 0), - A vertex at (-1/2, -0.866025403784439?), - A vertex at (-1/2, 0.866025403784439?), - A vertex at (1/2, -0.866025403784439?), - A vertex at (1/2, 0.866025403784439?), - A vertex at (1, 0)] - sage: perm_a2_reg.is_inscribed() - True - sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True) # long time - sage: perm_a3_reg.is_inscribed() # long time - True + sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 # long time # optional - sage.rings.number_field + A 3-dimensional polyhedron + in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3 + defined as the convex hull of 48 vertices - The same is possible with vertices in ``RDF``:: + Setting ``regular=True`` applies a linear transformation to get + isometric vertex figures and the result is inscribed. This cannot be done using + rational coordinates. We first do the computations using floating point + approximations (``RDF``):: - sage: perm_a2_inexact = polytopes.generalized_permutahedron(['A',2],exact=False) + sage: perm_a2_inexact = polytopes.generalized_permutahedron(['A',2], exact=False) sage: sorted(perm_a2_inexact.vertices()) [A vertex at (-1.0, -1.0), A vertex at (-1.0, 0.0), @@ -2678,7 +2648,7 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula A vertex at (1.0, 0.0), A vertex at (1.0, 1.0)] - sage: perm_a2_inexact_reg = polytopes.generalized_permutahedron(['A',2],exact=False,regular=True) + sage: perm_a2_inexact_reg = polytopes.generalized_permutahedron(['A',2], exact=False, regular=True) sage: sorted(perm_a2_inexact_reg.vertices()) [A vertex at (-1.0, 0.0), A vertex at (-0.5, -0.8660254038), @@ -2687,29 +2657,77 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula A vertex at (0.5, 0.8660254038), A vertex at (1.0, 0.0)] - It works also with types with non-rational coordinates:: + We can do the same computation using exact arithmetic with the field ``AA``:: + + sage: perm_a2_reg = polytopes.generalized_permutahedron(['A',2], regular=True) # optional - sage.rings.number_field + sage: V = sorted(perm_a2_reg.vertices()); V # random # optional - sage.rings.number_field + [A vertex at (-1, 0), + A vertex at (-1/2, -0.866025403784439?), + A vertex at (-1/2, 0.866025403784439?), + A vertex at (1/2, -0.866025403784439?), + A vertex at (1/2, 0.866025403784439?), + A vertex at (1.000000000000000?, 0.?e-18)] + + Even though the numbers look like floating point approximations, the computation is + actually exact. We can clean up the display a bit using ``exactify``:: + + sage: for v in V: # optional - sage.rings.number_field + ....: for x in v: + ....: x.exactify() + sage: V # optional - sage.rings.number_field + [A vertex at (-1, 0), + A vertex at (-1/2, -0.866025403784439?), + A vertex at (-1/2, 0.866025403784439?), + A vertex at (1/2, -0.866025403784439?), + A vertex at (1/2, 0.866025403784439?), + A vertex at (1, 0)] + sage: perm_a2_reg.is_inscribed() # optional - sage.rings.number_field + True - sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 # long time - A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3 defined as the convex hull of 48 vertices + Larger examples take longer:: - sage: perm_b3_reg = polytopes.generalized_permutahedron(['B',3],regular=True); perm_b3_reg # not tested - long time (12sec on 64 bits). + sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3], regular=True); perm_a3_reg # long time # optional - sage.rings.number_field + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices + sage: perm_a3_reg.is_inscribed() # long time # optional - sage.rings.number_field + True + sage: perm_b3_reg = polytopes.generalized_permutahedron(['B',3], regular=True); perm_b3_reg # not tested - long time (12sec on 64 bits). A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices - It is faster with the backend ``'normaliz'``:: + It is faster with the backend ``'number_field'``, which internally uses an embedded + number field instead of doing the computations directly with the base ring (``AA``):: - sage: perm_b3_reg_norm = polytopes.generalized_permutahedron(['B',3],regular=True,backend='normaliz') # optional - pynormaliz - sage: perm_b3_reg_norm # optional - pynormaliz + sage: perm_a3_reg_nf = polytopes.generalized_permutahedron( # optional - sage.rings.number_field + ....: ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices + sage: perm_a3_reg_nf.is_inscribed() # optional - sage.rings.number_field + True + sage: perm_b3_reg_nf = polytopes.generalized_permutahedron( # long time # optional - sage.rings.number_field + ....: ['B',3], regular=True, backend='number_field'); perm_b3_reg_nf A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices - The backend ``'normaliz'`` allows further faster computation in the - non-rational case:: + It is even faster with the backend ``'normaliz'``:: + + sage: perm_a3_reg_norm = polytopes.generalized_permutahedron( # optional - pynormaliz + ....: ['A',3], regular=True, backend='normaliz'); perm_a3_reg_norm + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices + sage: perm_a3_reg_norm.is_inscribed() # optional - pynormaliz + True + sage: perm_b3_reg_norm = polytopes.generalized_permutahedron( # optional - pynormaliz + ....: ['B',3], regular=True, backend='normaliz'); perm_b3_reg_norm + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices - sage: perm_h3 = polytopes.generalized_permutahedron(['H',3],backend='normaliz') # optional - pynormaliz - sage: perm_h3 # optional - pynormaliz - A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 defined as the convex hull of 120 vertices - sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz') # optional - pynormaliz, long time - sage: perm_f4 # optional - pynormaliz, long time - A 4-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4 defined as the convex hull of 1152 vertices + The speedups from using backend ``'normaliz'`` allow us to go even further:: + + sage: perm_h3 = polytopes.generalized_permutahedron(['H',3], backend='normaliz') # optional - pynormaliz + sage: perm_h3 # optional - pynormaliz + A 3-dimensional polyhedron + in (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 + defined as the convex hull of 120 vertices + sage: perm_f4 = polytopes.generalized_permutahedron(['F',4], backend='normaliz') # long time # optional - pynormaliz + sage: perm_f4 # long time # optional - pynormaliz + A 4-dimensional polyhedron + in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4 + defined as the convex hull of 1152 vertices .. SEEALSO:: diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index ba1987b1fe2..2b87a343b41 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -19,7 +19,6 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_double import RDF -from sage.rings.ring import CommutativeRing from sage.categories.fields import Fields from sage.categories.rings import Rings from sage.categories.modules import Modules @@ -121,6 +120,10 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * sage: SCR = SR.subring(no_variables=True) # optional - sage.symbolic sage: Polyhedra(SCR, 2, backend='normaliz') # optional - pynormaliz # optional - sage.symbolic Polyhedra in (Symbolic Constants Subring)^2 + + sage: Polyhedra(SCR, 2, backend='number_field') # optional - sage.symbolic + Polyhedra in (Symbolic Constants Subring)^2 + """ if ambient_space_or_base_ring is not None: if ambient_space_or_base_ring in Rings(): @@ -180,6 +183,8 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * except TypeError: raise ValueError(f"the 'polymake' backend for polyhedron cannot be used with {base_field}") return Polyhedra_polymake(base_field, ambient_dim, backend) + elif backend == 'number_field': + return Polyhedra_number_field(base_ring.fraction_field(), ambient_dim, backend) elif backend == 'field': if not base_ring.is_exact(): raise ValueError("the 'field' backend for polyhedron cannot be used with non-exact fields") @@ -1006,7 +1011,7 @@ def _get_action_(self, other, op, self_is_left): extended_self._internal_coerce_map_from(self).__copy__()) return action - if op is operator.mul and isinstance(other, CommutativeRing): + if op is operator.mul and other in Rings().Commutative(): ring = self._coerce_base_ring(other) if ring is self.base_ring(): return ActedUponAction(other, self, not self_is_left) @@ -1158,13 +1163,14 @@ def _make_Line(self, polyhedron, data): return obj - from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd lazy_import('sage.geometry.polyhedron.backend_cdd_rdf', 'Polyhedron_RDF_cdd') from sage.geometry.polyhedron.backend_ppl import Polyhedron_ZZ_ppl, Polyhedron_QQ_ppl from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz, Polyhedron_ZZ_normaliz, Polyhedron_QQ_normaliz from sage.geometry.polyhedron.backend_polymake import Polyhedron_polymake from sage.geometry.polyhedron.backend_field import Polyhedron_field +from sage.geometry.polyhedron.backend_number_field import Polyhedron_number_field + class Polyhedra_ZZ_ppl(Polyhedra_base): Element = Polyhedron_ZZ_ppl @@ -1245,6 +1251,9 @@ class Polyhedra_polymake(Polyhedra_base): class Polyhedra_field(Polyhedra_base): Element = Polyhedron_field +class Polyhedra_number_field(Polyhedra_base): + Element = Polyhedron_number_field + @cached_function def does_backend_handle_base_ring(base_ring, backend): r""" diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 6e40f119c1e..a4c30cadd0c 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1532,7 +1532,7 @@ def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): tikz_pic += "%% facet_color = {}\n".format(facet_color) tikz_pic += "%% opacity = {}\n".format(opacity) tikz_pic += "%% vertex_color = {}\n".format(vertex_color) - tikz_pic += "%% axis = {}\n\n".format(axis) + tikz_pic += "%% axis = {}\n%%\n".format(axis) # Draws the axes if True if axis: @@ -1681,7 +1681,7 @@ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color, tikz_pic += "%% facet_color = {}\n".format(facet_color) tikz_pic += "%% opacity = {}\n".format(opacity) tikz_pic += "%% vertex_color = {}\n".format(vertex_color) - tikz_pic += "%% axis = {}\n\n".format(axis) + tikz_pic += "%% axis = {}\n%%\n".format(axis) # Draws the axes if True if axis: @@ -1773,7 +1773,7 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, %% opacity = 0.8 %% vertex_color = green %% axis = False - <BLANKLINE> + %% %% Coordinate of the vertices: %% \coordinate (0.00000, 1.00000, -1.00000) at (0.00000, 1.00000, -1.00000); @@ -1881,7 +1881,7 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, tikz_pic += "%% facet_color = {}\n".format(facet_color) tikz_pic += "%% opacity = {}\n".format(opacity) tikz_pic += "%% vertex_color = {}\n".format(vertex_color) - tikz_pic += "%% axis = {}\n\n".format(axis) + tikz_pic += "%% axis = {}\n%%\n".format(axis) # Draws the axes if True if axis: diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 336c1900db5..6ce59ff5a8a 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -1670,7 +1670,7 @@ def repr_pretty(coefficients, type, prefix='x', indices=None, - ``type`` -- either ``0`` (``PolyhedronRepresentation.INEQUALITY``) or ``1`` (``PolyhedronRepresentation.EQUATION``) - - ``prefix`` -- a string + - ``prefix`` -- a string (default: ``x``) - ``indices`` -- a tuple or other iterable @@ -1705,9 +1705,9 @@ def repr_pretty(coefficients, type, prefix='x', indices=None, indices = range(len(coeffs)-1) vars = [1] if latex: - vars += ['x_{{{}}}'.format(i) for i in indices] + vars += [f'{prefix}_{{{i}}}' for i in indices] else: - vars += ['x{}'.format(i) for i in indices] + vars += [f'{prefix}{i}' for i in indices] if type == PolyhedronRepresentation.EQUATION: rel = '=' if latex else '==' elif type == PolyhedronRepresentation.INEQUALITY: diff --git a/src/sage/geometry/ribbon_graph.py b/src/sage/geometry/ribbon_graph.py index 60375b20ce2..6cc269c6b99 100644 --- a/src/sage/geometry/ribbon_graph.py +++ b/src/sage/geometry/ribbon_graph.py @@ -1,9 +1,9 @@ r""" Ribbon Graphs -This file implements objects called *ribbon graphs*. These are graphs -together with a cyclic ordering of the darts adjacent to each -vertex. This data allows us to unambiguously "thicken" the ribbon +This file implements objects called *ribbon graphs*. These are graphs +together with a cyclic ordering of the darts adjacent to each +vertex. This data allows us to unambiguously "thicken" the ribbon graph to an orientable surface with boundary. Also, every orientable surface with non-empty boundary is the thickening of a ribbon graph. @@ -103,10 +103,10 @@ class RibbonGraph(SageObject, UniqueRepresentation): **Brief introduction** - Let `\Sigma` be an orientable surface with non-empty boundary and let - `\Gamma` be the topological realization of a graph that is embedded in + Let `\Sigma` be an orientable surface with non-empty boundary and let + `\Gamma` be the topological realization of a graph that is embedded in `\Sigma` in such a way that the graph is a strong deformation retract of - the surface. + the surface. Let `v(\Gamma)` be the set of vertices of `\Gamma`, suppose that these are white vertices. Now we mark black vertices in an interior point @@ -121,7 +121,7 @@ class RibbonGraph(SageObject, UniqueRepresentation): `\Gamma` and suppose that we enumerate the set `D(\Gamma)` and that it has `n` elements. - With the orientation of the surface and the embedding of the graph in + With the orientation of the surface and the embedding of the graph in the surface we can produce two permutations: - A permutation that we denote by `\sigma`. This permutation is a @@ -139,13 +139,13 @@ class RibbonGraph(SageObject, UniqueRepresentation): .. RUBRIC:: Abstract definition - Consider a graph `\Gamma` (not a priori embedded in any surface). - Now we can again consider one vertex in the interior of each edge + Consider a graph `\Gamma` (not a priori embedded in any surface). + Now we can again consider one vertex in the interior of each edge splitting each edge in two darts. We label the darts with numbers. - We say that a ribbon structure on `\Gamma` is a set of two + We say that a ribbon structure on `\Gamma` is a set of two permutations `(\sigma, \rho)`. Where `\sigma` is formed by as many - disjoint cycles as vertices had `\Gamma`. And each cycle is a + disjoint cycles as vertices had `\Gamma`. And each cycle is a cyclic ordering of the darts adjacent to a vertex. The permutation `\rho` just tell us which two darts belong to the same edge. @@ -210,7 +210,7 @@ class RibbonGraph(SageObject, UniqueRepresentation): sage: R2.sigma() (1,3,5,8)(2,4,6) - This example is constructed by taking the bipartite graph of + This example is constructed by taking the bipartite graph of type `(3,3)`:: sage: s3 = PermutationGroupElement('(1,2,3)(4,5,6)(7,8,9)(10,11,12)(13,14,15)(16,17,18)') @@ -384,7 +384,7 @@ def number_boundaries(self): sage: R1.number_boundaries() 1 - This example is constructed by taking the bipartite graph of + This example is constructed by taking the bipartite graph of type `(3,3)`:: sage: s2 = PermutationGroupElement('(1,2,3)(4,5,6)(7,8,9)(10,11,12)(13,14,15)(16,17,18)') @@ -409,7 +409,7 @@ def contract_edge(self, k): INPUT: - - ``k`` -- non-negative integer; the position in `\rho` of the + - ``k`` -- non-negative integer; the position in `\rho` of the transposition that is going to be contracted OUTPUT: @@ -441,7 +441,7 @@ def contract_edge(self, k): ValueError: the edge is a loop and cannot be contracted In this example, we consider a graph that has one edge ``(19,20)`` - such that one of its ends is a vertex of valency 1. This is + such that one of its ends is a vertex of valency 1. This is the vertex ``(20)`` that is not specified when defining `\sigma`. We contract precisely this edge and get a ribbon graph with no vertices of valency 1:: @@ -460,16 +460,16 @@ def contract_edge(self, k): #the following two lines convert the list of tuples to list of lists aux_sigma = [list(x) for x in self._sigma.cycle_tuples(singletons=True)] aux_rho = [list(x) for x in self._rho.cycle_tuples()] - #The following ''if'' rules out the cases when we would be - #contracting a loop (which is not admissible since we would + #The following ''if'' rules out the cases when we would be + #contracting a loop (which is not admissible since we would #lose the topological type of the graph). - if (_find(aux_sigma, aux_rho[k][0])[0] == + if (_find(aux_sigma, aux_rho[k][0])[0] == _find(aux_sigma, aux_rho[k][1])[0]): raise ValueError("the edge is a loop and cannot be contracted") #We store in auxiliary variables the positions of the vertices #that are the ends of the edge to be contracted and we delete #from them the darts corresponding to the edge that is going - #to be contracted. We also delete the contracted edge + #to be contracted. We also delete the contracted edge #from aux_rho pos1 = _find(aux_sigma, aux_rho[k][0]) pos2 = _find(aux_sigma, aux_rho[k][1]) @@ -514,7 +514,7 @@ def extrude_edge(self, vertex, dart1, dart2): OUTPUT: - A ribbon graph resulting from extruding a new edge that + A ribbon graph resulting from extruding a new edge that pulls from ``vertex`` a new vertex that is, now, adjacent to all the darts from ``dart1``to ``dart2`` (not including ``dart2``) in the cyclic ordering given by the cycle corresponding @@ -553,7 +553,7 @@ def extrude_edge(self, vertex, dart1, dart2): (1,2)(3,4)(5,6)(7,8) In the following example we first extrude one edge from a vertex - of valency 3 generating a new vertex of valency 2. Then we + of valency 3 generating a new vertex of valency 2. Then we extrude a new edge from this vertex of valency 2:: sage: s1 = PermutationGroupElement('(1,3,5)(2,4,6)') @@ -582,7 +582,7 @@ def extrude_edge(self, vertex, dart1, dart2): for val in val_one: repr_sigma += [[val]] - # We find which is the highest value a dart has, in order to + # We find which is the highest value a dart has, in order to # add new darts that do not conflict with previous ones. k = max(darts_rho) @@ -627,7 +627,7 @@ def genus(self): """ #We now use the same procedure as in _repr_ to get the vertices #of valency 1 and distinguish them from the extra singletons of - #the permutation sigma. + #the permutation sigma. repr_sigma = [list(x) for x in self._sigma.cycle_tuples()] repr_rho = [list(x) for x in self._rho.cycle_tuples()] darts_rho = flatten(repr_rho) @@ -637,10 +637,10 @@ def genus(self): #the total number of vertices of sigma is its number of cycles #of length >1 plus the number of singletons that are actually #vertices of valency 1 - + vertices = len(self._sigma.cycle_tuples()) + len(val_one) edges = len(self._rho.cycle_tuples()) - #formula for the genus using that the thickening is homotopically + #formula for the genus using that the thickening is homotopically #equivalent to the graph g = (-vertices + edges - self.number_boundaries() + 2) // 2 @@ -674,7 +674,7 @@ def boundary(self): OUTPUT: - A list of lists. The number of inner lists is the number of + A list of lists. The number of inner lists is the number of boundary components of the surface. Each list in the list consists of an ordered tuple of numbers, each number comes from the number assigned to the corresponding dart before @@ -720,13 +720,13 @@ def boundary(self): bound = [] #since lists of tuples are not modifiable, we change the data to a - #list of lists + #list of lists aux_perm = (self._rho * self._sigma).cycle_tuples(singletons=True) - #the cycles of the permutation rho*sigma are in 1:1 correspondence with + #the cycles of the permutation rho*sigma are in 1:1 correspondence with #the boundary components of the thickening (see function number_boundaries()) #but they are not the labeled boundary components. - #With the next for, we convert the cycles of rho*sigma to actually + #With the next for, we convert the cycles of rho*sigma to actually #the labelling of the edges. Each edge, therefore, should appear twice for i,p in enumerate(aux_perm): @@ -799,16 +799,16 @@ def reduced(self): aux_sigma = [list(x) for x in aux_ribbon._sigma.cycle_tuples(singletons=True)] aux_rho = [list(x) for x in aux_ribbon._rho.cycle_tuples()] for j in range(len(aux_rho)): - if (_find(aux_sigma, aux_rho[j][0])[0] != + if (_find(aux_sigma, aux_rho[j][0])[0] != _find(aux_sigma, aux_rho[j][1])[0]): aux_ribbon = aux_ribbon.contract_edge(j) - aux_rho = [list(x) for + aux_rho = [list(x) for x in aux_ribbon._rho.cycle_tuples()] break #finally we change the data to a list of tuples and return the - #information as a ribbon graph. + #information as a ribbon graph. return aux_ribbon - + #the next function computes a basis of homology, it uses #the previous function. @@ -878,7 +878,7 @@ def make_generic(self): def homology_basis(self): r""" - Return an oriented basis of the first homology group of the + Return an oriented basis of the first homology group of the graph. OUTPUT: @@ -960,15 +960,15 @@ def homology_basis(self): basis = [[list(x)] for x in self.reduced()._rho.cycle_tuples()] - #Now we define center as the set of edges that were contracted - #in reduced() this set is contractible and can be define as the + #Now we define center as the set of edges that were contracted + #in reduced() this set is contractible and can be define as the #complement of reduced_rho in rho - center = [list(x) for x in self._rho.cycle_tuples() + center = [list(x) for x in self._rho.cycle_tuples() if (x not in self.reduced()._rho.cycle_tuples())] #We define an auxiliary list 'vertices' that will contain the - #vertices (cycles of sigma) corresponding to each half edge. + #vertices (cycles of sigma) corresponding to each half edge. vertices = [] @@ -1013,7 +1013,7 @@ def homology_basis(self): basis[i][j][0], basis[i][j][1] = \ basis[i][j][1], basis[i][j][0] - #the variable basis is a LIST of Lists of lists. Each List + #the variable basis is a LIST of Lists of lists. Each List #corresponds to an element of the basis and each list in a List #is just a 2-tuple which corresponds to an ''ordered'' edge of rho. @@ -1056,7 +1056,7 @@ def normalize(self): darts_rho = flatten(aux_rho) darts_sigma = flatten(aux_sigma) val_one = [x for x in darts_rho if x not in darts_sigma] - + #We add them to aux_sigma for i in range(len(val_one)): aux_sigma += [[val_one[i]]] @@ -1098,8 +1098,8 @@ def make_ribbon(g, r): OUTPUT: - - a ribbon graph that has 2 vertices (two non-trivial cycles - in its sigma permutation) of valency `2g + r` and it has + - a ribbon graph that has 2 vertices (two non-trivial cycles + in its sigma permutation) of valency `2g + r` and it has `2g + r` edges (and hence `4g + 2r` darts) EXAMPLES:: @@ -1147,7 +1147,7 @@ def make_ribbon(g, r): repr_sigma[1].append(i+(2*g+2)+1) repr_rho += [[i+2,i+(2*g+2)+1]] - #finally we add an edge for each additional boundary component. + #finally we add an edge for each additional boundary component. max_dart = 4*g+2 for j in range(r-1): repr_sigma[0].insert(0, max_dart+2*(j+1)-1) diff --git a/src/sage/geometry/triangulation/element.py b/src/sage/geometry/triangulation/element.py index 29de26bbc13..a436b40d441 100644 --- a/src/sage/geometry/triangulation/element.py +++ b/src/sage/geometry/triangulation/element.py @@ -878,7 +878,7 @@ def normal_cone(self): raise NotImplementedError('Only base rings ZZ and QQ are supported') from ppl import Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix - from sage.arith.all import lcm + from sage.arith.functions import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 8d05c3bcb19..f970a5faf4a 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -708,7 +708,7 @@ def _TOPCOM_triangulations(self, verbose=True): # points2triangs # [[0,0,1],[0,1,1],[1,0,1],[1,1,1],[-1,-1,1]] #### TOPCOM output #### - # T[0]:=[0->5,3:{{0,1,2},{1,2,3},{0,2,4},{0,1,4}}]; + # T[0] := {{0,1,2},{0,1,4},{0,2,4},{1,2,3}}; (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>) """ command = 'points2' @@ -1832,7 +1832,7 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): :class:`~sage.geometry.triangulation.base.Point` or ``None`` (default). A specific point to start with when picking the simplex vertices. - + - ``point_order`` -- a list or tuple of (some or all) :class:`~sage.geometry.triangulation.base.Point` s or ``None`` (default). @@ -1865,13 +1865,13 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): sage: pc.contained_simplex() (P(-1, -1), P(1, 1), P(0, 1)) sage: pc.contained_simplex(point_order = [pc[1],pc[3],pc[4],pc[2],pc[0]]) - (P(0, 1), P(1, 1), P(-1, -1)) + (P(0, 1), P(1, 1), P(-1, -1)) Lower-dimensional example:: sage: pc.contained_simplex(point_order = [pc[0],pc[3],pc[4]]) (P(0, 0), P(1, 1)) - + TESTS:: sage: pc = PointConfiguration([[0,0],[0,1],[1,0]]) @@ -1899,7 +1899,7 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): # PointConfiguration are actually ignored. if not points: return tuple() - + if initial_point is None: origin = points.pop() else: @@ -2005,7 +2005,7 @@ def facets_of_simplex(simplex): # input verification self._assert_is_affine() - + point_order_is_given = point_order is not None if point_order is None: point_order = list(self.points()) diff --git a/src/sage/graphs/base/graph_backends.pyx b/src/sage/graphs/base/graph_backends.pyx index 9daf0702185..3ff36d5cee2 100644 --- a/src/sage/graphs/base/graph_backends.pyx +++ b/src/sage/graphs/base/graph_backends.pyx @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Backends for Sage (di)graphs. +Backends for Sage (di)graphs This module implements :class:`GenericGraphBackend` (the base class for backends). diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 23fe59aa8e3..6c4bc1b7edd 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -1,7 +1,7 @@ # cython: binding=True # distutils: language = c++ r""" -Static Sparse Graphs +Static sparse graphs What is the point ? ------------------- diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 1040419c32e..9a6501b41ec 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -1,3 +1,4 @@ +# autopep8: off # -*- coding: utf-8 -*- r""" Bipartite graphs @@ -890,6 +891,38 @@ def delete_vertices(self, vertices): else: raise RuntimeError("vertex (%s) not found in partitions" % vertex) + def _flip_vertices(self, vertices): + r""" + Helper method to flip the sides of a list of vertices. + + INPUT: + + - ``vertices`` -- an iterable container of vertices + + TESTS:: + + sage: G = BipartiteGraph() + sage: G.add_vertices([0, 1, 2], left=[True, False, True]) + sage: G.bipartition() + ({0, 2}, {1}) + sage: G._flip_vertices([0, 1]) + sage: G.bipartition() + ({1, 2}, {0}) + sage: G._flip_vertices([7]) + Traceback (most recent call last): + ... + RuntimeError: vertex (7) is neither in left nor in right + """ + for vertex in vertices: + if vertex in self.left: + self.left.remove(vertex) + self.right.add(vertex) + elif vertex in self.right: + self.right.remove(vertex) + self.left.add(vertex) + else: + raise RuntimeError("vertex ({0}) is neither in left nor in right".format(vertex)) + def add_edge(self, u, v=None, label=None): r""" Add an edge from `u` to `v`. @@ -917,7 +950,12 @@ def add_edge(self, u, v=None, label=None): This method simply checks that the edge endpoints are in different partitions. If a new vertex is to be created, it will be added to the proper partition. If both vertices are created, the first one will be - added to the left partition, the second to the right partition. + added to the left partition, the second to the right partition. If + both vertices are in the same partition but different connected + components, one of the components will be "flipped", i.e. each vertex + will be put into whichever partition it's not currently in. This will + allow for the graph to remain bipartite, without changing the edges or + vertices. TESTS:: @@ -933,6 +971,13 @@ def add_edge(self, u, v=None, label=None): sage: bg.add_edge(5, 6); 5 in bg.left; 6 in bg.right True True + sage: G = BipartiteGraph() + sage: G.add_edges([(0, 1), (3, 2)]) + sage: G.bipartition() + ({0, 3}, {1, 2}) + sage: G.add_edge(1,2) + sage: G.bipartition() + ({0, 2}, {1, 3}) """ # logic for getting endpoints copied from generic_graph.py if label is None: @@ -946,9 +991,19 @@ def add_edge(self, u, v=None, label=None): if v is None: u, v = u - # check for endpoints in different partitions + # if endpoints are in the same partition if self.left.issuperset((u, v)) or self.right.issuperset((u, v)): - raise RuntimeError("edge vertices must lie in different partitions") + + # get v's connected component + v_connected_component = self.connected_component_containing_vertex(v, sort=False) + + # if u is in it, then the edge still cannot exist + if u in v_connected_component: + raise RuntimeError("edge vertices must lie in different partitions") + + # if not, we can "flip" the connected component + # swapping which partition the vertices are in + self._flip_vertices(v_connected_component) # automatically decide partitions for the newly created vertices if u not in self: @@ -976,7 +1031,12 @@ def add_edges(self, edges, loops=True): This method simply checks that the edge endpoints are in different partitions. If a new vertex is to be created, it will be added to the proper partition. If both vertices are created, the first one will be - added to the left partition, the second to the right partition. + added to the left partition, the second to the right partition. If + both vertices are in the same partition but different connected + components, one of the components will be "flipped", i.e. each vertex + will be put into whichever partition it's not currently in. This will + allow for the graph to remain bipartite, without changing the edges or + vertices. EXAMPLES:: @@ -986,15 +1046,37 @@ def add_edges(self, edges, loops=True): sage: bg.add_edges([[0, 2]]) Traceback (most recent call last): ... - RuntimeError: edge vertices must lie in different partitions + ValueError: the specified set of edges cannot be added while still preserving the bipartition property + sage: G = BipartiteGraph() + sage: G.add_edges([(0, 1), (3, 2), (1, 2)]) + sage: G.bipartition() + ({0, 2}, {1, 3}) + Loops will raise an error:: sage: bg.add_edges([[0, 3], [3, 3]]) Traceback (most recent call last): ... - RuntimeError: edge vertices must lie in different partitions + ValueError: the specified set of edges cannot be added while still preserving the bipartition property + + Adding edges is fine as long as there exists a valid bipartition. + Otherwise an error is raised without modifyiong the graph:: + + sage: G = BipartiteGraph() + sage: G.add_edges([(0, 1), (2, 3)]) + sage: G.bipartition() + ({0, 2}, {1, 3}) + sage: G.add_edges([(0,2), (0,3)]) + Traceback (most recent call last): + ... + ValueError: the specified set of edges cannot be added while still preserving the bipartition property + sage: G.bipartition() + ({0, 2}, {1, 3}) + sage: G.edges(labels=False, sort=True) + [(0, 1), (2, 3)] """ + edges_to_add = [] for edge in edges: try: if len(edge) == 3: @@ -1002,21 +1084,113 @@ def add_edges(self, edges, loops=True): else: u, v = edge label = None + edges_to_add.append((u, v, label)) except Exception: raise TypeError("cannot interpret {!r} as graph edge".format(edge)) - # check for endpoints in different partitions - if self.left.issuperset((u, v)) or self.right.issuperset((u, v)): - raise RuntimeError("edge vertices must lie in different partitions") + # Check whether there exists a bipartition supporting the addition of + # input edges to the current graph before adding any edge to the + # graph. This way, if an error is raised, self is not modified + vertex_in_left = self._check_bipartition_for_add_edges(edges_to_add) + + if vertex_in_left is False: + raise ValueError("the specified set of edges cannot be added while " + "still preserving the bipartition property") + + # If we get here, then we've found a valid bipartition. + # We update the bipartition + self.left.clear() + self.right.clear() + for v, left in vertex_in_left.items(): + if left: + self.left.add(v) + else: + self.right.add(v) - # automatically decide partitions for the newly created vertices + # Each edge now has one endpoint in left and the other in right + for u, v, label in edges_to_add: if u not in self: - self.add_vertex(u, left=(v in self.right or v not in self), right=(v in self.left)) + self.add_vertex(u, left=vertex_in_left[u], right=not vertex_in_left[u]) if v not in self: - self.add_vertex(v, left=(u in self.right), right=(u in self.left)) + self.add_vertex(v, left=vertex_in_left[v], right=not vertex_in_left[v]) self._backend.add_edge(u, v, label, self._directed) + def _check_bipartition_for_add_edges(self, edges): + r""" + Helper method for ``add_edges``. + + This method checks whether the input list of edges can be added to the + graph. More precisely, it checks whether there exists a bipartition of + the vertices supporting the addition of input edges. If so it returns it + as a mapping associating to each vertex a side of the bipartition. + Otherwise, it returns ``False``. + + INPUT: + + - ``edges`` -- an iterable of edges, given either as ``(u, v)`` + or ``(u, v, label)``. + + TESTS:: + + sage: bg = BipartiteGraph() + sage: bg.add_vertices([0, 1, 2, 3], left=[True, False, True, False]) + sage: b = bg._check_bipartition_for_add_edges([(0, 1), (3, 2), (1, 2)]) + sage: sorted(b.items()) + [(0, True), (1, False), (2, True), (3, False)] + sage: b = bg._check_bipartition_for_add_edges([(0, 2)]) + sage: sorted(b.items()) + [(0, True), (1, False), (2, False), (3, False)] + sage: bg.add_edges([(0, 1), (3, 2), (1, 2)]) + sage: bg._check_bipartition_for_add_edges([[0, 2]]) + False + """ + # Map each vertex of the graph to a side + vertex_in_left = {v: True for v in self.left} + for v in self.right: + vertex_in_left[v] = False + + # Map each vertex to the connected component it belongs to + vertex_to_component = {v: comp for comp in self.connected_components() + for v in comp} + + for e in edges: + u, v = e[:2] + # if we haven't encountered either/both vertices, we choose a side + # and extend components + if u not in vertex_in_left: + if v in vertex_in_left: + vertex_in_left[u] = not vertex_in_left[v] + else: + vertex_in_left[u] = True + vertex_in_left[v] = False + vertex_to_component[v] = [v] + vertex_to_component[v].append(u) + vertex_to_component[u] = vertex_to_component[v] + + elif v not in vertex_in_left: + vertex_in_left[v] = not vertex_in_left[u] + vertex_to_component[u].append(v) + vertex_to_component[v] = vertex_to_component[u] + + elif vertex_in_left[u] == vertex_in_left[v]: + if vertex_to_component[u] is vertex_to_component[v]: + # Same side and same component. We can't add that edge + return False + + # Otherwise, we flip the bipartition in v's component + for w in vertex_to_component[v]: + vertex_in_left[w] = not vertex_in_left[w] + + # and merge the components + comp_u = vertex_to_component[u] + comp_u.extend(vertex_to_component[v]) + for w in vertex_to_component[v]: + vertex_to_component[w] = comp_u + + # Return the bipartition + return vertex_in_left + def allow_loops(self, new, check=True): """ Change whether loops are permitted in the (di)graph diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index d3afec0e278..441ba58643d 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -1,6 +1,6 @@ # cython: binding=True """ -Chromatic Polynomial +Chromatic polynomial AUTHORS: diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 7e2a59e4abc..66a14bd30fb 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -192,7 +192,7 @@ class DiGraph(GenericGraph): pre-defined digraphs, see the :mod:`~sage.graphs.digraph_generators` module. A :class:`DiGraph` object has many methods whose list can be obtained by - typing ``g.<tab>`` (i.e. hit the 'tab' key) or by reading the documentation + typing ``g.<tab>`` (i.e. hit the :kbd:`Tab` key) or by reading the documentation of :mod:`~sage.graphs.digraph`, :mod:`~sage.graphs.generic_graph`, and :mod:`~sage.graphs.graph`. @@ -3472,7 +3472,7 @@ def period(self): :meth:`is_aperiodic` """ - from sage.arith.all import gcd + from sage.arith.misc import GCD as gcd g = 0 diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index cceb89df765..c0a0a0ef926 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -12,7 +12,7 @@ sage: p = digraphs.Circulant(10,[2,3]) More interestingly, one can get the list of all digraphs that Sage knows how to -build by typing ``digraphs.`` in Sage and then hitting tab. +build by typing ``digraphs.`` in Sage and then hitting :kbd:`Tab`. .. csv-table:: :class: contentstable @@ -393,7 +393,7 @@ def Paley(self, q): """ from sage.rings.finite_rings.integer_mod import mod from sage.rings.finite_rings.finite_field_constructor import FiniteField - from sage.arith.all import is_prime_power + from sage.arith.misc import is_prime_power if not is_prime_power(q): raise ValueError("parameter q must be a prime power") if not mod(q, 4) == 3: diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 7da899e88b8..87e83b4f5ce 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -1441,7 +1441,7 @@ cdef uint32_t diameter_DiFUB(short_digraph sd, - ``source`` -- starting node of the first BFS - TESTS:: + TESTS: The diameter of a weakly connected digraph is infinity :: diff --git a/src/sage/graphs/edge_connectivity.pyx b/src/sage/graphs/edge_connectivity.pyx index 9b4c2533a40..5263e712db5 100644 --- a/src/sage/graphs/edge_connectivity.pyx +++ b/src/sage/graphs/edge_connectivity.pyx @@ -9,7 +9,6 @@ from a `2k` edge-connected graph or a `k` edge-connected digraph. .. TODO:: - - Add speedup methods proposed in [GKLP2021]_ for the edge connectivity - Implement the tree-packing algorithms proposed in [Gabow1995]_ and [BHKP2008]_ - Extend to digraphs with multiple edges @@ -63,6 +62,24 @@ cdef class GabowEdgeConnectivity: sage: GabowEdgeConnectivity(D).edge_connectivity() 6 + A complete digraph with `n` vertices is `n-1`-edge-connected:: + + sage: from sage.graphs.edge_connectivity import GabowEdgeConnectivity + sage: D = DiGraph(digraphs.Complete(10)) + sage: GabowEdgeConnectivity(D, use_rec = True).edge_connectivity() + 9 + + Check that we get the same result when with and without the DFS-based + speed-up initialization proposed in [GKLP2021]_:: + + sage: G = graphs.RandomBarabasiAlbert(100, 2) + sage: D = DiGraph(G) + sage: ec1 = GabowEdgeConnectivity(D, dfs_preprocessing=False).edge_connectivity() + sage: ec2 = GabowEdgeConnectivity(D, dfs_preprocessing=True).edge_connectivity() + sage: ec3 = GabowEdgeConnectivity(D, dfs_preprocessing=True, use_rec=True).edge_connectivity() + sage: ec1 == ec2 and ec2 == ec3 + True + TESTS: :trac:`32169`:: @@ -120,6 +137,7 @@ cdef class GabowEdgeConnectivity: cdef vector[vector[int]] g_out cdef vector[vector[int]] g_in cdef vector[vector[int]] my_g # either g_out or g_in + cdef vector[vector[int]] my_g_reversed # either g_out or g_in # values associated to edges cdef int* tail # source of edge j @@ -140,9 +158,9 @@ cdef class GabowEdgeConnectivity: cdef int augmenting_root cdef bint* tree_flag # indicate whether a tree Ti has been touched cdef int* root # current root vertex of f_tree i - cdef int* L_roots # L_roots of the trees + cdef int* L_roots # L_roots of the trees cdef bint* forests # indicate whether the f_tree is active or inactive - cdef bint** labeled # + cdef bint** labeled # cdef int** parent_1 # parent of v in tree/forest Ti cdef int** parent_2 # parent of v in tree/forest Ti @@ -158,7 +176,7 @@ cdef class GabowEdgeConnectivity: cdef vector[int] A_path cdef vector[int] left_traverse cdef vector[int] right_traverse - + cdef bint* seen # for method re_init cdef int* stack # stack of vertices for DFS in re_init cdef vector[vector[int]] tree_edges # used to organise the edges of the trees @@ -169,7 +187,15 @@ cdef class GabowEdgeConnectivity: cdef queue[pair[int, int]] joining_edges # queue of tuples (edge id, edge state) cdef queue[int] incident_edges_Q # queue of edges - def __init__(self, G): + cdef int num_start_f_trees # number of f-trees at the beginning of an iteration + cdef int num_joins # number of joined vertices from dfs + cdef bint* T # whether an edge is in the proven k-intersection + cdef bint* visited # for method find_dfs_tree + cdef int * incident_edge_index # used for DFS initialization + cdef bint dfs_preprocessing # whether to use DFS-based fast initialization + cdef bint use_rec # whether to use the recursive DFS initialization + + def __init__(self, G, dfs_preprocessing=True, use_rec=False): r""" Initialize this object. @@ -177,6 +203,15 @@ cdef class GabowEdgeConnectivity: - ``G`` -- a :class:`~sage.graphs.digraph.DiGraph` + - ``dfs_preprocessing`` -- boolean (default: ``True``); whether to use + the DFS-based speed-up initialization proposed in [GKLP2021]_ + + - ``use_rec`` -- boolean (default: ``False``); whether to use a + recursive or non-recursive DFS for ``dfs_preprocessing``. The + recursive DFS tends to be faster than the non-recursive version on + complete digraphs and slower on other graphs. This parameter is + ignored when ``dfs_preprocessing`` is ``False``. + EXAMPLES:: sage: from sage.graphs.edge_connectivity import GabowEdgeConnectivity @@ -184,6 +219,8 @@ cdef class GabowEdgeConnectivity: sage: GabowEdgeConnectivity(D).edge_connectivity() 4 """ + self.dfs_preprocessing = dfs_preprocessing + self.use_rec = use_rec self.ec_checked = False from sage.graphs.digraph import DiGraph if not isinstance(G, DiGraph): @@ -199,9 +236,6 @@ cdef class GabowEdgeConnectivity: self.F.clear() return - # Set upper bound on the edge connectivity - self.max_ec = min(min(G.out_degree_iterator()), min(G.in_degree_iterator())) - # # Initialize some data structures # @@ -209,10 +243,23 @@ cdef class GabowEdgeConnectivity: self.n = G.order() self.m = G.size() self.mem = MemoryAllocator() - - # Build compact graph data structure with out and in adjacencies + + # Build compact graph data structure with out and in adjacencies. + # Loops are removed from the graph. self.build_graph_data_structure() # From now on, vertices are numbered in [0..n-1] and edges in [0..m-1] + # where m is the number of edges after the removal of the loops + + # Set upper bound on the edge connectivity + cdef int i, d + self.max_ec = INT_MAX + for i in range(self.n): + d = self.g_out[i].size() + if d < self.max_ec: + self.max_ec = d + d = self.g_in[i].size() + if d < self.max_ec: + self.max_ec = d self.labels = <int*>self.mem.calloc(self.m, sizeof(int)) self.tree_flag = <bint*>self.mem.calloc(self.max_ec, sizeof(bint)) @@ -230,18 +277,21 @@ cdef class GabowEdgeConnectivity: self.depth_1 = <int**>self.mem.calloc(self.max_ec, sizeof(int*)) self.depth_2 = <int**>self.mem.calloc(self.max_ec, sizeof(int*)) self.stack = <int*>self.mem.calloc(self.n, sizeof(int)) + self.incident_edge_index = <int*>self.mem.calloc(self.n, sizeof(int)) self.tree_edges.resize(self.max_ec) self.tree_edges_incident.resize(self.n) + self.T = <bint*>self.mem.calloc(self.m, sizeof(bint)) + self.visited = <bint*>self.mem.calloc(self.n, sizeof(bint)) # Set some constants self.UNUSED = INT_MAX self.FIRSTEDGE = INT_MAX - 1 - cdef int i for i in range(self.m): self.edge_state_1[i] = self.UNUSED # edge i is unused self.edge_state_2[i] = self.UNUSED self.labels[i] = self.UNUSED # edge i is unlabeled + self.T[i] = False # edge i doesn't belong to any k-intersection yet _ = self.compute_edge_connectivity() sig_check() @@ -271,17 +321,20 @@ cdef class GabowEdgeConnectivity: for i in range(self.n): self.g_out[i].clear() self.g_in[i].clear() - + cdef int x, y cdef int e_id = 0 for x, u in enumerate(self.int_to_vertex): for v in self.G.neighbor_out_iterator(u): y = vertex_to_int[v] - self.g_out[x].push_back(e_id) - self.g_in[y].push_back(e_id) - self.tail[e_id] = x - self.head[e_id] = y - e_id += 1 + if x != y: + self.g_out[x].push_back(e_id) + self.g_in[y].push_back(e_id) + self.tail[e_id] = x + self.head[e_id] = y + e_id += 1 + # Loops have been removed, so we update the number of edges + self.m = e_id cdef bint compute_edge_connectivity(self) except -1: """ @@ -336,6 +389,7 @@ cdef class GabowEdgeConnectivity: if reverse: # Search for a spanning tree in g-reversed self.my_g = self.g_out + self.my_g_reversed = self.g_in self.my_from = self.head self.my_to = self.tail self.my_parent = self.parent_2 @@ -345,6 +399,7 @@ cdef class GabowEdgeConnectivity: else: # Search for a spanning tree in g using incoming arcs self.my_g = self.g_in + self.my_g_reversed = self.g_out self.my_from = self.tail self.my_to = self.head self.my_parent = self.parent_1 @@ -358,7 +413,15 @@ cdef class GabowEdgeConnectivity: cdef int njoins = 0 cdef int z - while njoins < self.n - 1: + self.num_start_f_trees = self.n - self.num_joins + # There are fewer than n f-trees. We prepare to join them. If there's + # only one f-tree, we just save the edges and advance to the next + # iteration + if self.dfs_preprocessing and self.num_start_f_trees < self.n - 1: + self.re_init(tree) + + # There are n f-trees, and we try to join them + while njoins < self.num_start_f_trees-1: # Get the root of an active subtree or INT_MAX if none exists z = self.choose_root() while z != INT_MAX: @@ -406,7 +469,7 @@ cdef class GabowEdgeConnectivity: self.my_depth[tree] = <int*>self.mem.calloc(self.n, sizeof(int)) if not self.my_parent_edge_id[tree]: self.my_parent_edge_id[tree] = <int*>self.mem.calloc(self.n, sizeof(int)) - + cdef int j for j in range(self.n): self.my_parent[tree][j] = 0 @@ -416,12 +479,132 @@ cdef class GabowEdgeConnectivity: self.root[j] = j self.forests[j] = True - # Set inactive the f_trees of the root vertex + self.num_joins = 0 + + # Initialize T_k to be a DFS spanning forest of G \ T + if self.dfs_preprocessing: + self.compute_dfs_tree() + + # Set inactive the f-trees of the root vertex self.forests[self.root_vertex] = False self.L_roots[tree] = self.UNUSED self.tree_flag[tree] = False + cdef void compute_dfs_tree(self): + r""" + Find a DFS spanning forest of `G \backslash T`. + + This is the DFS-based speed-up initialization proposed in [GKLP2021]_. + + EXAMPLES:: + + sage: from sage.graphs.edge_connectivity import GabowEdgeConnectivity + sage: D = digraphs.Complete(5) + sage: GabowEdgeConnectivity(D, dfs_preprocessing=True).edge_connectivity() + 4 + sage: GabowEdgeConnectivity(D, dfs_preprocessing=False).edge_connectivity() + 4 + """ + # Mark all vertices as unvisited + cdef int i + for i in range(self.n): + self.visited[i] = False + + cdef int r + for r in range(self.n): + if not self.visited[r]: + # Make this vertex the root of the following dfs tree + self.root[r] = r + # Make the f-tree rooted at this vertex active + self.forests[r] = True + # Find connected vertices of the f-tree rooted at r + if self.use_rec: + self.find_dfs_tree_rec(r, r) + else: + self.find_dfs_tree(r) + # Each call of find_dfs_tree creates an f-tree + self.num_start_f_trees += 1 + + cdef void find_dfs_tree(self, int r): + r""" + Find more vertices of the f-tree rooted at `r`. + + This is part of the DFS-based speed-up initialization proposed in + [GKLP2021]_. + + EXAMPLES:: + + sage: from sage.graphs.edge_connectivity import GabowEdgeConnectivity + sage: D = digraphs.Complete(5) + sage: GabowEdgeConnectivity(D, dfs_preprocessing=True, use_rec=False).edge_connectivity() + 4 + """ + cdef int u, v, e_id + cdef int * stack = self.stack + cdef int * edge_index = self.incident_edge_index + # We initialize the stack and mark the root as visited + cdef int t = 0 # index pointing to the top of the stack + stack[0] = r + edge_index[r] = self.my_g_reversed[r].size() + self.visited[r] = True + + while t >= 0: + u = stack[t] + if edge_index[u]: + # Visit the next incident edge of u + edge_index[u] -= 1 + e_id = self.my_g_reversed[u][edge_index[u]] + if not self.T[e_id]: + v = self.my_to[e_id] + if not self.visited[v] and v != self.root_vertex: + # Make v belong to the f-tree rooted at r + self.root[v] = r + self.forests[v] = False + self.my_edge_state[e_id] = self.current_tree + self.num_joins += 1 + self.visited[v] = True + # add v to the stack + t += 1 + stack[t] = v + edge_index[v] = self.my_g_reversed[v].size() + # and proceed with v + else: + # We are done with u. We pop. + t -= 1 + + cdef void find_dfs_tree_rec(self, int u, int r): + r""" + Find more vertices of the f-tree rooted at `r`. + + This is part of the DFS-based speed-up initialization proposed in + [GKLP2021]_. + + EXAMPLES:: + + sage: from sage.graphs.edge_connectivity import GabowEdgeConnectivity + sage: D = digraphs.Complete(5) + sage: GabowEdgeConnectivity(D, dfs_preprocessing=True, use_rec=True).edge_connectivity() + 4 + """ + # Mark vertex u as visited to avoid visiting it multiple times + self.visited[u] = True + + # Visit outgoing arcs of current vertex + cdef int e_id, v + for e_id in self.my_g_reversed[u]: + v = self.my_to[e_id] + # Ensure a vertex is not visited, is not a proven k-intersection edge + # and root_vertex remains deficient + if not self.visited[v] and not self.T[e_id] and v != self.root_vertex: + # Make current vertex belong to the f_tree rooted at r + self.root[v] = r + self.forests[v] = False + self.my_edge_state[e_id] = self.current_tree + self.num_joins += 1 + # recursively find more vertices and grow the subtree rooted at r + self.find_dfs_tree_rec(v, r) + cdef int choose_root(self): """ Return the root of an active f_tree, or INT_MAX if none exists. @@ -728,7 +911,7 @@ cdef class GabowEdgeConnectivity: cdef bint label_step(self, int e_id, int e_label): """ - Label edge e_id with e_label and check wheteher edge e_id is joining. + Label edge e_id with e_label and check whether edge e_id is joining. EXAMPLES:: @@ -861,10 +1044,15 @@ cdef class GabowEdgeConnectivity: # Arrange the edges of each tree for j in range(tree + 1): + if self.dfs_preprocessing: + for e_id in self.tree_edges[j]: + self.T[e_id] = False self.tree_edges[j].clear() for j in range(self.m): if self.my_edge_state[j] != self.UNUSED: self.tree_edges[self.my_edge_state[j]].push_back(j) + if self.dfs_preprocessing: + self.T[j] = True for j in range(tree + 1): if not j or j == tree or self.tree_flag[j]: diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 4450192573d..059d46d0c7f 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -19,7 +19,7 @@ # **************************************************************************** from sage.graphs.graph import Graph -from sage.arith.all import is_prime_power +from sage.arith.misc import is_prime_power from sage.rings.finite_rings.finite_field_constructor import FiniteField diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 9837fe3a5fa..ec6a5c19e60 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -2558,7 +2558,7 @@ def PaleyGraph(q): """ from sage.rings.finite_rings.integer_mod import mod from sage.rings.finite_rings.finite_field_constructor import FiniteField - from sage.arith.all import is_prime_power + from sage.arith.misc import is_prime_power if not is_prime_power(q): raise ValueError("parameter q must be a prime power") if not mod(q, 4) == 1: @@ -3933,7 +3933,7 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None): from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix, block_matrix, \ ones_matrix, identity_matrix - from sage.arith.all import two_squares + from sage.arith.misc import two_squares p = 4*t + 1 try: x = two_squares(p) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 93cf637b8dd..1b0f4cd9d4d 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -805,19 +805,7 @@ def RandomHolmeKim(n, m, p, seed=None): may not be all linked to a new node on the first iteration like the BA model. - EXAMPLES: - - We check that a random graph on 8 nodes with 2 random edges per node and a - probability `p = 0.5` of forming triangles contains a triangle:: - - sage: G = graphs.RandomHolmeKim(8, 2, 0.5) - sage: G.order(), G.size() - (8, 12) - sage: C3 = graphs.CycleGraph(3) - sage: G.subgraph_search(C3) - Subgraph of (): Graph on 3 vertices - - :: + EXAMPLES:: sage: G = graphs.RandomHolmeKim(12, 3, .3) sage: G.show() # long time diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index 2b0e30686cc..42d0a95372f 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -311,7 +311,7 @@ def WorldMap(): sage: sorted(g.connected_component_containing_vertex('Ireland')) ['Ireland', 'United Kingdom'] - TESTS:: + TESTS: :trac:`24488`:: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0070705f781..fa48183ecc4 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -460,7 +460,8 @@ class GenericGraph(GenericGraph_pyx): """ # Nice defaults for plotting arrays of graphs (see sage.misc.functional.show) - graphics_array_defaults = {'layout': 'circular', 'vertex_size':50, 'vertex_labels':False, 'graph_border':True} + graphics_array_defaults = {'layout': 'circular', 'vertex_size': 50, + 'vertex_labels': False, 'graph_border': True} def __init__(self): r""" @@ -601,16 +602,16 @@ def __eq__(self, other): if not isinstance(other, GenericGraph): return False from sage.graphs.graph import Graph - g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph - g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph + g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph + g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph # Fast checks if (g1_is_graph != g2_is_graph or - self.allows_multiple_edges() != other.allows_multiple_edges() or - self.allows_loops() != other.allows_loops() or - self.order() != other.order() or - self.size() != other.size() or - self.weighted() != other.weighted()): - return False + self.allows_multiple_edges() != other.allows_multiple_edges() or + self.allows_loops() != other.allows_loops() or + self.order() != other.order() or + self.size() != other.size() or + self.weighted() != other.weighted()): + return False return self._backend.is_subgraph(other._backend, self, ignore_labels=not self.weighted()) @@ -809,11 +810,18 @@ def _bit_vector(self): n = self.order() if self._directed: total_length = n * n - bit = lambda x, y: x * n + y + + def bit(x, y): + return x * n + y else: total_length = (n * (n - 1)) // 2 - n_ch_2 = lambda b: int(b * (b - 1)) // 2 - bit = lambda x, y: n_ch_2(max(x, y)) + min(x, y) + + def n_ch_2(b): + return int(b * (b - 1)) // 2 + + def bit(x, y): + return n_ch_2(max(x, y)) + min(x, y) + bit_vector = set() try: @@ -821,13 +829,13 @@ def _bit_vector(self): except TypeError: V = self v_to_int = {v: i for i, v in enumerate(V)} - for u,v,_ in self.edge_iterator(): + for u, v in self.edge_iterator(labels=False): bit_vector.add(bit(v_to_int[u], v_to_int[v])) bit_vector = sorted(bit_vector) s = [] j = 0 for i in bit_vector: - s.append( '0' * (i - j) + '1' ) + s.append('0' * (i - j) + '1') j = i + 1 s = "".join(s) s += '0' * (total_length - len(s)) @@ -921,7 +929,7 @@ def _repr_(self): name += "multi-" if self._directed: name += "di" - name += "graph on %d vert"%self.order() + name += "graph on %d vert" % self.order() if self.order() == 1: name += "ex" else: @@ -945,7 +953,7 @@ def is_immutable(self): """ return getattr(self, '_immutable', False) - ### Formats + # Formats def copy(self, weighted=None, data_structure=None, sparse=None, immutable=None): """ @@ -1164,7 +1172,7 @@ def copy(self, weighted=None, data_structure=None, sparse=None, immutable=None): (weighted is None or self._weighted == weighted)): from sage.graphs.base.static_sparse_backend import StaticSparseBackend if (isinstance(self._backend, StaticSparseBackend) and - (data_structure=='static_sparse' or data_structure is None)): + (data_structure == 'static_sparse' or data_structure is None)): return self if data_structure is None: @@ -1306,7 +1314,7 @@ def export_to_file(self, filename, format=None, **kwds): if format not in formats: raise ValueError("format '{}' unknown".format(format)) - formats[format](self.networkx_graph(),filename,**kwds) + formats[format](self.networkx_graph(), filename, **kwds) def _scream_if_not_simple(self, allow_loops=False, allow_multiple_edges=False): r""" @@ -1931,20 +1939,20 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds i = new_indices[u] j = new_indices[v] if multiple_edges and (i, j) in D: - D[i,j] += 1 + D[i, j] += 1 if not directed and i != j: - D[j,i] += 1 + D[j, i] += 1 else: - D[i,j] = 1 + D[i, j] = 1 if not directed and i != j: - D[j,i] = 1 + D[j, i] = 1 from sage.matrix.constructor import matrix if base_ring is None: base_ring = ZZ M = matrix(base_ring, n, n, D, sparse=sparse, **kwds) return M - am = adjacency_matrix # shorter call makes life easier + am = adjacency_matrix # shorter call makes life easier def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None, *, base_ring=None, **kwds): @@ -2277,13 +2285,13 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): from sage.matrix.constructor import matrix if ((self.is_directed() and not self.is_strongly_connected()) or - (not self.is_directed() and not self.is_connected())): + (not self.is_directed() and not self.is_connected())): raise ValueError("input (di)graph must be (strongly) connected") if vertices is None: vertices = self.vertices(sort=True) elif (len(vertices) != self.order() or - set(vertices) != set(self.vertex_iterator())): + set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") # We extract from **kwds the arguments for distance_all_pairs @@ -2316,7 +2324,8 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): ret.set_immutable() return ret - def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=None, **kwds): + def weighted_adjacency_matrix(self, sparse=True, vertices=None, + default_weight=None, *, base_ring=None, **kwds): """ Return the weighted adjacency matrix of the graph. @@ -2333,6 +2342,10 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non each vertex is represented by its position in the list returned by method :meth:`vertices` + - ``default_weight`` -- (default: ``None``); specifies the weight to + replace any ``None`` edge label. When not specified an error is raised + if the label of an edge is ``None``. + - ``base_ring`` -- a ring (default: determined from the weights); the base ring of the matrix space to use. @@ -2386,6 +2399,22 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non [0 0 0] [1 0 0] [1 0 0] + + Check error message for non numerical edge weights (:trac:`33562`):: + + sage: G = Graph([(0, 1)]) + sage: G.weighted_adjacency_matrix() + Traceback (most recent call last): + ... + ValueError: cannot find the weight of (0, 1, None). Consider setting parameter 'default_weight' + sage: G.weighted_adjacency_matrix(default_weight=3) + [0 3] + [3 0] + sage: G = Graph([(0, 1, 'a')]) + sage: G.weighted_adjacency_matrix() + Traceback (most recent call last): + ... + TypeError: Cannot convert NoneType to sage.structure.parent.Parent """ if self.has_multiple_edges(): raise NotImplementedError("don't know how to represent weights for a multigraph") @@ -2396,20 +2425,35 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") + # Method for checking edge weights and setting default weight + if default_weight is None: + def func(u, v, label): + if label is None: + raise ValueError(f"cannot find the weight of ({u}, {v}, None). " + "Consider setting parameter 'default_weight'") + return label + else: + def func(u, v, label): + if label is None: + return default_weight + return label + new_indices = {v: i for i,v in enumerate(vertices)} D = {} if self._directed: - for u, v, l in self.edge_iterator(): + for u, v, label in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l + D[i, j] = func(u, v, label) else: - for u, v, l in self.edge_iterator(): + for u, v, label in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l - D[j,i] = l + label = func(u, v, label) + D[i, j] = label + D[j, i] = label + from sage.matrix.constructor import matrix if base_ring is None: M = matrix(self.num_verts(), D, sparse=sparse, **kwds) @@ -2572,29 +2616,28 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl if M.is_sparse(): row_sums = {} if indegree: - for (i,j), entry in M.dict().items(): + for (i, j), entry in M.dict().items(): row_sums[j] = row_sums.get(j, 0) + entry else: - for (i,j), entry in M.dict().items(): + for (i, j), entry in M.dict().items(): row_sums[i] = row_sums.get(i, 0) + entry - for i in range(M.nrows()): - D[i,i] += row_sums.get(i, 0) + D[i, i] += row_sums.get(i, 0) else: if indegree: col_sums = [sum(v) for v in M.columns()] for i in range(M.nrows()): - D[i,i] += col_sums[i] + D[i, i] += col_sums[i] else: - row_sums=[sum(v) for v in M.rows()] + row_sums = [sum(v) for v in M.rows()] for i in range(M.nrows()): - D[i,i] += row_sums[i] + D[i, i] += row_sums[i] if normalized: from sage.misc.functional import sqrt - Dsqrt = diagonal_matrix([1 / sqrt(D[i,i]) if D[i,i] else 1 \ + Dsqrt = diagonal_matrix([1 / sqrt(D[i, i]) if D[i, i] else 1 for i in range(D.nrows())]) if signless: ret = Dsqrt * (D + M) * Dsqrt @@ -2612,7 +2655,7 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl laplacian_matrix = kirchhoff_matrix - ### Attributes + # Attributes def set_embedding(self, embedding): """ @@ -2639,8 +2682,14 @@ def set_embedding(self, embedding): EXAMPLES:: sage: G = graphs.PetersenGraph() - sage: G.set_embedding({0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]}) - sage: G.set_embedding({'s': [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]}) + sage: G.set_embedding({0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], + ....: 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], + ....: 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], + ....: 9: [4, 6, 7]}) + sage: G.set_embedding({'s': [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], + ....: 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], + ....: 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], + ....: 9: [4, 6, 7]}) Traceback (most recent call last): ... ValueError: vertices in ['s'] from the embedding do not belong to the graph @@ -2677,7 +2726,9 @@ def get_embedding(self): sage: G.genus() 1 sage: G.get_embedding() - {0: [1, 4, 5], 1: [0, 2, 6], 2: [1, 3, 7], 3: [2, 4, 8], 4: [0, 3, 9], 5: [0, 7, 8], 6: [1, 9, 8], 7: [2, 5, 9], 8: [3, 6, 5], 9: [4, 6, 7]} + {0: [1, 4, 5], 1: [0, 2, 6], 2: [1, 3, 7], 3: [2, 4, 8], + 4: [0, 3, 9], 5: [0, 7, 8], 6: [1, 9, 8], 7: [2, 5, 9], + 8: [3, 6, 5], 9: [4, 6, 7]} Note that the embeddings gets properly modified on vertex or edge deletion:: @@ -2746,7 +2797,9 @@ def _check_embedding_validity(self, embedding=None, boolean=True): EXAMPLES:: - sage: d = {0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]} + sage: d = {0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], + ....: 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], + ....: 8: [3, 5, 6], 9: [4, 6, 7]} sage: G = graphs.PetersenGraph() sage: G._check_embedding_validity(d) True @@ -2794,28 +2847,36 @@ def _check_embedding_validity(self, embedding=None, boolean=True): if boolean: return False if set(embedding).difference(self): - raise ValueError("vertices in {} from the embedding do not belong to the graph".format(list(set(embedding).difference(self)))) + raise ValueError("vertices in {} from the embedding do not " + "belong to the graph".format(list(set(embedding).difference(self)))) else: - raise ValueError("vertices in {} have no corresponding entry in the embedding".format(list(set(self).difference(embedding)))) + raise ValueError("vertices in {} have no corresponding entry " + "in the embedding".format(list(set(self).difference(embedding)))) if self._directed: - connected = lambda u, v: self.has_edge(u, v) or self.has_edge(v, u) + def connected(u, v): + return self.has_edge(u, v) or self.has_edge(v, u) else: connected = self.has_edge for v in embedding: if len(embedding[v]) != self.degree(v): if boolean: return False - raise ValueError("the list associated with vertex {} has length {} but d({})={}".format(v, len(embedding[v]), v, self.degree(v))) + raise ValueError("the list associated with vertex {} has " + "length {} but d({})={}".format(v, len(embedding[v]), + v, self.degree(v))) if len(embedding[v]) != len(set(embedding[v])): if boolean: return False - raise ValueError("the list associated with vertex {} contains >1 occurrences of {}".format(v, [x for x in set(embedding[v]) if embedding[v].count(x) > 1])) + raise ValueError("the list associated with vertex {} contains >1 " + "occurrences of {}".format(v, [x for x in set(embedding[v]) + if embedding[v].count(x) > 1])) for u in embedding[v]: if not connected(v, u): if boolean: return False - raise ValueError("{} and {} are not neighbors but {} is in the list associated with {}".format(u, v, u, v)) + raise ValueError("{} and {} are not neighbors but {} is in " + "the list associated with {}".format(u, v, u, v)) return True def has_loops(self): @@ -3450,7 +3511,7 @@ def multiple_edges(self, to_undirected=False, labels=True, sort=False): else: multi_edges.extend((u, v) for _ in L_uv) multi_edges.extend((v, u) for _ in L_vu) - + elif not self.has_edge(v, u): L = self.edge_label(u, v) if len(L) > 1: @@ -3773,7 +3834,7 @@ def weighted(self, new=None): else: return bool(self._weighted) - ### Properties + # Properties def antisymmetric(self): r""" @@ -3928,8 +3989,9 @@ def is_bipartite(self, certificate=False): return False color = {} - tree = {} # inheritance of colors along the DFS to recover an odd - # cycle when certificate=True + # inheritance of colors along the DFS to recover an odd + # cycle when certificate=True + tree = {} # For any uncolored vertex in the graph (to ensure we do the right job # when the graph is not connected !) @@ -3989,7 +4051,6 @@ def is_bipartite(self, certificate=False): else: return True - def is_eulerian(self, path=False): r""" Check whether the graph is Eulerian. @@ -4077,7 +4138,7 @@ def is_eulerian(self, path=False): else: # if there was another vertex with the same sign of difference... if uv[(diff + 1) // 2] is not None: - return False # ... the graph is not semi-Eulerian + return False # ... the graph is not semi-Eulerian else: uv[(diff + 1) // 2] = v else: @@ -4140,7 +4201,7 @@ def size(self): num_edges = size - ### Orientations + # Orientations def eulerian_orientation(self): r""" @@ -4376,8 +4437,9 @@ def eulerian_circuit(self, return_vertices=False, labels=True, path=False): else: next_edge = next(g_edge_iter(v)) - if next_edge[0] == v: # in the undirected case we want to - # save the direction of traversal + if next_edge[0] == v: + # in the undirected case we want to save the + # direction of traversal next_edge_new = (next_edge[1], next_edge[0], next_edge[2]) else: next_edge_new = next_edge @@ -4654,7 +4716,10 @@ def wfunction_float(e): # contains fringe_vertex: (weight, vertex_in_tree) for each # fringe vertex. fringe_list = {e[0] if e[0] != v else e[1]: (wfunction_float(e), v) for e in self.edges_incident(v)} - cmp_fun = lambda x: fringe_list[x][0] + + def cmp_fun(x): + return fringe_list[x][0] + for i in range(self.order() - 1): # find the smallest-weight fringe vertex u = min(fringe_list, key=cmp_fun) @@ -4667,7 +4732,7 @@ def wfunction_float(e): fringe_list.pop(u) # update fringe list for e in self.edges_incident(u): - neighbor = e[0] if e[0] !=u else e[1] + neighbor = e[0] if e[0] != u else e[1] if neighbor in tree: continue w = wfunction_float(e) @@ -4712,7 +4777,7 @@ def wfunction_float(e): G = networkx.Graph([(e[0], e[1], {'weight': wfunction_float(e)}) for e in self.edge_iterator()]) E = networkx.minimum_spanning_edges(G, data=False) return [(u, v, self.edge_label(u, v)) if hash(u) < hash(v) else (v, u, self.edge_label(u, v)) - for u, v in E] + for u, v in E] else: raise NotImplementedError("minimum spanning tree algorithm '%s' is not implemented" % algorithm) @@ -4780,9 +4845,7 @@ def spanning_trees_count(self, root_vertex=None): 1 sage: D.spanning_trees_count(2) 2 - """ - if not self.order(): return 0 @@ -4797,7 +4860,7 @@ def spanning_trees_count(self, root_vertex=None): root_vertex = vertices[0] index = 0 elif root_vertex not in vertices: - raise ValueError("vertex (%s) not in the graph"%root_vertex) + raise ValueError("vertex (%s) not in the graph" % root_vertex) else: index = vertices.index(root_vertex) @@ -4934,8 +4997,7 @@ def cycle_basis(self, output='vertex'): raise ValueError('output must be either vertex or edge') if self.is_directed(): - raise NotImplementedError('not implemented for directed ' - 'graphs') + raise NotImplementedError('not implemented for directed graphs') if self.allows_multiple_edges(): if not self.is_connected(): @@ -5064,7 +5126,7 @@ def minimum_cycle_basis(self, algorithm=None, weight_function=None, by_weight=Fa else: raise NotImplementedError("only 'NetworkX' and Cython implementation is supported") - ### Planarity + # Planarity def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, set_pos=False): r""" @@ -5237,8 +5299,8 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se """ # Quick check first if (on_embedding is None and not kuratowski and not set_embedding and not set_pos - and not self.allows_loops() and not self.allows_multiple_edges() - and not self.is_directed()): + and not self.allows_loops() and not self.allows_multiple_edges() + and not self.is_directed()): if self.order() > 4 and self.size() > 3 * self.order() - 6: return False @@ -5249,7 +5311,7 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se return self.to_simple().is_planar(kuratowski=kuratowski) if on_embedding is not None: - self._check_embedding_validity(on_embedding,boolean=False) + self._check_embedding_validity(on_embedding, boolean=False) return (0 == self.genus(minimal=False, set_embedding=False, on_embedding=on_embedding)) else: from sage.graphs.planarity import is_planar @@ -5403,8 +5465,8 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, # Quick check first if (on_embedding is None and not kuratowski and set_embedding and - boundary is None and not ordered and not set_pos and - not self.allows_loops() and not self.allows_multiple_edges()): + boundary is None and not ordered and not set_pos and + not self.allows_loops() and not self.allows_multiple_edges()): if self.order() > 3 and self.size() > 2 * self.order() - 3: return False @@ -5630,11 +5692,11 @@ def layout_planar(self, set_embedding=False, on_embedding=None, embedding = dict() if len(G) >= 1: v1 = next(verts) - pos[v1] = [0,0] + pos[v1] = [0, 0] embedding[v1] = [] if len(G) == 2: v2 = next(verts) - pos[v2] = [0,1] + pos[v2] = [0, 1] embedding[v1] = [v2] embedding[v2] = [v1] if set_embedding: @@ -5666,25 +5728,29 @@ def layout_planar(self, set_embedding=False, on_embedding=None, embedding_copy = None if set_embedding: if not G.is_planar(set_embedding=True): - raise ValueError('%s is not a planar graph'%self) + raise ValueError('%s is not a planar graph' % self) embedding_copy = {v: neighbors[:] for v, neighbors in G._embedding.items()} else: if on_embedding is not None: G._check_embedding_validity(on_embedding, boolean=False) if not G.is_planar(on_embedding=on_embedding): - raise ValueError('provided embedding is not a planar embedding for %s'%self ) + raise ValueError('provided embedding is not a planar ' + 'embedding for %s' % self) G.set_embedding(on_embedding) else: - if hasattr(G,'_embedding'): + if hasattr(G, '_embedding'): if G._check_embedding_validity(): if not G.is_planar(on_embedding=G._embedding): - raise ValueError('%s has nonplanar _embedding attribute. Try putting set_embedding=True'%self) + raise ValueError('%s has nonplanar _embedding attribute. ' + 'Try putting set_embedding=True' % self) embedding_copy = {v: neighbors[:] for v, neighbors in G._embedding.items()} else: - raise ValueError('provided embedding is not a valid embedding for %s. Try putting set_embedding=True'%self) + raise ValueError('provided embedding is not a valid ' + 'embedding for %s. Try putting ' + 'set_embedding=True' % self) else: if not G.is_planar(set_embedding=True): - raise ValueError('%s is not a planar graph'%self) + raise ValueError('%s is not a planar graph' % self) if external_face: if not self.has_edge(external_face): @@ -5698,11 +5764,12 @@ def layout_planar(self, set_embedding=False, on_embedding=None, if test: if G._check_embedding_validity(): if not G.is_planar(on_embedding=G._embedding): - raise ValueError('%s has nonplanar _embedding attribute. Try putting set_embedding=True' % self) + raise ValueError('%s has nonplanar _embedding attribute. ' + 'Try putting set_embedding=True' % self) test_faces = G.faces(G._embedding) for face in test_faces: if len(face) != 3: - raise RuntimeError('BUG: Triangulation returned face: %s'%face) + raise RuntimeError('BUG: Triangulation returned face: %s' % face) faces = G.faces(G._embedding) outer_face = faces[0] @@ -5780,7 +5847,7 @@ def is_drawn_free_of_edge_crossings(self): t = (da * Rational(p1[1] - q1[1]) + db * Rational(q1[0] - p1[0])) / (db * dx - da * dy) if 0 <= s and s <= 1 and 0 <= t and t <= 1: - print('fail on', p1, p2, ' : ',q1, q2) + print('fail on', p1, p2, ' : ', q1, q2) print(edge1, edge2) return False return True @@ -5938,7 +6005,7 @@ def genus(self, set_embedding=True, on_embedding=None, minimal=True, maximal=Fal verts += 1 extra_edges = [] - if ordered: # WHEEL + if ordered: # WHEEL for e in zip(boundary[-1], boundary[1:]): if not G.has_edge(e): G.add_edge(e) @@ -5963,14 +6030,14 @@ def genus(self, set_embedding=True, on_embedding=None, minimal=True, maximal=Fal except AttributeError: raise AttributeError('graph must have attribute _embedding set to compute current (embedded) genus') return (2 - verts + edges - faces) // 2 - else: # then compute either maximal or minimal genus of all embeddings + else: # then compute either maximal or minimal genus of all embeddings from . import genus if set_embedding: if self.has_loops() or self.is_directed() or self.has_multiple_edges(): raise NotImplementedError("cannot work with embeddings of non-simple graphs") if minimal: - B,C = G.blocks_and_cut_vertices() + B, C = G.blocks_and_cut_vertices() embedding = {} g = 0 for block in B: @@ -5991,7 +6058,7 @@ def genus(self, set_embedding=True, on_embedding=None, minimal=True, maximal=Fal if maximal and (self.has_multiple_edges() or self.has_loops()): raise NotImplementedError("cannot compute the maximal genus of a graph with loops or multiple edges") if minimal: - B,C = G.blocks_and_cut_vertices() + B, C = G.blocks_and_cut_vertices() g = 0 for block in B: H = G.subgraph(block) @@ -6207,7 +6274,7 @@ def faces(self, embedding=None): # Establish set of possible edges edgeset = set() - for u,v in self.edge_iterator(labels=0): + for u, v in self.edge_iterator(labels=0): edgeset.add((u, v)) edgeset.add((v, u)) @@ -6219,7 +6286,7 @@ def faces(self, embedding=None): # Trace faces while edgeset: - u,v = path[-1] + u, v = path[-1] neighbors = embedding[v] next_node = neighbors[(neighbors.index(u) + 1) % len(neighbors)] e = (v, next_node) @@ -6417,8 +6484,7 @@ def planar_dual(self, embedding=None): edges.append([v1, v2, self.edge_label(e[0], e[1])]) return Graph([verts, edges]) - - ### Connectivity + # Connectivity def steiner_tree(self, vertices, weighted=False, solver=None, verbose=0, *, integrality_tolerance=1e-3): @@ -6520,7 +6586,9 @@ def steiner_tree(self, vertices, weighted=False, solver=None, verbose=0, cc = g.connected_component_containing_vertex(vertices[0]) if any(v not in cc for v in vertices): from sage.categories.sets_cat import EmptySetError - raise EmptySetError("the given vertices do not all belong to the same connected component. This problem has no solution !") + raise EmptySetError("the given vertices do not all belong to the " + "same connected component. This problem has " + "no solution !") # Can it be solved using the min spanning tree algorithm ? if not weighted: @@ -6551,22 +6619,22 @@ def steiner_tree(self, vertices, weighted=False, solver=None, verbose=0, p.add_constraint(p.sum(edges[frozenset(e)] for e in g.edges_incident(v, labels=False)), min=1) # The number of edges is equal to the number of vertices in our tree minus 1 - p.add_constraint( p.sum(vertex[v] for v in g) - - p.sum(edges[frozenset(e)] for e in g.edge_iterator(labels=False)), max=1, min=1) + p.add_constraint(p.sum(vertex[v] for v in g) + - p.sum(edges[frozenset(e)] for e in g.edge_iterator(labels=False)), max=1, min=1) # There are no cycles in our graph - for u,v in g.edge_iterator(labels=False): - p.add_constraint(r_edges[u,v]+ r_edges[v,u] - edges[frozenset((u,v))], min=0) + for u, v in g.edge_iterator(labels=False): + p.add_constraint(r_edges[u, v] + r_edges[v, u] - edges[frozenset((u, v))], min=0) - eps = 1/(5*Integer(g.order())) + eps = 1 / (5 * Integer(g.order())) for v in g: - p.add_constraint(p.sum(r_edges[u,v] for u in g.neighbor_iterator(v)), max=1-eps) - + p.add_constraint(p.sum(r_edges[u, v] for u in g.neighbor_iterator(v)), max=1 - eps) # Objective if weighted: - p.set_objective(p.sum((l if l is not None else 1)*edges[frozenset((u,v))] for u,v,l in g.edge_iterator())) + p.set_objective(p.sum((l if l is not None else 1) * edges[frozenset((u, v))] + for u, v, l in g.edge_iterator())) else: p.set_objective(p.sum(edges[frozenset(e)] for e in g.edge_iterator(labels=False))) @@ -6574,13 +6642,13 @@ def steiner_tree(self, vertices, weighted=False, solver=None, verbose=0, edges = p.get_values(edges, convert=bool, tolerance=integrality_tolerance) - st = g.subgraph(edges=[e for e in g.edge_iterator(labels=False) if edges[frozenset(e)]], - immutable=False) + st = g.subgraph(edges=[e for e in g.edge_iterator(labels=False) if edges[frozenset(e)]], + immutable=False) st.delete_vertices(v for v in g if not st.degree(v)) return st def edge_disjoint_spanning_trees(self, k, algorithm=None, root=None, solver=None, verbose=0, - *, integrality_tolerance=1e-3): + *, integrality_tolerance=1e-3): r""" Return the desired number of edge-disjoint spanning trees/arborescences. @@ -6815,8 +6883,8 @@ def edge_disjoint_spanning_trees(self, k, algorithm=None, root=None, solver=None # A vertex has at least one incident edge for u in D: p.add_constraint(p.sum(edge[e, c] for e in D.incoming_edge_iterator(u, labels=False)) - + p.sum(edge[e, c] for e in D.outgoing_edge_iterator(u, labels=False)) - >= 1) + + p.sum(edge[e, c] for e in D.outgoing_edge_iterator(u, labels=False)) + >= 1) # We use the Miller-Tucker-Zemlin subtour elimination constraints # combined with the Desrosiers-Langevin strengthening constraints @@ -6824,7 +6892,7 @@ def edge_disjoint_spanning_trees(self, k, algorithm=None, root=None, solver=None if D.has_edge(v, u): # DL p.add_constraint(pos[u, c] + (n - 1)*edge[(u, v), c] + (n - 3)*edge[(v, u), c] - <= pos[v, c] + n - 2) + <= pos[v, c] + n - 2) else: # MTZ: If edge uv is selected, v is after u in the partial ordering p.add_constraint(pos[u, c] + 1 - n * (1 - edge[(u, v), c]) <= pos[v, c]) @@ -7021,10 +7089,10 @@ def weight(x): flow_value, flow_graph = self.flow(s, t, value_only=value_only, use_edge_labels=use_edge_labels, algorithm=algorithm) - for u,v,l in flow_graph.edge_iterator(): + for u, v, l in flow_graph.edge_iterator(): g.add_edge(v, u) if (not use_edge_labels or - (weight(g.edge_label(u, v)) == weight(l))): + weight(g.edge_label(u, v)) == weight(l)): g.delete_edge(u, v) return_value = [flow_value] @@ -7067,22 +7135,22 @@ 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=None): p.add_constraint(v[x] + b[good_edge((x, y))] - v[y], min=0) else: # we minimize the number of edges - p.set_objective(p.sum(weight(w) * b[good_edge((x,y))] for x, y, w in g.edge_iterator())) + 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=None): 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) p.solve(log=verbose) b = p.get_values(b, convert=bool, tolerance=integrality_tolerance) if use_edge_labels: - obj = sum(weight(w) for x, y, w in g.edge_iterator() if b[good_edge((x,y))]) + obj = sum(weight(w) for x, y, w in g.edge_iterator() if b[good_edge((x, y))]) else: obj = Integer(sum(1 for e in g.edge_iterator(labels=False) if b[good_edge(e)])) @@ -7196,13 +7264,13 @@ def vertex_cut(self, s, t, value_only=True, vertices=False, solver=None, verbose if g.is_directed(): # adjacent vertices belong to the same part except if one of them # belongs to the cut - for x,y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=None): p.add_constraint(v[x] + b[y] - v[y], min=0) else: # adjacent vertices belong to the same part except if one of them # belongs to the cut - for x,y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=None): p.add_constraint(v[x] + b[y] >= v[y]) p.add_constraint(v[y] + b[x] >= v[x]) @@ -7228,7 +7296,6 @@ def vertex_cut(self, s, t, value_only=True, vertices=False, solver=None, verbose answer.append([l0, l1]) return tuple(answer) - def multiway_cut(self, vertices, value_only=False, use_edge_labels=False, solver=None, verbose=0, *, integrality_tolerance=1e-3): r""" @@ -7337,29 +7404,29 @@ def weight(l): p.set_objective(p.sum(weight(l) * cut[good_edge((u, v))] for u, v, l in self.edge_iterator())) if self.is_directed(): - for s,t in chain(combinations(vertices, 2), [(y, x) for x, y in combinations(vertices, 2)]): + for s, t in chain(combinations(vertices, 2), [(y, x) for x, y in combinations(vertices, 2)]): # For each commodity, the source is at height 0 # and the destination is at height 1 - p.add_constraint(height[(s,t),s], min=0, max=0) - p.add_constraint(height[(s,t),t], min=1, max=1) + p.add_constraint(height[(s, t), s], min=0, max=0) + p.add_constraint(height[(s, t), t], min=1, max=1) # given a commodity (s,t), the height of two adjacent vertices u,v # can differ of at most the value of the edge between them - for u,v in self.edge_iterator(labels=False): - p.add_constraint(height[(s,t),u] - height[(s,t),v] - cut[good_edge((u, v))], max=0) + for u, v in self.edge_iterator(labels=False): + p.add_constraint(height[(s, t), u] - height[(s, t), v] - cut[good_edge((u, v))], max=0) else: - for s,t in combinations(vertices, 2): + for s, t in combinations(vertices, 2): # For each commodity, the source is at height 0 # and the destination is at height 1 - p.add_constraint(height[(s,t),s], min=0, max=0) - p.add_constraint(height[(s,t),t], min=1, max=1) + p.add_constraint(height[(s, t), s], min=0, max=0) + p.add_constraint(height[(s, t), t], min=1, max=1) # given a commodity (s,t), the height of two adjacent vertices u,v # can differ of at most the value of the edge between them - for u,v in self.edge_iterator(labels=False): - p.add_constraint(height[(s,t),u] - height[(s,t),v] - cut[good_edge((u,v))], max=0) - p.add_constraint(height[(s,t),v] - height[(s,t),u] - cut[good_edge((u,v))], max=0) + for u, v in self.edge_iterator(labels=False): + p.add_constraint(height[(s, t), u] - height[(s, t), v] - cut[good_edge((u, v))], max=0) + p.add_constraint(height[(s, t), v] - height[(s, t), u] - cut[good_edge((u, v))], max=0) p.solve(log=verbose) cut = p.get_values(cut, convert=bool, tolerance=integrality_tolerance) @@ -7372,7 +7439,6 @@ def weight(l): return [e for e in self.edge_iterator() if cut[good_edge((e[0], e[1]))]] - def max_cut(self, value_only=True, use_edge_labels=False, vertices=False, solver=None, verbose=0, *, integrality_tolerance=1e-3): r""" @@ -7469,30 +7535,30 @@ def good_edge(e): # A vertex has to be in some set for v in g: - p.add_constraint(in_set[0,v] + in_set[1,v], max=1, min=1) + p.add_constraint(in_set[0, v] + in_set[1, v], max=1, min=1) # There is no empty set - p.add_constraint(p.sum(in_set[1,v] for v in g), min=1) - p.add_constraint(p.sum(in_set[0,v] for v in g), min=1) + p.add_constraint(p.sum(in_set[1, v] for v in g), min=1) + p.add_constraint(p.sum(in_set[0, v] for v in g), min=1) if g.is_directed(): # There is no edge from set 0 to set 1 which is not in the cut. # Besides, an edge can only be in the cut if its vertices # belong to different sets - for u,v in g.edge_iterator(labels=None): - p.add_constraint(in_set[0,u] + in_set[1,v] - in_cut[u,v], max=1) - p.add_constraint(in_set[0,u] + in_set[0,v] + in_cut[u,v], max=2) - p.add_constraint(in_set[1,u] + in_set[1,v] + in_cut[u,v], max=2) + for u, v in g.edge_iterator(labels=None): + p.add_constraint(in_set[0, u] + in_set[1, v] - in_cut[u, v], max=1) + p.add_constraint(in_set[0, u] + in_set[0, v] + in_cut[u, v], max=2) + p.add_constraint(in_set[1, u] + in_set[1, v] + in_cut[u, v], max=2) else: # Two adjacent vertices are in different sets if and only if # the edge between them is in the cut - for u,v in g.edge_iterator(labels=None): + for u, v in g.edge_iterator(labels=None): fuv = good_edge((u, v)) - p.add_constraint(in_set[0,u] + in_set[1,v] - in_cut[fuv], max=1) - p.add_constraint(in_set[1,u] + in_set[0,v] - in_cut[fuv], max=1) - p.add_constraint(in_set[0,u] + in_set[0,v] + in_cut[fuv], max=2) - p.add_constraint(in_set[1,u] + in_set[1,v] + in_cut[fuv], max=2) + p.add_constraint(in_set[0, u] + in_set[1, v] - in_cut[fuv], max=1) + p.add_constraint(in_set[1, u] + in_set[0, v] - in_cut[fuv], max=1) + p.add_constraint(in_set[0, u] + in_set[0, v] + in_cut[fuv], max=2) + p.add_constraint(in_set[1, u] + in_set[1, v] + in_cut[fuv], max=2) p.set_objective(p.sum(weight(l) * in_cut[good_edge((u, v))] for u, v, l in g.edge_iterator())) @@ -7515,11 +7581,11 @@ def good_edge(e): a = [] b = [] for v in g: - if in_set[0,v]: + if in_set[0, v]: a.append(v) else: b.append(v) - val.append([a,b]) + val.append([a, b]) return val @@ -7764,9 +7830,11 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", # Associating a weight to a label if use_edge_labels: - weight = lambda x: x if (x is not None and x != {}) else 1 + def weight(x): + return x if (x is not None and x != {}) else 1 else: - weight = lambda x: 1 + def weight(x): + return 1 from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram(solver=solver) @@ -7786,62 +7854,54 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", # if edge uv is used, vu cannot be for u, v in self.edge_iterator(labels=False): if self.has_edge(v, u): - p.add_constraint(edge_used[u,v] + edge_used[v,u] <= 1) + p.add_constraint(edge_used[u, v] + edge_used[v, u] <= 1) # A vertex is used if one of its incident edges is - for u,v in self.edge_iterator(labels=False): - p.add_constraint(vertex_used[v] >= edge_used[u,v]) - p.add_constraint(vertex_used[u] >= edge_used[u,v]) + for u, v in self.edge_iterator(labels=False): + p.add_constraint(vertex_used[v] >= edge_used[u, v]) + p.add_constraint(vertex_used[u] >= edge_used[u, v]) # A path is a tree. If n vertices are used, at most n-1 edges are - p.add_constraint( - p.sum(vertex_used[v] for v in self) - - p.sum(edge_used[e] for e in self.edge_iterator(labels=False)) - == 1) + p.add_constraint(p.sum(vertex_used[v] for v in self) + - p.sum(edge_used[e] for e in self.edge_iterator(labels=False)) + == 1) # A vertex has at most one incoming used edge and at most # one outgoing used edge for v in self: - p.add_constraint( - p.sum(edge_used[u,v] for u in self.neighbor_in_iterator(v)) <= 1) - p.add_constraint( - p.sum(edge_used[v,u] for u in self.neighbor_out_iterator(v)) <= 1) + p.add_constraint(p.sum(edge_used[u, v] for u in self.neighbor_in_iterator(v)) <= 1) + p.add_constraint(p.sum(edge_used[v, u] for u in self.neighbor_out_iterator(v)) <= 1) # r_edge_used is "more" than edge_used, though it ignores # the direction - for u,v in self.edge_iterator(labels=False): - p.add_constraint(r_edge_used[u,v] + r_edge_used[v,u] - >= edge_used[u,v]) + for u, v in self.edge_iterator(labels=False): + p.add_constraint(r_edge_used[u, v] + r_edge_used[v, u] + >= edge_used[u, v]) # No cycles for v in self: - p.add_constraint( - p.sum(r_edge_used[u,v] for u in self.neighbor_iterator(v)) - <= 1-epsilon) + p.add_constraint(p.sum(r_edge_used[u, v] for u in self.neighbor_iterator(v)) + <= 1 - epsilon) # Enforcing the source if asked.. If s is set, it has no # incoming edge and exactly one son if s is not None: - p.add_constraint( - p.sum(edge_used[u,s] for u in self.neighbor_in_iterator(s)), - max=0, min=0) - p.add_constraint( - p.sum(edge_used[s,u] for u in self.neighbor_out_iterator(s)), - min=1, max=1) + p.add_constraint(p.sum(edge_used[u, s] for u in self.neighbor_in_iterator(s)), + max=0, min=0) + p.add_constraint(p.sum(edge_used[s, u] for u in self.neighbor_out_iterator(s)), + min=1, max=1) # Enforcing the destination if asked.. If t is set, it has # no outgoing edge and exactly one parent if t is not None: - p.add_constraint( - p.sum(edge_used[u,t] for u in self.neighbor_in_iterator(t)), - min=1, max=1) - p.add_constraint( - p.sum(edge_used[t,u] for u in self.neighbor_out_iterator(t)), - max=0, min=0) + p.add_constraint(p.sum(edge_used[u, t] for u in self.neighbor_in_iterator(t)), + min=1, max=1) + p.add_constraint(p.sum(edge_used[t, u] for u in self.neighbor_out_iterator(t)), + max=0, min=0) # Defining the objective - p.set_objective( - p.sum(weight(l) * edge_used[u,v] for u, v, l in self.edge_iterator())) + p.set_objective(p.sum(weight(l) * edge_used[u, v] + for u, v, l in self.edge_iterator())) else: # We use edge_used[frozenset((u, v))] to avoid having two different # variables for edge (u, v) @@ -7849,40 +7909,37 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", # A vertex is used if one of its incident edges is for v in self: for u in self.neighbor_iterator(v): - p.add_constraint(vertex_used[v] - edge_used[frozenset((u,v))], min=0) + p.add_constraint(vertex_used[v] - edge_used[frozenset((u, v))], min=0) # A path is a tree. If n vertices are used, at most n-1 edges are - p.add_constraint( - p.sum(vertex_used[v] for v in self) - - p.sum(edge_used[frozenset((u,v))] for u, v in self.edge_iterator(labels=False)), - min=1, max=1) + p.add_constraint(p.sum(vertex_used[v] for v in self) + - p.sum(edge_used[frozenset((u, v))] + for u, v in self.edge_iterator(labels=False)), + min=1, max=1) # A vertex has at most two incident edges used for v in self: - p.add_constraint( - p.sum(edge_used[frozenset((u,v))] for u in self.neighbor_iterator(v)), max=2) + p.add_constraint(p.sum(edge_used[frozenset((u, v))] for u in self.neighbor_iterator(v)), + max=2) # r_edge_used is "more" than edge_used for u, v in self.edge_iterator(labels=False): - p.add_constraint(r_edge_used[u,v] - + r_edge_used[v,u] - - edge_used[frozenset((u,v))], + p.add_constraint(r_edge_used[u, v] + + r_edge_used[v, u] + - edge_used[frozenset((u, v))], min=0) # No cycles for v in self: - p.add_constraint( - p.sum(r_edge_used[u,v] for u in self.neighbor_iterator(v)), - max=1-epsilon) + p.add_constraint(p.sum(r_edge_used[u, v] for u in self.neighbor_iterator(v)), + max=1 - epsilon) # Enforcing the destination if asked.. If s or t are set, # they have exactly one incident edge if s is not None: - p.add_constraint( - p.sum(edge_used[frozenset((s,u))] for u in self.neighbor_iterator(s)), - max=1, min=1) + p.add_constraint(p.sum(edge_used[frozenset((s, u))] for u in self.neighbor_iterator(s)), + max=1, min=1) if t is not None: - p.add_constraint( - p.sum(edge_used[frozenset((t,u))] for u in self.neighbor_iterator(t)), - max=1, min=1) + p.add_constraint(p.sum(edge_used[frozenset((t, u))] for u in self.neighbor_iterator(t)), + max=1, min=1) # Defining the objective - p.set_objective(p.sum(weight(l) * edge_used[frozenset((u,v))] - for u, v, l in self.edge_iterator())) + p.set_objective(p.sum(weight(l) * edge_used[frozenset((u, v))] + for u, v, l in self.edge_iterator())) # Computing the result. No exception has to be raised, as this # problem always has a solution (there is at least one edge, @@ -7891,15 +7948,14 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", edge_used = p.get_values(edge_used, convert=bool, tolerance=integrality_tolerance) vertex_used = p.get_values(vertex_used, convert=bool, tolerance=integrality_tolerance) if self._directed: - g = self.subgraph( - vertices=(v for v in self if vertex_used[v]), - edges=((u,v,l) for u, v, l in self.edge_iterator() - if edge_used[u,v])) + g = self.subgraph(vertices=(v for v in self if vertex_used[v]), + edges=((u, v, l) for u, v, l in self.edge_iterator() + if edge_used[u, v])) else: g = self.subgraph( vertices=(v for v in self if vertex_used[v]), - edges=((u,v,l) for u, v, l in self.edge_iterator() - if edge_used[frozenset((u,v))])) + edges=((u, v, l) for u, v, l in self.edge_iterator() + if edge_used[frozenset((u, v))])) if use_edge_labels: return sum(map(weight, g.edge_labels())), g else: @@ -8058,7 +8114,6 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, else: g = self.copy(immutable=False) - new_s, new_t = s, t if g.is_directed(): # @@ -8098,7 +8153,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, # Set new_s and new_t if possible if new_s is None and new_t is None: - new_s,new_t = ones + new_s, new_t = ones elif new_s is not None and new_t is None: new_t = ones[1] if new_s == ones[0] else ones[0] elif new_s is None and new_t is not None: @@ -8128,7 +8183,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, # source and an edge from it to each vertex of the (di)graph. new_s = g.add_vertex() extra_vertices.append(new_s) - for u in self: # in original set of vertices + for u in self: # in original set of vertices g.add_edge(new_s, u, 0) if new_t is None: @@ -8152,22 +8207,23 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, from sage.categories.sets_cat import EmptySetError try: tsp = g.traveling_salesman_problem(use_edge_labels=use_edge_labels, - maximize=maximize, - solver=solver, verbose=verbose, - integrality_tolerance=integrality_tolerance) + maximize=maximize, + solver=solver, verbose=verbose, + integrality_tolerance=integrality_tolerance) except EmptySetError: return (0, None) if use_edge_labels else None tsp.delete_vertices(extra_vertices) tsp.name("Hamiltonian path from {}".format(self.name())) - weight = lambda l: 1 if l is None else l - return (sum(map(weight,tsp.edge_labels())), tsp) if use_edge_labels else tsp + def weight(label): + return 1 if label is None else label + return (sum(map(weight, tsp.edge_labels())), tsp) if use_edge_labels else tsp def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, - solver=None, constraint_generation=None, - verbose=0, verbose_constraints=False, - *, integrality_tolerance=1e-3): + solver=None, constraint_generation=None, + verbose=0, verbose_constraints=False, + *, integrality_tolerance=1e-3): r""" Solve the traveling salesman problem (TSP) @@ -8392,9 +8448,11 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # Associating a weight to a label if use_edge_labels: - weight = lambda l: 1 if l is None else l + def weight(label): + return 1 if label is None else label else: - weight = lambda l: 1 + def weight(label): + return 1 ######################## # 0 or 1 vertex graphs # @@ -8408,7 +8466,7 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, ##################### if self.order() == 2: - uu,vv = list(self) + uu, vv = list(self) if self.is_directed(): if self.has_edge(uu, vv) and self.has_edge(vv, uu): if self.allows_multiple_edges(): @@ -8433,7 +8491,7 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, edges = self.edges(sort=True, key=weight)[:2] answer = self.subgraph(edges=edges, immutable=self.is_immutable()) answer.set_pos(self.get_pos()) - answer.name("TSP from "+self.name()) + answer.name("TSP from " + self.name()) return answer raise EmptySetError("the given graph is not Hamiltonian") @@ -8466,7 +8524,6 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, else: g = self - if constraint_generation is None: if g.density() > .7: constraint_generation = False @@ -8495,13 +8552,13 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # Objective function if use_edge_labels: - p.set_objective(p.sum(weight(l)*b[u,v] for u,v,l in g.edge_iterator())) + p.set_objective(p.sum(weight(l) * b[u, v] for u, v, l in g.edge_iterator())) # All the vertices have in-degree 1 and out-degree 1 for v in g: - p.add_constraint(p.sum(b[u,v] for u in g.neighbor_in_iterator(v)), + p.add_constraint(p.sum(b[u, v] for u in g.neighbor_in_iterator(v)), min=1, max=1) - p.add_constraint(p.sum(b[v,u] for u in g.neighbor_out_iterator(v)), + p.add_constraint(p.sum(b[v, u] for u in g.neighbor_out_iterator(v)), min=1, max=1) # Initial Solve @@ -8514,9 +8571,9 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # We build the DiGraph representing the current solution h = DiGraph() b_val = p.get_values(b, convert=bool, tolerance=integrality_tolerance) - for u,v,l in g.edge_iterator(): - if b_val[u,v]: - h.add_edge(u,v,l) + for u, v, l in g.edge_iterator(): + if b_val[u, v]: + h.add_edge(u, v, l) # If there is only one circuit, we are done ! cc = h.connected_components(sort=False) @@ -8527,12 +8584,11 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, for c in cc: if verbose_constraints: print("Adding a constraint on set", c) - p.add_constraint(p.sum(b[u,v] for u,v in - g.edge_boundary(c, labels=False)), - min=1) + p.add_constraint(p.sum(b[u, v] for u, v in g.edge_boundary(c, labels=False)), + min=1) try: - p.solve(log = verbose) + p.solve(log=verbose) except MIPSolverException: raise EmptySetError("the given graph is not Hamiltonian") @@ -8545,16 +8601,16 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # Objective function if use_edge_labels: - p.set_objective(p.sum(weight(l) * b[frozenset((u,v))] for u,v,l in g.edge_iterator())) + p.set_objective(p.sum(weight(l) * b[frozenset((u, v))] for u, v, l in g.edge_iterator())) # All the vertices have degree 2 for v in g: - p.add_constraint(p.sum(b[frozenset((u,v))] for u in g.neighbor_iterator(v)), + p.add_constraint(p.sum(b[frozenset((u, v))] for u in g.neighbor_iterator(v)), min=2, max=2) # Initial Solve try: - p.solve(log = verbose) + p.solve(log=verbose) except MIPSolverException: raise EmptySetError("the given graph is not Hamiltonian") @@ -8562,7 +8618,7 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # We build the DiGraph representing the current solution h = Graph() b_val = p.get_values(b, convert=bool, tolerance=integrality_tolerance) - h.add_edges((u,v,l) for u,v,l in g.edge_iterator() if b_val[frozenset((u,v))]) + h.add_edges((u, v, l) for u, v, l in g.edge_iterator() if b_val[frozenset((u, v))]) # If there is only one circuit, we are done ! cc = h.connected_components(sort=False) @@ -8573,8 +8629,8 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, for c in cc: if verbose_constraints: print("Adding a constraint on set", c) - p.add_constraint(p.sum(b[frozenset((u,v))] for u,v in g.edge_boundary(c, labels=False)), - min=2) + p.add_constraint(p.sum(b[frozenset((u, v))] for u, v in g.edge_boundary(c, labels=False)), + min=2) try: p.solve(log=verbose) @@ -8602,27 +8658,27 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, if g.is_directed(): # All the vertices have in-degree 1 and out-degree 1 for v in g: - p.add_constraint(p.sum(f[u,v] for u in g.neighbor_in_iterator(v)), + p.add_constraint(p.sum(f[u, v] for u in g.neighbor_in_iterator(v)), min=1, max=1) - p.add_constraint(p.sum(f[v,u] for u in g.neighbor_out_iterator(v)), + p.add_constraint(p.sum(f[v, u] for u in g.neighbor_out_iterator(v)), min=1, max=1) # r is greater than f vertex_to_int = {u: i for i, u in enumerate(g)} - for u,v in g.edge_iterator(labels=None): - if g.has_edge(v,u): + for u, v in g.edge_iterator(labels=None): + if g.has_edge(v, u): if vertex_to_int[u] < vertex_to_int[v]: - p.add_constraint(r[u,v] + r[v,u]- f[u,v] - f[v,u], min=0) + p.add_constraint(r[u, v] + r[v, u] - f[u, v] - f[v, u], min=0) # no 2-cycles - p.add_constraint(f[u,v] + f[v,u], max=1) + p.add_constraint(f[u, v] + f[v, u], max=1) else: - p.add_constraint(r[u,v] + r[v,u] - f[u,v], min=0) + p.add_constraint(r[u, v] + r[v, u] - f[u, v], min=0) if use_edge_labels: - p.set_objective(p.sum(weight(l) * f[u,v] for u,v,l in g.edge_iterator())) + p.set_objective(p.sum(weight(l) * f[u, v] for u, v, l in g.edge_iterator())) # defining the answer when g is directed from sage.graphs.digraph import DiGraph @@ -8631,44 +8687,42 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, else: # All the vertices have degree 2 for v in g: - p.add_constraint(p.sum(f[frozenset((u,v))] for u in g.neighbor_iterator(v)), + p.add_constraint(p.sum(f[frozenset((u, v))] for u in g.neighbor_iterator(v)), min=2, max=2) # r is greater than f - for u,v in g.edge_iterator(labels = None): - p.add_constraint( r[u,v] + r[v,u] - f[frozenset((u,v))], min=0) + for u, v in g.edge_iterator(labels=None): + p.add_constraint(r[u, v] + r[v, u] - f[frozenset((u, v))], min=0) if use_edge_labels: - p.set_objective(p.sum(weight(l) * f[frozenset((u,v))] for u,v,l in g.edge_iterator())) + p.set_objective(p.sum(weight(l) * f[frozenset((u, v))] for u, v, l in g.edge_iterator())) from sage.graphs.graph import Graph # defining the answer when g is not directed tsp = Graph() - # no cycle which does not contain x for v in g: if v != x: - p.add_constraint(p.sum(r[u,v] for u in g.neighbor_iterator(v)), max=1-eps) + p.add_constraint(p.sum(r[u, v] for u in g.neighbor_iterator(v)), max=1 - eps) try: p.solve(log=verbose) f_val = p.get_values(f, convert=bool, tolerance=integrality_tolerance) tsp.add_vertices(g.vertex_iterator()) tsp.set_pos(g.get_pos()) - tsp.name("TSP from "+g.name()) + tsp.name("TSP from " + g.name()) if g.is_directed(): - tsp.add_edges((u,v,l) for u,v,l in g.edge_iterator() if f_val[u,v] == 1) + tsp.add_edges((u, v, l) for u, v, l in g.edge_iterator() if f_val[u, v] == 1) else: - tsp.add_edges((u,v,l) for u,v,l in g.edge_iterator() if f_val[frozenset((u,v))] == 1) + tsp.add_edges((u, v, l) for u, v, l in g.edge_iterator() if f_val[frozenset((u, v))] == 1) return tsp except MIPSolverException: raise EmptySetError("the given graph is not Hamiltonian") - def hamiltonian_cycle(self, algorithm='tsp', solver=None, constraint_generation=None, verbose=0, verbose_constraints=False, *, integrality_tolerance=1e-3): @@ -8943,7 +8997,7 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, # It would be a pity to start a LP if the graph is already acyclic if ((not self.is_directed() and self.is_forest()) or - ( self.is_directed() and self.is_directed_acyclic())): + (self.is_directed() and self.is_directed_acyclic())): if value_only: return 0 return [] @@ -9003,9 +9057,9 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, else: - ###################################### - # Ordering-based MILP Implementation # - ###################################### + ###################################### + # Ordering-based MILP Implementation # + ###################################### p = MixedIntegerLinearProgram(maximization=False, solver=solver) @@ -9014,7 +9068,7 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, n = self.order() # The removed vertices cover all the back arcs ( third condition ) - for u,v in self.edge_iterator(labels=None): + for u, v in self.edge_iterator(labels=None): p.add_constraint(d[u] - d[v] + n * (b[u] + b[v]), min=1) for u in self: @@ -9222,14 +9276,15 @@ def capacity(z): algorithm = "FF" if (algorithm == "FF"): - return self._ford_fulkerson(x,y, value_only=value_only, integer=integer, use_edge_labels=use_edge_labels) + return self._ford_fulkerson(x, y, value_only=value_only, integer=integer, use_edge_labels=use_edge_labels) elif (algorithm == 'igraph'): vertices = list(self) x_int = vertices.index(x) y_int = vertices.index(y) if use_edge_labels: g_igraph = self.igraph_graph(vertex_list=vertices, - edge_attrs={'capacity':[float(capacity(e[2])) for e in self.edge_iterator()]}) + edge_attrs={'capacity': [float(capacity(e[2])) + for e in self.edge_iterator()]}) maxflow = g_igraph.maxflow(x_int, y_int, 'capacity') else: g_igraph = self.igraph_graph(vertex_list=vertices) @@ -9263,7 +9318,6 @@ def capacity(z): raise ValueError("the algorithm argument has to be equal to either " "\"FF\", \"LP\", \"igraph\", or None") - from sage.numerical.mip import MixedIntegerLinearProgram g = self p = MixedIntegerLinearProgram(maximization=True, solver=solver) @@ -9272,24 +9326,30 @@ def capacity(z): if g.is_directed(): # This function return the balance of flow at X - flow_sum = lambda X: (p.sum(flow[X,v] for u,v in g.outgoing_edge_iterator([X], labels=None)) - - p.sum(flow[u,X] for u,v in g.incoming_edge_iterator([X], labels=None))) + def flow_sum(X): + return (p.sum(flow[X, v] for u, v in g.outgoing_edge_iterator([X], labels=None)) + - p.sum(flow[u, X] for u, v in g.incoming_edge_iterator([X], labels=None))) # The flow leaving x - flow_leaving = lambda X: p.sum(flow[uu,vv] for uu,vv in g.outgoing_edge_iterator([X], labels=None)) + def flow_leaving(X): + return p.sum(flow[uu, vv] for uu, vv in g.outgoing_edge_iterator([X], labels=None)) # The flow to be considered when defining the capacity constraints - capacity_sum = lambda u,v: flow[u,v] + def capacity_sum(u, v): + return flow[u, v] else: # This function return the balance of flow at X - flow_sum = lambda X: p.sum(flow[X,v] - flow[v,X] for v in g[X]) + def flow_sum(X): + return p.sum(flow[X, v] - flow[v, X] for v in g[X]) # The flow leaving x - flow_leaving = lambda X: p.sum(flow[X,vv] for vv in g[X]) + def flow_leaving(X): + return p.sum(flow[X, vv] for vv in g[X]) # The flow to be considered when defining the capacity constraints - capacity_sum = lambda u,v: flow[u,v] + flow[v,u] + def capacity_sum(u, v): + return flow[u, v] + flow[v, u] # Maximizes the flow leaving x p.add_constraint(flow_sum(x) == obj[0]) @@ -9301,8 +9361,8 @@ def capacity(z): p.add_constraint(flow_sum(v), min=0, max=0) # Capacity constraints - for u,v,w in g.edge_iterator(): - p.add_constraint(capacity_sum(u,v), max=capacity(w)) + for u, v, w in g.edge_iterator(): + p.add_constraint(capacity_sum(u, v), max=capacity(w)) # No vertex except the sources can send more than 1 if vertex_bound: @@ -9477,7 +9537,7 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0, *, integrality_toler ValueError: parameter 'k' must be at least 2 """ if k is None: - k = 6 # See [Sey1981]_ + k = 6 # See [Sey1981]_ elif k < 2: raise ValueError("parameter 'k' must be at least 2") @@ -9496,8 +9556,8 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0, *, integrality_toler return solution # If the (di)graph has bridges, the problem is not feasible - if ( (self.is_directed() and not self.is_strongly_connected() and next(self.to_undirected().bridges(), False)) - or (not self.is_directed() and next(self.bridges(), False)) ): + if ((self.is_directed() and not self.is_strongly_connected() and next(self.to_undirected().bridges(), False)) + or (not self.is_directed() and next(self.bridges(), False))): raise EmptySetError("(di)graphs with bridges have no feasible solution") # @@ -9507,13 +9567,13 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0, *, integrality_toler G = copy(self) if self.is_directed() else next(self.orientations()) # We assign flow 1 to loops, if any - solution = DiGraph([list(G), [(u,v,1) for u,v in G.loops(labels=0)]], + solution = DiGraph([list(G), [(u, v, 1) for u, v in G.loops(labels=False)]], loops=G.has_loops(), multiedges=G.has_multiple_edges()) G.allow_loops(False) # We ensure that multiple edges have distinct labels - multiedges = {(u,v,i) for i,(u,v) in enumerate(G.multiple_edges(labels=0))} + multiedges = {(u, v, i) for i, (u, v) in enumerate(G.multiple_edges(labels=0))} G.delete_edges(G.multiple_edges()) G.add_edges(multiedges) @@ -9527,20 +9587,20 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0, *, integrality_toler # # We use a MIP formulation to solve the problem # - from sage.numerical.mip import MixedIntegerLinearProgram,MIPSolverException + from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException p = MixedIntegerLinearProgram(solver=solver) f = p.new_variable(nonnegative=False, integer=True) b = p.new_variable(nonnegative=True, binary=True) # flow conservation constraints for u in G: - p.add_constraint( p.sum(f[e] for e in G.incoming_edge_iterator(u)) - == p.sum(f[e] for e in G.outgoing_edge_iterator(u))) + p.add_constraint(p.sum(f[e] for e in G.incoming_edge_iterator(u)) == + p.sum(f[e] for e in G.outgoing_edge_iterator(u))) # The flow on edge e has value in {-k+1,..., -1, 1, ..., k-1} for e in G.edge_iterator(): - p.add_constraint(p.sum(b[e,i] for i in range(-k+1, k) if i) == 1) - p.add_constraint(f[e] == p.sum(i * b[e,i] for i in range(-k+1, k) if i)) + p.add_constraint(p.sum(b[e, i] for i in range(1 - k, k) if i) == 1) + p.add_constraint(f[e] == p.sum(i * b[e, i] for i in range(1 - k, k) if i)) # We solve the MIP. try: @@ -9551,7 +9611,7 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0, *, integrality_toler # Extract and return the solution. If the graph is not directed, we # reverse edges with a negative flow to obtain a positive k-NZF f_val = p.get_values(f, convert=True, tolerance=integrality_tolerance) - for (u,v,_), val in f_val.items(): + for (u, v, _), val in f_val.items(): if self.is_directed() or val > 0: solution.add_edge(u, v, val) else: @@ -9631,9 +9691,11 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only # Whether we should consider the edges labeled if use_edge_labels: - l_capacity=lambda x: 1 if (x is None or x == {}) else (floor(x) if integer else x) + def l_capacity(x): + return 1 if (x is None or x == {}) else (floor(x) if integer else x) else: - l_capacity=lambda x: 1 + def l_capacity(x): + return 1 directed = self.is_directed() @@ -9652,7 +9714,7 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only # Initializing the variables if directed: - for u,v,l in self.edge_iterator(): + for u, v, l in self.edge_iterator(): if l_capacity(l) > 0: capacity[u, v] = l_capacity(l) + capacity.get((u, v), 0) capacity[v, u] = capacity.get((v, u), 0) @@ -9660,7 +9722,7 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only flow[u, v] = 0 flow[v, u] = 0 else: - for u,v,l in self.edge_iterator(): + for u, v, l in self.edge_iterator(): if l_capacity(l) > 0: capacity[u, v] = l_capacity(l) + capacity.get((u, v), 0) capacity[v, u] = l_capacity(l) + capacity.get((v, u), 0) @@ -9671,11 +9733,14 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only # Rewrites a path as a list of edges : # ex : [0,1,2,3,4,5] becomes [(0,1), (1,2), (2,3), (3,4), (4,5)] - path_to_edges = lambda P: zip(P[:-1], P[1:]) + def path_to_edges(P): + return zip(P[:-1], P[1:]) # Rewrites a path as a list of edges labeled with their # available capacity - path_to_labelled_edges = lambda P : [(x_y[0], x_y[1], capacity[x_y[0], x_y[1]] - flow[x_y[0], x_y[1]] + flow[x_y[1], x_y[0]]) for x_y in path_to_edges(P)] + def path_to_labelled_edges(P): + return [(x_y[0], x_y[1], capacity[x_y[0], x_y[1]] - flow[x_y[0], x_y[1]] + flow[x_y[1], x_y[0]]) + for x_y in path_to_edges(P)] # Total flow going from s to t flow_intensity = 0 @@ -9697,7 +9762,7 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only flow_intensity = flow_intensity + epsilon # Updating variables - for uu,vv,ll in edges: + for uu, vv, ll in edges: # The flow on the back arc other = flow[vv, uu] @@ -9817,7 +9882,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, # defining the set of terminals set_terminals = set() - for s,t,_ in terminals: + for s, t, _ in terminals: set_terminals.add(s) set_terminals.add(t) @@ -9827,34 +9892,42 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, # Whether to use edge labels if use_edge_labels: from sage.rings.real_mpfr import RR - capacity = lambda x: x if x in RR else 1 + + def capacity(x): + return x if x in RR else 1 else: - capacity = lambda x: 1 + def capacity(x): + return 1 if g.is_directed(): # This function return the balance of flow at X - flow_sum = lambda i,X: (p.sum(flow[i,(X,v)] for u,v in g.outgoing_edge_iterator([X], labels=None)) - - p.sum(flow[i,(u,X)] for u,v in g.incoming_edge_iterator([X], labels=None))) + def flow_sum(i, X): + return (p.sum(flow[i, (X, v)] for u, v in g.outgoing_edge_iterator([X], labels=None)) + - p.sum(flow[i, (u, X)] for u, v in g.incoming_edge_iterator([X], labels=None))) # The flow leaving x - flow_leaving = lambda i,X: p.sum(flow[i,(uu,vv)] for uu,vv in g.outgoing_edge_iterator([X], labels=None)) + def flow_leaving(i, X): + return p.sum(flow[i, (uu, vv)] for uu, vv in g.outgoing_edge_iterator([X], labels=None)) # the flow to consider when defining the capacity constraints - capacity_sum = lambda i,u,v: flow[i,(u,v)] + def capacity_sum(i, u, v): + return flow[i, (u, v)] else: # This function return the balance of flow at X - flow_sum = lambda i,X: p.sum(flow[i,(X,v)] - flow[i,(v,X)] for v in g.neighbor_iterator(X)) + def flow_sum(i, X): + return p.sum(flow[i, (X, v)] - flow[i, (v, X)] for v in g.neighbor_iterator(X)) # The flow leaving x - flow_leaving = lambda i, X: p.sum(flow[i,(X,vv)] for vv in g.neighbor_iterator(X)) + def flow_leaving(i, X): + return sum(flow[i, (X, vv)] for vv in g.neighbor_iterator(X)) # the flow to consider when defining the capacity constraints - capacity_sum = lambda i,u,v: flow[i,(u,v)] + flow[i,(v,u)] - + def capacity_sum(i, u, v): + return flow[i, (u, v)] + flow[i, (v, u)] # Flow constraints - for i,(s,t,l) in enumerate(terminals): + for i, (s, t, l) in enumerate(terminals): for v in g: if v == s: p.add_constraint(flow_sum(i, v), min=l, max=l) @@ -9864,7 +9937,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, p.add_constraint(flow_sum(i, v), min=0, max=0) # Capacity constraints - for u,v,w in g.edge_iterator(): + for u, v, w in g.edge_iterator(): p.add_constraint(p.sum(capacity_sum(i, u, v) for i in range(len(terminals))), max=capacity(w)) if vertex_bound: @@ -9874,7 +9947,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, # which is an endpoint if v in set_terminals: - for i,(s,t,_) in enumerate(terminals): + for i, (s, t, _) in enumerate(terminals): # only tolerates the commodities of which it is an endpoint if not (v == s or v == t): @@ -9883,7 +9956,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, # which is not an endpoint else: # can stand at most 1 unit of flow through itself - p.add_constraint(p.sum(flow_leaving(i,v) for i in range(len(terminals))), max=1) + p.add_constraint(p.sum(flow_leaving(i, v) for i in range(len(terminals))), max=1) p.set_objective(None) @@ -9900,7 +9973,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, flow = p.get_values(flow, convert=True, tolerance=integrality_tolerance) # building clean flow digraphs - flow_graphs = [g._build_flow_graph({e: f for (ii,e),f in flow.items() if ii == i}, integer=integer) + flow_graphs = [g._build_flow_graph({e: f for (ii, e), f in flow.items() if ii == i}, integer=integer) for i in range(len(terminals))] # which could be .. graphs ! @@ -9959,7 +10032,7 @@ def _build_flow_graph(self, flow, integer): g = DiGraph() # add significant edges - for (u,v),l in flow.items(): + for (u, v), l in flow.items(): if l: g.add_edge(u, v, l) @@ -10338,10 +10411,10 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, algorithm = algorithm.lower() if algorithm == 'networkx' or algorithm == 'scipy': import networkx - return networkx.pagerank(self.networkx_graph - (weight_function=weight_function), alpha=alpha, - personalization=personalization, weight=weight, - dangling=dangling) + gnx = self.networkx_graph(weight_function=weight_function) + return networkx.pagerank(gnx, alpha=alpha, + personalization=personalization, + weight=weight, dangling=dangling) elif algorithm == 'igraph': # An error will be raised if igraph is not installed if personalization: @@ -10358,7 +10431,7 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, else: raise NotImplementedError("only 'NetworkX', 'Scipy', and 'igraph' are supported") - ### Vertex handlers + # Vertex handlers def add_vertex(self, name=None): r""" @@ -10493,7 +10566,7 @@ def delete_vertex(self, vertex, in_order=False): if in_order: vertex = self.vertices(sort=True)[vertex] if vertex not in self: - raise ValueError("vertex (%s) not in the graph"%str(vertex)) + raise ValueError("vertex (%s) not in the graph" % str(vertex)) # TODO: remove this update from this method which should be as fast # as possible @@ -10544,7 +10617,7 @@ def delete_vertices(self, vertices): vertices = list(vertices) for v in vertices: if v not in self: - raise ValueError("vertex (%s) not in the graph"%str(v)) + raise ValueError("vertex (%s) not in the graph" % str(v)) # TODO: remove this update from this method which should be as fast # as possible @@ -11226,7 +11299,6 @@ def neighbors(self, vertex, closed=False): __getitem__ = neighbors - def merge_vertices(self, vertices): r""" Merge vertices. @@ -11324,14 +11396,14 @@ def merge_vertices(self, vertices): if self.is_directed(): out_edges = self.edge_boundary(vertices) in_edges = self.edge_boundary([v for v in self - if v not in vertices]) + if v not in vertices]) self.delete_vertices(vertices[1:]) self.add_edges((u, v0, l) for (u0, v0, l) in out_edges if u0 != u) self.add_edges((v0, u, l) for (v0, u0, l) in in_edges if u0 != u) else: edges = self.edge_boundary(vertices) self.delete_vertices(vertices[1:]) - add_edges=[] + add_edges = [] for u0, v0, l in edges: if v0 in vertices and v0 != u: add_edges.append((u, u0, l)) @@ -11339,7 +11411,7 @@ def merge_vertices(self, vertices): add_edges.append((u, v0, l)) self.add_edges(add_edges) - ### Edge handlers + # Edge handlers def add_edge(self, u, v=None, label=None): r""" @@ -11518,7 +11590,10 @@ def subdivide_edge(self, *args): sage: g.subdivide_edge(1, 2, "label2", 5) sage: print(g.edges(sort=True)) - [(0, 3, 'label1'), (1, 5, 'label1'), (1, 6, 'label2'), (2, 10, 'label2'), (3, 4, 'label1'), (4, 5, 'label1'), (6, 7, 'label2'), (7, 8, 'label2'), (8, 9, 'label2'), (9, 10, 'label2')] + [(0, 3, 'label1'), (1, 5, 'label1'), (1, 6, 'label2'), + (2, 10, 'label2'), (3, 4, 'label1'), (4, 5, 'label1'), + (6, 7, 'label2'), (7, 8, 'label2'), (8, 9, 'label2'), + (9, 10, 'label2')] If too many arguments are given, an exception is raised :: @@ -11816,8 +11891,8 @@ def contract_edge(self, u, v=None, label=None): if u == v: return - if (self.allows_loops() and (self.allows_multiple_edges() or - not self.has_edge(u, u))): + if (self.allows_loops() and + (self.allows_multiple_edges() or not self.has_edge(u, u))): # add loops for x, y, l in self.edges_incident(v): if set([x, y]) == set([u, v]): @@ -12000,7 +12075,11 @@ def set_edge_label(self, u, v, l): EXAMPLES:: - sage: SD = DiGraph({1:[18,2], 2:[5,3], 3:[4,6], 4:[7,2], 5:[4], 6:[13,12], 7:[18,8,10], 8:[6,9,10], 9:[6], 10:[11,13], 11:[12], 12:[13], 13:[17,14], 14:[16,15], 15:[2], 16:[13], 17:[15,13], 18:[13]}, sparse=True) + sage: d = {1: [18, 2], 2: [5, 3], 3: [4, 6], 4: [7, 2], 5: [4], + ....: 6: [13, 12], 7: [18, 8, 10], 8: [6, 9, 10], 9: [6], + ....: 10: [11, 13], 11: [12], 12: [13], 13: [17, 14], + ....: 14: [16, 15], 15: [2], 16: [13], 17: [15, 13], 18: [13]} + sage: SD = DiGraph(d, sparse=True) sage: SD.set_edge_label(1, 18, 'discrete') sage: SD.set_edge_label(4, 7, 'discrete') sage: SD.set_edge_label(2, 5, 'h = 0') @@ -12013,7 +12092,11 @@ def set_edge_label(self, u, v, l): sage: SD.set_edge_label(13, 14, 'k = h') sage: SD.set_edge_label(17, 15, 'v_k finite') sage: SD.set_edge_label(14, 15, 'v_k m.c.r.') - sage: posn = {1:[ 3,-3], 2:[0,2], 3:[0, 13], 4:[3,9], 5:[3,3], 6:[16, 13], 7:[6,1], 8:[6,6], 9:[6,11], 10:[9,1], 11:[10,6], 12:[13,6], 13:[16,2], 14:[10,-6], 15:[0,-10], 16:[14,-6], 17:[16,-10], 18:[6,-4]} + sage: posn = {1: [3, -3], 2: [0, 2], 3: [0, 13], 4: [3, 9], + ....: 5: [3, 3], 6: [16, 13], 7: [6, 1], 8: [6, 6], + ....: 9: [6, 11], 10: [9, 1], 11: [10, 6], 12: [13, 6], + ....: 13: [16, 2], 14: [10, -6], 15: [0, -10], 16: [14, -6], + ....: 17: [16, -10], 18: [6, -4]} sage: SD.plot(pos=posn, vertex_size=400, vertex_colors={'#FFFFFF':list(range(1,19))}, edge_labels=True).show() # long time :: @@ -12066,7 +12149,8 @@ def set_edge_label(self, u, v, l): """ if self.allows_multiple_edges(): if len(self.edge_label(u, v)) > 1: - raise RuntimeError("cannot set edge label, since there are multiple edges from %s to %s"%(u,v)) + raise RuntimeError("cannot set edge label, since there are " + "multiple edges from %s to %s" % (u, v)) self._backend.set_edge_label(u, v, l, self._directed) def has_edge(self, u, v=None, label=None): @@ -12102,7 +12186,8 @@ def has_edge(self, u, v=None, label=None): label = None return self._backend.has_edge(u, v, label) - def edges(self, vertices=None, labels=True, sort=None, key=None, ignore_direction=False, sort_vertices=True): + def edges(self, vertices=None, labels=True, sort=None, key=None, + ignore_direction=False, sort_vertices=True): r""" Return a :class:`~EdgesView` of edges. @@ -12162,20 +12247,53 @@ def edges(self, vertices=None, labels=True, sort=None, key=None, ignore_directio EXAMPLES:: sage: graphs.DodecahedralGraph().edges(sort=True) - [(0, 1, None), (0, 10, None), (0, 19, None), (1, 2, None), (1, 8, None), (2, 3, None), (2, 6, None), (3, 4, None), (3, 19, None), (4, 5, None), (4, 17, None), (5, 6, None), (5, 15, None), (6, 7, None), (7, 8, None), (7, 14, None), (8, 9, None), (9, 10, None), (9, 13, None), (10, 11, None), (11, 12, None), (11, 18, None), (12, 13, None), (12, 16, None), (13, 14, None), (14, 15, None), (15, 16, None), (16, 17, None), (17, 18, None), (18, 19, None)] + [(0, 1, None), (0, 10, None), (0, 19, None), (1, 2, None), + (1, 8, None), (2, 3, None), (2, 6, None), (3, 4, None), + (3, 19, None), (4, 5, None), (4, 17, None), (5, 6, None), + (5, 15, None), (6, 7, None), (7, 8, None), (7, 14, None), + (8, 9, None), (9, 10, None), (9, 13, None), (10, 11, None), + (11, 12, None), (11, 18, None), (12, 13, None), (12, 16, None), + (13, 14, None), (14, 15, None), (15, 16, None), (16, 17, None), + (17, 18, None), (18, 19, None)] :: sage: graphs.DodecahedralGraph().edges(sort=True, labels=False) - [(0, 1), (0, 10), (0, 19), (1, 2), (1, 8), (2, 3), (2, 6), (3, 4), (3, 19), (4, 5), (4, 17), (5, 6), (5, 15), (6, 7), (7, 8), (7, 14), (8, 9), (9, 10), (9, 13), (10, 11), (11, 12), (11, 18), (12, 13), (12, 16), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19)] + [(0, 1), (0, 10), (0, 19), (1, 2), (1, 8), (2, 3), (2, 6), (3, 4), + (3, 19), (4, 5), (4, 17), (5, 6), (5, 15), (6, 7), (7, 8), (7, 14), + (8, 9), (9, 10), (9, 13), (10, 11), (11, 12), (11, 18), (12, 13), + (12, 16), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), + (18, 19)] :: sage: D = graphs.DodecahedralGraph().to_directed() sage: D.edges(sort=True) - [(0, 1, None), (0, 10, None), (0, 19, None), (1, 0, None), (1, 2, None), (1, 8, None), (2, 1, None), (2, 3, None), (2, 6, None), (3, 2, None), (3, 4, None), (3, 19, None), (4, 3, None), (4, 5, None), (4, 17, None), (5, 4, None), (5, 6, None), (5, 15, None), (6, 2, None), (6, 5, None), (6, 7, None), (7, 6, None), (7, 8, None), (7, 14, None), (8, 1, None), (8, 7, None), (8, 9, None), (9, 8, None), (9, 10, None), (9, 13, None), (10, 0, None), (10, 9, None), (10, 11, None), (11, 10, None), (11, 12, None), (11, 18, None), (12, 11, None), (12, 13, None), (12, 16, None), (13, 9, None), (13, 12, None), (13, 14, None), (14, 7, None), (14, 13, None), (14, 15, None), (15, 5, None), (15, 14, None), (15, 16, None), (16, 12, None), (16, 15, None), (16, 17, None), (17, 4, None), (17, 16, None), (17, 18, None), (18, 11, None), (18, 17, None), (18, 19, None), (19, 0, None), (19, 3, None), (19, 18, None)] + [(0, 1, None), (0, 10, None), (0, 19, None), (1, 0, None), + (1, 2, None), (1, 8, None), (2, 1, None), (2, 3, None), + (2, 6, None), (3, 2, None), (3, 4, None), (3, 19, None), + (4, 3, None), (4, 5, None), (4, 17, None), (5, 4, None), + (5, 6, None), (5, 15, None), (6, 2, None), (6, 5, None), + (6, 7, None), (7, 6, None), (7, 8, None), (7, 14, None), + (8, 1, None), (8, 7, None), (8, 9, None), (9, 8, None), + (9, 10, None), (9, 13, None), (10, 0, None), (10, 9, None), + (10, 11, None), (11, 10, None), (11, 12, None), (11, 18, None), + (12, 11, None), (12, 13, None), (12, 16, None), (13, 9, None), + (13, 12, None), (13, 14, None), (14, 7, None), (14, 13, None), + (14, 15, None), (15, 5, None), (15, 14, None), (15, 16, None), + (16, 12, None), (16, 15, None), (16, 17, None), (17, 4, None), + (17, 16, None), (17, 18, None), (18, 11, None), (18, 17, None), + (18, 19, None), (19, 0, None), (19, 3, None), (19, 18, None)] sage: D.edges(sort=True, labels=False) - [(0, 1), (0, 10), (0, 19), (1, 0), (1, 2), (1, 8), (2, 1), (2, 3), (2, 6), (3, 2), (3, 4), (3, 19), (4, 3), (4, 5), (4, 17), (5, 4), (5, 6), (5, 15), (6, 2), (6, 5), (6, 7), (7, 6), (7, 8), (7, 14), (8, 1), (8, 7), (8, 9), (9, 8), (9, 10), (9, 13), (10, 0), (10, 9), (10, 11), (11, 10), (11, 12), (11, 18), (12, 11), (12, 13), (12, 16), (13, 9), (13, 12), (13, 14), (14, 7), (14, 13), (14, 15), (15, 5), (15, 14), (15, 16), (16, 12), (16, 15), (16, 17), (17, 4), (17, 16), (17, 18), (18, 11), (18, 17), (18, 19), (19, 0), (19, 3), (19, 18)] + [(0, 1), (0, 10), (0, 19), (1, 0), (1, 2), (1, 8), (2, 1), (2, 3), + (2, 6), (3, 2), (3, 4), (3, 19), (4, 3), (4, 5), (4, 17), (5, 4), + (5, 6), (5, 15), (6, 2), (6, 5), (6, 7), (7, 6), (7, 8), (7, 14), + (8, 1), (8, 7), (8, 9), (9, 8), (9, 10), (9, 13), (10, 0), (10, 9), + (10, 11), (11, 10), (11, 12), (11, 18), (12, 11), (12, 13), + (12, 16), (13, 9), (13, 12), (13, 14), (14, 7), (14, 13), (14, 15), + (15, 5), (15, 14), (15, 16), (16, 12), (16, 15), (16, 17), (17, 4), + (17, 16), (17, 18), (18, 11), (18, 17), (18, 19), (19, 0), (19, 3), + (19, 18)] The default is to sort the returned list in the default fashion, as in the above examples. This can be overridden by specifying a key @@ -12263,7 +12381,7 @@ def edges(self, vertices=None, labels=True, sort=None, key=None, ignore_directio vertices = [vertices] return EdgesView(self, vertices=vertices, labels=labels, sort=sort, key=key, - ignore_direction=ignore_direction, sort_vertices=sort_vertices) + ignore_direction=ignore_direction, sort_vertices=sort_vertices) def edge_boundary(self, vertices1, vertices2=None, labels=True, sort=False): r""" @@ -12322,19 +12440,19 @@ def edge_boundary(self, vertices1, vertices2=None, labels=True, sort=False): if vertices2 is not None: vertices2 = set(v for v in vertices2 if v in self) output = [e for e in self.outgoing_edge_iterator(vertices1, labels=labels) - if e[1] in vertices2] + if e[1] in vertices2] else: output = [e for e in self.outgoing_edge_iterator(vertices1, labels=labels) - if e[1] not in vertices1] + if e[1] not in vertices1] else: if vertices2 is not None: vertices2 = set(v for v in vertices2 if v in self) output = [e for e in self.edges(vertices=vertices1, labels=labels, sort=False) - if (e[0] in vertices1 and e[1] in vertices2) or - (e[1] in vertices1 and e[0] in vertices2)] + if (e[0] in vertices1 and e[1] in vertices2) or + (e[1] in vertices1 and e[0] in vertices2)] else: output = [e for e in self.edges(vertices=vertices1, labels=labels, sort=False) - if e[1] not in vertices1 or e[0] not in vertices1] + if e[1] not in vertices1 or e[0] not in vertices1] if sort: output.sort() return output @@ -12509,7 +12627,7 @@ def edge_label(self, u, v): sage: g.edge_label(2, 3) is None True """ - return self._backend.get_edge_label(u,v) + return self._backend.get_edge_label(u, v) def edge_labels(self): """ @@ -12611,7 +12729,7 @@ def remove_loops(self, vertices=None): if self.has_edge(v, v): self.delete_multiedge(v, v) - ### Modifications + # Modifications def clear(self): """ @@ -12646,7 +12764,7 @@ def clear(self): self.name('') self.delete_vertices(self.vertex_iterator()) - ### Degree functions + # Degree functions def degree(self, vertices=None, labels=False): """ @@ -12944,11 +13062,11 @@ def is_regular(self, k=None): return True - ### Substructures + # Substructures def subgraph(self, vertices=None, edges=None, inplace=False, - vertex_property=None, edge_property=None, algorithm=None, - immutable=None): + vertex_property=None, edge_property=None, algorithm=None, + immutable=None): r""" Return the subgraph containing the given vertices and edges. @@ -13227,7 +13345,7 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm """ G = self.__class__(weighted=self._weighted, loops=self.allows_loops(), multiedges=self.allows_multiple_edges()) - G.name("Subgraph of (%s)"%self.name()) + G.name("Subgraph of (%s)" % self.name()) if edges is None and edge_property is None: self._backend.subgraph_given_vertices(G._backend, vertices) else: @@ -13254,7 +13372,7 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm else: s_vertices = set(vertices) edges_to_keep = [e for e in self.edges(vertices=vertices, sort=False, sort_vertices=False) - if e[0] in s_vertices and e[1] in s_vertices] + if e[0] in s_vertices and e[1] in s_vertices] if edge_property is not None: edges_to_keep = [e for e in edges_to_keep if edge_property(e)] @@ -13409,7 +13527,7 @@ def _subgraph_by_deleting(self, vertices=None, edges=None, inplace=False, if vertices is not None: vertices = set(vertices) G.delete_vertices([v for v in G if v not in vertices]) - G.name("Subgraph of (%s)"%self.name()) + G.name("Subgraph of (%s)" % self.name()) else: G = self._subgraph_by_adding(vertices) @@ -13849,7 +13967,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): G._scream_if_not_simple() if self.is_directed() and not G.is_directed(): raise ValueError("cannot search for a graph in a digraph") - if not self.is_directed() and G.is_directed(): + if not self.is_directed() and G.is_directed(): raise ValueError("cannot search for a digraph in a graph") DoG = self.__class__ if not G.order(): @@ -14056,7 +14174,7 @@ def is_chordal(self, certificate=False, algorithm="B"): if algorithm == "A": - peo,t_peo = self.lex_BFS(tree=True) + peo, t_peo = self.lex_BFS(tree=True) peo.reverse() # Iteratively removing vertices and checking everything is fine. @@ -14100,7 +14218,7 @@ def is_chordal(self, certificate=False, algorithm="B"): elif algorithm == "B": - peo,t_peo = self.lex_BFS(reverse=True, tree=True) + peo, t_peo = self.lex_BFS(reverse=True, tree=True) # Remembering the (closed) neighborhoods of each vertex neighbors_subsets = {v: frozenset(self.neighbors(v) + [v]) for v in g} @@ -14145,7 +14263,6 @@ def is_chordal(self, certificate=False, algorithm="B"): else: return False - # Returning values # ---------------- @@ -14161,7 +14278,6 @@ def is_chordal(self, certificate=False, algorithm="B"): return (False, hole) - # 2- The graph is chordal if certificate: return True, peo @@ -14253,9 +14369,9 @@ def is_circulant(self, certificate=False): # The automorphism group, the translation between the vertices of self # and 1..n, and the orbits. ag, orbits = self.automorphism_group([list(self)], - order=False, - return_group=True, - orbits=True) + order=False, + return_group=True, + orbits=True) # Not transitive ? Not a circulant graph ! if len(orbits) != 1: @@ -14268,8 +14384,7 @@ def is_circulant(self, certificate=False): # If the automorphism is not the identity and has exactly one # cycle that contains all vertices. - if ((not cycles) or - len(cycles[0]) != self.order()): + if not cycles or len(cycles[0]) != self.order(): continue # From now on the graph is a circulant graph ! @@ -14370,7 +14485,10 @@ def is_interval(self, certificate=False): Test certificate on a larger graph by re-doing isomorphic graph:: - sage: g = Graph(':S__@_@A_@AB_@AC_@ACD_@ACDE_ACDEF_ACDEFG_ACDEGH_ACDEGHI_ACDEGHIJ_ACDEGIJK_ACDEGIJKL_ACDEGIJKLMaCEGIJKNaCEGIJKNaCGIJKNPaCIP', loops=False, multiedges=False) + sage: s6 = ':S__@_@A_@AB_@AC_@ACD_@ACDE_ACDEF_ACDEFG_ACDEGH_ACDEGHI' + sage: s6 += '_ACDEGHIJ_ACDEGIJK_ACDEGIJKL_ACDEGIJKLMaCEGIJKNaCEGIJK' + sage: s6 += 'NaCGIJKNPaCIP' + sage: g = Graph(s6, loops=False, multiedges=False) sage: d = g.is_interval(certificate=True)[1] sage: g2 = graphs.IntervalGraph(d.values()) sage: g2.is_isomorphic(g) @@ -14633,13 +14751,15 @@ def is_clique(self, vertices=None, directed_clique=False, induced=True, loops=Fa # We check that we have edges between all pairs of vertices v_to_int = {v: i for i, v in enumerate(self)} if G.is_directed() and not directed_clique: - R = lambda u,v: (u, v) if u <= v else (v, u) + def R(u, v): + return (u, v) if u <= v else (v, u) else: - R = lambda u,v:(u,v) + def R(u, v): + return (u, v) if loops: - edges = set(R(v_to_int[u], v_to_int[v]) for u,v in G.edge_iterator(labels=False)) + edges = set(R(v_to_int[u], v_to_int[v]) for u, v in G.edge_iterator(labels=False)) else: - edges = set(R(v_to_int[u], v_to_int[v]) for u,v in G.edge_iterator(labels=False) if u != v) + edges = set(R(v_to_int[u], v_to_int[v]) for u, v in G.edge_iterator(labels=False) if u != v) # If induced == True, we already know that G.size() == M, so # we only need to check that we have the right set of edges. @@ -14872,7 +14992,7 @@ def is_subgraph(self, other, induced=True, up_to_isomorphism=False): else: return self._backend.is_subgraph(other._backend, self) - ### Cluster + # Cluster def cluster_triangles(self, nbunch=None, implementation=None): r""" @@ -14938,7 +15058,7 @@ def cluster_triangles(self, nbunch=None, implementation=None): elif implementation == 'sparse_copy': from sage.graphs.base.static_sparse_graph import triangles_count - elif implementation =="dense_copy": + elif implementation == "dense_copy": from sage.graphs.base.static_dense_graph import triangles_count else: @@ -15131,8 +15251,7 @@ def clustering_coeff(self, raise ValueError("the implementation can only be 'networkx', " "'boost', 'sparse_copy', 'dense_copy' or None") - if ((self.is_directed() or weight) and - implementation != 'networkx'): + if (self.is_directed() or weight) and implementation != 'networkx': raise ValueError("this value of 'implementation' is invalid for directed/weighted graphs") if (implementation in ['sparse_copy', 'dense_copy'] and nodes is not None): @@ -15157,7 +15276,7 @@ def coeff_from_triangle_count(v, count): from sage.graphs.base.static_sparse_graph import triangles_count return {v: coeff_from_triangle_count(v, count) for v, count in triangles_count(self).items()} - elif implementation =="dense_copy": + elif implementation == "dense_copy": from sage.graphs.base.static_dense_graph import triangles_count return {v: coeff_from_triangle_count(v, count) for v, count in triangles_count(self).items()} @@ -15180,7 +15299,7 @@ def cluster_transitivity(self): import networkx return networkx.transitivity(self.networkx_graph()) - ### Distance + # Distance def distance(self, u, v, by_weight=False, weight_function=None, check_weight=True): """ @@ -15301,7 +15420,16 @@ def distance_all_pairs(self, by_weight=False, algorithm=None, sage: g = graphs.PetersenGraph() sage: print(g.distance_all_pairs()) - {0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2}, 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2}, 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2}, 3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2}, 4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1}, 5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2}, 6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1}, 7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1}, 8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2}, 9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}} + {0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2}, + 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2}, + 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2}, + 3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2}, + 4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1}, + 5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2}, + 6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1}, + 7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1}, + 8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2}, + 9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}} Testing on Random Graphs:: @@ -15634,7 +15762,7 @@ def _girth_bfs(self, odd=False, certificate=False): else: return best - ### Centrality + # Centrality def centrality_betweenness(self, k=None, normalized=True, weight=None, endpoints=False, seed=None, exact=False, @@ -15719,10 +15847,10 @@ def centrality_betweenness(self, k=None, normalized=True, weight=None, if algorithm == "NetworkX" and exact: raise ValueError("'exact' is not available with the NetworkX implementation") if (algorithm is None and - seed is None and - weight is None and - endpoints is False and - k is None): + seed is None and + weight is None and + endpoints is False and + k is None): algorithm = "Sage" elif algorithm is None: algorithm = "NetworkX" @@ -15741,7 +15869,6 @@ def centrality_betweenness(self, k=None, normalized=True, weight=None, else: raise ValueError("'algorithm' can be \"NetworkX\", \"Sage\" or None") - def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, weight_function=None, check_weight=True): r""" @@ -15832,7 +15959,12 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, Standard examples:: sage: (graphs.ChvatalGraph()).centrality_closeness() - {0: 0.61111111111111..., 1: 0.61111111111111..., 2: 0.61111111111111..., 3: 0.61111111111111..., 4: 0.61111111111111..., 5: 0.61111111111111..., 6: 0.61111111111111..., 7: 0.61111111111111..., 8: 0.61111111111111..., 9: 0.61111111111111..., 10: 0.61111111111111..., 11: 0.61111111111111...} + {0: 0.61111111111111..., 1: 0.61111111111111..., + 2: 0.61111111111111..., 3: 0.61111111111111..., + 4: 0.61111111111111..., 5: 0.61111111111111..., + 6: 0.61111111111111..., 7: 0.61111111111111..., + 8: 0.61111111111111..., 9: 0.61111111111111..., + 10: 0.61111111111111..., 11: 0.61111111111111...} sage: D = DiGraph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: D.show(figsize=[2,2]) sage: D.centrality_closeness(vert=[0,1]) @@ -16276,7 +16408,12 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [] [] [] - """ # TODO- multiple edges?? + + .. TODO:: + + Add options to return a path as a list of edges with or without edge + labels. This can be useful in (di)graphs with multiple edges. + """ if not self.has_vertex(u): raise ValueError("vertex '{}' is not in the (di)graph".format(u)) if not self.has_vertex(v): @@ -16693,7 +16830,12 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, sage: D = graphs.DodecahedralGraph() sage: D.shortest_paths(0) - {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 19, 3], 4: [0, 19, 3, 4], 5: [0, 1, 2, 6, 5], 6: [0, 1, 2, 6], 7: [0, 1, 8, 7], 8: [0, 1, 8], 9: [0, 10, 9], 10: [0, 10], 11: [0, 10, 11], 12: [0, 10, 11, 12], 13: [0, 10, 9, 13], 14: [0, 1, 8, 7, 14], 15: [0, 19, 18, 17, 16, 15], 16: [0, 19, 18, 17, 16], 17: [0, 19, 18, 17], 18: [0, 19, 18], 19: [0, 19]} + {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 19, 3], 4: [0, 19, 3, 4], + 5: [0, 1, 2, 6, 5], 6: [0, 1, 2, 6], 7: [0, 1, 8, 7], 8: [0, 1, 8], + 9: [0, 10, 9], 10: [0, 10], 11: [0, 10, 11], 12: [0, 10, 11, 12], + 13: [0, 10, 9, 13], 14: [0, 1, 8, 7, 14], + 15: [0, 19, 18, 17, 16, 15], 16: [0, 19, 18, 17, 16], + 17: [0, 19, 18, 17], 18: [0, 19, 18], 19: [0, 19]} All these paths are obviously induced graphs:: @@ -16703,7 +16845,9 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, :: sage: D.shortest_paths(0, cutoff=2) - {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 19, 3], 8: [0, 1, 8], 9: [0, 10, 9], 10: [0, 10], 11: [0, 10, 11], 18: [0, 19, 18], 19: [0, 19]} + {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 19, 3], 8: [0, 1, 8], + 9: [0, 10, 9], 10: [0, 10], 11: [0, 10, 11], 18: [0, 19, 18], + 19: [0, 19]} sage: G = Graph( { 0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2} }, sparse=True) sage: G.plot(edge_labels=True).show() # long time sage: G.shortest_paths(0, by_weight=True) @@ -16894,7 +17038,7 @@ def _path_length(self, path, by_weight=False, weight_function=None): weight_function=weight_function, check_weight=False) return sum(weight_function((u, v, self.edge_label(u, v))) - for u, v in zip(path[:-1], path[1:])) + for u, v in zip(path[:-1], path[1:])) else: return len(path) - 1 @@ -17116,17 +17260,33 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: G.plot(edge_labels=True).show() # long time sage: dist, pred = G.shortest_path_all_pairs(by_weight = True) sage: dist - {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 2}, 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}, 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 3}, 3: {0: 3, 1: 2, 2: 1, 3: 0, 4: 2}, 4: {0: 2, 1: 3, 2: 3, 3: 2, 4: 0}} + {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 2}, + 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}, + 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 3}, + 3: {0: 3, 1: 2, 2: 1, 3: 0, 4: 2}, + 4: {0: 2, 1: 3, 2: 3, 3: 2, 4: 0}} sage: pred - {0: {0: None, 1: 0, 2: 1, 3: 2, 4: 0}, 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0}, 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3}, 3: {0: 1, 1: 2, 2: 3, 3: None, 4: 3}, 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None}} + {0: {0: None, 1: 0, 2: 1, 3: 2, 4: 0}, + 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0}, + 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3}, + 3: {0: 1, 1: 2, 2: 3, 3: None, 4: 3}, + 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None}} sage: pred[0] {0: None, 1: 0, 2: 1, 3: 2, 4: 0} sage: G = Graph( { 0: {1: {'weight':1}}, 1: {2: {'weight':1}}, 2: {3: {'weight':1}}, 3: {4: {'weight':2}}, 4: {0: {'weight':2}} }, sparse=True) sage: dist, pred = G.shortest_path_all_pairs(weight_function = lambda e:e[2]['weight']) sage: dist - {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 2}, 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}, 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 3}, 3: {0: 3, 1: 2, 2: 1, 3: 0, 4: 2}, 4: {0: 2, 1: 3, 2: 3, 3: 2, 4: 0}} + {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 2}, + 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}, + 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 3}, + 3: {0: 3, 1: 2, 2: 1, 3: 0, 4: 2}, + 4: {0: 2, 1: 3, 2: 3, 3: 2, 4: 0}} sage: pred - {0: {0: None, 1: 0, 2: 1, 3: 2, 4: 0}, 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0}, 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3}, 3: {0: 1, 1: 2, 2: 3, 3: None, 4: 3}, 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None}} + {0: {0: None, 1: 0, 2: 1, 3: 2, 4: 0}, + 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0}, + 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3}, + 3: {0: 1, 1: 2, 2: 3, 3: None, 4: 3}, + 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None}} So for example the shortest weighted path from `0` to `3` is obtained as follows. The predecessor of `3` is ``pred[0][3] == 2``, the predecessor @@ -17344,11 +17504,12 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, return_predecessors=True, unweighted=not by_weight) # and format the result - dist = {int_to_vertex[i]: {int_to_vertex[j]: dd[i, j] for j in range(n) if dd[i, j] != +Infinity} - for i in range(n)} + dist = {int_to_vertex[i]: {int_to_vertex[j]: dd[i, j] + for j in range(n) if dd[i, j] != +Infinity} + for i in range(n)} pred = {int_to_vertex[i]: {int_to_vertex[j]: (int_to_vertex[pp[i, j]] if i != j else None) - for j in range(n) if (i == j or pp[i, j] != -9999)} - for i in range(n)} + for j in range(n) if (i == j or pp[i, j] != -9999)} + for i in range(n)} return dist, pred elif algorithm == "Johnson_Boost": @@ -17367,9 +17528,9 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, dist = dict() pred = dict() for u in self: - paths=self.shortest_paths(u, by_weight=by_weight, - algorithm=algorithm, - weight_function=weight_function) + paths = self.shortest_paths(u, by_weight=by_weight, + algorithm=algorithm, + weight_function=weight_function) dist[u] = {v: self._path_length(p, by_weight=by_weight, weight_function=weight_function) for v, p in paths.items()} @@ -17605,7 +17766,7 @@ def wiener_index(self, by_weight=False, algorithm=None, G = networkx.Graph(list(self.edges(labels=False, sort=False))) G.add_nodes_from(self) total = sum(sum(networkx.single_source_dijkstra_path_length(G, u).values()) - for u in G) + for u in G) WI = total if self.is_directed() else (total / 2) else: @@ -17687,14 +17848,14 @@ def average_distance(self, by_weight=False, algorithm=None, """ if self.order() < 2: raise ValueError("average distance is not defined for empty or one-element graph") - WI = self.wiener_index(by_weight=by_weight, algorithm=algorithm, - weight_function=weight_function, check_weight=check_weight) + WI = self.wiener_index(by_weight=by_weight, algorithm=algorithm, + weight_function=weight_function, check_weight=check_weight) f = 1 if self.is_directed() else 2 if WI in ZZ: return QQ((f * WI, self.order() * (self.order() - 1))) return f * WI / (self.order() * (self.order() - 1)) - ### Searches + # Searches def breadth_first_search(self, start, ignore_direction=False, distance=None, neighbors=None, @@ -18038,7 +18199,7 @@ def depth_first_search(self, start, ignore_direction=False, if x not in seen: queue.append((w, x, d + 1)) - ### Constructors + # Constructors def add_clique(self, vertices, loops=False): """ @@ -18543,7 +18704,9 @@ def cartesian_product(self, other): sage: H = Graph([('a', 'b')]) sage: C1 = G.cartesian_product(H) sage: C1.edges(sort=True, labels=None) - [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] + [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'b'), (1, 'b')), + ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'b'), (2, 'b')), + ((2, 'a'), (2, 'b'))] sage: C2 = H.cartesian_product(G) sage: C1.is_isomorphic(C2) True @@ -18562,7 +18725,16 @@ def cartesian_product(self, other): sage: B = digraphs.DeBruijn(['a', 'b'], 2) sage: Q = P.cartesian_product(B) sage: Q.edges(sort=True, labels=None) - [((0, 'aa'), (0, 'aa')), ((0, 'aa'), (0, 'ab')), ((0, 'aa'), (1, 'aa')), ((0, 'ab'), (0, 'ba')), ((0, 'ab'), (0, 'bb')), ((0, 'ab'), (1, 'ab')), ((0, 'ba'), (0, 'aa')), ((0, 'ba'), (0, 'ab')), ((0, 'ba'), (1, 'ba')), ((0, 'bb'), (0, 'ba')), ((0, 'bb'), (0, 'bb')), ((0, 'bb'), (1, 'bb')), ((1, 'aa'), (1, 'aa')), ((1, 'aa'), (1, 'ab')), ((1, 'ab'), (1, 'ba')), ((1, 'ab'), (1, 'bb')), ((1, 'ba'), (1, 'aa')), ((1, 'ba'), (1, 'ab')), ((1, 'bb'), (1, 'ba')), ((1, 'bb'), (1, 'bb'))] + [((0, 'aa'), (0, 'aa')), ((0, 'aa'), (0, 'ab')), + ((0, 'aa'), (1, 'aa')), ((0, 'ab'), (0, 'ba')), + ((0, 'ab'), (0, 'bb')), ((0, 'ab'), (1, 'ab')), + ((0, 'ba'), (0, 'aa')), ((0, 'ba'), (0, 'ab')), + ((0, 'ba'), (1, 'ba')), ((0, 'bb'), (0, 'ba')), + ((0, 'bb'), (0, 'bb')), ((0, 'bb'), (1, 'bb')), + ((1, 'aa'), (1, 'aa')), ((1, 'aa'), (1, 'ab')), + ((1, 'ab'), (1, 'ba')), ((1, 'ab'), (1, 'bb')), + ((1, 'ba'), (1, 'aa')), ((1, 'ba'), (1, 'ab')), + ((1, 'bb'), (1, 'ba')), ((1, 'bb'), (1, 'bb'))] sage: Q.strongly_connected_components_digraph().num_verts() 2 sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) @@ -18709,7 +18881,10 @@ def lexicographic_product(self, other): sage: H = Graph([('a', 'b')]) sage: T = G.lexicographic_product(H) sage: T.edges(sort=True, labels=None) - [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] + [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), + ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (1, 'b')), + ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), + ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(H.lexicographic_product(G)) False @@ -18719,7 +18894,10 @@ def lexicographic_product(self, other): sage: J = DiGraph([('a', 'b')]) sage: T = I.lexicographic_product(J) sage: T.edges(sort=True, labels=None) - [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] + [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), + ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (1, 'b')), + ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), + ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(J.lexicographic_product(I)) False """ @@ -18861,7 +19039,11 @@ def disjunctive_product(self, other): sage: H = Graph([('a', 'b')]) sage: T = G.disjunctive_product(H) sage: T.edges(sort=True, labels=None) - [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), ((0, 'a'), (2, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((0, 'b'), (2, 'a')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] + [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), + ((0, 'a'), (2, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), + ((0, 'b'), (2, 'a')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), + ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), + ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(H.disjunctive_product(G)) True @@ -18871,7 +19053,11 @@ def disjunctive_product(self, other): sage: J = DiGraph([('a', 'b')]) sage: T = I.disjunctive_product(J) sage: T.edges(sort=True, labels=None) - [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), ((0, 'a'), (2, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), ((1, 'a'), (0, 'b')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), ((2, 'a'), (0, 'b')), ((2, 'a'), (1, 'b')), ((2, 'a'), (2, 'b'))] + [((0, 'a'), (0, 'b')), ((0, 'a'), (1, 'a')), ((0, 'a'), (1, 'b')), + ((0, 'a'), (2, 'b')), ((0, 'b'), (1, 'a')), ((0, 'b'), (1, 'b')), + ((1, 'a'), (0, 'b')), ((1, 'a'), (1, 'b')), ((1, 'a'), (2, 'a')), + ((1, 'a'), (2, 'b')), ((1, 'b'), (2, 'a')), ((1, 'b'), (2, 'b')), + ((2, 'a'), (0, 'b')), ((2, 'a'), (1, 'b')), ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(J.disjunctive_product(I)) True """ @@ -19047,8 +19233,7 @@ def is_transitively_reduced(self): return self.is_forest() - - ### Visualization + # Visualization def _color_by_label(self, format='hex', as_function=False, default_color="black"): """ @@ -19149,7 +19334,8 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" color_of_label = dict(zip(labels, colors)) color_of_label = color_of_label.__getitem__ elif isinstance(format, dict): - color_of_label = lambda label: format.get(label, default_color) + def color_of_label(label): + return format.get(label, default_color) else: # This assumes that ``format`` is already a function color_of_label = format @@ -19218,7 +19404,6 @@ def set_latex_options(self, **kwds): opts = self.latex_options() opts.set_options(**kwds) - def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): """ Return a layout for the vertices of this graph. @@ -19341,10 +19526,10 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): if pos is None: layout = 'default' - if hasattr(self, "layout_%s"%layout): - pos = getattr(self, "layout_%s"%layout)(dim=dim, **options) + if hasattr(self, "layout_%s" % layout): + pos = getattr(self, "layout_%s" % layout)(dim=dim, **options) elif layout is not None: - raise ValueError("unknown layout algorithm: %s"%layout) + raise ValueError("unknown layout algorithm: %s" % layout) if len(pos) < self.order(): pos = self.layout_extend_randomly(pos, dim=dim) @@ -19353,7 +19538,6 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): self.set_pos(pos, dim=dim) return pos - def layout_spring(self, by_component=True, **options): """ Return a spring layout for this graph. @@ -19490,11 +19674,11 @@ def layout_extend_randomly(self, pos, dim=2): sage: (xmin, ymin) == (0, 0) and (xmax, ymax) == (1, 1) True """ - assert dim == 2 # 3d not yet implemented + assert dim == 2 # 3d not yet implemented from sage.misc.randstate import current_randstate random = current_randstate().python_random().random - xmin, xmax,ymin, ymax = self._layout_bounding_box(pos) + xmin, xmax, ymin, ymax = self._layout_bounding_box(pos) dx = xmax - xmin dy = ymax - ymin @@ -19505,7 +19689,6 @@ def layout_extend_randomly(self, pos, dim=2): pos[v] = [xmin + dx * random(), ymin + dy * random()] return pos - def layout_circular(self, dim=2, center=(0, 0), radius=1, shift=0, angle=0, **options): r""" Return a circular layout for this graph @@ -19947,9 +20130,9 @@ def _layout_bounding_box(self, pos): ys = [pos[v][1] for v in pos] if not xs: xmin = -1 - xmax = 1 + xmax = 1 ymin = -1 - ymax = 1 + ymax = 1 else: xmin = min(xs) xmax = max(xs) @@ -20047,7 +20230,7 @@ def _circle_embedding(self, vertices, center=(0, 0), radius=1, shift=0, angle=0, pos = self._pos = {} from math import sin, cos, pi - for i,v in enumerate(vertices): + for i, v in enumerate(vertices): i += shift # We round cos and sin to avoid results like 1.2246467991473532e-16 # when asking for sin(pi) @@ -20365,7 +20548,11 @@ def plot(self, **options): :: - sage: D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], 18: [19], 19: []} , sparse=True) + sage: D = DiGraph({0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + ....: 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], + ....: 8: [9], 9: [10, 13], 10: [11], 11: [12, 18], + ....: 12: [16, 13], 13: [14], 14: [15], 15: [16], + ....: 16: [17], 17: [18], 18: [19]}, sparse=True) sage: for u,v,l in D.edges(sort=False): ....: D.set_edge_label(u, v, '(' + str(u) + ',' + str(v) + ')') sage: D.plot(edge_labels=True, layout='circular').show() @@ -20559,7 +20746,7 @@ def show(self, method="matplotlib", **kwds): return from sage.misc.viewer import browser import os - os.system('%s %s 2>/dev/null 1>/dev/null &'% (browser(), filename)) + os.system('%s %s 2>/dev/null 1>/dev/null &' % (browser(), filename)) return from .graph_plot import graphplot_options @@ -22999,7 +23186,6 @@ def is_hamiltonian(self, solver=None, constraint_generation=None, except EmptySetError: return False - def is_isomorphic(self, other, certificate=False, verbosity=0, edge_labels=False): r""" Tests for isomorphism between self and other. @@ -24028,7 +24214,7 @@ def katz_centrality(self, alpha, u=None): other nodes. :: sage: G = DiGraph({1: [10], 2:[10,11], 3:[10,11], 4:[], 5:[11, 4], 6:[11], 7:[10,11], 8:[10,11], 9:[10], 10:[11, 5, 8], 11:[6]}) - sage: G.katz_centrality(.85) + sage: G.katz_centrality(.85) # rel tol 1e-14 {1: 0.000000000000000, 2: 0.000000000000000, 3: 0.000000000000000, diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index a2f98dd755c..022c02c3b06 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -120,30 +120,6 @@ def layout_split(layout_function, G, **options): return pos -def spring_layout_fast_split(G, **options): - """ - Graph each component of ``G`` separately, placing them adjacent to each - other. - - In ticket :trac:`29522` the function was modified so that it can - work with any layout method and renamed ``layout_split``. - Please use :func:`layout_split` from now on. - - TESTS:: - - sage: from sage.graphs.generic_graph_pyx import spring_layout_fast_split - sage: G = Graph(4) - sage: _ = spring_layout_fast_split(G) - doctest:...: DeprecationWarning: spring_layout_fast_split is deprecated, please use layout_split instead - See https://trac.sagemath.org/29522 for details. - - """ - from sage.misc.superseded import deprecation - deprecation(29522, ('spring_layout_fast_split is deprecated, please use ' - 'layout_split instead'), stacklevel=3) - return layout_split(spring_layout_fast, G, **options) - - def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True, bint height=False, by_component=False, **options): """ diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index af93c60a7a2..3bd3983579e 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -440,7 +440,7 @@ class Graph(GenericGraph): pre-defined graphs, see the :mod:`~sage.graphs.graph_generators` module. A :class:`Graph` object has many methods whose list can be obtained by - typing ``g.<tab>`` (i.e. hit the 'tab' key) or by reading the documentation + typing ``g.<tab>`` (i.e. hit the :kbd:`Tab` key) or by reading the documentation of :mod:`~sage.graphs.graph`, :mod:`~sage.graphs.generic_graph`, and :mod:`~sage.graphs.digraph`. diff --git a/src/sage/graphs/graph_coloring.pyx b/src/sage/graphs/graph_coloring.pyx index 1e49b24a0dd..15e3ac5770b 100644 --- a/src/sage/graphs/graph_coloring.pyx +++ b/src/sage/graphs/graph_coloring.pyx @@ -1270,13 +1270,16 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No - ``vizing`` -- boolean (default: ``False``): - - When set to ``True``, tries to find a `\Delta + 1`-edge-coloring, where - `\Delta` is equal to the maximum degree in the graph + - When set to ``True``, finds an edge coloring using the algorithm + described at [MG1992]_. This always results in a coloring with `\Delta + 1` + colors, where `\Delta` is equal to the maximum degree in the graph, even + if one of the colors is empty, for the sake of consistency. - - When set to ``False``, tries to find a `\Delta`-edge-coloring, where - `\Delta` is equal to the maximum degree in the graph. If impossible, - tries to find and returns a `\Delta + 1`-edge-coloring. This implies - that ``value_only=False`` + - When set to ``False``, tries to find a `\Delta`-edge-coloring using + Mixed Integer Linear Programming (MILP). If impossible, returns a + `(\Delta + 1)`-edge-coloring. Please note that determinating if the + chromatic index of a graph equals `\Delta` is computationally difficult, + and could take a long time. - ``hex_colors`` -- boolean (default: ``False``); when set to ``True``, the partition returned is a dictionary whose keys are colors and whose values @@ -1339,16 +1342,18 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No sage: g = graphs.PetersenGraph() sage: edge_coloring(g, value_only=True, solver='GLPK') 4 - sage: edge_coloring(g, value_only=False, solver='GLPK') - [[(0, 1), (2, 3), (4, 9), (5, 7), (6, 8)], - [(0, 4), (1, 2), (3, 8), (6, 9)], - [(0, 5), (2, 7)], - [(1, 6), (3, 4), (5, 8), (7, 9)]] - sage: edge_coloring(g, value_only=False, hex_colors=True, solver='GLPK') - {'#00ffff': [(0, 5), (2, 7)], - '#7f00ff': [(1, 6), (3, 4), (5, 8), (7, 9)], - '#7fff00': [(0, 4), (1, 2), (3, 8), (6, 9)], - '#ff0000': [(0, 1), (2, 3), (4, 9), (5, 7), (6, 8)]} + sage: color_classes = edge_coloring(g, value_only=False, solver='GLPK') + sage: len(color_classes) + 4 + sage: len(set(frozenset(e) for C in color_classes for e in C)) == g.size() + True + sage: all(g.has_edge(e) for C in color_classes for e in C) + True + sage: all(len(Graph(C).matching()) == len(C) for C in color_classes) + True + sage: color_classes = edge_coloring(g, value_only=False, hex_colors=True, solver='GLPK') + sage: sorted(color_classes.keys()) + ['#00ffff', '#7f00ff', '#7fff00', '#ff0000'] Complete graphs are colored using the linear-time round-robin coloring:: @@ -1383,9 +1388,6 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No return 0 return dict() if hex_colors else list() - if vizing: - value_only = False - # The chromatic index of g is the maximum value over its connected # components, and the edge coloring is the union of the edge # coloring of its connected components @@ -1393,81 +1395,96 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No cdef int chi = 0 cdef list classes = [], vertices cdef list values - for h in L: - if not h.size(): - continue + if vizing: + classes = _vizing_edge_coloring(g) + if len(classes) == max(g.degree()): + # guaranteeing that vizing=True always returns Delta+1 colors + # for backward compatibility + classes.append([]) + else: + def extend_color_classes(classes, coloring): + # create missing color classes, if any + for _ in range(len(classes), len(coloring)): + classes.append([]) + # add edges to classes + for i, edges in enumerate(coloring): + classes[i].extend(edges) - # We get the vertex of maximum degree and its degree - Delta, X = max([(d, v) for v, d in h.degree_iterator(labels=True)], key=lambda x: x[0]) + for h in L: - if value_only: - if Delta + 1 <= chi: + if not h.size(): continue - if h.is_overfull(): - chi = max(chi, Delta + 1) + + # We get the vertex of maximum degree and its degree + Delta, X = max([(d, v) for v, d in h.degree_iterator(labels=True)], key=lambda x: x[0]) + + if Delta + 1 <= chi: + c = _vizing_edge_coloring(h) + extend_color_classes(classes, c) continue - if h.is_clique(): if value_only: - chi = max(chi, h.order() if h.order() % 2 else (h.order() - 1)) - continue - vertices = list(h) - r = round_robin(h.order()) - # create missing color classes, if any - for i in range(len(classes), max(r.edge_labels()) + 1): - classes.append([]) - # add edges to classes - r.relabel(perm=vertices, inplace=True) - for u, v, c in r.edge_iterator(): - classes[c].append((u, v)) - continue + if Delta + 1 <= chi: + continue + if h.is_overfull(): + chi = max(chi, Delta + 1) + continue - # Vizing's coloring uses Delta + 1 colors. Otherwise, we try both. - values = [Delta + 1] if vizing else [Delta, Delta + 1] + if h.is_clique(): + if value_only: + chi = max(chi, h.order() if h.order() % 2 else (h.order() - 1)) + continue + vertices = list(h) + r = round_robin(h.order()) + # create missing color classes, if any + for i in range(len(classes), max(r.edge_labels()) + 1): + classes.append([]) + # add edges to classes + r.relabel(perm=vertices, inplace=True) + for u, v, c in r.edge_iterator(): + classes[c].append((u, v)) + continue - for k in values: p = MixedIntegerLinearProgram(maximization=True, solver=solver) color = p.new_variable(binary=True) # A vertex cannot have two incident edges with the same color. for v in h: - for i in range(k): + for i in range(Delta): p.add_constraint(p.sum(color[frozenset((u, v)), i] for u in h.neighbor_iterator(v)) <= 1) # An edge must have a color for u, v in h.edge_iterator(labels=False): - p.add_constraint(p.sum(color[frozenset((u, v)), i] for i in range(k)) == 1) + p.add_constraint(p.sum(color[frozenset((u, v)), i] for i in range(Delta)) == 1) # We color the edges of the vertex of maximum degree for i, v in enumerate(h.neighbor_iterator(X)): p.add_constraint(color[frozenset((v, X)), i] == 1) try: p.solve(objective_only=value_only, log=verbose) - break except MIPSolverException: - if k == Delta + 1: - raise RuntimeError("Something is wrong! Certainly a problem in the" - " algorithm... please contact sage-devel@googlegroups.com") # The coloring fails with Delta colors - if value_only: - k = k + 1 - break + chi = max(chi, Delta + 1) + if not value_only: + c = _vizing_edge_coloring(h) + extend_color_classes(classes, c) + continue + + chi = max(chi, Delta) + if not value_only: + # create missing color classes, if any + for i in range(len(classes), Delta): + classes.append([]) + # add edges to color classes + color = p.get_values(color, convert=bool, tolerance=integrality_tolerance) + for e in h.edge_iterator(labels=False): + fe = frozenset(e) + for i in range(Delta): + if color[fe, i]: + classes[i].append(e) + break if value_only: - chi = max(chi, k) - else: - # create missing color classes, if any - for i in range(len(classes), k): - classes.append([]) - # add edges to color classes - color = p.get_values(color, convert=bool, tolerance=integrality_tolerance) - for e in h.edge_iterator(labels=False): - fe = frozenset(e) - for i in range(k): - if color[fe, i]: - classes[i].append(e) - break + return chi - if value_only: - return chi # if needed, builds a dictionary from the color classes adding colors if hex_colors: return dict(zip(rainbow(len(classes)), classes)) @@ -1475,6 +1492,163 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No return classes +def _vizing_edge_coloring(g): + r""" + Compute an edge coloring with at most `\Delta + 1` colors. + + INPUT: + + - ``g`` -- a graph. + + OUTPUT: + + - Returns a partition of the edge set into at most `\Delta + 1` matchings. + + .. SEEALSO:: + + - :wikipedia:`Edge_coloring` for further details on edge coloring + - :wikipedia:`Vizing's_theorem` for further details on Vizing's theorem + + ALGORITHM: + + This function's implementation is based on the algorithm described at [MG1992]_ + + EXAMPLES: + + Coloring the edges of the Petersen Graph:: + + sage: from sage.graphs.graph_coloring import _vizing_edge_coloring + sage: g = graphs.PetersenGraph() + sage: color_classes = _vizing_edge_coloring(g) + sage: len(color_classes) == max(g.degree()) + 1 + True + sage: len(set(frozenset(e) for C in color_classes for e in C)) == g.size() + True + sage: all(g.has_edge(e) for C in color_classes for e in C) + True + sage: all(len(Graph(C).matching()) == len(C) for C in color_classes) + True + + Coloring the edges of the Star Graph:: + + sage: from sage.graphs.graph_coloring import _vizing_edge_coloring + sage: g = graphs.StarGraph(5) + sage: len(_vizing_edge_coloring(g)) + 5 + + TESTS: + + Graph without edge:: + + sage: g = Graph(2) + sage: _vizing_edge_coloring(g) + [] + + Random graphs:: + + sage: from sage.graphs.generators.random import RandomGNP + sage: g = RandomGNP(randint(1, 20), random()) + sage: colors = _vizing_edge_coloring(g) + sage: Delta = max(g.degree()) + sage: len(colors) in [Delta, Delta + 1] + True + sage: len(set(frozenset(e) for C in colors for e in C)) == g.size() + True + sage: all(g.has_edge(e) for C in colors for e in C) + True + sage: all(len(Graph(C).matching()) == len(C) for C in colors) + True + """ + # This implementation was discussed in trac ticket #34809 + + # dictionary mapping edges to colors + e_colors = {frozenset(e): None for e in g.edge_iterator(labels=False, sort_vertices=False)} + + # finds every color adjacent to vertex v + def colors_of(v): + colors = {e_colors[frozenset((u, v))] for u in g.neighbor_iterator(v)} + colors.discard(None) + return colors + + # constructs a maximal fan <f..l> of X where X is edge[0] and f is edge[1] + def maximal_fan(edge): + fan_center, rear = edge + cdef set rear_colors = colors_of(rear) + cdef list neighbors = [n for n in g.neighbor_iterator(fan_center) + if e_colors[frozenset((n, fan_center))] is not None] + cdef list fan = [rear] + cdef bint can_extend_fan = True + while can_extend_fan: + can_extend_fan = False + new_neighbors = [] + for n in neighbors: + if e_colors[frozenset((fan_center, n))] not in rear_colors: + fan.append(n) + rear = n + rear_colors = colors_of(rear) + can_extend_fan = True + else: + new_neighbors.append(n) + neighbors = new_neighbors + return fan + + # gives each edge Xu in the fan <f..w> the color of Xu+ and uncolors Xw + def rotate_fan(fan_center, fan): + for i in range(1, len(fan)): + e_colors[frozenset((fan_center, fan[i - 1]))] = e_colors[frozenset((fan_center, fan[i]))] + e_colors[frozenset((fan_center, fan[-1]))] = None + + # computes the maximal ab-path starting at v + def find_path(v, a, b): + cdef list path = [v] + cdef bint stop = False + while not stop: + stop = True + for u in g.neighbor_iterator(v): + if e_colors[frozenset((u, v))] == a: + path.append(u) + # exchange the role of colors a and b and go to next vertex + a, b = b, a + v = u + stop = False + break + return path + + # exchanges the color of every edge on the ab-path starting at v + def invert_path(v, a, b): + cdef list path = find_path(v, a, b) + for e in zip(path[:-1], path[1:]): + f = frozenset(e) + e_colors[f] = a if e_colors[f] == b else b + + # returns the ´smallest´ color free at v + def find_free_color(v): + colors = colors_of(v) + for c in range(g.degree(v) + 1): + if c not in colors: + return c + + for e in g.edge_iterator(labels=False, sort_vertices=False): + fan = maximal_fan(e) + fan_center = e[0] + rear = fan[-1] + c = find_free_color(fan_center) + d = find_free_color(rear) + invert_path(fan_center, d, c) + for i, v in enumerate(fan): + if d not in colors_of(v): + fan = fan[:i + 1] + break + rotate_fan(fan_center, fan) + e_colors[frozenset((fan_center, fan[-1]))] = d + + matchings = dict() + for edge, c in e_colors.items(): + matchings[c] = matchings.get(c, []) + [tuple(edge)] + classes = list(matchings.values()) + + return classes + def round_robin(n): r""" Compute a round-robin coloring of the complete graph on `n` vertices. diff --git a/src/sage/graphs/graph_database.py b/src/sage/graphs/graph_database.py index f416d7c04ee..a875f00396d 100644 --- a/src/sage/graphs/graph_database.py +++ b/src/sage/graphs/graph_database.py @@ -967,7 +967,8 @@ def query(self, query_dict=None, display_cols=None, **kwds): """ Create a GraphQuery on this database. - For full class details, type ``GraphQuery?`` and press ``shift+enter``. + For full class details, type ``GraphQuery?`` + and press :kbd:`Shift` + :kbd:`Enter`. EXAMPLES:: diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 9aef9721fc8..0c1ae4da5b3 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -1278,7 +1278,7 @@ def permute_decomposition(trials, algorithm, vertices, prob, verbose=False): t1p = relabel_tree(t1, random_perm) assert(equivalent_trees(t1p, t2)) if verbose: - print("Passses!") + print("Passes!") def random_md_tree(max_depth, max_fan_out, leaf_probability): diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index c0a7ca8dc47..3138ee1fea2 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -474,7 +474,7 @@ def linear_ordering_to_path_decomposition(G, L): ################################################################## def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False, - max_prefix_length=20, max_prefix_number=10**6): + max_prefix_length=20, max_prefix_number=10**6, *, solver=None): r""" Compute the pathwidth of ``self`` (and provides a decomposition) @@ -513,6 +513,14 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False, number of stored prefixes used to prevent using too much memory. This parameter is used only when ``algorithm=="BAB"``. + - ``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 + <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class + :class:`MixedIntegerLinearProgram + <sage.numerical.mip.MixedIntegerLinearProgram>`. + OUTPUT: Return the pathwidth of ``self``. When ``k`` is specified, it returns @@ -566,6 +574,12 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False, Traceback (most recent call last): ... ValueError: algorithm "SuperFast" has not been implemented yet, please contribute + + Using a specific solver:: + + sage: g = graphs.PetersenGraph() + sage: g.pathwidth(solver='SCIP') # optional - pyscipopt + 5 """ from sage.graphs.graph import Graph if not isinstance(self, Graph): @@ -574,7 +588,8 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False, pw, L = vertex_separation(self, algorithm=algorithm, verbose=verbose, cut_off=k, upper_bound=None if k is None else (k+1), max_prefix_length=max_prefix_length, - max_prefix_number=max_prefix_number) + max_prefix_number=max_prefix_number, + solver=solver) if k is None: return (pw, linear_ordering_to_path_decomposition(self, L)) if certificate else pw @@ -786,6 +801,13 @@ def vertex_separation(G, algorithm="BAB", cut_off=None, upper_bound=None, verbos sage: print(vertex_separation(D)) (3, [10, 11, 8, 9, 4, 5, 6, 7, 0, 1, 2, 3]) + Using a specific MILP solver:: + + sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation + sage: G = graphs.PetersenGraph() + sage: vs, L = vertex_separation(G, algorithm="MILP", solver="SCIP"); vs # optional - pyscipopt + 5 + TESTS: Given a wrong algorithm:: @@ -1249,6 +1271,108 @@ def width_of_path_decomposition(G, L): # MILP formulation for vertex separation # ########################################## +def _vertex_separation_MILP_formulation(G, integrality=False, solver=None): + r""" + MILP formulation of the vertex separation of `G` and the optimal ordering of its vertices. + + This MILP is an improved version of the formulation proposed in [SP2010]_. See the + :mod:`module's documentation <sage.graphs.graph_decompositions.vertex_separation>` for + more details on this MILP formulation. + + INPUT: + + - ``G`` -- a Graph or a DiGraph + + - ``integrality`` -- boolean (default: ``False``); specify if variables + `x_v^t` and `u_v^t` must be integral or if they can be relaxed. This has + no impact on the validity of the solution, but it is sometimes faster to + solve the problem using binary variables only. + + - ``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 + <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class + :class:`MixedIntegerLinearProgram + <sage.numerical.mip.MixedIntegerLinearProgram>`. + + OUTPUT: + + - the :class:`~sage.numerical.mip.MixedIntegerLinearProgram` + + - :class:`sage.numerical.mip.MIPVariable` objects ``x``, ``u``, ``y``, ``z``. + + EXAMPLES:: + + sage: from sage.graphs.graph_decompositions.vertex_separation import _vertex_separation_MILP_formulation + sage: G = digraphs.DeBruijn(2,3) + sage: p, x, u, y, z = _vertex_separation_MILP_formulation(G) + sage: p + Mixed Integer Program (minimization, 193 variables, 449 constraints) + """ + from sage.graphs.graph import Graph + from sage.graphs.digraph import DiGraph + if not isinstance(G, Graph) and not isinstance(G, DiGraph): + raise ValueError("the first input parameter must be a Graph or a DiGraph") + + from sage.numerical.mip import MixedIntegerLinearProgram + p = MixedIntegerLinearProgram(maximization=False, solver=solver) + + # Declaration of variables. + x = p.new_variable(binary=integrality, nonnegative=True) + u = p.new_variable(binary=integrality, nonnegative=True) + y = p.new_variable(binary=True) + z = p.new_variable(integer=True, nonnegative=True) + + N = G.order() + V = list(G) + neighbors_out = G.neighbors_out if G.is_directed() else G.neighbors + + # (2) x[v,t] <= x[v,t+1] for all v in V, and for t:=0..N-2 + # (3) y[v,t] <= y[v,t+1] for all v in V, and for t:=0..N-2 + for v in V: + for t in range(N - 1): + p.add_constraint(x[v, t] - x[v, t + 1] <= 0) + p.add_constraint(y[v, t] - y[v, t + 1] <= 0) + + # (4) y[v,t] <= x[w,t] for all v in V, for all w in N^+(v), and for all t:=0..N-1 + for v in V: + for w in neighbors_out(v): + for t in range(N): + p.add_constraint(y[v, t] - x[w, t] <= 0) + + # (5) sum_{v in V} y[v,t] == t+1 for t:=0..N-1 + for t in range(N): + p.add_constraint(p.sum(y[v, t] for v in V) == t + 1) + + # (6) u[v,t] >= x[v,t]-y[v,t] for all v in V, and for all t:=0..N-1 + for v in V: + for t in range(N): + p.add_constraint(x[v, t] - y[v, t] - u[v, t] <= 0) + + # (7) z >= sum_{v in V} u[v,t] for all t:=0..N-1 + for t in range(N): + p.add_constraint(p.sum(u[v, t] for v in V) - z['z'] <= 0) + + # (8)(9) 0 <= x[v,t] and u[v,t] <= 1 + if not integrality: + for v in V: + for t in range(N): + p.add_constraint(x[v, t], min=0, max=1) + p.add_constraint(u[v, t], min=0, max=1) + + # (10) y[v,t] in {0,1} + # already declared + + # (11) 0 <= z <= |V| + p.add_constraint(z['z'] <= N) + + # (1) Minimize z + p.set_objective(z['z']) + + return p, x, u, y, z + + @rename_keyword(deprecation=32222, verbosity='verbose') def vertex_separation_MILP(G, integrality=False, solver=None, verbose=0, *, integrality_tolerance=1e-3): @@ -1341,65 +1465,11 @@ def vertex_separation_MILP(G, integrality=False, solver=None, verbose=0, ... ValueError: the first input parameter must be a Graph or a DiGraph """ - from sage.graphs.graph import Graph - from sage.graphs.digraph import DiGraph - if not isinstance(G, Graph) and not isinstance(G, DiGraph): - raise ValueError("the first input parameter must be a Graph or a DiGraph") - - from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException - p = MixedIntegerLinearProgram(maximization=False, solver=solver) - - # Declaration of variables. - x = p.new_variable(binary=integrality, nonnegative=True) - u = p.new_variable(binary=integrality, nonnegative=True) - y = p.new_variable(binary=True) - z = p.new_variable(integer=True, nonnegative=True) + from sage.numerical.mip import MIPSolverException + p, x, u, y, z = _vertex_separation_MILP_formulation(G, integrality=integrality, solver=solver) N = G.order() V = list(G) - neighbors_out = G.neighbors_out if G.is_directed() else G.neighbors - - # (2) x[v,t] <= x[v,t+1] for all v in V, and for t:=0..N-2 - # (3) y[v,t] <= y[v,t+1] for all v in V, and for t:=0..N-2 - for v in V: - for t in range(N - 1): - p.add_constraint(x[v, t] - x[v, t + 1] <= 0) - p.add_constraint(y[v, t] - y[v, t + 1] <= 0) - - # (4) y[v,t] <= x[w,t] for all v in V, for all w in N^+(v), and for all t:=0..N-1 - for v in V: - for w in neighbors_out(v): - for t in range(N): - p.add_constraint(y[v, t] - x[w, t] <= 0) - - # (5) sum_{v in V} y[v,t] == t+1 for t:=0..N-1 - for t in range(N): - p.add_constraint(p.sum(y[v, t] for v in V) == t + 1) - - # (6) u[v,t] >= x[v,t]-y[v,t] for all v in V, and for all t:=0..N-1 - for v in V: - for t in range(N): - p.add_constraint(x[v, t] - y[v, t] - u[v, t] <= 0) - - # (7) z >= sum_{v in V} u[v,t] for all t:=0..N-1 - for t in range(N): - p.add_constraint(p.sum(u[v, t] for v in V) - z['z'] <= 0) - - # (8)(9) 0 <= x[v,t] and u[v,t] <= 1 - if not integrality: - for v in V: - for t in range(N): - p.add_constraint(x[v, t], min=0, max=1) - p.add_constraint(u[v, t], min=0, max=1) - - # (10) y[v,t] in {0,1} - # already declared - - # (11) 0 <= z <= |V| - p.add_constraint(z['z'] <= N) - - # (1) Minimize z - p.set_objective(z['z']) try: obj = p.solve(log=verbose) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 9a8c0877d3d..df88bbe2713 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -13,7 +13,7 @@ sage: h = graphs.HouseGraph() More interestingly, one can get the list of all graphs that Sage knows how to -build by typing ``graphs.`` in Sage and then hitting tab. +build by typing ``graphs.`` in Sage and then hitting :kbd:`Tab`. """ import subprocess @@ -484,7 +484,7 @@ class GraphGenerators(): A list of all graphs and graph structures (other than isomorphism class representatives) in this database is available via tab completion. Type - "graphs." and then hit the tab key to see which graphs are available. + "graphs." and then hit the :kbd:`Tab` key to see which graphs are available. The docstrings include educational information about each named graph with the hopes that this class can be used as a reference. @@ -1014,8 +1014,10 @@ def nauty_genbg(self, options="", debug=False): The possible options, obtained as output of ``genbg --help``:: - n1 : the number of vertices in the first class - n2 : the number of vertices in the second class + n1 : the number of vertices in the first class. + We must have n1=1..24. + n2 : the number of vertices in the second class. + We must have n2=0..32 and n1+n2=1..32. mine:maxe : <int>:<int> a range for the number of edges <int>:0 means '<int> or more' except in the case 0:0 res/mod : only generate subset res out of subsets 0..mod-1 @@ -1129,6 +1131,27 @@ def nauty_genbg(self, options="", debug=False): ['>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] + + We must have n1=1..24, n2=0..32 and n1+n2=1..32 (:trac:`34179`):: + + sage: next(graphs.nauty_genbg("25 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("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... """ import shlex from sage.features.nauty import NautyExecutable diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 9b571953fed..fe1de72dcc5 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -1,5 +1,5 @@ r""" -Functions for reading/building graphs/digraphs. +Functions for reading/building graphs/digraphs This module gathers functions needed to build a graph from any other data. @@ -371,6 +371,8 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted - ``loops``, ``multiedges``, ``weighted`` -- booleans (default: ``False``); whether to consider the graph as having loops, multiple edges, or weights + .. NOTE:: ``weighted`` is currently ignored. + EXAMPLES:: sage: from sage.graphs.graph_input import from_oriented_incidence_matrix @@ -426,8 +428,6 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted positions.append(tuple(NZ)) else: positions.append((NZ[1], NZ[0])) - if weighted is None: - weighted = False if multiedges is None: total = len(positions) multiedges = len(set(positions)) < total diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 16fd32608d8..5d646060add 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Graph Plotting +Graph plotting *(For LaTeX drawings of graphs, see the* :mod:`~sage.graphs.graph_latex` *module.)* diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index cb728e3b4e1..a76b5477961 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -155,7 +155,7 @@ from cysignals.signals cimport sig_on, sig_off from memory_allocator cimport MemoryAllocator from sage.graphs.distances_all_pairs cimport c_distances_all_pairs -from sage.arith.all import binomial +from sage.arith.misc import binomial from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.data_structures.bitset import Bitset diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 2ea1adce781..147e2ba2778 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -869,6 +869,7 @@ def update_db(self): EXAMPLES:: sage: graph_classes.update_db() # optional - internet + Database downloaded """ self._download_db() diff --git a/src/sage/graphs/matchpoly.pyx b/src/sage/graphs/matchpoly.pyx index 444d99ebee3..4a8c24be8ca 100644 --- a/src/sage/graphs/matchpoly.pyx +++ b/src/sage/graphs/matchpoly.pyx @@ -1,6 +1,6 @@ # cython: binding=True """ -Matching Polynomial +Matching polynomial This module contains the following methods: diff --git a/src/sage/graphs/partial_cube.py b/src/sage/graphs/partial_cube.py index 0831df402e2..bce06dee677 100644 --- a/src/sage/graphs/partial_cube.py +++ b/src/sage/graphs/partial_cube.py @@ -166,7 +166,6 @@ def depth_first_traversal(G, start): sage: t = list(sage.graphs.partial_cube.depth_first_traversal(H, '00')) sage: len(t) 16 - """ neighbors = G.neighbor_out_iterator seen = set() @@ -286,7 +285,7 @@ def is_partial_cube(G, certificate=False): # Initial sanity check: are there few enough edges? # Needed so that we don't try to use union-find on a dense # graph and incur superquadratic runtimes. - if 1 << (2*G.size()//n) > n: + if 1 << (2 * G.size() // n) > n: return fail # Check for bipartiteness. @@ -399,13 +398,13 @@ def is_partial_cube(G, certificate=False): # Rest of data structure: point from states to list and list to states state_to_active_token = {v: -1 for v in g} - token_to_states = [[] for i in activeTokens] # (i.e. vertices on which each token acts) + token_to_states = [[] for _ in activeTokens] # (i.e. vertices on which each token acts) def scan(v): """ Find the next token that is effective for v. """ - a = next(i for i in range(state_to_active_token[v]+1, len(activeTokens)) + a = next(i for i in range(state_to_active_token[v] + 1, len(activeTokens)) if activeTokens[i] is not None and activeTokens[i] in action[v]) state_to_active_token[v] = a token_to_states[a].append(v) diff --git a/src/sage/graphs/path_enumeration.pyx b/src/sage/graphs/path_enumeration.pyx index 2e3122b6134..0ef63dd0c17 100644 --- a/src/sage/graphs/path_enumeration.pyx +++ b/src/sage/graphs/path_enumeration.pyx @@ -2,7 +2,7 @@ # cython: binding=True # distutils: language = c++ r""" -Path Enumeration +Path enumeration This module is meant for all functions related to path enumeration in graphs. diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index a29bce8dd3c..8a33cc8c70e 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -1,5 +1,5 @@ """ -Schnyder's Algorithm for straight-line planar embeddings +Schnyder's algorithm for straight-line planar embeddings A module for computing the (x,y) coordinates for a straight-line planar embedding of any connected planar graph with at least three vertices. Uses @@ -213,7 +213,6 @@ def _normal_label(g, comb_emb, external_face): except Exception: raise RuntimeError('Contractible list is empty but graph still has %d vertices. (Expected 3.)' % g.order()) - break # going to contract v v_neighbors = Set(g.neighbors(v)) contracted.append((v, v_neighbors, diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index a4eff7ecff8..945b2301159 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -32,7 +32,9 @@ import os from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown -from sage.arith.all import is_square, is_prime_power, divisors +from sage.arith.misc import is_square +from sage.arith.misc import is_prime_power +from sage.arith.misc import divisors from sage.misc.cachefunc import cached_function from sage.combinat.designs.orthogonal_arrays import orthogonal_array from sage.combinat.designs.bibd import balanced_incomplete_block_design @@ -47,6 +49,7 @@ from libc.stdint cimport uint_fast32_t cdef dict _brouwer_database = None _small_srg_database = None + @cached_function def is_paley(int v, int k, int l, int mu): r""" @@ -72,12 +75,13 @@ def is_paley(int v, int k, int l, int mu): (13, 6, 2, 3) sage: t = is_paley(5,5,5,5); t """ - if (v%4 == 1 and is_prime_power(v) and - k == (v-1)//2 and - l == (v-5)//4 and - mu == (v-1)//4): + if (v % 4 == 1 and is_prime_power(v) and + k == (v - 1)//2 and + l == (v - 5)//4 and + mu == (v - 1)//4): from sage.graphs.generators.families import PaleyGraph - return (PaleyGraph,v) + return (PaleyGraph, v) + @cached_function def is_mathon_PC_srg(int v, int k, int l, int mu): @@ -119,21 +123,21 @@ def is_mathon_PC_srg(int v, int k, int l, int mu): sage: t = is_mathon_PC_srg(4*mu+1,2*mu,mu-1,mu); t """ cdef int t - if (v%4 == 1 and - k == (v-1)//2 and - l == (v-5)//4 and - mu == (v-1)//4): + if (v % 4 == 1 and + k == (v - 1)//2 and + l == (v - 5)//4 and + mu == (v - 1)//4): from sage.rings.integer_ring import ZZ K = ZZ['x'] x = K.gen() - rpoly = (w for w in (x*(4*x*(4*x-1)-1) - mu).roots() if w[0] > 0) + rpoly = (w for w in (x*(4*x*(4*x - 1) - 1) - mu).roots() if w[0] > 0) try: t = next(rpoly)[0] - if (is_prime_power(4*t-1) and - is_prime_power(4*t+1)): # extra assumption in TODO! + if (is_prime_power(4*t - 1) and + is_prime_power(4*t + 1)): # extra assumption in TODO! from sage.graphs.generators.families import \ MathonPseudocyclicStronglyRegularGraph - return (MathonPseudocyclicStronglyRegularGraph,t) + return (MathonPseudocyclicStronglyRegularGraph, t) except StopIteration: pass @@ -167,16 +171,19 @@ def is_muzychuk_S6(int v, int k, int l, int mu): """ cdef int n, d from sage.rings.integer_ring import ZZ - n_list = [n for n in range(l-1) if ZZ(n).is_prime_power()] + n_list = [n for n in range(l - 1) if ZZ(n).is_prime_power()] for n in n_list: d = 2 - while n**d * ((n**d-1)//(n-1)+1) <= v: - if v == n**d * ((n**d-1)//(n-1)+1) and k == n**(d-1)*(n**d-1)//(n-1) - 1\ - and l == mu - 2 and mu == n**(d-1) * (n**(d-1)-1) // (n-1): + while n**d * ((n**d - 1)//(n - 1) + 1) <= v: + if (v == n**d * ((n**d - 1)//(n - 1) + 1) and + k == n**(d - 1)*(n**d - 1)//(n - 1) - 1 and + l == mu - 2 and + mu == n**(d - 1) * (n**(d - 1) - 1)//(n - 1)): from sage.graphs.generators.families import MuzychukS6Graph return (MuzychukS6Graph, n, d) d += 1 + @cached_function def is_orthogonal_array_block_graph(int v, int k, int l, int mu): r""" @@ -232,19 +239,20 @@ def is_orthogonal_array_block_graph(int v, int k, int l, int mu): m, n = latin_squares_graph_parameters(v, k, l, mu) except Exception: return - if orthogonal_array(m,n,existence=True) is True: + if orthogonal_array(m, n, existence=True) is True: from sage.graphs.generators.intersection import OrthogonalArrayBlockGraph - return (lambda m,n : OrthogonalArrayBlockGraph(m, n), m,n) + return (lambda m, n: OrthogonalArrayBlockGraph(m, n), m, n) - elif n>2 and skew_hadamard_matrix(n+1, existence=True) is True: - if m==(n+1)/2: + elif n > 2 and skew_hadamard_matrix(n+1, existence=True) is True: + if m == (n + 1)/2: from sage.graphs.generators.families import SquaredSkewHadamardMatrixGraph as G - elif m==(n-1)//2: + elif m == (n - 1)//2: from sage.graphs.generators.families import PasechnikGraph as G else: return return (G, (n+1)//4) + @cached_function def is_johnson(int v, int k, int l, int mu): r""" @@ -276,10 +284,11 @@ def is_johnson(int v, int k, int l, int mu): # J(n,m) has parameters v = m(m – 1)/2, k = 2(m – 2), λ = m – 2, μ = 4. m = l + 2 if (mu == 4 and - k == 2*(m-2) and - v == m*(m-1)//2): + k == 2*(m - 2) and + v == m*(m - 1)//2): from sage.graphs.generators.families import JohnsonGraph - return (lambda m: JohnsonGraph(m,2), m) + return (lambda m: JohnsonGraph(m, 2), m) + @cached_function def is_steiner(int v, int k, int l, int mu): @@ -317,15 +326,16 @@ def is_steiner(int v, int k, int l, int mu): if mu <= 1 or not is_square(mu): return m = int(sqrt(mu)) - n = (k*(m-1))//m+m + n = (k*(m - 1))//m + m - if (v == (n*(n-1))/(m*(m-1)) and - k == m*(n-m)/(m-1) and - l == (m-1)**2 + (n-1)/(m-1)-2 and - balanced_incomplete_block_design(n,m,existence=True) is True): + if (v == (n*(n - 1))/(m*(m - 1)) and + k == m*(n - m)/(m - 1) and + l == (m - 1)**2 + (n - 1)/(m - 1) - 2 and + balanced_incomplete_block_design(n, m, existence=True) is True): from sage.graphs.generators.intersection import IntersectionGraph return (lambda n, m: IntersectionGraph([frozenset(b) for b in balanced_incomplete_block_design(n, m)]), n, m) + @cached_function def is_affine_polar(int v, int k, int l, int mu): r""" @@ -361,25 +371,25 @@ def is_affine_polar(int v, int k, int l, int mu): # # VO−(2e,q) has parameters v = q^(2e), k = (q^(e−1) - 1)(q^e + 1), λ = # q(q^(e−2) - 1)(q^(e−1) + 1) + q − 2, μ = q^(e−1)(q^(e−1) - 1) - if (not is_square(v) or - not is_prime_power(v)): + if not is_square(v) or not is_prime_power(v): return - prime,power = is_prime_power(v,get_data=True) - if power%2: + prime, power = is_prime_power(v, get_data=True) + if power % 2: return for e in divisors(power/2): q = prime**(power//(2*e)) assert v == q**(2*e) - if (k == (q**(e-1) + 1)*(q**e-1) and - l == q*(q**(e-2) + 1)*(q**(e-1)-1)+q-2 and - mu== q**(e-1)*(q**(e-1) + 1)): + if (k == (q**(e - 1) + 1)*(q**e - 1) and + l == q*(q**(e - 2) + 1)*(q**(e - 1) - 1) + q - 2 and + mu == q**(e - 1)*(q**(e - 1) + 1)): from sage.graphs.generators.classical_geometries import AffineOrthogonalPolarGraph - return (lambda d,q : AffineOrthogonalPolarGraph(d,q,sign='+'),2*e,q) - if (k == (q**(e-1) - 1)*(q**e+1) and - l == q*(q**(e-2)- 1)*(q**(e-1)+1)+q-2 and - mu== q**(e-1)*(q**(e-1) - 1)): + return (lambda d, q: AffineOrthogonalPolarGraph(d, q, sign='+'), 2*e, q) + if (k == (q**(e - 1) - 1)*(q**e + 1) and + l == q*(q**(e - 2) - 1)*(q**(e - 1) + 1) + q - 2 and + mu == q**(e - 1)*(q**(e - 1) - 1)): from sage.graphs.generators.classical_geometries import AffineOrthogonalPolarGraph - return (lambda d,q : AffineOrthogonalPolarGraph(d,q,sign='-'),2*e,q) + return (lambda d, q: AffineOrthogonalPolarGraph(d, q, sign='-'), 2*e, q) + @cached_function def is_orthogonal_polar(int v, int k, int l, int mu): @@ -427,35 +437,36 @@ def is_orthogonal_polar(int v, int k, int l, int mu): q_pow_m_minus_one = -s-1 if abs(s) > r else r+1 if is_prime_power(q_pow_m_minus_one): - prime,power = is_prime_power(q_pow_m_minus_one,get_data=True) + prime, power = is_prime_power(q_pow_m_minus_one, get_data=True) for d in divisors(power): q = prime**d - m = (power//d)+1 + m = (power//d) + 1 # O(2m+1,q) - if (v == (q**(2*m)-1)//(q-1) and - k == q*(q**(2*m-2)-1)//(q-1) and - l == q**2*(q**(2*m-4)-1)//(q-1) + q-1 and - mu== (q**(2*m-2)-1)//(q-1)): + if (v == (q**(2*m) - 1)//(q - 1) and + k == q*(q**(2*m - 2) - 1)//(q - 1) and + l == q**2*(q**(2*m - 4) - 1)//(q - 1) + q - 1 and + mu == (q**(2*m - 2) - 1)//(q - 1)): from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph return (OrthogonalPolarGraph, 2*m+1, q, "") # O^+(2m,q) - if (v == (q**(2*m-1)-1)//(q-1) + q**(m-1) and - k == q*(q**(2*m-3)-1)//(q-1) + q**(m-1) and - k == q**(2*m-3) + l + 1 and - mu== k//q): + if (v == (q**(2*m - 1) - 1)//(q - 1) + q**(m - 1) and + k == q*(q**(2*m - 3) - 1)//(q - 1) + q**(m - 1) and + k == q**(2*m - 3) + l + 1 and + mu == k//q): from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph return (OrthogonalPolarGraph, 2*m, q, "+") # O^+(2m+1,q) - if (v == (q**(2*m-1)-1)//(q-1) - q**(m-1) and - k == q*(q**(2*m-3)-1)//(q-1) - q**(m-1) and - k == q**(2*m-3) + l + 1 and - mu== k//q): + if (v == (q**(2*m - 1) - 1)//(q - 1) - q**(m - 1) and + k == q*(q**(2*m - 3) - 1)//(q - 1) - q**(m - 1) and + k == q**(2*m - 3) + l + 1 and + mu == k//q): from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph return (OrthogonalPolarGraph, 2*m, q, "-") + @cached_function def is_goethals_seidel(int v, int k, int l, int mu): r""" @@ -520,15 +531,15 @@ def is_goethals_seidel(int v, int k, int l, int mu): # - the number of vertices v is equal to v_bibd*(r_bibd+1) # - the degree k of the graph is equal to k=(v+r_bibd-1)/2 - r_bibd = k - (v-1-k) - v_bibd = v//(r_bibd+1) - k_bibd = (v_bibd-1)//r_bibd + 1 if r_bibd>0 else -1 + r_bibd = k - (v - 1 - k) + v_bibd = v//(r_bibd + 1) + k_bibd = (v_bibd - 1)//r_bibd + 1 if r_bibd > 0 else -1 - if (v == v_bibd*(r_bibd+1) and - 2*k == v+r_bibd-1 and - 4*l == -2*v + 6*k -v_bibd -k_bibd and - hadamard_matrix(r_bibd+1, existence=True) is True and - balanced_incomplete_block_design(v_bibd, k_bibd, existence = True) is True): + if (v == v_bibd*(r_bibd + 1) and + 2*k == v + r_bibd - 1 and + 4*l == -2*v + 6*k - v_bibd - k_bibd and + hadamard_matrix(r_bibd + 1, existence=True) is True and + balanced_incomplete_block_design(v_bibd, k_bibd, existence=True) is True): from sage.graphs.generators.families import GoethalsSeidelGraph return [GoethalsSeidelGraph, k_bibd, r_bibd] @@ -1204,6 +1215,7 @@ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): ValueError: I do not know how to build a (784, 0, 14, 38)-SRG from a RSHCD """ from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal + def sgn(x): return 1 if x >= 0 else -1 n = v diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 14374831be8..a16ac6d701e 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -2,7 +2,7 @@ # cython: binding=True # distutils: language = c++ r""" -Graph traversals. +Graph traversals **This module implements the following graph traversals** diff --git a/src/sage/graphs/views.pyx b/src/sage/graphs/views.pyx index b2c66f81995..51a4a738e46 100644 --- a/src/sage/graphs/views.pyx +++ b/src/sage/graphs/views.pyx @@ -9,7 +9,7 @@ through a view. Views can be iterated multiple times. .. TODO:: - View of neighborhood to get open/close neighborhood of a vertex/set of - vertices, and also the vertex boundary + vertices Classes ------- @@ -48,6 +48,16 @@ cdef class EdgesView: vertices or ``None``. When set, consider only edges incident to specified vertices. + - ``vertices2`` -- list (default: ``None``); an iterable container of + vertices or ``None``. When set, consider only edges incident to specified + vertices. More precisely: + + - When both ``vertices`` and ``vertices2`` are set, consider only edges + ``(u, v, l)`` with ``u`` in ``vertices`` and ``v`` in ``vertices2``. + + - When ``vertices`` is ``None`` and ``vertices2`` is set, consider only + edges ``(u, v, l)`` with ``v`` in ``vertices2``. + - ``labels`` -- boolean (default: ``True``); if ``False``, each edge is simply a pair ``(u, v)`` of vertices @@ -202,6 +212,37 @@ cdef class EdgesView: sage: G.has_edge(1, 0) False + We can consider only the edges between two specifed sets of vertices:: + + sage: G = Graph([(0, 1), (1, 2)]) + sage: E = EdgesView(G, vertices=[0], vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(G, vertices=[0], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(G, vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) in E + True + sage: D = DiGraph([(0, 1), (1, 2)]) + sage: E = EdgesView(D, vertices=[0], vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: EdgesView(D, vertices=[1], vertices2=[0], labels=False) + [] + sage: E = EdgesView(D, vertices=[1], vertices2=[0], labels=False, ignore_direction=True) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(D, vertices=[0], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(D, vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(D, vertices2=[1], labels=False, ignore_direction=True) + sage: (0, 1) in E and (1, 2) in E + True + A view is updated as the graph is updated:: sage: G = Graph() @@ -299,13 +340,16 @@ cdef class EdgesView: cdef readonly GenericGraph_pyx _graph cdef list _vertices cdef frozenset _vertex_set + cdef list _vertices2 + cdef frozenset _vertex_set2 cdef readonly bint _labels cdef readonly bint _ignore_direction cdef bint _sort_vertices cdef bint _sort_edges cdef _sort_edges_key - def __init__(self, G, vertices=None, labels=True, ignore_direction=False, + def __init__(self, G, vertices=None, vertices2=None, labels=True, + ignore_direction=False, sort=None, key=None, sort_vertices=True): """ Construction of this :class:`EdgesView`. @@ -333,6 +377,13 @@ cdef class EdgesView: self._vertices = list(vertices) self._vertex_set = frozenset(self._vertices) + if vertices2 is None: + self._vertices2 = None + self._vertex_set2 = None + else: + self._vertices2 = list(vertices2) + self._vertex_set2 = frozenset(self._vertices2) + # None and 0 are interpreted as False, 1 as True self._labels = labels self._ignore_direction = ignore_direction @@ -365,7 +416,7 @@ cdef class EdgesView: sage: len(EdgesView(G, ignore_direction=True, sort=False)) 8 """ - if self._vertices is self._graph: + if self._vertices is None and self._vertices2 is None: if self._graph._directed and self._ignore_direction: return 2 * self._graph.size() return self._graph.size() @@ -388,7 +439,7 @@ cdef class EdgesView: """ return "[%s]" % ', '.join(map(repr, self)) - def _iter_unsorted(self, vertices): + def _iter_unsorted(self): """ Iterator over the unsorted edges in ``self``. @@ -400,15 +451,40 @@ cdef class EdgesView: sage: list(E) [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)] """ - if self._graph._directed: - yield from self._graph._backend.iterator_out_edges(vertices, self._labels) - if self._ignore_direction: - yield from self._graph._backend.iterator_in_edges(vertices, self._labels) + if self._vertices is None: + vertices = self._graph + vertex_set = self._graph else: - if self._sort_vertices: + vertices = self._vertices + vertex_set = self._vertex_set + + if self._vertices2 is None: + if self._graph._directed: + yield from self._graph._backend.iterator_out_edges(vertices, self._labels) + if self._ignore_direction: + yield from self._graph._backend.iterator_in_edges(vertices, self._labels) + elif self._sort_vertices: yield from self._graph._backend.iterator_edges(vertices, self._labels) else: yield from self._graph._backend.iterator_unsorted_edges(vertices, self._labels) + elif self._graph._directed: + for e in self._graph._backend.iterator_out_edges(vertices, self._labels): + if e[1] in self._vertex_set2: + yield e + if self._ignore_direction: + for e in self._graph._backend.iterator_in_edges(vertices, self._labels): + if e[0] in self._vertex_set2: + yield e + elif self._sort_vertices: + for e in self._graph._backend.iterator_edges(vertices, self._labels): + if ((e[0] in vertex_set and e[1] in self._vertex_set2) or + (e[1] in vertex_set and e[0] in self._vertex_set2)): + yield e + else: + for e in self._graph._backend.iterator_unsorted_edges(vertices, self._labels): + if ((e[0] in vertex_set and e[1] in self._vertex_set2) or + (e[1] in vertex_set and e[0] in self._vertex_set2)): + yield e def __iter__(self): """ @@ -427,18 +503,14 @@ cdef class EdgesView: sage: sum(1 for e in E for ee in E) == G.size() * G.size() True """ - if self._vertices is None: - vertices = self._graph - else: - vertices = self._vertices if self._sort_edges: - yield from sorted(self._iter_unsorted(vertices), key=self._sort_edges_key) + yield from sorted(self._iter_unsorted(), key=self._sort_edges_key) else: - yield from self._iter_unsorted(vertices) + yield from self._iter_unsorted() def __bool__(self): """ - Check whether ``self`` is empty. + Check whether ``self`` is not empty. EXAMPLES:: @@ -450,19 +522,8 @@ cdef class EdgesView: sage: bool(E) True """ - if self._vertices is None: - vertices = self._graph - else: - vertices = self._vertices - if self._graph._directed: - for _ in self._graph._backend.iterator_out_edges(vertices, False): - return True - if self._ignore_direction: - for _ in self._graph._backend.iterator_in_edges(vertices, False): - return True - else: - for _ in self._graph._backend.iterator_edges(vertices, False): - return True + for _ in self._iter_unsorted(): + return True return False def __eq__(self, right): @@ -563,6 +624,16 @@ cdef class EdgesView: False sage: (0, 1, None) in E True + sage: G = Graph([(0, 1), (1, 2)]) + sage: E = EdgesView(G, vertices=[0], vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(G, vertices=[0], labels=False) + sage: (0, 1) in E and (1, 2) not in E + True + sage: E = EdgesView(G, vertices2=[1], labels=False) + sage: (0, 1) in E and (1, 2) in E + True """ if self._labels: try: @@ -575,13 +646,28 @@ cdef class EdgesView: except Exception: return False label = None - if (self._vertex_set is not None - and u not in self._vertex_set and v not in self._vertex_set): - return False - if self._graph._directed and self._ignore_direction: - return (self._graph._backend.has_edge(u, v, label) - or self._graph._backend.has_edge(v, u, label)) - return self._graph._backend.has_edge(u, v, label) + if self._vertices2 is None: + if (self._vertex_set is not None + and u not in self._vertex_set and v not in self._vertex_set): + return False + if self._graph._directed and self._ignore_direction: + return (self._graph._backend.has_edge(u, v, label) + or self._graph._backend.has_edge(v, u, label)) + return self._graph._backend.has_edge(u, v, label) + else: + if u not in self._vertex_set2 and v not in self._vertex_set2: + return False + if (self._vertex_set is not None + and u not in self._vertex_set and v not in self._vertex_set): + return False + if self._graph._directed: + if self._ignore_direction: + return (self._graph._backend.has_edge(u, v, label) + or self._graph._backend.has_edge(v, u, label)) + elif ((self._vertex_set is not None and u not in self._vertex_set) + or v not in self._vertex_set2): + return False + return self._graph._backend.has_edge(u, v, label) def __getitem__(self, i): r""" diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index 45c692d8955..79e574fc4f5 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -53,7 +53,7 @@ cdef inline is_long_hole_free_process(g, short_digraph sd, bitset_t dense_graph, bint certificate, int a, int b, int c, int n): """ - This method is part of method `is_long_hole_free`. + This method is part of method ``is_long_hole_free``. EXAMPLES:: @@ -285,7 +285,7 @@ cdef inline is_long_antihole_free_process(g, short_digraph sd, bitset_t dense_gr bint certificate, int a, int b, int c, int n): """ - This method is part of method `is_long_antihole_free`. + This method is part of method ``is_long_antihole_free``. EXAMPLES:: diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 8a6afa76bbb..215b516b464 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -646,9 +646,9 @@ def is_trivial(self): """ return self.elementary_divisors() == () - def __bool__(self): + def __bool__(self) -> bool: """ - Returns True if this group is nontrivial. + Return ``True`` if this group is nontrivial. EXAMPLES:: @@ -662,8 +662,6 @@ def __bool__(self): """ return not self.is_trivial() - - @cached_method def dual_group(self, names="X", base_ring=None): """ diff --git a/src/sage/groups/abelian_gps/dual_abelian_group_element.py b/src/sage/groups/abelian_gps/dual_abelian_group_element.py index aaabe107781..18fac135c94 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group_element.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group_element.py @@ -41,7 +41,6 @@ - Volker Braun (2012-11) port to new Parent base. Use tuples for immutables. Default to cyclotomic base ring. """ - # **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # Copyright (C) 2006 David Joyner<wdjoyner@gmail.com> @@ -53,62 +52,21 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - -import operator - from sage.arith.all import LCM -from sage.misc.misc_c import prod from sage.groups.abelian_gps.element_base import AbelianGroupElementBase -from functools import reduce -def add_strings(x, z=0): - """ - This was in sage.misc.misc but commented out. Needed to add - lists of strings in the word_problem method below. - - Return the sum of the elements of x. If x is empty, - return z. - - INPUT: - - - ``x`` -- iterable - - - ``z`` -- the ``0`` that will be returned if ``x`` is empty. - - OUTPUT: - - The sum of the elements of ``x``. - - EXAMPLES:: - - sage: from sage.groups.abelian_gps.dual_abelian_group_element import add_strings - sage: add_strings([], z='empty') - 'empty' - sage: add_strings(['a', 'b', 'c']) - 'abc' - """ - if len(x) == 0: - return z - if not isinstance(x, list): - m = iter(x) - y = next(m) - return reduce(operator.add, m, y) - else: - return reduce(operator.add, x[1:], x[0]) - - -def is_DualAbelianGroupElement(x): +def is_DualAbelianGroupElement(x) -> bool: """ Test whether ``x`` is a dual Abelian group element. INPUT: - - ``x`` -- anything. + - ``x`` -- anything OUTPUT: - Boolean. + Boolean EXAMPLES:: @@ -169,10 +127,10 @@ def __call__(self, g): N = LCM(order) order_not = [N / o for o in order] zeta = F.zeta(N) - return F.prod(zeta ** (expsX[i] * expsg[i] * order_not[i]) + return F.prod(zeta**(expsX[i] * expsg[i] * order_not[i]) for i in range(len(expsX))) - def word_problem(self, words, display=True): + def word_problem(self, words): """ This is a rather hackish method and is included for completeness. @@ -196,43 +154,21 @@ def word_problem(self, words, display=True): sage: w = a^7*b^3*c^5*d^4*e^4 sage: x = a^3*b^2*c^2*d^3*e^5 sage: y = a^2*b^4*c^2*d^4*e^5 - sage: e.word_problem([u,v,w,x,y],display=False) + sage: e.word_problem([u,v,w,x,y]) [[b^2*c^2*d^3*e^5, 245]] - - The command e.word_problem([u,v,w,x,y],display=True) returns - the same list but also prints ``e = (b^2*c^2*d^3*e^5)^245``. """ - ## First convert the problem to one using AbelianGroups - import copy - from sage.groups.abelian_gps.abelian_group import AbelianGroup - from sage.interfaces.gap import gap - M = self.parent() - G = M.group() - gens = M.variable_names() - g = prod([G.gen(i)**(self.list()[i]) for i in range(G.ngens())]) - gap.eval("l:=One(Rationals)") ## trick needed for LL line below to keep Sage from parsing - s1 = "gens := GeneratorsOfGroup(%s)"%G._gap_init_() - gap.eval(s1) - for i in range(len(gens)): - cmd = ("%s := gens["+str(i+1)+"]") % gens[i] - gap.eval(cmd) - s2 = "g0:=%s; gensH:=%s" % (str(g), words) - gap.eval(s2) - s3 = 'G:=Group(gens); H:=Group(gensH)' - gap.eval(s3) - phi = gap.eval("hom:=EpimorphismFromFreeGroup(H)") - l1 = gap.eval("ans:=PreImagesRepresentative(hom,g0)") - l2 = copy.copy(l1) - l4 = [] - l3 = l1.split("*") - for i in range(1,len(words)+1): - l2 = l2.replace("x"+str(i),"("+str(words[i-1])+")") - l3 = eval(gap.eval("L3:=ExtRepOfObj(ans)")) - nn = eval(gap.eval("n:=Int(Length(L3)/2)")) - LL1 = eval(gap.eval("L4:=List([l..n],i->L3[2*i])")) ## note the l not 1 - LL2 = eval(gap.eval("L5:=List([l..n],i->L3[2*i-1])")) ## note the l not 1 - if display: - s = str(g)+" = "+add_strings(["("+str(words[LL2[i]-1])+")^"+str(LL1[i])+"*" for i in range(nn)]) - m = len(s) - print(" ", s[:m-1], "\n") - return [[words[LL2[i]-1],LL1[i]] for i in range(nn)] + from sage.libs.gap.libgap import libgap + A = libgap.AbelianGroup(self.parent().gens_orders()) + gens = A.GeneratorsOfGroup() + gap_g = libgap.Product([gi**Li for gi, Li in zip(gens, self.list())]) + gensH = [libgap.Product([gi**Li for gi, Li in zip(gens, w.list())]) + for w in words] + H = libgap.Group(gensH) + + hom = H.EpimorphismFromFreeGroup() + ans = hom.PreImagesRepresentative(gap_g) + + resu = ans.ExtRepOfObj().sage() # (indice, power, indice, power, etc) + indices = resu[0::2] + powers = resu[1::2] + return [[words[indi - 1], powi] for indi, powi in zip(indices, powers)] diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index 3efc1d7621c..49f0dc97a4b 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -352,7 +352,7 @@ def exponent(self): 1 """ if not self.invariants(): - return 1 + return ZZ(1) else: ann = self.annihilator().gen() if ann: diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 7c0aec05ff8..1894ac24bc1 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -371,6 +371,9 @@ def discrete_log(self, x, gens=None): y = cofactor * x pvals = [o.valuation(p) for o in ords] + if not any(pvals): + continue + plog = _discrete_log_pgroup(p, pvals, pgens, y) for i, (r, v) in enumerate(zip(plog, pvals)): @@ -472,7 +475,7 @@ def _element_constructor_(self, x, check=False): def _discrete_log_pgroup(p, vals, aa, b): r""" Attempt to express an element of p-power order in terms of - generators of a p-subgroup of this group. + generators of a nontrivial p-subgroup of this group. Used as a subroutine in :meth:`discrete_log`. @@ -491,6 +494,18 @@ def _discrete_log_pgroup(p, vals, aa, b): sage: from sage.groups.additive_abelian.additive_abelian_wrapper import _discrete_log_pgroup sage: _discrete_log_pgroup(5, [1,2,4,4], gs, a + 17*b + 123*c + 456*d) (1, 17, 123, 456) + + TESTS: + + Check for :trac:`34716`:: + + sage: E = EllipticCurve(GF(487^2), [311,205]) + sage: G = E.abelian_group().torsion_subgroup(42) + sage: G.invariants() + (6, 42) + sage: P, Q = G.torsion_subgroup(6).gens() + sage: G.discrete_log(2*P + 3*Q, [P, Q]) # indirect doctest + (2, 3) """ from itertools import product as iproduct diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 99c468f75dd..af5fa4b8e29 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2200,7 +2200,7 @@ def reduced_word(self): .. TODO:: - Paralellize this function, calculating all summands in the sum + Parallelize this function, calculating all summands in the sum in parallel. """ M = self._algebra._indices @@ -2245,7 +2245,7 @@ def eps(self, N): .. TODO:: - Paralellize this function, calculating all summands in the sum + Parallelize this function, calculating all summands in the sum in parallel. """ def eps_monom(q_tuple): diff --git a/src/sage/groups/cactus_group.py b/src/sage/groups/cactus_group.py new file mode 100644 index 00000000000..9a481cc783c --- /dev/null +++ b/src/sage/groups/cactus_group.py @@ -0,0 +1,999 @@ +r""" +Cactus Groups + +AUTHORS: + +- Travis Scrimshaw (1-2023): initial version +""" + +# **************************************************************************** +# Copyright (C) 2023 Travis Scrimshaw <tcscrims at gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.element import MultiplicativeGroupElement, parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.richcmp import richcmp +from sage.matrix.constructor import matrix +from sage.categories.groups import Groups +from sage.groups.group import Group +from sage.groups.kernel_subgroup import KernelSubgroup +from sage.combinat.permutation import Permutations +from sage.sets.family import Family +from sage.graphs.graph import Graph +from itertools import combinations + + +class CactusGroup(UniqueRepresentation, Group): + r""" + The cactus group. + + The `n`-fruit cactus group `J_n` is the group generated by `s_{pq}` + for `1 \leq p < q \leq n` with relations: + + - `s_{pq}^2 = 1` + - `s_{pq} s_{kl} = s_{kl} s_{pq}` if the intervals `[p, q]` and `[k, l]` + are disjoint, and + - `s_{pq} s_{kl} = s_{p+q-l,p+q-k} s_{pq}` if `[k, l] \subseteq [p, q]`. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES: + + We construct the cactus group `J_3` and do some basic computations:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.group_generators() + sage: s12 * s13 + s[1,2]*s[1,3] + sage: x = s12 * s23; x + s[1,2]*s[2,3] + sage: x^4 + s[1,2]*s[2,3]*s[1,2]*s[2,3]*s[1,2]*s[2,3]*s[1,2]*s[2,3] + sage: s12 * s13 == s13 * s23 + True + + We verify the key equality in Lemma 2.3 in [White2015]_, which shows + that `J_5` is generated by `s_{1q}`:: + + sage: J5 = groups.misc.Cactus(5) + sage: gens = J5.group_generators() + sage: all(gens[(p,q)] == gens[(1,q)] * gens[(1,q-p+1)] * gens[(1,q)] + ....: for p in range(1, 6) for q in range(p+1, 6)) + True + """ + def __init__(self, n): + r""" + Initialize ``self``. + + TESTS:: + + sage: J3 = groups.misc.Cactus(3) + sage: it = iter(J3) + sage: elts = [next(it) for _ in range(100)] + sage: TestSuite(J3).run(elements=elts[::7], skip="_test_enumerated_set_contains") + + We run this test separately because the words grow very long, very + quickly. This means the code needs to check a lot of normalizations, + so we limit the number of tests:: + + sage: J3._test_enumerated_set_contains(max_runs=500) + + :: + + sage: J4 = groups.misc.Cactus(4) + sage: it = iter(J4) + sage: elts = [next(it) for _ in range(100)] + sage: TestSuite(J4).run(elements=elts[::8]) + """ + self._n = n + ell = len(str(n)) + names = ['s{}{}'.format('0' * (ell - len(str(i))) + str(i), + '0' * (ell - len(str(j))) + str(j)) + for i in range(1, self._n + 1) + for j in range(i + 1, self._n + 1)] + cat = Groups().FinitelyGeneratedAsMagma() + if n > 2: + cat = cat.Infinite() + else: + cat = cat.Finite() + Group.__init__(self, category=cat) + # Group.__init__ doesn't take a "names" parameter + self._assign_names(names) + + @lazy_attribute + def _WG(self): + r""" + Get the graph for the right-angled Coxeter group that ``self`` + embeds into (set theoretically) and set the ``_subsets`` and + ``_subsets_inv`` attributes. + + We do this initialization lazily in order to make the creation of + the parent quick. However, this is used to normalize the product + of elements. + + EXAMPLES:: + + sage: J4 = groups.misc.Cactus(4) + sage: J4._WG + Graph on 11 vertices + sage: J4._subsets + [frozenset({1, 2}), frozenset({1, 3}), frozenset({1, 4}), frozenset({2, 3}), + frozenset({2, 4}), frozenset({3, 4}), frozenset({1, 2, 3}), frozenset({1, 2, 4}), + frozenset({1, 3, 4}), frozenset({2, 3, 4}), frozenset({1, 2, 3, 4})] + """ + n = self._n + I = list(range(1, n+1)) + PS = sum(([frozenset(A) for A in combinations(I, k)] for k in range(2,n+1)), []) + G = Graph([list(range(len(PS))), + [[i,j,-1] for j in range(1, len(PS)) for i in range(j) + if PS[i] & PS[j] not in [frozenset(), PS[i], PS[j]]] + ], format="vertices_and_edges") + self._subsets = PS + self._subsets_inv = {X: i for i,X in enumerate(PS)} + return G + + def right_angled_coxeter_group(self): + r""" + Return the right-angled Coxeter group that ``self`` + (set-theoretically) embeds into. + + This is defined following [Most2019]_, where it was called the + diagram group. It has generators (of order `2`) indexed by subsets + of `\{1, \ldots, n\}` that commute if and only if one subset is + contained in the other or they are disjoint. For the pure cactus + group, this is also a group homomorphism, otherwise it is a + group 1-cocycle [BCL2022]_. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.right_angled_coxeter_group() + Coxeter group over Rational Field with Coxeter matrix: + [ 1 -1 -1 2] + [-1 1 -1 2] + [-1 -1 1 2] + [ 2 2 2 1] + """ + from sage.rings.rational_field import QQ + from sage.combinat.root_system.coxeter_group import CoxeterGroup + from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix + return CoxeterGroup(CoxeterMatrix(self._WG), base_ring=QQ) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: groups.misc.Cactus(3) + Cactus Group with 3 fruit + """ + return "Cactus Group with {} fruit".format(self._n) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: latex(J3) + J_{3} + """ + return "J_{{{}}}".format(self._n) + + def n(self): + """ + Return the value `n`. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.n() + 3 + """ + return self._n + + @cached_method + def group_generators(self): + """ + Return the group generators of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.group_generators() + Finite family {(1, 2): s[1,2], (1, 3): s[1,3], (2, 3): s[2,3]} + """ + l = [(i, j) for i in range(1, self._n + 1) + for j in range(i + 1, self._n + 1)] + return Family(l, lambda x: self.element_class(self, [x])) + + @cached_method + def gens(self): + """ + Return the generators of ``self`` as a tuple. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.gens() + (s[1,2], s[1,3], s[2,3]) + """ + return tuple(self.group_generators()) + + def gen(self, i, j=None): + r""" + Return the `i`-th generator of ``self`` or the generator `s_{ij}`. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.gen(1) + s[1,3] + sage: J3.gen(1,2) + s[1,2] + sage: J3.gen(0,2) + Traceback (most recent call last): + ... + ValueError: s[0,2] is not a valid generator + sage: J3.gen(1,4) + Traceback (most recent call last): + ... + ValueError: s[1,4] is not a valid generator + sage: J3.gen(2,1) + Traceback (most recent call last): + ... + ValueError: s[2,1] is not a valid generator + """ + if j is None: + return self.gens()[i] + if not (1 <= i < j <= self._n): + raise ValueError(f"s[{i},{j}] is not a valid generator") + return self.element_class(self, [(i,j)]) + + @cached_method + def one(self): + r""" + Return the identity element in ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.one() + 1 + """ + return self.element_class(self, []) + + def _coerce_map_from_(self, G): + r""" + Return if there is a coerce map from ``G``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J5 = groups.misc.Cactus(5) + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ5 = groups.misc.PureCactus(5) + + sage: J3._coerce_map_from_(J5) + False + sage: J5._coerce_map_from_(J3) + True + sage: J3._coerce_map_from_(PJ3) + True + sage: J3._coerce_map_from_(PJ5) + False + sage: J5._coerce_map_from_(PJ3) + True + sage: J5._coerce_map_from_(PJ5) + True + """ + if isinstance(G, CactusGroup): + return G._n <= self._n + elif isinstance(G, PureCactusGroup): + return G.n() <= self._n + return super()._coerce_map_from_(G) + + def _element_constructor_(self, x): + r""" + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J5 = groups.misc.Cactus(5) + sage: PJ3 = groups.misc.PureCactus(3) + + sage: J5(J3.an_element()) + s[1,2]*s[2,3]*s[1,3] + sage: J3(J5.an_element()) + s[1,2]*s[2,3]*s[1,3] + sage: it = iter(PJ3) + sage: [J3(next(it)) for _ in range(3)] + [1, s[1,2]*s[2,3]*s[1,2]*s[1,3], s[2,3]*s[1,2]*s[2,3]*s[1,3]] + + sage: J3([[1,2], [1,3], [2,3]]) + s[1,3] + """ + if parent(x) is self: + return x + if isinstance(x, PureCactusGroup.Element): + x = x.value + if isinstance(x, CactusGroup.Element): + if any(d[1] > self._n for d in x._data): + raise ValueError(f"{x} is not an element of {self}") + return self.element_class(self, x._data) + ret = self.element_class(self, x) + ret._normalize() + return ret + + def _an_element_(self): + r""" + Return an element of ``self``. + + TESTS:: + + sage: J1 = groups.misc.Cactus(1) + sage: J1._an_element_() + 1 + sage: J2 = groups.misc.Cactus(2) + sage: J2._an_element_() + s[1,2] + sage: J3 = groups.misc.Cactus(3) + sage: x = J3._an_element_(); x + s[1,2]*s[2,3]*s[1,3] + + sage: x._normalize() + sage: x + s[1,2]*s[2,3]*s[1,3] + """ + if self._n <= 1: + return self.one() + if self._n == 2: + return self.element_class(self, [(1, 2)]) + return self.element_class(self, [(1, 2), (2, 3), (1, 3)]) + + def random_element(self, max_length=10): + r""" + Return a random element of ``self`` of length at most ``max_length``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.random_element() # random + s[1,2]*s[2,3]*s[1,2]*s[1,3] + """ + from sage.misc.prandom import randint + l = randint(0, max_length) + gens = list(self.group_generators()) + ret = self.one() + for _ in range(l): + ret *= gens[randint(0, len(gens) - 1)] + return ret + + def bilinear_form(self, t=None): + r""" + Return the ``t``-bilinear form of ``self``. + + We define a bilinear form `B` on the group algebra by + + .. MATH:: + + B(s_{ij}, s_{pq}) = \begin{cases} + 1 & \text{if } i = p, j = q, \\ + -t & \text{if } [i, j] \not\subseteq [p, q] \text{ and } + [p, q] \not\subseteq [i, j], \\ + 0 & \text{otherwise}. + \end{cases} + + In other words, it is `1` if `s_{ij} = s_{pq}`, `-t` if `s_{ij}` + and `s_{pq}` generate a free group, and `0` otherwise (they commute + or almost commute). + + INPUT: + + - ``t`` -- (default: `t` in `\ZZ[t]`) the variable `t` + + EXAMPLES:: + + sage: J = groups.misc.Cactus(4) + sage: B = J.bilinear_form() + sage: B + [ 1 0 0 -t -t 0] + [ 0 1 0 0 -t -t] + [ 0 0 1 0 0 0] + [-t 0 0 1 0 -t] + [-t -t 0 0 1 0] + [ 0 -t 0 -t 0 1] + + We reorder the generators so the bilinear form is more + "Coxeter-like". In particular, when we remove the generator + `s_{1,4}`, we recover the bilinear form in Example 6.2.5 + of [DJS2003]_:: + + sage: J.gens() + (s[1,2], s[1,3], s[1,4], s[2,3], s[2,4], s[3,4]) + sage: S = SymmetricGroup(6) + sage: g = S([1,4,6,2,5,3]) + sage: B.permute_rows_and_columns(g, g) + sage: B + [ 1 -t 0 0 -t 0] + [-t 1 -t 0 0 0] + [ 0 -t 1 -t 0 0] + [ 0 0 -t 1 -t 0] + [-t 0 0 -t 1 0] + [ 0 0 0 0 0 1] + """ + if t is None: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.integer_ring import ZZ + t = PolynomialRing(ZZ, 't').gen() + R = t.parent() + ret = [] + K = self.group_generators().keys() + for x in K: + for y in K: + if x is y: + ret.append(R.one()) + elif (x[1] < y[0] or x[0] > y[1] or # Disjoint + (x[0] <= y[0] and y[1] <= x[1]) or # y <= x + (y[0] <= x[0] and x[1] <= y[1])): # x <= y + ret.append(R.zero()) + else: + ret.append(-t) + return matrix(R, len(K), len(K), ret) + + def geometric_representation_generators(self, t=None): + r""" + Return the matrices corresponding to the generators of ``self``. + + We construct a representation over `R = \ZZ[t]` of `J_n` as follows. + Let `E` be the vector space over `R` spanned by `\{\epsilon_v\}_v`, + where `v` is a generator of `J_n`. Fix some generator `v`, and + let `E_v` denote the span of `\epsilon_u - \epsilon_{u'}`, + where `u'` is the reflected interval of `u` in `v`, over all + `u` such that `u \subset v`. Let `F_v` denote the orthogonal + complement of `R \epsilon_v \oplus E_v` with respect to the + :meth:`bilinear form <bilinear_form>` `B`. We define the action + of `v` on `E` by + + .. MATH:: + + \rho(v) = -I |_{R\epsilon_v \oplus E_v} \oplus I |_{F_v}. + + By Theorem 6.2.3 of [DJS2003]_, this defines a representation of `J_n`. + It is expected that this is a faithful representation (see + Remark 6.2.4 of [DJS2003]_). As this arises from a blow-up and an + analog of the geometric representation of the corresponding + Coxeter group (the symmetric group), we call this the + *geometric representation*. + + INPUT: + + - ``t`` -- (default: `t` in `\ZZ[t]`) the variable `t` + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: list(J3.geometric_representation_generators()) + [ + [ -1 0 2*t] [ 0 0 1] [ 1 0 0] + [ 0 1 0] [ 0 -1 0] [ 0 1 0] + [ 0 0 1], [ 1 0 0], [2*t 0 -1] + ] + + We ran the following code with ``max_tests = 15000`` and did + not find a counterexample to the faithfulness of this representation:: + + sage: visited = set([J3.one()]) + sage: cur = set([(J3.one(), J3.one().to_matrix())]) + sage: mats = set([J3.one().to_matrix()]) + sage: RG = list(J3.geometric_representation_generators()) + sage: count = 0 + sage: max_tests = 1000 + sage: while cur: + ....: count += 1 + ....: if count >= max_tests: + ....: break + ....: elt, mat = cur.pop() + ....: for g,r in zip(J3, RG): + ....: val = elt * g + ....: if val in visited: + ....: continue + ....: visited.add(val) + ....: matp = mat * r + ....: matp.set_immutable() + ....: assert matp not in mats, f"not injective {val} \n{matp}" + ....: mats.add(matp) + ....: cur.add((val, matp)) + """ + B = self.bilinear_form(t) + F = B.base_ring().fraction_field() + K = self.group_generators().keys() + from sage.modules.free_module import FreeModule + V = FreeModule(F, len(K)) + basis = V.basis() + ret = {} + for ik, k in enumerate(K): + E = [basis[ik]] + ME = matrix(E) + # The only non-trivial elements are those reflected by the interval k + for low in range(k[0], k[1] + 1): + for high in range(low + 1, k[1] + 1): + v = (low, high) + vp = (k[0] + k[1] - high, k[0] + k[1] - low) + if v == vp: + continue + elt = basis[K.index(v)] - basis[K.index(vp)] + if elt not in ME.row_space(): + E.append(elt) + ME = ME.stack(elt) + # Get the orthogonal complement wrt to the bilinear form B + Fv = (ME * B).right_kernel().basis_matrix() + T = ME.stack(Fv).transpose() + rho = matrix.diagonal(F, [-1] * len(E) + [1] * (len(K) - len(E))) + ret[k] = T * rho * ~T + return Family(K, lambda k: ret[k]) + + class Element(MultiplicativeGroupElement): + """ + An element of a cactus group. + """ + def __init__(self, parent, data): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: elt = J3.an_element() + sage: TestSuite(elt).run() + """ + self._data = tuple(data) + MultiplicativeGroupElement.__init__(self, parent) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: J3.one() + 1 + sage: J3.an_element() + s[1,2]*s[2,3]*s[1,3] + """ + if not self._data: + return '1' + return '*'.join(f"s[{i},{j}]" for i,j in self._data) + + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: latex(J3.one()) + 1 + sage: latex(J3.an_element()) + s_{1,2} s_{2,3} s_{1,3} + """ + if not self._data: + return '1' + return " ".join(f"s_{{{i},{j}}}" for i,j in self._data) + + def _unicode_art_(self): + """ + Return unicode art of ``self``. + + EXAMPLES:: + + sage: J4 = groups.misc.Cactus(4) + sage: s12,s13,s14,s23,s24,s34 = J4.gens() + sage: unicode_art(s12 * s23 * s13) + s₁₂ s₂₃ s₁₃ + sage: unicode_art(J4.one()) + 1 + sage: J12 = groups.misc.Cactus(12) + sage: unicode_art(J12.gen(1,3)) + s₁,₃ + sage: unicode_art(J12.gen(3,11)) + s₃,₁₁ + """ + from sage.typeset.unicode_art import unicode_subscript, unicode_art + if not self._data: + return unicode_art('1') + if self.parent()._n < 10: + return unicode_art(' '.join('s{}{}'.format(unicode_subscript(p), unicode_subscript(q)) for p,q in self._data)) + return unicode_art(' '.join('s{},{}'.format(unicode_subscript(p), unicode_subscript(q)) for p,q in self._data)) + + def __hash__(self): + r""" + Return the hash of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: elt = J3.gen(1,2) * J3.gen(2,3) * J3.gen(1,3) + sage: hash(elt) == hash((((1,2), (2,3), (1,3)))) + True + """ + return hash(self._data) + + def _richcmp_(self, other, op): + r""" + Compare ``self`` and ``other``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: elt = J3.an_element() + sage: elt == J3.one() + False + sage: elt != J3.one() + True + sage: s12,s13,s23 = J3.gens() + sage: elt == s12*s23*s13 + True + """ + return richcmp(self._data, other._data, op) + + def _mul_(self, other): + r""" + Return the product of ``self`` and ``other``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: s12*s23 + s[1,2]*s[2,3] + sage: s12*s13 + s[1,2]*s[1,3] + sage: s13*s12 + s[2,3]*s[1,3] + sage: J3.one() * (s13*s12*s13*s12*s23*s13) + s[2,3]*s[1,2]*s[2,3]*s[1,3] + """ + ret = type(self)(self.parent(), self._data + other._data) + ret._normalize() + return ret + + def __invert__(self): + r""" + Return the inverse of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: elt = s12*s23*s13 + sage: ~elt + s[1,2]*s[2,3]*s[1,3] + sage: elt * elt + 1 + """ + ret = type(self)(self.parent(), reversed(self._data)) + ret._normalize() + return ret + + def to_permutation(self): + r""" + Return the image of ``self`` under the canonical projection + to the permutation group. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: s12.to_permutation() + [2, 1, 3] + sage: s23.to_permutation() + [1, 3, 2] + sage: s13.to_permutation() + [3, 2, 1] + sage: elt = s12*s23*s13 + sage: elt.to_permutation() + [1, 3, 2] + + sage: J7 = groups.misc.Cactus(7) + sage: J7.group_generators()[3,6].to_permutation() + [1, 2, 6, 5, 4, 3, 7] + + We check that this respects the multiplication order + of permutations:: + + sage: P3 = Permutations(3) + sage: elt = s12*s23 + sage: elt.to_permutation() == P3(s12) * P3(s23) + True + sage: Permutations.options.mult='r2l' + sage: elt.to_permutation() == P3(s12) * P3(s23) + True + sage: Permutations.options.mult='l2r' + """ + n = self.parent().n() + P = Permutations(n) + ret = P.one() + for x in self._data: + lst = list(range(1, n + 1)) + lst[x[0] - 1:x[1]] = list(reversed(lst[x[0] - 1:x[1]])) + ret *= P(lst) + return ret + + def _matrix_(self): + r""" + Return ``self`` as a matrix. + + The resulting matrix is the :meth:`geometric representation + <sage.groups.cactus_group.CactusGroup.geometric_representation_generators>` + of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: s12.to_matrix() + [ -1 0 2*t] + [ 0 1 0] + [ 0 0 1] + sage: (s12*s13).to_matrix() + [2*t 0 -1] + [ 0 -1 0] + [ 1 0 0] + sage: (s13*s23).to_matrix() + [2*t 0 -1] + [ 0 -1 0] + [ 1 0 0] + sage: (s13*s12).to_matrix() + [ 0 0 1] + [ 0 -1 0] + [ -1 0 2*t] + sage: all(x.to_matrix() * y.to_matrix() == (x*y).to_matrix() + ....: for x in J3.gens() for y in J3.gens()) + True + """ + G = self.parent().geometric_representation_generators() + ret = G[(1,2)].parent().one() + for x in self._data: + ret *= G[x] + ret.set_immutable() + return ret + + to_matrix = _matrix_ + + def _normalize(self): + r""" + Return the word for ``self`` in normalized form. + + ALGORITHM: + + We perform the normalization by using the lexicographically + minimum reduced word for the corresponding right-angled Coxeter + group (RACG) element under the (set-theoretic) embedding + introduced by [Most2019]_. This embedding is a group 1-cocycle + and also realizes the cactus group as a subgroup of `W \rtimes S_n`, + where `W` is the RACG (see also [Yu2022]_). See Section 2 + of [BCL2022]_ for precise statements. + + TESTS:: + + sage: J6 = groups.misc.Cactus(6) + sage: s26 = J6.gen(2,6) + sage: s45 = J6.gen(4,5) + sage: s13 = J6.gen(1,3) + sage: s26 * s45 * s13 == s26 * s45 * s13 # indirect doctest + True + + sage: J4 = groups.misc.Cactus(4) + sage: s12,s13,s14,s23,s24,s34 = J4.gens() + sage: s12 * (s12 * s23) + s[2,3] + sage: (s12 * s23) * s23 + s[1,2] + sage: s23 * s13 * s34 + s[2,3]*s[1,3]*s[3,4] + """ + P = self.parent() + n = P._n + G = P._WG # The defining graph + + # Convert to an element in the right-angled Coxeter group + perm = list(range(1,n+1)) + word = [] + for p,q in self._data: + word.append(P._subsets_inv[frozenset(perm[p-1:q])]) + perm[p-1:q] = reversed(perm[p-1:q]) + + # Normalize the word + # This code works for any right-angled Coxeter group + pos = 0 + supp = sorted(set(word)) + while pos < len(word) - 1: + cur = word[pos] + for i in supp: + if i > cur: + break + if G.has_edge(cur, i): + continue + did_swap = False + for j in range(pos+1, len(word)): + if word[j] == i: + word.pop(j) + if cur == i: # canceling s_i s_i = 1 + word.pop(pos) + pos = -1 # start again at the beginning + break + word.insert(pos, i) + did_swap = True + break + if G.has_edge(i, word[j]): + break + if did_swap: + break + pos += 1 + + # Convert back + ret = [] + perm = list(range(1,n+1)) + for i in word: + X = P._subsets[i] + pos = [j for j,val in enumerate(perm) if val in X] + for j in range(len(pos)//2): + perm[pos[j]], perm[pos[-j-1]] = perm[pos[-j-1]], perm[pos[j]] + pos.sort() + assert all(pos[k] + 1 == pos[k+1] for k in range(len(pos)-1)) + ret.append((pos[0]+1, pos[-1]+1)) + + self._data = tuple(ret) + + +class PureCactusGroup(KernelSubgroup): + r""" + The pure cactus group. + + The *pure cactus group* `PJ_n` is the kernel of the natural + surjection of the cactus group `J_n` onto the symmetric group + `S_n`. In particular, we have the following (non-split) exact sequence: + + .. MATH:: + + 1 \longrightarrow PJ_n \longrightarrow J_n \longrightarrow S_n + \longrightarrow 1. + """ + def __init__(self, n): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: it = iter(PJ3) + sage: elts = [next(it) for _ in range(10)] + sage: TestSuite(PJ3).run(elements=elts) + """ + J = CactusGroup(n) + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + S = SymmetricGroup(n) + KernelSubgroup.__init__(self, S.coerce_map_from(J)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: groups.misc.PureCactus(3) + Pure Cactus Group with 3 fruit + """ + return "Pure Cactus Group with {} fruit".format(self.n()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: latex(PJ3) + PJ_{3} + """ + return "PJ_{{{}}}".format(self.n()) + + @cached_method + def n(self): + """ + Return the value `n`. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.n() + 3 + """ + return self.ambient().n() + + def gen(self, i): + r""" + Return the ``i``-th generator of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.gen(0) + s[2,3]*s[1,2]*s[2,3]*s[1,3] + sage: PJ3.gen(1) + s[1,2]*s[2,3]*s[1,2]*s[1,3] + sage: PJ3.gen(5) + Traceback (most recent call last): + ... + IndexError: tuple index out of range + """ + return self.gens()[i] + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + ALGORITHM: + + We use :wikipedia:`Schreier's_lemma` and compute the traversal + using the lex minimum elements (defined by the order of the + generators of the ambient cactus group). + + EXAMPLES: + + We verify Corollary A.2 of [BCL2022]_:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.gens() + (s[2,3]*s[1,2]*s[2,3]*s[1,3], s[1,2]*s[2,3]*s[1,2]*s[1,3]) + sage: a, b = PJ3.gens() + sage: a * b # they are inverses of each other + 1 + + sage: J3 = groups.misc.Cactus(3) + sage: gen = (J3.gen(1,2)*J3.gen(1,3))^3 + sage: gen + s[1,2]*s[2,3]*s[1,2]*s[1,3] + sage: gen == b + True + """ + from sage.functions.other import factorial + J = self.ambient() + G = J.gens() + one = J.one() + n = self.n() + nfac = factorial(n) + reprs = {one.to_permutation(): one} + next_level = [one] + while len(reprs) < nfac: + cur = next_level + next_level = [] + for val in cur: + for g in G: + temp = val * g + p = temp.to_permutation() + if p in reprs: + continue + reprs[p] = temp + next_level.append(temp) + gens = [] + for s in reprs.values(): + for g in G: + val = s * g * ~(reprs[(s*g).to_permutation()]) + if val == one or val in gens: + continue + gens.append(val) + return tuple([self(g) for g in gens]) diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index 90f8e07ba2c..a9465ef2b3d 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -527,13 +527,13 @@ def find_root(domain): min_pol_root_bur = min_pol_root_bur.change_ring(domain) if not min_pol_root_bur(root_bur).is_zero(): - raise ValueError('root_bur must vanish on %s' %(min_pol_root_bur)) + raise ValueError('root_bur must vanish on %s' % (min_pol_root_bur)) - def conv2domain (laur_pol): + def conv2domain(laur_pol): l1, l2 = laur_pol.polynomial_construction() p1 = l1.change_ring(domain) p2 = root_bur**(l2) - res = p1(root_bur)*p2 + res = p1(root_bur) * p2 return res from sage.matrix.constructor import matrix @@ -2028,19 +2028,17 @@ def cubic_braid_subgroup(self, nstrands=None): True """ if nstrands is None: - nstrands = self.strands() -1 + nstrands = self.strands() - 1 n = self.strands() nstrands = Integer(nstrands) if nstrands >= n or nstrands <= 0: - raise ValueError("nstrands must be positive and less than %s" %(self.strands())) - + raise ValueError("nstrands must be positive and less than %s" % (self.strands())) names = self.variable_names() - names_red = names[:nstrands-1] + names_red = names[:nstrands - 1] subgrp = CubicBraidGroup(names=names_red, cbg_type=self._cbg_type) subgrp._ambient = self return subgrp - diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index d953022d3da..8d6e443683c 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -146,9 +146,9 @@ class GroupMorphismWithGensImages(SetMorphism): Class used for morphisms from finitely presented groups to other groups. It just adds the images of the generators at the end of the representation. - + EXAMPLES:: - + sage: F = FreeGroup(3) sage: G = F / [F([1, 2, 3, 1, 2, 3]), F([1, 1, 1])] sage: H = AlternatingGroup(3) @@ -166,9 +166,9 @@ class GroupMorphismWithGensImages(SetMorphism): def _repr_defn(self): r""" Return the part of the representation that includes the images of the generators. - + EXAMPLES:: - + sage: F = FreeGroup(3) sage: G = F / [F([1,2,3,1,2,3]),F([1,1,1])] sage: H = AlternatingGroup(3) @@ -1427,17 +1427,17 @@ def simplified(self): Finitely presented group < e0 | e0^2 > """ return self.simplification_isomorphism().codomain() - + def epimorphisms(self, H): r""" Return the epimorphisms from `self` to `H`, up to automorphism of `H`. - + INPUT: - + - `H` -- Another group - + EXAMPLES:: - + sage: F = FreeGroup(3) sage: G = F / [F([1, 2, 3, 1, 2, 3]), F([1, 1, 1])] sage: H = AlternatingGroup(3) @@ -1468,7 +1468,7 @@ def epimorphisms(self, H): x2 |--> (1,2,3)] ALGORITHM: - + Uses libgap's GQuotients function. """ from sage.misc.misc_c import prod diff --git a/src/sage/groups/finitely_presented_catalog.py b/src/sage/groups/finitely_presented_catalog.py index 06d80ab7c0c..512e851628b 100644 --- a/src/sage/groups/finitely_presented_catalog.py +++ b/src/sage/groups/finitely_presented_catalog.py @@ -23,3 +23,4 @@ from .finitely_presented_named import QuaternionPresentation as Quaternion from .finitely_presented_named import AlternatingPresentation as Alternating from .finitely_presented_named import BinaryDihedralPresentation as BinaryDihedral +from .finitely_presented_named import CactusPresentation as Cactus diff --git a/src/sage/groups/finitely_presented_named.py b/src/sage/groups/finitely_presented_named.py index f1a6b223bfb..248049fadb7 100644 --- a/src/sage/groups/finitely_presented_named.py +++ b/src/sage/groups/finitely_presented_named.py @@ -12,6 +12,9 @@ - Alternating group, `A_n` of order `n!/2` -- :func:`groups.presentation.Alternating <sage.groups.finitely_presented_named.AlternatingPresentation>` +- the `n`-fruit Cactus group, a standard notation for which is `J_n` -- + :func:`groups.presentation.Cactus <sage.groups.finitely_presented_named.CactusPresentation>` + - Cyclic group, `C_n` of order `n` -- :func:`groups.presentation.Cyclic <sage.groups.finitely_presented_named.CyclicPresentation>` @@ -214,7 +217,7 @@ def FinitelyGeneratedHeisenbergPresentation(n=1, p=0): - ``p`` -- (optional) a prime number, where we construct the Heisenberg group over the finite field `\ZZ/p\ZZ` - + OUTPUT: Finitely generated Heisenberg group over the finite field @@ -249,7 +252,7 @@ def FinitelyGeneratedHeisenbergPresentation(n=1, p=0): [0 0 1] sage: p = 3 sage: Hp = groups.presentation.Heisenberg(p=3) - sage: Hp.order() == p**3 + sage: Hp.order() == p**3 True sage: Hnp = groups.presentation.Heisenberg(n=2, p=3) sage: len(Hnp.relations()) @@ -560,3 +563,35 @@ def BinaryDihedralPresentation(n): x,y,z = F.gens() 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. + + OUTPUT: + + Cactus group `J_n` as a finitely presented group. + + EXAMPLES:: + + sage: J3 = groups.presentation.Cactus(3); J3 + Finitely presented group < s12, s13, s23 | + s12^2, s13^2, s23^2, s13*s12*s13^-1*s23^-1, s13*s23*s13^-1*s12^-1 > + """ + from sage.groups.cactus_group import CactusGroup + G = CactusGroup(n) + F = FreeGroup(G.variable_names()) + gens = F.gens() + rls = [g**2 for g in gens] + Gg = G.group_generators() + K = Gg.keys() + for i,key in enumerate(K): + for j,key2 in enumerate(K): + if i == j: + continue + x,y = (Gg[key] * Gg[key2])._data + if key == x and key2 == y: + continue + elt = gens[i] * gens[j] * ~gens[K.index(y)] * ~gens[K.index(x)] + rls.append(elt) + return FinitelyPresentedGroup(F, tuple(rls)) diff --git a/src/sage/groups/fqf_orthogonal.py b/src/sage/groups/fqf_orthogonal.py index 75de4089120..7fc4dbe7548 100644 --- a/src/sage/groups/fqf_orthogonal.py +++ b/src/sage/groups/fqf_orthogonal.py @@ -245,10 +245,10 @@ def _element_constructor_(self, x, check=True): sage: all(x*f==x*fbar for x in q.gens()) True sage: L = IntegralLattice("A2").twist(3) - sage: q = L.discriminant_group() sage: OL = L.orthogonal_group() + sage: assert OL(OL.0.matrix()) == OL.0 + sage: q = L.discriminant_group() sage: Oq = q.orthogonal_group() - sage: assert Oq(OL.0) == Oq(OL.0.matrix()) sage: assert Oq(Oq.0.matrix()) == Oq.0 """ from sage.libs.gap.element import GapElement diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index 15aee2f0a20..d5823ebf9e0 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -130,7 +130,7 @@ def multiple(a, n, operation='*', identity=None, inverse=None, op=None): a Python object on which a group operation such as addition or multiplication is defined. Uses the standard binary algorithm. - INPUT: See the documentation for ``discrete_logarithm()``. + INPUT: See the documentation for ``discrete_logarithm()``. EXAMPLES:: @@ -691,11 +691,13 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i arguments are provided automatically; otherwise they must be provided by the caller. - OUTPUT: Returns an integer `n` such that `b^n = a` (or `nb = a`), + OUTPUT: + + This returns an integer `n` such that `b^n = a` (or `nb = a`), assuming that ``ord`` is a multiple of the order of the base `b`. If ``ord`` is not specified, an attempt is made to compute it. - If no such `n` exists, this function raises a ValueError exception. + If no such `n` exists, this function raises a ``ValueError`` exception. .. warning:: diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index 1bdd1a46346..47e6e230508 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -5,7 +5,7 @@ Using tab-completion on this object is an easy way to discover and quickly create the groups that are available (as listed here). -Let ``<tab>`` indicate pressing the tab key. So begin by typing +Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing ``groups.<tab>`` to the see primary divisions, followed by (for example) ``groups.matrix.<tab>`` to access various groups implemented as sets of matrices. @@ -43,6 +43,7 @@ - Finitely Presented Groups (``groups.presentation.<tab>``) - :func:`groups.presentation.Alternating <sage.groups.finitely_presented_named.AlternatingPresentation>` + - :func:`groups.presentation.Cactus <sage.groups.finitely_presented_named.CactusPresentation>` - :func:`groups.presentation.Cyclic <sage.groups.finitely_presented_named.CyclicPresentation>` - :func:`groups.presentation.Dihedral <sage.groups.finitely_presented_named.DihedralPresentation>` - :func:`groups.presentation.DiCyclic <sage.groups.finitely_presented_named.DiCyclicPresentation>` @@ -65,7 +66,9 @@ - Coxeter, reflection and related groups - :func:`groups.misc.Braid <sage.groups.braid.BraidGroup>` + - :func:`groups.misc.Cactus <sage.groups.cactus_group.CactusGroup>` - :func:`groups.misc.CoxeterGroup <sage.combinat.root_system.coxeter_group.CoxeterGroup>` + - :func:`groups.misc.PureCactus <sage.groups.cactus_group.PureCactusGroup>` - :func:`groups.misc.ReflectionGroup <sage.combinat.root_system.reflection_group_real.ReflectionGroup>` - :class:`groups.misc.RightAngledArtin <sage.groups.raag.RightAngledArtinGroup>` - :func:`groups.misc.WeylGroup <sage.combinat.root_system.weyl_group.WeylGroup>` diff --git a/src/sage/groups/kernel_subgroup.py b/src/sage/groups/kernel_subgroup.py new file mode 100644 index 00000000000..024e8f47b3b --- /dev/null +++ b/src/sage/groups/kernel_subgroup.py @@ -0,0 +1,225 @@ +r""" +Kernel Subgroups + +The kernel of a homomorphism implemented as a subgroup. + +AUTHORS: + +- Travis Scrimshaw (1-2023): Initial version +""" + +# **************************************************************************** +# Copyright (C) 2023 Travis Scrimshaw <tcscrims at gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.categories.groups import Groups +from sage.structure.element_wrapper import ElementWrapper +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation + +class KernelSubgroup(UniqueRepresentation, Parent): + r""" + The kernel (normal) subgroup. + + Let `\phi : G \to H` be a group homomorphism. The kernel + `K = \{\phi(g) = 1 | g \in G\}` is a normal subgroup of `G`. + """ + def __init__(self, morphism): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: S2 = SymmetricGroup(2) + sage: S3 = SymmetricGroup(3) + sage: H = Hom(S3, S2) + sage: phi = H(S2.__call__) + sage: from sage.groups.kernel_subgroup import KernelSubgroup + sage: K = KernelSubgroup(phi) + sage: TestSuite(K).run() + """ + self._morphism = morphism + cat = Groups().Subobjects() + base_cat = morphism.domain().category() + if base_cat in Groups().Finite(): + cat = cat.Finite() + elif base_cat in Groups().Enumerated(): + cat = cat.Enumerated() + Parent.__init__(self, category=cat) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: S2 = SymmetricGroup(2) + sage: S3 = SymmetricGroup(3) + sage: H = Hom(S3, S2) + sage: phi = H(S2.__call__) + sage: from sage.groups.kernel_subgroup import KernelSubgroup + sage: KernelSubgroup(phi) + Kernel subgroup defined by Generic morphism: + From: Symmetric group of order 3! as a permutation group + To: Symmetric group of order 2! as a permutation group + """ + return "Kernel subgroup defined by {}".format(self._morphism) + + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: S2 = SymmetricGroup(2) + sage: S3 = SymmetricGroup(3) + sage: H = Hom(S3, S2) + sage: phi = H(S2.__call__) + sage: from sage.groups.kernel_subgroup import KernelSubgroup + sage: K = KernelSubgroup(phi) + sage: K.gens() + ((),) + """ + if self.ambient() in Groups().Finite(): + return tuple(self) + raise NotImplementedError("only implemented for finite groups") + + def defining_morphism(self): + r""" + Return the defining morphism of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.defining_morphism() + Conversion via _from_cactus_group_element map: + From: Cactus Group with 3 fruit + To: Symmetric group of order 3! as a permutation group + """ + return self._morphism + + @cached_method + def ambient(self): + r""" + Return the ambient group of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.ambient() + Cactus Group with 3 fruit + """ + return self._morphism.domain() + + def _an_element_(self): + r""" + Return an element of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.an_element() + 1 + """ + return self.element_class(self, self.ambient().one()) + + def lift(self, x): + r""" + Lift ``x`` to the ambient group of ``self``. + + EXAMPLES:: + + sage: PJ3 = groups.misc.PureCactus(3) + sage: PJ3.lift(PJ3.an_element()).parent() + Cactus Group with 3 fruit + """ + return x.value + + def retract(self, x): + r""" + Convert ``x`` to an element of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.group_generators() + sage: PJ3 = groups.misc.PureCactus(3) + sage: elt = PJ3.retract(s23*s12*s23*s13); elt + s[2,3]*s[1,2]*s[2,3]*s[1,3] + sage: elt.parent() is PJ3 + True + """ + return self._element_constructor_(x) + + def _element_constructor_(self, x): + r""" + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.group_generators() + sage: PJ3 = groups.misc.PureCactus(3) + sage: elt = PJ3(s23*s12*s23*s13) + sage: elt.parent() is PJ3 + True + """ + if self._morphism(x) != self._morphism.codomain().one(): + raise ValueError("{} is not in the kernel of {}".format(x, self._morphism)) + return self.element_class(self, x) + + def __iter__(self): + r""" + Iterate through ``self``. + + EXAMPLES:: + + sage: S2 = SymmetricGroup(2) + sage: S3 = SymmetricGroup(3) + sage: H = Hom(S3, S2) + sage: phi = H(S2.__call__) + sage: from sage.groups.kernel_subgroup import KernelSubgroup + sage: K = KernelSubgroup(phi) + sage: list(K) + [()] + """ + for g in self.ambient(): + try: + yield self(g) + except ValueError: + pass + + class Element(ElementWrapper): + def _mul_(self, other): + r""" + Multiply ``self`` and ``other``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.group_generators() + sage: PJ3 = groups.misc.PureCactus(3) + sage: elt = PJ3(s23*s12*s23*s13) + sage: elt * elt + s[2,3]*s[1,2]*s[2,3]*s[1,2]*s[2,3]*s[1,2] + """ + return type(self)(self.parent(), self.value * other.value) + + def __invert__(self): + r""" + Return the inverse of ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.group_generators() + sage: PJ3 = groups.misc.PureCactus(3) + sage: elt = PJ3(s23*s12*s23*s13) + sage: ~elt + s[1,2]*s[2,3]*s[1,2]*s[1,3] + """ + return type(self)(self.parent(), ~self.value) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index a76afc2ac94..d25121aa792 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -627,7 +627,7 @@ cdef class ElementLibGAP(MultiplicativeGroupElement): True """ P = (<ElementLibGAP> left)._parent - return P.element_class(P, (<ElementLibGAP> left)._libgap * \ + return P.element_class(P, (<ElementLibGAP> left)._libgap * (<ElementLibGAP> right)._libgap) cpdef _richcmp_(left, right, int op): @@ -669,7 +669,7 @@ cdef class ElementLibGAP(MultiplicativeGroupElement): False """ P = (<ElementLibGAP> left)._parent - return P.element_class(P, (<ElementLibGAP> left)._libgap / \ + return P.element_class(P, (<ElementLibGAP> left)._libgap / (<ElementLibGAP> right)._libgap) def __pow__(self, n, dummy): diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx index e1bd8b15248..777b141beb0 100644 --- a/src/sage/groups/matrix_gps/group_element.pyx +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -823,4 +823,3 @@ def _unpickle_generic_element(G, mat): True """ return G.element_class(G, mat, False, False) - diff --git a/src/sage/groups/matrix_gps/isometries.py b/src/sage/groups/matrix_gps/isometries.py index f9111a2c926..cca45e71752 100644 --- a/src/sage/groups/matrix_gps/isometries.py +++ b/src/sage/groups/matrix_gps/isometries.py @@ -11,11 +11,11 @@ sage: L = IntegralLattice("D4") sage: O = L.orthogonal_group() sage: O - Group of isometries with 5 generators ( - [-1 0 0 0] [0 0 0 1] [-1 -1 -1 -1] [ 1 1 0 0] [ 1 0 0 0] - [ 0 -1 0 0] [0 1 0 0] [ 0 0 1 0] [ 0 0 1 0] [-1 -1 -1 -1] - [ 0 0 -1 0] [0 0 1 0] [ 0 1 0 1] [ 0 1 0 1] [ 0 0 1 0] - [ 0 0 0 -1], [1 0 0 0], [ 0 -1 -1 0], [ 0 -1 -1 0], [ 0 0 0 1] + Group of isometries with 3 generators ( + [0 0 0 1] [ 1 1 0 0] [ 1 0 0 0] + [0 1 0 0] [ 0 0 1 0] [-1 -1 -1 -1] + [0 0 1 0] [ 0 1 0 1] [ 0 0 1 0] + [1 0 0 0], [ 0 -1 -1 0], [ 0 0 0 1] ) Basic functionality is provided by GAP:: diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index 40c99099b5f..3715f531913 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -74,9 +74,9 @@ def Sp(n, R, var='a', invariant_form=None): - ``var`` -- (optional, default: ``'a'``) variable used to represent generator of the finite field, if needed - - ``invariant_form`` -- (optional) instances being accepted by + - ``invariant_form`` -- (optional) instances being accepted by the matrix-constructor which define a `n \times n` square matrix - over ``R`` describing the alternating form to be kept invariant + over ``R`` describing the alternating form to be kept invariant by the symplectic group EXAMPLES:: diff --git a/src/sage/groups/misc_gps/argument_groups.py b/src/sage/groups/misc_gps/argument_groups.py index a02305df78e..9fa0fe07974 100644 --- a/src/sage/groups/misc_gps/argument_groups.py +++ b/src/sage/groups/misc_gps/argument_groups.py @@ -31,15 +31,15 @@ Classes and Methods =================== """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2018 Daniel Krenn <dev@danielkrenn.at> # # 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.element import MultiplicativeGroupElement from sage.structure.factory import UniqueFactory @@ -48,6 +48,7 @@ from sage.structure.unique_representation import UniqueRepresentation import sage.rings.abc + class AbstractArgument(MultiplicativeGroupElement): r""" An element of :class:`AbstractArgumentGroup`. This abstract class @@ -488,7 +489,7 @@ def _symbolic_(self, R=None): if R is None: R = SR - return exp(2*R('pi')*R('I') * self.exponent) + return exp(2 * R('pi') * R('I') * self.exponent) def _mul_(self, other): r""" @@ -609,7 +610,7 @@ def is_minus_one(self): False """ from sage.rings.rational_field import QQ - return self.exponent == QQ(1)/QQ(2) + return self.exponent == QQ((1, 2)) class UnitCircleGroup(AbstractArgumentGroup): @@ -750,7 +751,7 @@ def _element_constructor_(self, data, exponent=None, **kwds): exponent = 0 elif data == -1 or data == '-1': - exponent = QQ(1)/QQ(2) + exponent = QQ((1, 2)) else: try: @@ -762,7 +763,7 @@ def _element_constructor_(self, data, exponent=None, **kwds): if data.is_one(): exponent = 0 elif data.is_minus_one(): - exponent = QQ(1)/QQ(2) + exponent = QQ((1, 2)) elif isinstance(P, UnitCircleGroup): exponent = data.exponent @@ -961,11 +962,11 @@ def _repr_(self): from sage.rings.rational_field import QQ if self.exponent == 0: return '1' - if self.exponent == QQ(1)/QQ(2): + if self.exponent == QQ((1, 2)): return '-1' - if self.exponent == QQ(1)/QQ(4): + if self.exponent == QQ((1, 4)): return 'I' - if self.exponent == QQ(3)/QQ(4): + if self.exponent == QQ((3, 4)): return '-I' num = self.exponent_numerator() den = self.exponent_denominator() @@ -1008,8 +1009,7 @@ def __classcall__(cls, category=None): Category of commutative groups """ category = cls._determine_category_(category) - return super(AbstractArgumentGroup, cls).__classcall__( - cls, category) + return super(AbstractArgumentGroup, cls).__classcall__(cls, category) def __init__(self, category): r""" @@ -1143,7 +1143,7 @@ def _symbolic_(self, R=None): if R is None: R = SR - return exp(R('I')*arg(self._element_)) + return exp(R('I') * arg(self._element_)) def _mul_(self, other): r""" @@ -1624,9 +1624,9 @@ def __classcall__(cls, category=None): sage: from sage.groups.misc_gps.argument_groups import SignGroup sage: S = SignGroup() sage: S.category() # indirect doctest - Category of commutative groups + Category of finite commutative groups """ - category = cls._determine_category_(category) + category = cls._determine_category_(category).Finite() return super(AbstractArgumentGroup, cls).__classcall__( cls, category) diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 40d9d66d15d..5396677d0b1 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -23,3 +23,5 @@ from sage.combinat.root_system.weyl_group import WeylGroup from sage.groups.raag import RightAngledArtinGroup as RightAngledArtin from sage.combinat.root_system.reflection_group_real import ReflectionGroup +from sage.groups.cactus_group import CactusGroup as Cactus +from sage.groups.cactus_group import PureCactusGroup as PureCactus diff --git a/src/sage/groups/perm_gps/all.py b/src/sage/groups/perm_gps/all.py index 999f0bbfb2c..83afad7c691 100644 --- a/src/sage/groups/perm_gps/all.py +++ b/src/sage/groups/perm_gps/all.py @@ -1,17 +1,20 @@ from .permgroup_named import (SymmetricGroup, AlternatingGroup, - DihedralGroup, SplitMetacyclicGroup, SemidihedralGroup, CyclicPermutationGroup, - DiCyclicGroup, TransitiveGroup, PGL, PSL, PSp,PSU,PGU, - MathieuGroup, KleinFourGroup, QuaternionGroup, - PrimitiveGroup, PrimitiveGroups, - SuzukiGroup, TransitiveGroups, GeneralDihedralGroup) + DihedralGroup, SplitMetacyclicGroup, + SemidihedralGroup, CyclicPermutationGroup, + DiCyclicGroup, TransitiveGroup, + PGL, PSL, PSp, PSU, PGU, + MathieuGroup, KleinFourGroup, QuaternionGroup, + PrimitiveGroup, PrimitiveGroups, + SuzukiGroup, TransitiveGroups, + GeneralDihedralGroup) -from .permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup, direct_product_permgroups +from .permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup, direct_product_permgroups from .constructor import PermutationGroupElement from .permgroup_morphism import (PermutationGroupMorphism as PermutationGroupMap, - PermutationGroupMorphism_im_gens, - PermutationGroupMorphism_id) + PermutationGroupMorphism_im_gens, + PermutationGroupMorphism_id) PermutationGroupMorphism = PermutationGroupMorphism_im_gens from .cubegroup import CubeGroup, RubiksCube diff --git a/src/sage/groups/perm_gps/constructor.py b/src/sage/groups/perm_gps/constructor.py index 1992679efd3..73dc5f94c41 100644 --- a/src/sage/groups/perm_gps/constructor.py +++ b/src/sage/groups/perm_gps/constructor.py @@ -7,7 +7,7 @@ objects have a more group theoretic flavor than the more combinatorial :class:`~sage.combinat.permutation.Permutation`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # Copyright (C) 2006 David Joyner # Copyright (C) 2019 Vincent Delecroix <20100.delecroix@gmail.com> @@ -16,16 +16,17 @@ # 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 . import permgroup_element from sage.misc.sage_eval import sage_eval from sage.misc.lazy_import import lazy_import from sage.interfaces.gap import GapElement -lazy_import('sage.combinat.permutation', ['Permutation', 'from_cycles']) from sage.libs.pari.all import pari_gen from sage.libs.gap.element import GapElement_Permutation +lazy_import('sage.combinat.permutation', ['Permutation', 'from_cycles']) + def PermutationGroupElement(g, parent=None, check=True): r""" @@ -118,6 +119,7 @@ def PermutationGroupElement(g, parent=None, check=True): return parent.element_class(g, parent, check) + def string_to_tuples(g): """ EXAMPLES:: @@ -136,10 +138,11 @@ def string_to_tuples(g): raise ValueError("g (= %s) must be a string" % g) elif g == '()': return [] - g = g.replace('\n','').replace(' ', '').replace(')(', '),(').replace(')', ',)') + g = g.replace('\n', '').replace(' ', '').replace(')(', '),(').replace(')', ',)') g = '[' + g + ']' return sage_eval(g, preparse=False) + def standardize_generator(g, convert_dict=None, as_cycles=False): r""" Standardize the input for permutation group elements to a list @@ -258,6 +261,6 @@ def standardize_generator(g, convert_dict=None, as_cycles=False): if convert_dict is not None and needs_conversion: g = [tuple([convert_dict[x] for x in cycle]) for cycle in g] if not as_cycles: - degree = max([1] + [max(cycle+(1,)) for cycle in g]) + degree = max([1] + [max(cycle + (1,)) for cycle in g]) g = from_cycles(degree, g) return g diff --git a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx index eb3263d914d..ad30101e161 100644 --- a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +++ b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx @@ -99,15 +99,15 @@ REFERENCE: """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 - 2011 Robert L. Miller <rlmillster@gmail.com> # # 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 libc.string cimport memcmp, memcpy from cysignals.memory cimport sig_malloc, sig_realloc, sig_free @@ -134,24 +134,25 @@ cdef int compare_structures_trivial(int *gamma_1, int *gamma_2, void *S1, void * def test_get_aut_gp_and_can_lab_trivially(int n=6, list partition=[[0,1,2],[3,4],[5]], canonical_label=True, base=False): """ - sage: tttt = sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label.test_get_aut_gp_and_can_lab_trivially - sage: tttt() - 12 - sage: tttt(canonical_label=False, base=False) - 12 - sage: tttt(canonical_label=False, base=True) - 12 - sage: tttt(canonical_label=True, base=True) - 12 - sage: tttt(n=0, partition=[]) - 1 - sage: tttt(n=0, partition=[], canonical_label=False, base=False) - 1 - sage: tttt(n=0, partition=[], canonical_label=False, base=True) - 1 - sage: tttt(n=0, partition=[], canonical_label=True, base=True) - 1 + TESTS:: + sage: tttt = sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label.test_get_aut_gp_and_can_lab_trivially + sage: tttt() + 12 + sage: tttt(canonical_label=False, base=False) + 12 + sage: tttt(canonical_label=False, base=True) + 12 + sage: tttt(canonical_label=True, base=True) + 12 + sage: tttt(n=0, partition=[]) + 1 + sage: tttt(n=0, partition=[], canonical_label=False, base=False) + 1 + sage: tttt(n=0, partition=[], canonical_label=False, base=True) + 1 + sage: tttt(n=0, partition=[], canonical_label=True, base=True) + 1 """ cdef aut_gp_and_can_lab *output cdef Integer I = Integer(0) @@ -894,17 +895,3 @@ cdef aut_gp_and_can_lab *get_aut_gp_and_can_lab(void *S, deallocate_agcl_work_space(work_space) return output - - - - - - - - - - - - - - diff --git a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx index 6daef964ebc..3f726879292 100644 --- a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx +++ b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx @@ -733,8 +733,3 @@ cdef int double_coset(void *S1, void *S2, PartitionStack *partition1, int *order if work_space_prealloc is NULL: deallocate_dc_work_space(work_space) return 1 if (possible and not unknown) else 0 - - - - - diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx index f13fccf891a..e18fdf5f41a 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx @@ -1148,5 +1148,3 @@ def random_tests(num=50, n_max=50, k_max=6, nwords_max=200, perms_per_code=10, d num_codes += 2 print("All passed: %d random tests on %d codes." % (num_tests, num_codes)) - - diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index 4ab08bd6ba7..1acb46201a0 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -7,10 +7,9 @@ EXAMPLES:: REFERENCE: -- [1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium, +- [1] McKay, Brendan D. *Practical Graph Isomorphism*. Congressus Numerantium, Vol. 30 (1981), pp. 45-87. """ - # **************************************************************************** # Copyright (C) 2006 - 2011 Robert L. Miller <rlmillster@gmail.com> # @@ -643,9 +642,8 @@ cdef int compare_graphs(int *gamma_1, int *gamma_2, void *S1, void *S2, int degr r""" Compare gamma_1(S1) and gamma_2(S2). - Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == - gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2). (Just like the python - \code{cmp}) function. + Return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == + gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2). INPUT: 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 815b3bcc89d..a24b0499888 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx @@ -582,6 +582,3 @@ def double_coset_python(S1, S2, partition1, ordering2, n, output_py = False sig_free(output) return output_py - - - diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx index 6e80294db3c..912c9a31a0d 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx @@ -854,9 +854,3 @@ def sets_modulo_perm_group(list generators, int max_size, else: out_list.append(MemoryError()) return out_list - - - - - - diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 0a447179cf8..ebdf7c4c6eb 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -135,15 +135,14 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** - +from __future__ import annotations from functools import wraps from sage.misc.randstate import current_randstate from sage.groups.group import FiniteGroup from sage.rings.all import QQ, Integer -from sage.interfaces.expect import is_ExpectElement -from sage.interfaces.gap import GapElement +from sage.interfaces.abc import ExpectElement, GapElement from sage.libs.gap.libgap import libgap from sage.libs.gap.element import GapElement as LibGapElement from sage.groups.perm_gps.permgroup_element import PermutationGroupElement @@ -385,7 +384,7 @@ def PermutationGroup(gens=None, *args, **kwds): See https://trac.sagemath.org/31510 for details. """ - if not is_ExpectElement(gens) and hasattr(gens, '_permgroup_'): + if not isinstance(gens, ExpectElement) and hasattr(gens, '_permgroup_'): return gens._permgroup_() if gens is not None and not isinstance(gens, (tuple, list, GapElement)): raise TypeError("gens must be a tuple, list, or GapElement") @@ -492,7 +491,7 @@ def __init__(self, gens=None, gap_group=None, canonicalize=True, if isinstance(gap_group, LibGapElement): self._libgap = gap_group - #Handle the case where only the GAP group is specified. + # Handle the case where only the GAP group is specified. if gens is None: gens = [gen for gen in gap_group.GeneratorsOfGroup()] @@ -509,9 +508,9 @@ def __init__(self, gens=None, gap_group=None, canonicalize=True, # Fallback (not ideal: find a better solution?) domain = sorted(domain, key=str) - #Here we need to check if all of the points are integers - #to make the domain contain all integers up to the max. - #This is needed for backward compatibility + # Here we need to check if all of the points are integers + # to make the domain contain all integers up to the max. + # This is needed for backward compatibility if all(isinstance(p, (int, Integer)) for p in domain): domain = list(range(min([1] + domain), max([1] + domain)+1)) @@ -1237,9 +1236,11 @@ def elements(SGS): else: raise ValueError("the input algorithm (='%s') must be 'SGS', 'BFS' or 'DFS'" % algorithm) - def gens(self): + def gens(self) -> tuple: """ - Return tuple of generators of this group. These need not be + Return tuple of generators of this group. + + These need not be minimal, as they are the generators used in defining this group. EXAMPLES:: @@ -1270,14 +1271,14 @@ def gens(self): We make sure that the trivial group gets handled correctly:: sage: SymmetricGroup(1).gens() - [()] + ((),) """ return self._gens - def gens_small(self): """ For this group, returns a generating set which has few elements. + As neither irredundancy nor minimal length is proven, it is fast. EXAMPLES:: @@ -3358,8 +3359,7 @@ def character_table(self): sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3)]]) sage: CT = gap(G).CharacterTable() - Type ``print(gap.eval("Display(%s)"%CT.name()))`` to display this - nicely. + Type ``CT.Display()`` to display this nicely. :: @@ -3374,8 +3374,7 @@ def character_table(self): [ 2 0 0 0 -2] sage: CT = gap(G).CharacterTable() - Again, type ``print(gap.eval("Display(%s)"%CT.name()))`` to display this - nicely. + Again, type ``CT.Display()`` to display this nicely. :: @@ -4666,17 +4665,23 @@ def molien_series(self): sage: G = PermutationGroup([[(2,)]]) sage: G.molien_series() 1/(x^2 - 2*x + 1) - """ - pi = self._libgap_().NaturalCharacter() - # because NaturalCharacter forgets about fixed points: - pi += self._libgap_().TrivialCharacter() * len(self.fixed_points()) - # TODO: pi is a Character from a CharacterTable on self, however libgap - # does not know about this type and when adding two Characters just - # returns a plain List; this needs to be fixed on the libgap side but - # in the meantime we can fix by converting pi back to the right type - pi = libgap.VirtualCharacter(self._libgap_().CharacterTable(), pi) + TESTS: + + Check that :trac:`34854` is fixed:: + + sage: PG = PermutationGroup(["(1,2,3,4,5,6,7)","(5,6,7)"]) + sage: PG.molien_series() + (-x^18 + x^15 - x^12 + x^9 - x^6 + x^3 - 1)/(x^25 - x^24 - x^23 - x^22 + x^21 + 2*x^20 + x^19 - x^17 - x^16 - x^15 - x^13 + x^12 + x^10 + x^9 + x^8 - x^6 - 2*x^5 - x^4 + x^3 + x^2 + x - 1) + and 2 extra fixed points are correctly accounted for:: + + sage: PG1 = PermutationGroup(["(9,2,3,4,5,6,7)","(5,6,7)"]) + sage: R.<x> = QQ[] + sage: PG.molien_series() == PG1.molien_series()*(1-x)^2 + True + """ + pi = self._libgap_().PermutationCharacter(list(self.domain()),libgap.OnPoints) M = pi.MolienSeries() R = QQ['x'] diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 840273a3eea..6582bd16c00 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -88,7 +88,7 @@ We create element of a permutation group of large degree:: (1,30)(2,29)(3,28)(4,27)(5,26)(6,25)(7,24)(8,23)(9,22)(10,21)(11,20)(12,19)(13,18)(14,17)(15,16) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # Copyright (C) 2006 David Joyner # Copyright (C) 2019 Vincent Delecroix <20100.delecroix@gmail.com> @@ -97,8 +97,8 @@ We create element of a permutation group of large degree:: # 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 copy import random @@ -122,8 +122,9 @@ from sage.sets.finite_enumerated_set import FiniteEnumeratedSet import sage.structure.coerce as coerce from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.structure.coerce cimport coercion_model -from sage.interfaces.gap import GapElement as PExpectGapElement -from sage.interfaces.gp import GpElement +from sage.interfaces.abc import GpElement + +import sage.interfaces.abc from sage.libs.gap.libgap import libgap from sage.libs.gap.gap_includes cimport (UInt, UInt2, UInt4, T_PERM2, T_PERM4, @@ -478,7 +479,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): self._set_list_images(g.sage(), convert) else: raise ValueError("invalid data to initialize a permutation") - elif isinstance(g, PExpectGapElement): + elif isinstance(g, sage.interfaces.abc.GapElement): if g.IsPerm(): self._set_list_images(g.ListPerm(), False) else: @@ -944,7 +945,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): sage: S = SymmetricGroup(['a', 'b']) sage: latex(S.gens()) - \left[(\text{\texttt{a}},\text{\texttt{b}})\right] + \left((\text{\texttt{a}},\text{\texttt{b}})\right) """ from sage.misc.latex import latex return "".join(("(" + ",".join(latex(x) for x in cycle) + ")") @@ -1061,11 +1062,10 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): # current behavior where if you pass in an integer which # is not in the domain of the permutation group, then that # integer itself will be returned. - if isinstance(i, (long, int, Integer)): + if isinstance(i, (int, Integer)): return i - - if not isinstance(i,(list,tuple,str)): + if not isinstance(i, (list, tuple, str)): raise ValueError("must be in the domain or a list, tuple or string") permuted = [i[self.perm[j]] for j from 0 <= j < self.n] diff --git a/src/sage/groups/perm_gps/permgroup_morphism.py b/src/sage/groups/perm_gps/permgroup_morphism.py index d190df81614..917ddd01865 100644 --- a/src/sage/groups/perm_gps/permgroup_morphism.py +++ b/src/sage/groups/perm_gps/permgroup_morphism.py @@ -30,24 +30,26 @@ Cyclic group of order 4 as a permutation group """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 David Joyner and William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.categories.morphism import Morphism from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic + class PermutationGroupMorphism(Morphism): """ A set-theoretic map between PermutationGroups. """ def _repr_type(self): """ - Returns the type of this morphism. This is used for printing - the morphism. + Return the type of this morphism. + + This is used for printing the morphism. EXAMPLES:: @@ -60,7 +62,7 @@ def _repr_type(self): def kernel(self): """ - Returns the kernel of this homomorphism as a permutation group. + Return the kernel of this homomorphism as a permutation group. EXAMPLES:: @@ -134,7 +136,7 @@ def image(self, J): def __call__(self, g): """ Some python code for wrapping GAP's Images function but only for - permutation groups. Returns an error if g is not in G. + permutation groups. This returns an error if g is not in G. EXAMPLES:: @@ -148,9 +150,11 @@ def __call__(self, g): """ return self.image(g) + class PermutationGroupMorphism_id(PermutationGroupMorphism): pass + class PermutationGroupMorphism_from_gap(PermutationGroupMorphism): def __init__(self, G, H, gap_hom): """ @@ -201,7 +205,7 @@ def _repr_defn(self): def _gap_(self, gap=None): """ - Returns a GAP version of this morphism. + Return a GAP version of this morphism. EXAMPLES:: @@ -217,7 +221,7 @@ def _gap_(self, gap=None): def __call__(self, g): """ Some python code for wrapping GAP's Images function but only for - permutation groups. Returns an error if g is not in G. + permutation groups. This returns an error if g is not in G. EXAMPLES:: @@ -236,7 +240,7 @@ def __init__(self, G, H, gens=None): """ Some python code for wrapping GAP's GroupHomomorphismByImages function but only for permutation groups. Can be expensive if G is - large. Returns "fail" if gens does not generate self or if the map + large. This returns "fail" if gens does not generate self or if the map does not extend to a group homomorphism, self - other. EXAMPLES:: @@ -270,8 +274,9 @@ def __init__(self, G, H, gens=None): def _repr_defn(self): """ - Returns the definition of this morphism. This is used when - printing the morphism. + Return the definition of this morphism. + + This is used when printing the morphism. EXAMPLES:: @@ -281,11 +286,11 @@ def _repr_defn(self): sage: phi._repr_defn() '[(1,2,3,4)] -> [(1,2,3,4)]' """ - return "%s -> %s"%(list(self.domain().gens()), self._images) + return "%s -> %s" % (list(self.domain().gens()), self._images) def _gap_(self): """ - Returns a GAP representation of this morphism. + Return a GAP representation of this morphism. EXAMPLES:: @@ -298,9 +303,10 @@ def _gap_(self): """ return self.domain()._gap_().GroupHomomorphismByImages(self.codomain(), self.domain().gens(), self._images) -def is_PermutationGroupMorphism(f): + +def is_PermutationGroupMorphism(f) -> bool: """ - Returns True if the argument ``f`` is a PermutationGroupMorphism. + Return True if the argument ``f`` is a PermutationGroupMorphism. EXAMPLES:: diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index d5078f03b40..0381e1912a1 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -272,8 +272,8 @@ def __init__(self, domain=None): gens = [tuple(self._domain)] if len(self._domain) > 2: gens.append(tuple(self._domain[:2])) - self._gens = [self.element_class(g, self, check=False) - for g in gens] + self._gens = tuple([self.element_class(g, self, check=False) + for g in gens]) def _gap_init_(self, gap=None): """ @@ -331,6 +331,42 @@ def _repr_(self): """ return "Symmetric group of order {}! as a permutation group".format(self.degree()) + def _coerce_map_from_(self, G): + """ + Return if there is a coercion map from ``G`` into ``self``. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: S5 = SymmetricGroup(5) + sage: S5.coerce_map_from(J3) + Conversion via _from_cactus_group_element map: + From: Cactus Group with 3 fruit + To: Symmetric group of order 5! as a permutation group + sage: S2 = SymmetricGroup(2) + sage: S2._coerce_map_from_(J3) is None + True + """ + from sage.groups.cactus_group import CactusGroup + if isinstance(G, CactusGroup) and G._n <= self._deg: + return self._from_cactus_group_element + return super()._coerce_map_from_(G) + + def _from_cactus_group_element(self, x): + """ + Return an element of ``self`` from a cactus group element. + + EXAMPLES:: + + sage: J3 = groups.misc.Cactus(3) + sage: s12,s13,s23 = J3.gens() + sage: elt = s12*s23*s13 + sage: S5 = SymmetricGroup(5) + sage: S5._from_cactus_group_element(elt) + (2,3) + """ + return self(x.to_permutation()) + def cartan_type(self): r""" Return the Cartan type of ``self`` diff --git a/src/sage/groups/perm_gps/permutation_groups_catalog.py b/src/sage/groups/perm_gps/permutation_groups_catalog.py index ae3a0ebf33a..52f99920307 100644 --- a/src/sage/groups/perm_gps/permutation_groups_catalog.py +++ b/src/sage/groups/perm_gps/permutation_groups_catalog.py @@ -26,6 +26,6 @@ from .permgroup_named import JankoGroup as Janko from .permgroup_named import SuzukiSporadicGroup as SuzukiSporadic from .permgroup_named import SuzukiGroup as Suzuki -from .permgroup_named import (PGL, PSL, PSp,PSU,PGU,) +from .permgroup_named import (PGL, PSL, PSp, PSU, PGU) from .permgroup_named import TransitiveGroup as Transitive from .cubegroup import CubeGroup as RubiksCube diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index b338d616b70..d6112df67af 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -48,7 +48,7 @@ def _repr_(self): Conjugacy class of cycle type [4] in Symmetric group of order 4! as a permutation group """ - return "Conjugacy class of cycle type %s in %s"%(self._part, self._parent) + return "Conjugacy class of cycle type %s in %s" % (self._part, self._parent) def __eq__(self, other): r""" @@ -167,9 +167,10 @@ def set(self): """ if not self._set: self._set = Set(self._parent.element_class(x, self._parent, check=False) - for x in conjugacy_class_iterator(self._part, self._domain) ) + for x in conjugacy_class_iterator(self._part, self._domain)) return self._set + class PermutationsConjugacyClass(SymmetricGroupConjugacyClassMixin, ConjugacyClass): """ A conjugacy class of the permutations of `n`. @@ -197,7 +198,7 @@ def __init__(self, P, part): part = elt.cycle_type() else: elt = P.element_in_conjugacy_classes(part) - SymmetricGroupConjugacyClassMixin.__init__(self, range(1, P.n+1), part) + SymmetricGroupConjugacyClassMixin.__init__(self, range(1, P.n + 1), part) ConjugacyClass.__init__(self, P, elt) def __iter__(self): @@ -241,11 +242,12 @@ def set(self): """ if not self._set: self._set = Set(from_cycles(self._parent.n, x, self._parent) - for x in conjugacy_class_iterator(self._part, self._domain) ) + for x in conjugacy_class_iterator(self._part, self._domain)) return self._set + ##################################################################### -## Helper functions +# Helper functions def default_representative(part, G): r""" @@ -284,7 +286,7 @@ def default_representative(part, G): total = 0 cycles = [] for p in part: - cycles.append(tuple(D[total:total+p])) + cycles.append(tuple(D[total:total + p])) total += p return G.element_class(cycles, G, check=False) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 59b78c6d757..736430e8af2 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -656,7 +656,7 @@ def __init__(self, R, A): if R not in Fields(): raise NotImplementedError("only implemented with coefficients in a field") self._group = A - + names = tuple(['e' + name[1:] for name in A.variable_names()]) from sage.graphs.independent_sets import IndependentSets from sage.sets.finite_enumerated_set import FiniteEnumeratedSet @@ -857,4 +857,3 @@ class Element(CohomologyRAAGElement): """ An element in the cohomology ring of a right-angled Artin group. """ - diff --git a/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py b/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py index 6119a36d3ed..78c485bfc02 100644 --- a/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py +++ b/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py @@ -51,6 +51,7 @@ sage: TestSuite(S).run() sage: TestSuite(S.an_element()).run() """ +from __future__ import annotations from sage.rings.integer import Integer from sage.groups.group import FiniteGroup @@ -285,7 +286,7 @@ def __contains__(self, item) -> bool: return False return True - def gens(self): + def gens(self) -> tuple: r""" Return a tuple of generators of ``self``. @@ -293,11 +294,11 @@ def gens(self): sage: F.<a> = GF(4) sage: SemimonomialTransformationGroup(F, 3).gens() - [((a, 1, 1); (), Ring endomorphism of Finite Field in a of size 2^2 + (((a, 1, 1); (), Ring endomorphism of Finite Field in a of size 2^2 Defn: a |--> a), ((1, 1, 1); (1,2,3), Ring endomorphism of Finite Field in a of size 2^2 Defn: a |--> a), ((1, 1, 1); (1,2), Ring endomorphism of Finite Field in a of size 2^2 Defn: a |--> a), ((1, 1, 1); (), Ring endomorphism of Finite Field in a of size 2^2 - Defn: a |--> a + 1)] + Defn: a |--> a + 1)) """ from sage.groups.perm_gps.permgroup_named import SymmetricGroup R = self.base_ring() @@ -306,7 +307,7 @@ def gens(self): l.append(self(perm=Permutation(g))) if R.is_field() and not R.is_prime_field(): l.append(self(autom=R.hom([R.primitive_element()**R.characteristic()]))) - return l + return tuple(l) def order(self) -> Integer: r""" diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index a29ccaae1a7..b049b534e0b 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -140,7 +140,7 @@ def __init__(self, matrices, C, D, check=True): d = C.degree_of_differential() if d != D.degree_of_differential(): raise ValueError('degree of differential does not match') - + degrees = list(C.differential()) + list(D.differential()) degrees = sorted(set(degrees)) initial_matrices = dict(matrices) diff --git a/src/sage/homology/chain_homotopy.py b/src/sage/homology/chain_homotopy.py index 16a1c385edc..afeaee2430c 100644 --- a/src/sage/homology/chain_homotopy.py +++ b/src/sage/homology/chain_homotopy.py @@ -269,7 +269,7 @@ def is_homology_gradient_vector_field(self): if matrices[i] * self.domain().differential(i-deg) * matrices[i] != matrices[i]: return False return True - + def in_degree(self, n): """ The matrix representing this chain homotopy in degree ``n``. diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 7d3ea29057e..424086d283e 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -891,4 +891,3 @@ def _maps(self): r = minres(res(std(mod), 0)) return si2sa_resolution(r) - diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index 97a3cb9624b..d9aa9e11119 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -87,6 +87,7 @@ FiniteFreeResolution_free_module, FiniteFreeResolution_singular) + class GradedFiniteFreeResolution(FiniteFreeResolution): r""" Graded finite free resolutions. @@ -573,3 +574,6 @@ def _maps(self): self._res_shifts = res_shifts return res_mats + +from sage.misc.superseded import deprecated_function_alias +GradedFreeResolution = deprecated_function_alias(34873, GradedFiniteFreeResolution_singular) diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index ef68448c685..f372a08e0e8 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -669,7 +669,7 @@ def arrow_art(d): def _add_(self, other): """ Module addition - + EXAMPLES:: sage: F.<x,y> = FreeAlgebra(ZZ) diff --git a/src/sage/homology/simplicial_complex_morphism.py b/src/sage/homology/simplicial_complex_morphism.py index 4673be7d414..889c266dc0a 100644 --- a/src/sage/homology/simplicial_complex_morphism.py +++ b/src/sage/homology/simplicial_complex_morphism.py @@ -3,11 +3,10 @@ The current version is :mod:`sage.topology.simplicial_complex_morphism`. """ - from sage.misc.superseded import deprecated_function_alias import sage.topology.simplicial_complex_morphism -is_SimplicialComplexMorphism = deprecated_function_alias(31925, +is_SimplicialComplexMorphism = deprecated_function_alias(31925, sage.topology.simplicial_complex_morphism.is_SimplicialComplexMorphism) SimplicialComplexMorphism = deprecated_function_alias(31925, sage.topology.simplicial_complex_morphism.SimplicialComplexMorphism) diff --git a/src/sage/homology/simplicial_set_examples.py b/src/sage/homology/simplicial_set_examples.py index 8a19cef5a86..8945e2b51de 100644 --- a/src/sage/homology/simplicial_set_examples.py +++ b/src/sage/homology/simplicial_set_examples.py @@ -4,7 +4,6 @@ The current version is :mod:`sage.topology.simplicial_set_examples`. """ - from sage.misc.superseded import deprecated_function_alias import sage.topology.simplicial_set_examples @@ -22,4 +21,3 @@ 'simplicial_data_from_kenzo_output', 'HopfMap']: exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set_examples.{})'.format(f, f)) - diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index 89c86857d44..89dc727e20e 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -47,9 +47,9 @@ def random_chain_complex(level=1): sage: from sage.homology.tests import random_chain_complex sage: C = random_chain_complex() - sage: C + sage: C # random Chain complex with at most ... nonzero terms over Integer Ring - sage: len(C.nonzero_degrees()) in [1, 2] + sage: len(C.nonzero_degrees()) in [0, 1, 2] True sage: C.degree_of_differential() in [-1, 1] True diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 06d680109a0..bb6dfbcf8f4 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -12,7 +12,7 @@ HTML and Sage code which creates the mathlet:: sage: interacts.calculus.taylor_polynomial() - Interactive function <function taylor_polynomial at ...> with 3 widgets + ...Interactive function <function taylor_polynomial at ...> with 3 widgets title: HTMLText(value='<h2>Taylor polynomial</h2>') f: EvalText(value='e^(-x)*sin(x)', description='$f(x)=$', layout=Layout(max_width='81em')) order: SelectionSlider(description='order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) @@ -101,7 +101,7 @@ def library_interact( ....: def f(n): ....: print(n) sage: f() # an interact appears if using the notebook, else code - Interactive function <function f at ...> with 1 widget + ...Interactive function <function f at ...> with 1 widget n: TransformIntSlider(value=5, description='n', max=15, min=-5) TESTS: @@ -117,7 +117,7 @@ def library_interact( DeprecationWarning: Use decorator factory @library_interact(widgets) instead of @library_interact without any arguments. See https://trac.sagemath.org/33382 for details. sage: f() # an interact appears if using the notebook, else code - Interactive function <function f at ...> with 1 widget + ...Interactive function <function f at ...> with 1 widget n: TransformIntSlider(value=5, description='n', max=15, min=-5) .. NOTE:: @@ -183,7 +183,7 @@ def demo(n: int, m: int): creates the mathlet:: sage: interacts.demo() - Interactive function <function demo at ...> with 2 widgets + ...Interactive function <function demo at ...> with 2 widgets n: SelectionSlider(description='n', options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=0) m: SelectionSlider(description='m', options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=0) """ @@ -213,7 +213,7 @@ def taylor_polynomial(title, f, order: int): creates the mathlet:: sage: interacts.calculus.taylor_polynomial() - Interactive function <function taylor_polynomial at ...> with 3 widgets + ...Interactive function <function taylor_polynomial at ...> with 3 widgets title: HTMLText(value='<h2>Taylor polynomial</h2>') f: EvalText(value='e^(-x)*sin(x)', description='$f(x)=$', layout=Layout(max_width='81em')) order: SelectionSlider(description='order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) @@ -259,7 +259,7 @@ def definite_integral(title, f, g, interval, x_range, selection): creates the mathlet:: sage: interacts.calculus.definite_integral() - Interactive function <function definite_integral at ...> with 6 widgets + ...Interactive function <function definite_integral at ...> with 6 widgets title: HTMLText(value='<h2>Definite integral</h2>') f: EvalText(value='3*x', description='$f(x)=$', layout=Layout(max_width='81em')) g: EvalText(value='x^2', description='$g(x)=$', layout=Layout(max_width='81em')) @@ -345,7 +345,7 @@ def function_derivative(title, function, x_range, y_range): creates the mathlet:: sage: interacts.calculus.function_derivative() - Interactive function <function function_derivative at ...> with 4 widgets + ...Interactive function <function function_derivative at ...> with 4 widgets title: HTMLText(value='<h2>Derivative grapher</h2>') function: EvalText(value='x^5-3*x^3+1', description='Function:', layout=Layout(max_width='81em')) x_range: FloatRangeSlider(value=(-2.0, 2.0), description='Range (x)', max=15.0, min=-15.0) @@ -393,7 +393,7 @@ def difference_quotient(title, f, interval, a, x0): creates the mathlet:: sage: interacts.calculus.difference_quotient() - Interactive function <function difference_quotient at ...> with 5 widgets + ...Interactive function <function difference_quotient at ...> with 5 widgets title: HTMLText(value='<h2>Difference quotient</h2>') f: EvalText(value='sin(x)', description='f(x)', layout=Layout(max_width='81em')) interval: FloatRangeSlider(value=(0.0, 10.0), description='Range', max=10.0) @@ -458,7 +458,7 @@ def quadratic_equation(A, B, C): creates the mathlet:: sage: interacts.calculus.quadratic_equation() - Interactive function <function quadratic_equation at ...> with 3 widgets + ...Interactive function <function quadratic_equation at ...> with 3 widgets A: IntSlider(value=1, description='A', max=7, min=-7) B: IntSlider(value=1, description='B', max=7, min=-7) C: IntSlider(value=-2, description='C', max=7, min=-7) @@ -520,7 +520,7 @@ def trigonometric_properties_triangle(a0, a1, a2): creates the mathlet:: sage: interacts.geometry.trigonometric_properties_triangle() - Interactive function <function trigonometric_properties_triangle at ...> with 3 widgets + ...Interactive function <function trigonometric_properties_triangle at ...> with 3 widgets a0: IntSlider(value=30, description='A', max=360) a1: IntSlider(value=180, description='B', max=360) a2: IntSlider(value=300, description='C', max=360) @@ -601,7 +601,7 @@ def unit_circle(function, x): creates the mathlet:: sage: interacts.geometry.unit_circle() - Interactive function <function unit_circle at ...> with 2 widgets + ...Interactive function <function unit_circle at ...> with 2 widgets function: Dropdown(description='function', options=(('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)), value=0) x: TransformFloatSlider(value=0.0, description='x', max=6.283185307179586, step=0.015707963267948967) """ @@ -702,7 +702,7 @@ def special_points( creates the mathlet:: sage: interacts.geometry.special_points() - Interactive function <function special_points at ...> with 10 widgets + ...Interactive function <function special_points at ...> with 10 widgets title: HTMLText(value='<h2>Special points in triangle</h2>') a0: IntSlider(value=30, description='A', max=360) a1: IntSlider(value=180, description='B', max=360) @@ -875,7 +875,7 @@ def coin(n, interval): creates the mathlet:: sage: interacts.statistics.coin() - Interactive function <function coin at ...> with 2 widgets + ...Interactive function <function coin at ...> with 2 widgets n: IntSlider(value=1000, description='Number of Tosses', max=10000, min=2, step=100) interval: IntRangeSlider(value=(0, 0), description='Plotting range (y)', max=1) """ @@ -915,7 +915,7 @@ def bisection_method(title, f, interval, d, maxn): creates the mathlet:: sage: interacts.calculus.secant_method() - Interactive function <function secant_method at ...> with 5 widgets + ...Interactive function <function secant_method at ...> with 5 widgets title: HTMLText(value='<h2>Secant method for numerical root finding</h2>') f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) @@ -995,7 +995,7 @@ def secant_method(title, f, interval, d, maxn): creates the mathlet:: sage: interacts.calculus.secant_method() - Interactive function <function secant_method at ...> with 5 widgets + ...Interactive function <function secant_method at ...> with 5 widgets title: HTMLText(value='<h2>Secant method for numerical root finding</h2>') f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) @@ -1068,7 +1068,7 @@ def newton_method(title, f, c, d, maxn, interval, list_steps): creates the mathlet:: sage: interacts.calculus.newton_method() - Interactive function <function newton_method at ...> with 7 widgets + ...Interactive function <function newton_method at ...> with 7 widgets title: HTMLText(value='<h2>Newton method</h2>') f: EvalText(value='x^2 - 2', description='f', layout=Layout(max_width='81em')) c: IntSlider(value=6, description='Start ($x$)', max=10, min=-10) @@ -1152,7 +1152,7 @@ def trapezoid_integration( creates the mathlet:: sage: interacts.calculus.trapezoid_integration() - Interactive function <function trapezoid_integration at ...> with 7 widgets + ...Interactive function <function trapezoid_integration at ...> with 7 widgets title: HTMLText(value='<h2>Trapezoid integration</h2>') f: EvalText(value='x^2-5*x + 10', description='$f(x)=$', layout=Layout(max_width='81em')) n: IntSlider(value=5, description='# divisions', min=1) @@ -1285,7 +1285,7 @@ def simpson_integration( creates the mathlet:: sage: interacts.calculus.simpson_integration() - Interactive function <function simpson_integration at ...> with 7 widgets + ...Interactive function <function simpson_integration at ...> with 7 widgets title: HTMLText(value='<h2>Simpson integration</h2>') f: EvalText(value='x*sin(x)+x+1', description='$f(x)=$', layout=Layout(max_width='81em')) n: IntSlider(value=6, description='# divisions', min=2, step=2) @@ -1549,7 +1549,7 @@ def function_tool(f, g, xrange, yrange, a, action, do_plot): creates the mathlet:: sage: interacts.calculus.function_tool() - Interactive function <function function_tool at ...> with 7 widgets + ...Interactive function <function function_tool at ...> with 7 widgets f: EvalText(value='sin(x)', description='f') g: EvalText(value='cos(x)', description='g') xrange: IntRangeSlider(value=(0, 1), description='x-range', max=3, min=-3) @@ -1679,7 +1679,7 @@ def julia(expo, c_real, c_imag, iterations, zoom_x, zoom_y, plot_points, dpi): creates the mathlet:: sage: interacts.fractals.julia() - Interactive function <function julia at ...> with 8 widgets + ...Interactive function <function julia at ...> with 8 widgets expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) c_real: FloatSlider(value=0.5, description='real part const.', max=2.0, min=-2.0, step=0.01) c_imag: FloatSlider(value=0.5, description='imag part const.', max=2.0, min=-2.0, step=0.01) @@ -1731,7 +1731,7 @@ def mandelbrot(expo, iterations, zoom_x, zoom_y, plot_points, dpi): creates the mathlet:: sage: interacts.fractals.mandelbrot() - Interactive function <function mandelbrot at ...> with 6 widgets + ...Interactive function <function mandelbrot at ...> with 6 widgets expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) iterations: IntSlider(value=20, description='# iterations', min=1) zoom_x: FloatRangeSlider(value=(-2.0, 1.0), description='Zoom X', max=2.0, min=-2.0, step=0.01) @@ -1776,7 +1776,7 @@ def cellular_automaton(N, rule_number, size): creates the mathlet:: sage: interacts.fractals.cellular_automaton() - Interactive function <function cellular_automaton at ...> with 3 widgets + ...Interactive function <function cellular_automaton at ...> with 3 widgets N: IntSlider(value=100, description='Number of iterations', max=500, min=1) rule_number: IntSlider(value=110, description='Rule number', max=255) size: IntSlider(value=6, description='size of graphic', max=11, min=1) @@ -1835,7 +1835,7 @@ def polar_prime_spiral(interval, show_factors, highlight_primes, show_curves, n, creates the mathlet:: sage: sage.interacts.algebra.polar_prime_spiral() - Interactive function <function polar_prime_spiral at ...> with 6 widgets + ...Interactive function <function polar_prime_spiral at ...> with 6 widgets interval: IntRangeSlider(value=(1, 1000), description='range', max=4000, min=1, step=10) show_factors: Checkbox(value=True, description='show_factors') highlight_primes: Checkbox(value=True, description='highlight_primes') diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst index 0228ea227c1..3335a7423c3 100644 --- a/src/sage/interacts/test_jupyter.rst +++ b/src/sage/interacts/test_jupyter.rst @@ -40,7 +40,7 @@ This is just to test that failures in the interact are actually seen:: Test all interacts from the Sage interact library:: sage: test(interacts.algebra.polar_prime_spiral) # long time - Interactive function <function polar_prime_spiral at ...> with 6 widgets + ...Interactive function <function polar_prime_spiral at ...> with 6 widgets interval: IntRangeSlider(value=(1, 1000), description='range', max=4000, min=1, step=10) show_factors: Checkbox(value=True, description='show_factors') highlight_primes: Checkbox(value=True, description='highlight_primes') @@ -53,7 +53,7 @@ Test all interacts from the Sage interact library:: Green Curve: \(n^2 + n + -1\) sage: test(interacts.calculus.taylor_polynomial) - Interactive function <function taylor_polynomial at ...> with 3 widgets + ...Interactive function <function taylor_polynomial at ...> with 3 widgets title: HTMLText(value='<h2>Taylor polynomial</h2>') f: EvalText(value='e^(-x)*sin(x)', description='$f(x)=$', layout=Layout(max_width='81em')) order: SelectionSlider(description='order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) @@ -61,7 +61,7 @@ Test all interacts from the Sage interact library:: \(\hat{f}(x;0)\;=\;x+\mathcal{O}(x^{2})\) sage: test(interacts.calculus.definite_integral) - Interactive function <function definite_integral at ...> with 6 widgets + ...Interactive function <function definite_integral at ...> with 6 widgets title: HTMLText(value='<h2>Definite integral</h2>') f: EvalText(value='3*x', description='$f(x)=$', layout=Layout(max_width='81em')) g: EvalText(value='x^2', description='$g(x)=$', layout=Layout(max_width='81em')) @@ -71,7 +71,7 @@ Test all interacts from the Sage interact library:: \(\int_{0.00}^{3.00}(\color{Blue}{f(x)})\,\mathrm{d}x=\int_{0.00}^{3.00}(3 \, x)\,\mathrm{d}x=13.50\)<br/>\(\int_{0.00}^{3.00}(\color{Green}{g(x)})\,\mathrm{d}x=\int_{0.00}^{3.00}(x^{2})\,\mathrm{d}x=9.00\) sage: test(interacts.calculus.function_derivative) - Interactive function <function function_derivative at ...> with 4 widgets + ...Interactive function <function function_derivative at ...> with 4 widgets title: HTMLText(value='<h2>Derivative grapher</h2>') function: EvalText(value='x^5-3*x^3+1', description='Function:', layout=Layout(max_width='81em')) x_range: FloatRangeSlider(value=(-2.0, 2.0), description='Range (x)', max=15.0, min=-15.0) @@ -81,7 +81,7 @@ Test all interacts from the Sage interact library:: <center>\(\color{Red}{f''(x) = 20 \, x^{3} - 18 \, x}\)</center> sage: test(interacts.calculus.difference_quotient) - Interactive function <function difference_quotient at ...> with 5 widgets + ...Interactive function <function difference_quotient at ...> with 5 widgets title: HTMLText(value='<h2>Difference quotient</h2>') f: EvalText(value='sin(x)', description='f(x)', layout=Layout(max_width='81em')) interval: FloatRangeSlider(value=(0.0, 10.0), description='Range', max=10.0) @@ -96,7 +96,7 @@ Test all interacts from the Sage interact library:: \(k = \frac{f(x_0)-f(a)}{x_0-a} = -0.62274\)<br> sage: test(interacts.calculus.quadratic_equation) - Interactive function <function quadratic_equation at ...> with 3 widgets + ...Interactive function <function quadratic_equation at ...> with 3 widgets A: IntSlider(value=1, description='A', max=7, min=-7) B: IntSlider(value=1, description='B', max=7, min=-7) C: IntSlider(value=-2, description='C', max=7, min=-7) @@ -106,7 +106,7 @@ Test all interacts from the Sage interact library:: \(x = \frac{-B\pm\sqrt{B^2-4AC}}{2A} = \frac{-1\pm\sqrt{1^2-4*1*-2}}{2*1} = \frac{-1\pm\sqrt{\color{Green}{9}}}{2} = \begin{cases}1\\-2\end{cases}\) sage: test(interacts.calculus.secant_method) - Interactive function <function secant_method at ...> with 5 widgets + ...Interactive function <function secant_method at ...> with 5 widgets title: HTMLText(value='<h2>Secant method for numerical root finding</h2>') f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) @@ -118,7 +118,7 @@ Test all interacts from the Sage interact library:: \(6 \text{ iterations}\) sage: test(interacts.calculus.newton_method) - Interactive function <function newton_method at ...> with 7 widgets + ...Interactive function <function newton_method at ...> with 7 widgets title: HTMLText(value='<h2>Newton method</h2>') f: EvalText(value='x^2 - 2', description='f', layout=Layout(max_width='81em')) c: IntSlider(value=6, description='Start ($x$)', max=10, min=-10) @@ -132,7 +132,7 @@ Test all interacts from the Sage interact library:: \(6 \text{ iterations}\) sage: test(interacts.calculus.trapezoid_integration) - Interactive function <function trapezoid_integration at ...> with 7 widgets + ...Interactive function <function trapezoid_integration at ...> with 7 widgets title: HTMLText(value='<h2>Trapezoid integration</h2>') f: EvalText(value='x^2-5*x + 10', description='$f(x)=$', layout=Layout(max_width='81em')) n: IntSlider(value=5, description='# divisions', min=1) @@ -155,7 +155,7 @@ Test all interacts from the Sage interact library:: <BLANKLINE> sage: test(interacts.calculus.simpson_integration) - Interactive function <function simpson_integration at ...> with 7 widgets + ...Interactive function <function simpson_integration at ...> with 7 widgets title: HTMLText(value='<h2>Simpson integration</h2>') f: EvalText(value='x*sin(x)+x+1', description='$f(x)=$', layout=Layout(max_width='81em')) n: IntSlider(value=6, description='# divisions', min=2, step=2) @@ -178,7 +178,7 @@ Test all interacts from the Sage interact library:: <BLANKLINE> sage: test(interacts.calculus.bisection_method) - Interactive function <function bisection_method at ...> with 5 widgets + ...Interactive function <function bisection_method at ...> with 5 widgets title: HTMLText(value='<h2>Bisection method</h2>') f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) @@ -190,7 +190,7 @@ Test all interacts from the Sage interact library:: \(9 \text{ iterations}\) sage: test(interacts.calculus.riemann_sum) - Manual interactive function <function riemann_sum at ...> with 9 widgets + ...Manual interactive function <function riemann_sum at ...> with 9 widgets title: HTMLText(value='<h2>Riemann integral with random sampling</h2>') f: EvalText(value='x^2+1', description='$f(x)=$', layout=Layout(max_width='41em')) n: IntSlider(value=5, description='# divisions', max=30, min=1) @@ -206,7 +206,7 @@ Test all interacts from the Sage interact library:: 1\,\mathrm{d}x=4.666666666666668\) sage: test(interacts.calculus.function_tool) - Interactive function <function function_tool at ...> with 7 widgets + ...Interactive function <function function_tool at ...> with 7 widgets f: EvalText(value='sin(x)', description='f') g: EvalText(value='cos(x)', description='g') xrange: IntRangeSlider(value=(0, 1), description='x-range', max=3, min=-3) @@ -219,7 +219,7 @@ Test all interacts from the Sage interact library:: <center><font color="blue"><b>\(h = f = \sin\left(x\right)\)</b></font></center> sage: test(interacts.fractals.mandelbrot) - Interactive function <function mandelbrot at ...> with 6 widgets + ...Interactive function <function mandelbrot at ...> with 6 widgets expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) iterations: IntSlider(value=20, description='# iterations', min=1) zoom_x: FloatRangeSlider(value=(-2.0, 1.0), description='Zoom X', max=2.0, min=-2.0, step=0.01) @@ -230,7 +230,7 @@ Test all interacts from the Sage interact library:: Recursive Formula: \(z \leftarrow z^{2.00} + c\) for \(c \in \mathbb{C}\) sage: test(interacts.fractals.julia) - Interactive function <function julia at ...> with 8 widgets + ...Interactive function <function julia at ...> with 8 widgets expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) c_real: FloatSlider(value=0.5, description='real part const.', max=2.0, min=-2.0, step=0.01) c_imag: FloatSlider(value=0.5, description='imag part const.', max=2.0, min=-2.0, step=0.01) @@ -243,20 +243,20 @@ Test all interacts from the Sage interact library:: Recursive Formula: \(z \leftarrow z^{2.00} + (0.50+0.50*\mathbb{I})\) sage: test(interacts.fractals.cellular_automaton) - Interactive function <function cellular_automaton at ...> with 3 widgets + ...Interactive function <function cellular_automaton at ...> with 3 widgets N: IntSlider(value=100, description='Number of iterations', max=500, min=1) rule_number: IntSlider(value=110, description='Rule number', max=255) size: IntSlider(value=6, description='size of graphic', max=11, min=1) <h2>Cellular Automaton</h2><div style="white-space: normal;">"A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells." — <a target="_blank" href="http://mathworld.wolfram.com/CellularAutomaton.html">Mathworld, Cellular Automaton</a></div> <div>Rule 110 expands to 01110110</div> sage: test(interacts.geometry.unit_circle) - Interactive function <function unit_circle at ...> with 2 widgets + ...Interactive function <function unit_circle at ...> with 2 widgets function: Dropdown(description='function', options=(('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)), value=0) x: TransformFloatSlider(value=0.0, description='x', max=6.283185307179586, step=0.015707963267948967) <div style="white-space: normal;">Lines of the same color have the same length</div> sage: test(interacts.geometry.trigonometric_properties_triangle) - Interactive function <function trigonometric_properties_triangle at ...> with 3 widgets + ...Interactive function <function trigonometric_properties_triangle at ...> with 3 widgets a0: IntSlider(value=30, description='A', max=360) a1: IntSlider(value=180, description='B', max=360) a2: IntSlider(value=300, description='C', max=360) @@ -266,7 +266,7 @@ Test all interacts from the Sage interact library:: Area of triangle \(ABC = 1.183013\) sage: test(interacts.geometry.special_points) - Interactive function <function special_points at ...> with 10 widgets + ...Interactive function <function special_points at ...> with 10 widgets title: HTMLText(value='<h2>Special points in triangle</h2>') a0: IntSlider(value=30, description='A', max=360) a1: IntSlider(value=180, description='B', max=360) @@ -279,10 +279,9 @@ Test all interacts from the Sage interact library:: show_euler: Checkbox(value=False, description="Euler's Line") sage: test(interacts.statistics.coin) - Interactive function <function coin at ...> with 2 widgets + ...Interactive function <function coin at ...> with 2 widgets n: IntSlider(value=1000, description='Number of Tosses', max=10000, min=2, step=100) interval: IntRangeSlider(value=(0, 0), description='Plotting range (y)', max=1) - doctest:...: UserWarning: Attempting to set identical bottom == top == 0.0 results in singular transformations; automatically expanding. Test matrix control (see :trac:`27735`):: @@ -291,7 +290,7 @@ Test matrix control (see :trac:`27735`):: ....: print(A) ....: print(parent(A)) sage: test(matrix_test) - Interactive function <function matrix_test at ...> with 1 widget + ...Interactive function <function matrix_test at ...> with 1 widget A: Grid(value=[[0, 1], [2, 3]], children=(Label(value='A'), VBox(children=(EvalText(value='0', layout=Layout(max_width='5em')), EvalText(value='2', layout=Layout(max_width='5em')))), VBox(children=(EvalText(value='1', layout=Layout(max_width='5em')), EvalText(value='3', layout=Layout(max_width='5em')))))) [0 1] [2 3] diff --git a/src/sage/interfaces/abc.py b/src/sage/interfaces/abc.py new file mode 100644 index 00000000000..5fbba099dd5 --- /dev/null +++ b/src/sage/interfaces/abc.py @@ -0,0 +1,138 @@ +r""" +Abstract base classes for interface elements +""" + +class AxiomElement: + r""" + Abstract base class for :class:`~sage.interfaces.axiom.AxiomElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.AxiomElement.__subclasses__()) <= 1 + True + """ + pass + + +class ExpectElement: + r""" + Abstract base class for :class:`~sage.interfaces.expect.ExpectElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.ExpectElement.__subclasses__()) <= 1 + True + """ + pass + + +class FriCASElement: + r""" + Abstract base class for :class:`~sage.interfaces.fricas.FriCASElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.FriCASElement.__subclasses__()) <= 1 + True + """ + pass + + +class GapElement: + r""" + Abstract base class for :class:`~sage.interfaces.gap.GapElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.GapElement.__subclasses__()) <= 1 + True + """ + pass + + +class GpElement: + r""" + Abstract base class for :class:`~sage.interfaces.gp.GpElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.GpElement.__subclasses__()) <= 1 + True + """ + pass + + +class Macaulay2Element: + r""" + Abstract base class for :class:`~sage.interfaces.macaulay2.Macaulay2Element`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.Macaulay2Element.__subclasses__()) <= 1 + True + """ + pass + + +class MagmaElement: + r""" + Abstract base class for :class:`~sage.interfaces.magma.MagmaElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.MagmaElement.__subclasses__()) <= 1 + True + """ + pass + + +class SingularElement: + r""" + Abstract base class for :class:`~sage.interfaces.singular.SingularElement`. + + This class is defined for the purpose of ``isinstance`` tests. It should not be + instantiated. + + EXAMPLES: + + By design, there is a unique direct subclass:: + + sage: len(sage.interfaces.abc.SingularElement.__subclasses__()) <= 1 + True + """ + pass diff --git a/src/sage/interfaces/all.py b/src/sage/interfaces/all.py index 73d032ff518..bb1d65494e1 100644 --- a/src/sage/interfaces/all.py +++ b/src/sage/interfaces/all.py @@ -1,80 +1,51 @@ # interfaces to other interpreters -from sage.misc.lazy_import import lazy_import - -from .frobby import frobby -from .four_ti_2 import four_ti_2 -from .axiom import Axiom, axiom -from .fricas import FriCAS, fricas - +from .sage0 import sage0, sage0_version, Sage from .gap import gap, gap_reset_workspace, Gap -from .gap3 import gap3, gap3_version, Gap3 -lazy_import('sage.interfaces.genus2reduction', ['genus2reduction', 'Genus2reduction']) -from .gfan import gfan, Gfan -from .giac import giac, Giac from .gp import gp, gp_version, Gp -from .gnuplot import gnuplot -from .kash import kash, kash_version, Kash -from .lisp import lisp, Lisp -from .magma import magma, Magma -from .magma_free import magma_free -from .macaulay2 import macaulay2, Macaulay2 -from .maple import maple, Maple -from .maxima import maxima, Maxima # import problems #from maxima_lib import maxima_lib -from .mathematica import mathematica, Mathematica -from .mathics import mathics, Mathics -from .matlab import matlab, matlab_version, Matlab -from .mupad import mupad, Mupad # NOT functional yet -from .mwrank import mwrank, Mwrank -from .octave import octave, Octave -from .polymake import polymake -from .qepcad import qepcad, qepcad_version, qepcad_formula -from .qsieve import qsieve +from .maxima import maxima, Maxima from .singular import singular, singular_version, Singular -from .sage0 import sage0 as sage0, sage0_version, Sage -from .scilab import scilab -from .tachyon import tachyon_rt -from .psage import PSage -from .ecm import ECM, ecm -from .povray import povray -from .lie import lie, LiE -from .r import r, R, r_version -from .read_data import read_data -interfaces = ['gap', 'gap3', 'giac', 'gp', 'mathematica', 'gnuplot', \ - 'kash', 'magma', 'macaulay2', 'maple', 'maxima', \ - 'mathematica', 'mwrank', 'octave', 'r', \ - 'singular', 'sage0', 'sage'] +from .magma import magma, Magma +from .polymake import polymake + +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.interfaces.axiom', ['Axiom', 'axiom']) +lazy_import('sage.interfaces.ecm', ['ECM', 'ecm']) +lazy_import('sage.interfaces.four_ti_2', 'four_ti_2') +lazy_import('sage.interfaces.fricas', ['FriCAS', 'fricas']) +lazy_import('sage.interfaces.frobby', 'frobby') +lazy_import('sage.interfaces.gap3', ['gap3', 'gap3_version', 'Gap3']) +lazy_import('sage.interfaces.genus2reduction', ['genus2reduction', 'Genus2reduction']) +lazy_import('sage.interfaces.gfan', ['gfan', 'Gfan']) +lazy_import('sage.interfaces.giac', ['giac', 'Giac']) +lazy_import('sage.interfaces.gnuplot', 'gnuplot') +lazy_import('sage.interfaces.kash', ['kash', 'kash_version', 'Kash']) +lazy_import('sage.interfaces.lie', ['lie', 'LiE']) +lazy_import('sage.interfaces.lisp', ['lisp', 'Lisp']) +lazy_import('sage.interfaces.macaulay2', ['macaulay2', 'Macaulay2']) +lazy_import('sage.interfaces.magma_free', 'magma_free') +lazy_import('sage.interfaces.maple', ['maple', 'Maple']) +lazy_import('sage.interfaces.mathematica', ['mathematica', 'Mathematica']) +lazy_import('sage.interfaces.mathics', ['mathics', 'Mathics']) +lazy_import('sage.interfaces.matlab', ['matlab', 'matlab_version', 'Matlab']) +lazy_import('sage.interfaces.mupad', ['mupad', 'Mupad']) # NOT functional yet +lazy_import('sage.interfaces.mwrank', ['mwrank', 'Mwrank']) +lazy_import('sage.interfaces.octave', ['octave', 'Octave']) +lazy_import('sage.interfaces.povray', 'povray') +lazy_import('sage.interfaces.psage', 'PSage') +lazy_import('sage.interfaces.qepcad', ['qepcad', 'qepcad_version', 'qepcad_formula']) +lazy_import('sage.interfaces.qsieve', 'qsieve') +lazy_import('sage.interfaces.r', ['r', 'R', 'r_version']) +lazy_import('sage.interfaces.read_data', 'read_data') +lazy_import('sage.interfaces.scilab', 'scilab') +lazy_import('sage.interfaces.tachyon', 'tachyon_rt') -try: - from sage.repl.rich_output.display_manager import get_display_manager as _get_display_manager -except ImportError: - pass -else: - if _get_display_manager().is_in_terminal(): - from .axiom import axiom_console - from .fricas import fricas_console - from .gap import gap_console - from .gap3 import gap3_console - from .giac import giac_console - from .gp import gp_console - from .gnuplot import gnuplot_console - from .kash import kash_console - from .lisp import lisp_console - from .magma import magma_console - from .macaulay2 import macaulay2_console - from .maple import maple_console - from .maxima_abstract import maxima_console - from .mathematica import mathematica_console - from .mathics import mathics_console - from .matlab import matlab_console - from .mupad import mupad_console - from .mwrank import mwrank_console - from .octave import octave_console - from .qepcad import qepcad_console - from .singular import singular_console - from .sage0 import sage0_console - from .lie import lie_console - from .r import r_console +# The following variable is used by sage-shell-mode in emacs: +interfaces = ['gap', 'gap3', 'giac', 'gp', 'mathematica', 'gnuplot', + 'kash', 'magma', 'macaulay2', 'maple', 'maxima', + 'mathematica', 'mwrank', 'octave', 'r', 'singular', + 'sage0', 'sage'] diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index 0bc5334b071..160fbd84c54 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -179,6 +179,8 @@ import os import re +import sage.interfaces.abc + from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction from sage.env import DOT_SAGE from pexpect import EOF @@ -490,7 +492,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: axiom.__reduce__() + sage: Axiom().__reduce__() (<function reduce_load_Axiom at 0x...>, ()) sage: f, args = _ sage: f(*args) @@ -555,7 +557,7 @@ def console(self): @instancedoc -class PanAxiomElement(ExpectElement): +class PanAxiomElement(ExpectElement, sage.interfaces.abc.AxiomElement): def __call__(self, x): """ EXAMPLES:: @@ -944,18 +946,24 @@ def __init__(self, parent, name): def is_AxiomElement(x): """ - Returns True of x is of type AxiomElement. + Return True if ``x`` is of type :class:`AxiomElement`. EXAMPLES:: sage: from sage.interfaces.axiom import is_AxiomElement - sage: is_AxiomElement(axiom(2)) #optional - axiom - True sage: is_AxiomElement(2) + doctest:...: DeprecationWarning: the function is_AxiomElement is deprecated; use isinstance(x, sage.interfaces.abc.AxiomElement) instead + See https://trac.sagemath.org/34804 for details. False + sage: is_AxiomElement(axiom(2)) # optional - axiom + True """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_AxiomElement is deprecated; use isinstance(x, sage.interfaces.abc.AxiomElement) instead") + return isinstance(x, AxiomElement) + #Instances axiom = Axiom(name='axiom') diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index c4dc2d4f27a..8903a454df5 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -52,6 +52,7 @@ import pexpect from pexpect import ExceptionPexpect +import sage.interfaces.abc from sage.interfaces.sagespawn import SageSpawn from sage.interfaces.interface import (Interface, InterfaceElement, InterfaceFunction, InterfaceFunctionElement) @@ -941,7 +942,6 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if The interface still works after this interrupt:: sage: singular('2+3') - Singular crashed -- automatically restarting. 5 Last, we demonstrate that by default the execution of a command @@ -1466,11 +1466,28 @@ class FunctionElement(InterfaceFunctionElement): def is_ExpectElement(x): + """ + Return True if ``x`` is of type :class:`ExpectElement` + + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.ExpectElement`) instead. + + EXAMPLES:: + + sage: from sage.interfaces.expect import is_ExpectElement + sage: is_ExpectElement(2) + doctest:...: DeprecationWarning: the function is_ExpectElement is deprecated; use isinstance(x, sage.interfaces.abc.ExpectElement) instead + See https://trac.sagemath.org/34804 for details. + False + """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_ExpectElement is deprecated; use isinstance(x, sage.interfaces.abc.ExpectElement) instead") + return isinstance(x, ExpectElement) @instancedoc -class ExpectElement(InterfaceElement): +class ExpectElement(InterfaceElement, sage.interfaces.abc.ExpectElement): """ Expect element. """ diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 22381799212..6097c850862 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -198,6 +198,9 @@ import re import os + +import sage.interfaces.abc + from sage.interfaces.tab_completion import ExtraTabCompletion from sage.interfaces.expect import Expect, ExpectElement, FunctionElement, ExpectFunction from sage.env import DOT_SAGE, LOCAL_IDENTIFIER @@ -888,10 +891,10 @@ def __reduce__(self): """ EXAMPLES:: - sage: fricas.__reduce__() # optional - fricas + sage: FriCAS().__reduce__() (<function reduce_load_fricas at 0x...>, ()) - sage: f, args = _ # optional - fricas - sage: f(*args) # optional - fricas + sage: f, args = _ + sage: f(*args) FriCAS """ return reduce_load_fricas, tuple([]) @@ -995,7 +998,7 @@ def console(self): @instancedoc -class FriCASElement(ExpectElement): +class FriCASElement(ExpectElement, sage.interfaces.abc.FriCASElement): """ Instances of this class represent objects in FriCAS. @@ -2106,12 +2109,17 @@ def is_FriCASElement(x): EXAMPLES:: - sage: from sage.interfaces.fricas import is_FriCASElement # optional - fricas + sage: from sage.interfaces.fricas import is_FriCASElement + sage: is_FriCASElement(2) + doctest:...: DeprecationWarning: the function is_FriCASElement is deprecated; use isinstance(x, sage.interfaces.abc.FriCASElement) instead + See https://trac.sagemath.org/34804 for details. + False sage: is_FriCASElement(fricas(2)) # optional - fricas True - sage: is_FriCASElement(2) # optional - fricas - False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_FriCASElement is deprecated; use isinstance(x, sage.interfaces.abc.FriCASElement) instead") + return isinstance(x, FriCASElement) @@ -2120,13 +2128,12 @@ def is_FriCASElement(x): def reduce_load_fricas(): """ - Return the FriCAS interface object defined in - :sage.interfaces.fricas. + Return the FriCAS interface object defined in :mod:`sage.interfaces.fricas`. EXAMPLES:: - sage: from sage.interfaces.fricas import reduce_load_fricas # optional - fricas - sage: reduce_load_fricas() # optional - fricas + sage: from sage.interfaces.fricas import reduce_load_fricas + sage: reduce_load_fricas() FriCAS """ return fricas diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index ba175d4e340..7ae1792e9de 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -202,6 +202,8 @@ from sage.interfaces.tab_completion import ExtraTabCompletion from sage.structure.element import ModuleElement +import sage.interfaces.abc + import re import os import io @@ -952,8 +954,6 @@ def __bool__(self): P = self._check_valid() return self != P(0) and repr(self) != 'false' - - def __len__(self): """ EXAMPLES:: @@ -1526,7 +1526,7 @@ def gap_reset_workspace(max_workspace_size=None, verbose=False): @instancedoc -class GapElement(GapElement_generic): +class GapElement(GapElement_generic, sage.interfaces.abc.GapElement): def __getitem__(self, n): """ EXAMPLES:: @@ -1635,18 +1635,26 @@ def _instancedoc_(self): def is_GapElement(x): """ - Returns True if x is a GapElement. + Return True if ``x`` is a :class:`GapElement` + + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.GapElement`) instead. EXAMPLES:: sage: from sage.interfaces.gap import is_GapElement sage: is_GapElement(gap(2)) + doctest:...: DeprecationWarning: the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead + See https://trac.sagemath.org/34823 for details. True sage: is_GapElement(2) False """ + from sage.misc.superseded import deprecation + deprecation(34823, "the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead") return isinstance(x, GapElement) + def gfq_gap_to_sage(x, F): """ INPUT: diff --git a/src/sage/interfaces/gap3.py b/src/sage/interfaces/gap3.py index fc21ee46d06..78d05035cbf 100644 --- a/src/sage/interfaces/gap3.py +++ b/src/sage/interfaces/gap3.py @@ -12,7 +12,7 @@ The experimental package for GAP3 is Jean Michel's pre-packaged GAP3, which is a minimal GAP3 distribution containing packages that have - no equivalent in GAP4, see :trac:`20107` and also + no equivalent in GAP4, see :trac:`20107` and also https://webusers.imj-prg.fr/~jean.michel/gap3/ @@ -212,7 +212,7 @@ ... """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Franco Saliola <saliola@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) @@ -224,8 +224,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.interfaces.expect import Expect @@ -236,6 +236,7 @@ # gap3_cmd should point to the gap3 executable gap3_cmd = 'gap3' + class Gap3(Gap_generic): r""" A simple Expect interface to GAP3. @@ -624,9 +625,10 @@ def _tab_completion(self): """ return [] - + gap3 = Gap3() + class GAP3Element(GapElement_generic): r""" A GAP3 element diff --git a/src/sage/interfaces/genus2reduction.py b/src/sage/interfaces/genus2reduction.py index 56ae04b2357..7a4794daf22 100644 --- a/src/sage/interfaces/genus2reduction.py +++ b/src/sage/interfaces/genus2reduction.py @@ -143,31 +143,31 @@ class is R, then the following is the meaning of sur un corps de valuation discrète", Trans. AMS 348 (1996), 4577-4610, Section 7.2, Proposition 4). """ - def __init__(self, pari_result, P, Q, minimal_equation, minimal_disc, - local_data, conductor, prime_to_2_conductor_only): + def __init__(self, pari_result, P, Q, Pmin, Qmin, minimal_disc, + local_data, conductor): self.pari_result = pari_result self.P = P self.Q = Q - self.minimal_equation = minimal_equation + self.Pmin = Pmin + self.Qmin = Qmin self.minimal_disc = minimal_disc self.local_data = local_data self.conductor = conductor - self.prime_to_2_conductor_only = prime_to_2_conductor_only def _repr_(self): - if self.prime_to_2_conductor_only: - ex = ' (away from 2)' - else: - ex = '' if self.Q == 0: yterm = '' else: yterm = '+ (%s)*y '%self.Q + s = 'Reduction data about this proper smooth genus 2 curve:\n' s += '\ty^2 %s= %s\n'%(yterm, self.P) - s += 'A Minimal Equation (away from 2):\n\ty^2 = %s\n'%self.minimal_equation - s += 'Minimal Discriminant (away from 2): %s\n'%self.minimal_disc - s += 'Conductor%s: %s\n'%(ex, self.conductor) + if self.Qmin: + s += 'A Minimal Equation:\n\ty^2 + (%s)y = %s\n'%(self.Qmin, self.Pmin) + else: + s += 'A Minimal Equation:\n\ty^2 = %s\n'%self.Pmin + s += 'Minimal Discriminant: %s\n'%self.minimal_disc + s += 'Conductor: %s\n'%self.conductor s += 'Local Data:\n%s'%self._local_data_str() return s @@ -242,17 +242,7 @@ class Genus2reduction(SageObject): sage: factor(R.conductor) 5^4 * 2267 - This means that only the odd part of the conductor is known. - - :: - - sage: R.prime_to_2_conductor_only - True - - The discriminant is always minimal away from 2, but possibly not at - 2. - - :: + The discriminant is always minimal:: sage: factor(R.minimal_disc) 2^3 * 5^5 * 2267 @@ -264,10 +254,10 @@ class Genus2reduction(SageObject): sage: R Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 - 2*x^2 - 2*x + 1)*y = -5*x^5 - A Minimal Equation (away from 2): - y^2 = x^6 - 240*x^4 - 2550*x^3 - 11400*x^2 - 24100*x - 19855 - Minimal Discriminant (away from 2): 56675000 - Conductor (away from 2): 1416875 + A Minimal Equation: + y^2 ... + Minimal Discriminant: 56675000 + Conductor: 1416875 Local Data: p=2 (potential) stable reduction: (II), j=1 @@ -293,10 +283,10 @@ class Genus2reduction(SageObject): sage: genus2reduction(0, x^6 + 3*x^3 + 63) Reduction data about this proper smooth genus 2 curve: y^2 = x^6 + 3*x^3 + 63 - A Minimal Equation (away from 2): - y^2 = x^6 + 3*x^3 + 63 - Minimal Discriminant (away from 2): 10628388316852992 - Conductor (away from 2): 2893401 + A Minimal Equation: + y^2 ... + Minimal Discriminant: -10628388316852992 + Conductor: 2893401 Local Data: p=2 (potential) stable reduction: (V), j1+j2=0, j1*j2=0 @@ -327,9 +317,9 @@ class Genus2reduction(SageObject): sage: genus2reduction(x^3-x^2-1, x^2 - x) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 - x^2 - 1)*y = x^2 - x - A Minimal Equation (away from 2): - y^2 = x^6 + 58*x^5 + 1401*x^4 + 18038*x^3 + 130546*x^2 + 503516*x + 808561 - Minimal Discriminant (away from 2): 169 + A Minimal Equation: + y^2 ... + Minimal Discriminant: -169 Conductor: 169 Local Data: p=13 @@ -370,10 +360,10 @@ def __call__(self, Q, P): sage: genus2reduction(x^3 - 2*x^2 - 2*x + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 - 2*x^2 - 2*x + 1)*y = -5*x^5 - A Minimal Equation (away from 2): - y^2 = x^6 - 240*x^4 - 2550*x^3 - 11400*x^2 - 24100*x - 19855 - Minimal Discriminant (away from 2): 56675000 - Conductor (away from 2): 1416875 + A Minimal Equation: + y^2 ... + Minimal Discriminant: 56675000 + Conductor: 1416875 Local Data: p=2 (potential) stable reduction: (II), j=1 @@ -389,9 +379,9 @@ def __call__(self, Q, P): sage: genus2reduction(x^2 + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^2 + 1)*y = -5*x^5 - A Minimal Equation (away from 2): - y^2 = -20*x^5 + x^4 + 2*x^2 + 1 - Minimal Discriminant (away from 2): 48838125 + A Minimal Equation: + y^2 ... + Minimal Discriminant: 48838125 Conductor: 32025 Local Data: p=3 @@ -412,9 +402,9 @@ def __call__(self, Q, P): sage: genus2reduction(x^3 + x^2 + x,-2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 + x^2 + x)*y = -2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2 - A Minimal Equation (away from 2): - y^2 = x^6 + 18*x^3 + 36*x^2 - 27 - Minimal Discriminant (away from 2): 1520984142 + A Minimal Equation: + y^2 ... + Minimal Discriminant: 1520984142 Conductor: 954 Local Data: p=2 @@ -436,18 +426,10 @@ def __call__(self, Q, P): raise ValueError("Q (=%s) must have degree at most 3" % Q) res = pari.genus2red([P, Q]) - conductor = ZZ(res[0]) - minimal_equation = R(res[2]) - - minimal_disc = QQ(res[2].poldisc()).abs() - if minimal_equation.degree() == 5: - minimal_disc *= minimal_equation[5]**2 - # Multiply with suitable power of 2 of the form 2^(2*(d-1) - 12) - b = 2 * (minimal_equation.degree() - 1) - k = QQ((12 - minimal_disc.valuation(2), b)).ceil() - minimal_disc >>= 12 - b*k - minimal_disc = ZZ(minimal_disc) + Pmin = R(res[2][0]) + Qmin = R(res[2][1]) + minimal_disc = ZZ(pari.hyperelldisc(res[2])) local_data = {} for red in res[3]: @@ -468,9 +450,7 @@ def __call__(self, Q, P): local_data[p] = data - prime_to_2_conductor_only = (-1 in res[1].component(2)) - return ReductionData(res, P, Q, minimal_equation, minimal_disc, local_data, - conductor, prime_to_2_conductor_only) + return ReductionData(res, P, Q, Pmin, Qmin, minimal_disc, local_data, conductor) def __reduce__(self): return _reduce_load_genus2reduction, tuple([]) diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 87ff828edec..d7a259fd3d5 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -133,7 +133,7 @@ Another important feature of giac is its online help. We can access this through sage as well. After reading the description of -the command, you can press q to immediately get back to your +the command, you can press :kbd:`q` to immediately get back to your original prompt. Incidentally you can always get into a giac console by the @@ -371,7 +371,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: giac.__reduce__() + sage: Giac().__reduce__() (<function reduce_load_Giac at 0x...>, ()) sage: f, args = _ sage: f(*args) diff --git a/src/sage/interfaces/gp.py b/src/sage/interfaces/gp.py index ff925b0ac8e..6c7035abeb5 100644 --- a/src/sage/interfaces/gp.py +++ b/src/sage/interfaces/gp.py @@ -147,6 +147,7 @@ from sage.libs.pari.all import pari import sage.rings.complex_mpfr from sage.misc.instancedoc import instancedoc +import sage.interfaces.abc class Gp(ExtraTabCompletion, Expect): @@ -821,7 +822,7 @@ def new_with_bits_prec(self, s, precision=0): @instancedoc -class GpElement(ExpectElement): +class GpElement(ExpectElement, sage.interfaces.abc.GpElement): """ EXAMPLES: This example illustrates dumping and loading GP elements to compressed strings. @@ -1036,16 +1037,24 @@ def _tab_completion(self): def is_GpElement(x): """ - Returns True of x is a GpElement. + Return True if ``x`` is of type :class:`GpElement` + + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.GpElement`) instead. EXAMPLES:: sage: from sage.interfaces.gp import is_GpElement sage: is_GpElement(gp(2)) + doctest:...: DeprecationWarning: the function is_GpElement is deprecated; use isinstance(x, sage.interfaces.abc.GpElement) instead + See https://trac.sagemath.org/34804 for details. True sage: is_GpElement(2) False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_GpElement is deprecated; use isinstance(x, sage.interfaces.abc.GpElement) instead") + return isinstance(x, GpElement) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 47ccd0cdbff..5ed25e6138d 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -173,7 +173,9 @@ def set_seed(self, seed=None): def interact(self): r""" This allows you to interactively interact with the child - interpreter. Press Ctrl-D or type 'quit' or 'exit' to exit and + interpreter. + + Press :kbd:`Ctrl` + :kbd:`D` or type 'quit' or 'exit' to exit and return to Sage. .. note:: @@ -709,6 +711,20 @@ def _instancedoc_(self): def is_InterfaceElement(x): + """ + Return True if ``x`` is of type :class:`InterfaceElement`. + + EXAMPLES:: + + sage: from sage.interfaces.interface import is_InterfaceElement + sage: is_InterfaceElement(2) + doctest:...: DeprecationWarning: the function is_InterfaceElement is deprecated; use isinstance(x, sage.interfaces.abc.InterfaceElement) instead + See https://trac.sagemath.org/34804 for details. + False + """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_InterfaceElement is deprecated; use isinstance(x, sage.interfaces.abc.InterfaceElement) instead") + return isinstance(x, InterfaceElement) @@ -1338,8 +1354,6 @@ def __bool__(self): P._false_symbol()) return P.eval(cmd) != P._true_symbol() - - def __float__(self): """ EXAMPLES:: diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py index ce837b87737..97d5a2828e5 100644 --- a/src/sage/interfaces/kash.py +++ b/src/sage/interfaces/kash.py @@ -38,14 +38,13 @@ Issues ------ -For some reason hitting Control-C to interrupt a calculation -doesn't work correctly. (TODO) +For some reason hitting :kbd:`Control` + :kbd:`C` to interrupt a calculation +does not work correctly. (TODO) Tutorial -------- -The examples in this tutorial require that kash -be installed. +The examples in this tutorial require that kash be installed. Basics ~~~~~~ @@ -673,7 +672,7 @@ def _function_call_string(self, function, args, kwds): EXAMPLES:: - sage: kash._function_call_string('Expand', ['x', 'y'], ['Prec:=10']) + sage: Kash()._function_call_string('Expand', ['x', 'y'], ['Prec:=10']) 'Expand(x,y,rec(Prec:=10))' """ if not kwds: @@ -785,6 +784,22 @@ def __repr__(self): def is_KashElement(x): + """ + Returns True if ``x`` is of type :class:`KashElement`. + + EXAMPLES:: + + sage: from sage.interfaces.kash import is_KashElement + sage: is_KashElement(2) + doctest:...: DeprecationWarning: the function is_KashElement is deprecated; use isinstance(x, sage.interfaces.abc.KashElement) instead + See https://trac.sagemath.org/34804 for details. + False + sage: is_KashElement(kash(2)) # optional - kash + True + """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_KashElement is deprecated; use isinstance(x, sage.interfaces.abc.KashElement) instead") + return isinstance(x, KashElement) ###### diff --git a/src/sage/interfaces/lie.py b/src/sage/interfaces/lie.py index 3d3a0ded886..ccd70c84758 100644 --- a/src/sage/interfaces/lie.py +++ b/src/sage/interfaces/lie.py @@ -474,7 +474,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: lie.__reduce__() + sage: LiE().__reduce__() (<function reduce_load_lie at 0x...>, ()) """ return reduce_load_lie, tuple([]) @@ -889,12 +889,17 @@ def is_LiEElement(x) -> bool: EXAMPLES:: sage: from sage.interfaces.lie import is_LiEElement + sage: is_LiEElement(2) + doctest:...: DeprecationWarning: the function is_LiEElement is deprecated; use isinstance(x, sage.interfaces.abc.LiEElement) instead + See https://trac.sagemath.org/34804 for details. + False sage: l = lie(2) # optional - lie sage: is_LiEElement(l) # optional - lie True - sage: is_LiEElement(2) - False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_LiEElement is deprecated; use isinstance(x, sage.interfaces.abc.LiEElement) instead") + return isinstance(x, LiEElement) diff --git a/src/sage/interfaces/lisp.py b/src/sage/interfaces/lisp.py index 0f9d09605bf..2e843fc77bc 100644 --- a/src/sage/interfaces/lisp.py +++ b/src/sage/interfaces/lisp.py @@ -220,7 +220,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: lisp.__reduce__() + sage: Lisp().__reduce__() (<function reduce_load_Lisp at 0x...>, ()) """ return reduce_load_Lisp, tuple([]) @@ -434,8 +434,6 @@ def __bool__(self): """ return self != 0 and repr(self) != 'NIL' - - def _add_(self, right): """ EXAMPLES:: @@ -445,7 +443,7 @@ def _add_(self, right): 3 """ P = self._check_valid() - return P.new('(+ %s %s)'%(self._name, right._name)) + return P.new('(+ %s %s)' % (self._name, right._name)) def _sub_(self, right): """ @@ -527,11 +525,16 @@ def is_LispElement(x): EXAMPLES:: sage: from sage.interfaces.lisp import is_LispElement - sage: is_LispElement(lisp(2)) - True sage: is_LispElement(2) + doctest:...: DeprecationWarning: the function is_LispElement is deprecated; use isinstance(x, sage.interfaces.abc.LispElement) instead + See https://trac.sagemath.org/34804 for details. False + sage: is_LispElement(lisp(2)) + True """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_LispElement is deprecated; use isinstance(x, sage.interfaces.abc.LispElement) instead") + return isinstance(x, LispElement) # An instance diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 14d217cff9e..d0588d43acc 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -120,6 +120,8 @@ import os import re +import sage.interfaces.abc + from sage.interfaces.expect import (Expect, ExpectElement, ExpectFunction, FunctionElement) from sage.interfaces.interface import AsciiArtString @@ -237,7 +239,7 @@ def __reduce__(self): EXAMPLES:: - sage: rlm2, t = macaulay2.__reduce__() + sage: rlm2, t = Macaulay2().__reduce__() sage: rlm2(*t) Macaulay2 """ @@ -867,7 +869,7 @@ def _macaulay2_input_ring(self, base_ring, vars, order='GRevLex'): @instancedoc -class Macaulay2Element(ExtraTabCompletion, ExpectElement): +class Macaulay2Element(ExtraTabCompletion, ExpectElement, sage.interfaces.abc.Macaulay2Element): """ Instances of this class represent objects in Macaulay2. @@ -1179,8 +1181,6 @@ def __bool__(self): P = self.parent() return P.eval('{0}===false or {0}==0'.format(self._name)) != 'true' - - def sage_polystring(self): """ If this Macaulay2 element is a polynomial, return a string @@ -1834,14 +1834,24 @@ def _sage_src_(self): def is_Macaulay2Element(x): """ + Return True if ``x`` is a :class:`Macaulay2Element` + + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.Macaulay2Element`) instead. + EXAMPLES:: sage: from sage.interfaces.macaulay2 import is_Macaulay2Element sage: is_Macaulay2Element(2) # optional - macaulay2 + doctest:...: DeprecationWarning: the function is_Macaulay2Element is deprecated; use isinstance(x, sage.interfaces.abc.MacaulayElement) instead + See https://trac.sagemath.org/34823 for details. False sage: is_Macaulay2Element(macaulay2(2)) # optional - macaulay2 True """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_Macaulay2Element is deprecated; use isinstance(x, sage.interfaces.abc.Macaulay2Element) instead") + return isinstance(x, Macaulay2Element) # An instance diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index bd75a6a1850..8502633bae4 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -197,7 +197,7 @@ magma.functions... """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) @@ -210,8 +210,8 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#***************************************************************************** - +# **************************************************************************** +from __future__ import annotations import re import sys @@ -225,6 +225,7 @@ from sage.env import SAGE_EXTCODE, DOT_SAGE import sage.misc.misc import sage.misc.sage_eval +import sage.interfaces.abc from sage.interfaces.tab_completion import ExtraTabCompletion from sage.misc.instancedoc import instancedoc @@ -566,6 +567,7 @@ def _preparse(self, s): EXAMPLES:: + sage: magma = Magma() sage: magma._preparse_colon_equals = False sage: magma._preparse('a = 5') 'a = 5' @@ -1812,29 +1814,32 @@ def _instancedoc_(self): def is_MagmaElement(x): """ - Return True if x is of type MagmaElement, and False otherwise. + Return True if ``x`` is of type :class:`MagmaElement`, and False otherwise. INPUT: - - ``x`` - any object - OUTPUT: bool EXAMPLES:: sage: from sage.interfaces.magma import is_MagmaElement sage: is_MagmaElement(2) + doctest:...: DeprecationWarning: the function is_MagmaElement is deprecated; use isinstance(x, sage.interfaces.abc.MagmaElement) instead + See https://trac.sagemath.org/34804 for details. False sage: is_MagmaElement(magma(2)) # optional - magma True """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_MagmaElement is deprecated; use isinstance(x, sage.interfaces.abc.MagmaElement) instead") + return isinstance(x, MagmaElement) @instancedoc -class MagmaElement(ExtraTabCompletion, ExpectElement): +class MagmaElement(ExtraTabCompletion, ExpectElement, sage.interfaces.abc.MagmaElement): def _ref(self): """ Return a variable name that is a new reference to this particular @@ -2133,14 +2138,14 @@ def gen(self, n): """ if n <= 0: raise IndexError("index must be positive since Magma indexes are 1-based") - return self.gens()[n-1] + return self.gens()[n - 1] - def gens(self): + def gens(self) -> tuple: """ - Return generators for self. + Return generators for ``self``. If self is named X in Magma, this function evaluates X.1, X.2, - etc., in Magma until an error occurs. It then returns a Sage list + etc., in Magma until an error occurs. It then returns a Sage tuple of the resulting X.i. Note - I don't think there is a Magma command that returns the list of valid X.i. There are numerous ad hoc functions for various classes but nothing systematic. This function @@ -2154,9 +2159,9 @@ def gens(self): EXAMPLES:: sage: magma("VectorSpace(RationalField(),3)").gens() # optional - magma - [(1 0 0), (0 1 0), (0 0 1)] + ((1 0 0), (0 1 0), (0 0 1)) sage: magma("AbelianGroup(EllipticCurve([1..5]))").gens() # optional - magma - [$.1] + ($.1,) """ try: return self._magma_gens @@ -2172,8 +2177,9 @@ def gens(self): except (RuntimeError, TypeError): break i += 1 - self._magma_gens = G - return G + tG = tuple(G) + self._magma_gens = tG + return tG def gen_names(self): """ @@ -2651,17 +2657,13 @@ def __bool__(self): pass return True - - def sub(self, gens): """ Return the sub-object of self with given gens. INPUT: - - - ``gens`` - object or list/tuple of generators - + - ``gens`` -- object or list/tuple of generators EXAMPLES:: diff --git a/src/sage/interfaces/maple.py b/src/sage/interfaces/maple.py index b3818b73dec..e9f0d5d6fb0 100644 --- a/src/sage/interfaces/maple.py +++ b/src/sage/interfaces/maple.py @@ -105,7 +105,7 @@ Another important feature of maple is its online help. We can access this through sage as well. After reading the description of -the command, you can press q to immediately get back to your +the command, you can press :kbd:`q` to immediately get back to your original prompt. Incidentally you can always get into a maple console by the @@ -337,7 +337,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: maple.__reduce__() + sage: Maple().__reduce__() (<function reduce_load_Maple at 0x...>, ()) sage: f, args = _ sage: f(*args) diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index 6d0558d8717..9c6e3ca86e9 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -377,7 +377,7 @@ sage: FL=[sin, cos, tan, csc, sec, cot, # optional - mathematica ....: sinh, cosh, tanh, csch, sech, coth] sage: IFL=[arcsin, arccos, arctan, arccsc, # optional - mathematica - ....: arcsec, arccot, arcsinh, arccosh, + ....: arcsec, arccot, arcsinh, arccosh, ....: arctanh, arccsch, arcsech, arccoth] sage: [mathematica.TrigToExp(u(x)).sage() # optional - mathematica ....: for u in FL] @@ -1079,8 +1079,6 @@ def __bool__(self): cmd = '%s===%s' % (self._name, P._false_symbol()) return P.eval(cmd).strip() != P._true_symbol() - - def n(self, *args, **kwargs): r""" Numerical approximation by converting to Sage object first diff --git a/src/sage/interfaces/mathics.py b/src/sage/interfaces/mathics.py index aa86c956635..0bf0bc78829 100644 --- a/src/sage/interfaces/mathics.py +++ b/src/sage/interfaces/mathics.py @@ -1242,8 +1242,6 @@ def __bool__(self): cmd = '%s===%s' % (self._name, P._false_symbol()) return not str(P(cmd)) == P._true_symbol() - - def n(self, *args, **kwargs): r""" Numerical approximation by converting to Sage object first diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index 8950adfdea1..79a17ddb009 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -1125,17 +1125,22 @@ def _object_function_class(self): def is_MaximaElement(x): """ - Returns True if x is of type MaximaElement. + Returns True if ``x`` is of type :class:`MaximaElement`. EXAMPLES:: sage: from sage.interfaces.maxima import is_MaximaElement + sage: is_MaximaElement(1) + doctest:...: DeprecationWarning: the function is_MaximaElement is deprecated; use isinstance(x, sage.interfaces.abc.MaximaElement) instead + See https://trac.sagemath.org/34804 for details. + False sage: m = maxima(1) sage: is_MaximaElement(m) True - sage: is_MaximaElement(1) - False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_MaximaElement is deprecated; use isinstance(x, sage.interfaces.abc.MaximaElement) instead") + return isinstance(x, MaximaElement) diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 3c563149393..cc1d88160bb 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1148,8 +1148,6 @@ def __bool__(self): # but be careful, since for relations things like is(equal(a,b)) are # what Maxima needs - - def _richcmp_(self, other, op): """ Compare this Maxima object with ``other``. @@ -1523,7 +1521,7 @@ def nintegral(self, var='x', a=0, b=1, EXAMPLES:: sage: maxima('exp(-sqrt(x))').nintegral('x',0,1) - (0.5284822353142306, 4.1633141378838...e-11, 231, 0) + (0.5284822353142306, 4.163...e-11, 231, 0) Note that GP also does numerical integration, and can do so to very high precision very quickly:: diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index e772ede1101..c54de09c544 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -158,14 +158,14 @@ (mterpri)))))))) """) -## Redirection of ECL and Maxima stdout to /dev/null +# Redirection of ECL and Maxima stdout to /dev/null ecl_eval(r"""(defparameter *dev-null* (make-two-way-stream (make-concatenated-stream) (make-broadcast-stream)))""") ecl_eval("(setf original-standard-output *standard-output*)") ecl_eval("(setf *standard-output* *dev-null*)") #ecl_eval("(setf *error-output* *dev-null*)") -## Default options set in Maxima +# Default options set in Maxima # display2d -- no ascii art output # keepfloat -- don't automatically convert floats to rationals @@ -183,48 +183,21 @@ init_code.append('nolabels : true') for l in init_code: ecl_eval("#$%s$"%l) -## To get more debug information uncomment the next line -## should allow to do this through a method +# To get more debug information uncomment the next line +# should allow to do this through a method #ecl_eval("(setf *standard-output* original-standard-output)") -## This is the main function (ECL object) used for evaluation +# This is the main function (ECL object) used for evaluation # This returns an EclObject maxima_eval=ecl_eval(""" (defun maxima-eval( form ) - (let ((result (catch 'macsyma-quit (cons 'maxima_eval (meval form))))) - ;(princ (list "result=" result)) - ;(terpri) - ;(princ (list "$error=" $error)) - ;(terpri) - (cond - ((and (consp result) (eq (car result) 'maxima_eval)) (cdr result)) - ((eq result 'maxima-error) - (let ((the-jig (process-error-argl (cddr $error)))) - (mapc #'set (car the-jig) (cadr the-jig)) - (error (concatenate 'string - "Error executing code in Maxima: " - (with-output-to-string (stream) - (apply #'mformat stream (cadr $error) - (caddr the-jig))))) - )) - (t - (let ((the-jig (process-error-argl (cddr $error)))) - (mapc #'set (car the-jig) (cadr the-jig)) - (error (concatenate 'string "Maxima condition. result:" - (princ-to-string result) "$error:" - (with-output-to-string (stream) - (apply #'mformat stream (cadr $error) - (caddr the-jig))))) - )) - ) - ) -) + (with-$error (meval form))) """) -## Number of instances of this interface +# Number of instances of this interface maxima_lib_instances = 0 -## Here we define several useful ECL/Maxima objects +# Here we define several useful ECL/Maxima objects # The Maxima string function can change the structure of its input #maxprint=EclObject("$STRING") maxprint=EclObject(r"""(defun mstring-for-sage (form) @@ -678,10 +651,10 @@ def _object_function_class(self): """ return MaximaLibElementFunction - ## some helper functions to wrap the calculus use of the maxima interface. - ## these routines expect arguments living in the symbolic ring - ## and return something that is hopefully coercible into the symbolic - ## ring again. + # some helper functions to wrap the calculus use of the maxima interface. + # these routines expect arguments living in the symbolic ring + # and return something that is hopefully coercible into the symbolic + # ring again. def sr_integral(self,*args): """ @@ -864,7 +837,7 @@ def sr_sum(self,*args): sage: sum(1/(m^4 + 2*m^3 + 3*m^2 + 2*m)^2, m, 0, infinity) Traceback (most recent call last): ... - RuntimeError: ECL says: Error executing code in Maxima: Zero to negative power computed. + RuntimeError: ECL says: Zero to negative power computed. Similar situation for :trac:`12410`:: @@ -872,8 +845,7 @@ def sr_sum(self,*args): sage: sum(1/x*(-1)^x, x, 0, oo) Traceback (most recent call last): ... - RuntimeError: ECL says: Error executing code in Maxima: Zero to negative power computed. - + RuntimeError: ECL says: Zero to negative power computed. """ try: return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_sum],([max_sum],[sr_to_max(SR(a)) for a in args])]])) @@ -1037,17 +1009,22 @@ def _missing_assumption(self,errstr): def is_MaximaLibElement(x): r""" - Returns True if x is of type MaximaLibElement. + Return True if ``x`` is of type :class:`MaximaLibElement`. EXAMPLES:: sage: from sage.interfaces.maxima_lib import maxima_lib, is_MaximaLibElement + sage: is_MaximaLibElement(1) + doctest:...: DeprecationWarning: the function is_MaximaLibElement is deprecated; use isinstance(x, sage.interfaces.abc.MaximaLibElement) instead + See https://trac.sagemath.org/34804 for details. + False sage: m = maxima_lib(1) sage: is_MaximaLibElement(m) True - sage: is_MaximaLibElement(1) - False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_MaximaLibElement is deprecated; use isinstance(x, sage.interfaces.abc.MaximaLibElement) instead") + return isinstance(x, MaximaLibElement) @@ -1201,7 +1178,7 @@ def reduce_load_MaximaLib(): NIL=EclObject("NIL") lisp_length=EclObject("length") -## Dictionaries for standard operators +# Dictionaries for standard operators sage_op_dict = { sage.functions.other.abs : "MABS", add_vararg : "MPLUS", @@ -1230,7 +1207,7 @@ def reduce_load_MaximaLib(): max_op_dict = dict([(sage_op_dict[k],k) for k in sage_op_dict]) -## Here we correct the dictionaries for some simple operators +# Here we correct the dictionaries for some simple operators def sage_rat(x,y): r""" @@ -1260,7 +1237,7 @@ def sage_rat(x,y): max_op_dict[rat]=sage_rat -## Here we build dictionaries for operators needing special conversions. +# Here we build dictionaries for operators needing special conversions. ratdisrep = EclObject("ratdisrep") mrat = EclObject("MRAT") mqapply = EclObject("MQAPPLY") @@ -1508,12 +1485,12 @@ def max_pochhammer_to_sage(expr): } -## Dictionaries for symbols +# Dictionaries for symbols sage_sym_dict={} max_sym_dict={} -## Generic conversion functions +# Generic conversion functions max_i=EclObject("$%I") def pyobject_to_max(obj): diff --git a/src/sage/interfaces/mupad.py b/src/sage/interfaces/mupad.py index e4e8531e6c2..00034b4af16 100644 --- a/src/sage/interfaces/mupad.py +++ b/src/sage/interfaces/mupad.py @@ -147,7 +147,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: mupad.__reduce__() + sage: Mupad().__reduce__() (<function reduce_load_mupad at 0x...>, ()) """ diff --git a/src/sage/interfaces/mwrank.py b/src/sage/interfaces/mwrank.py index 701b91b5afe..ae65d0ffea0 100644 --- a/src/sage/interfaces/mwrank.py +++ b/src/sage/interfaces/mwrank.py @@ -210,7 +210,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: mwrank.__reduce__() + sage: Mwrank().__reduce__() (<function _reduce_load_mwrank at 0x...>, ()) """ diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index ae1b87c55cb..81d48cf3c10 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -156,14 +156,17 @@ class Octave(Expect): EXAMPLES:: - sage: octave.eval("a = [ 1, 1, 2; 3, 5, 8; 13, 21, 33 ]") # optional - octave - 'a =\n\n 1 1 2\n 3 5 8\n 13 21 33\n' - sage: octave.eval("b = [ 1; 3; 13]") # optional - octave - 'b =\n\n 1\n 3\n 13\n' - sage: octave.eval(r"c=a \ b") # solves linear equation: a*c = b # optional - octave; random output - 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n' - sage: octave.eval("c") # optional - octave; random output - 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n' + sage: octave.eval("a = [ 1, 1, 2; 3, 5, 8; 13, 21, 33 ]").strip() # optional - octave + 'a =\n\n 1 1 2\n 3 5 8\n 13 21 33' + sage: octave.eval("b = [ 1; 3; 13]").strip() # optional - octave + 'b =\n\n 1\n 3\n 13' + + The following solves the linear equation: a*c = b:: + + sage: octave.eval(r"c=a \ b").strip() # optional - octave # abs tol 0.01 + 'c =\n\n 1\n -0\n 0' + sage: octave.eval("c").strip() # optional - octave # abs tol 0.01 + 'c =\n\n 1\n -0\n 0' TESTS: @@ -228,7 +231,7 @@ def __reduce__(self): """ EXAMPLES:: - sage: octave.__reduce__() + sage: Octave().__reduce__() (<function reduce_load_Octave at 0x...>, ()) """ return reduce_load_Octave, tuple([]) @@ -662,8 +665,6 @@ def __bool__(self): """ return str(self) != ' [](0x0)' and any(x != '0' for x in str(self).split()) - - def _matrix_(self, R=None): r""" Return Sage matrix from this octave element. diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index c459a7268e6..c857d14daf9 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -27,12 +27,9 @@ import os import re -import sys -import time from .expect import Expect from .interface import (Interface, InterfaceElement, InterfaceFunctionElement) -from sage.cpython.string import bytes_to_str, str_to_bytes from sage.misc.verbose import get_verbose from sage.misc.cachefunc import cached_method from sage.interfaces.tab_completion import ExtraTabCompletion diff --git a/src/sage/interfaces/primecount.py b/src/sage/interfaces/primecount.py index e647bf9b980..518378e1fe2 100644 --- a/src/sage/interfaces/primecount.py +++ b/src/sage/interfaces/primecount.py @@ -1,5 +1,5 @@ from sage.misc.superseded import deprecation deprecation(32894, "the module sage.interfaces.primecount is deprecated - use primecountpy.primecount instead") from sage.misc.lazy_import import lazy_import -lazy_import("primecountpy.primecount", ['phi', 'nth_prime', 'prime_pi', 'prime_pi_128'], +lazy_import("primecountpy.primecount", ['phi', 'nth_prime', 'prime_pi', 'prime_pi_128'], deprecation=(32894, "the module sage.interfaces.primecount is deprecated - use primecountpy.primecount instead")) diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index d4b10353e0c..8adddead331 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -595,16 +595,15 @@ - Carl Witty (2008-03): initial version - Thierry Monteil (2015-07) repackaging + noncommutative doctests. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Carl Witty <Carl.Witty@gmail.com> # # 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 os from sage.env import SAGE_LOCAL import pexpect import re @@ -1676,10 +1675,9 @@ def qepcad(formula, assume=None, interact=False, solution=None, raise ValueError("Unknown solution type ({})".format(solution)) -import os def qepcad_console(memcells=None): r""" - Run QEPCAD directly. To exit early, press Control-C. + Run QEPCAD directly. To exit early, press :kbd:`Control` + :kbd:`C`. EXAMPLES:: diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 72481d3e1ef..0eabaccecf7 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -1606,9 +1606,7 @@ def __bool__(self): sage: bool(r(1)) # optional - rpy2 True """ - return "FALSE" in repr(self==0) - - + return "FALSE" in repr(self == 0) def _comparison(self, other, symbol): """ @@ -2003,10 +2001,15 @@ def is_RElement(x): sage: from sage.interfaces.r import is_RElement # optional - rpy2 sage: is_RElement(2) # optional - rpy2 + doctest:...: DeprecationWarning: the function is_RElement is deprecated; use isinstance(x, sage.interfaces.abc.RElement) instead + See https://trac.sagemath.org/34804 for details. False sage: is_RElement(r(2)) # optional - rpy2 True """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_RElement is deprecated; use isinstance(x, sage.interfaces.abc.RElement) instead") + return isinstance(x, RElement) # An instance of R diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 9c9586d8bc7..cb93395125e 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -341,9 +341,12 @@ import sys import pexpect import shlex +import time from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction +import sage.interfaces.abc + from sage.interfaces.tab_completion import ExtraTabCompletion from sage.structure.sequence import Sequence_generic from sage.structure.element import RingElement @@ -508,6 +511,52 @@ def _quit_string(self): """ return 'quit;' + def _send_interrupt(self): + """ + Send an interrupt to Singular. If needed, additional + semi-colons are sent until we get back at the prompt. + + TESTS: + + The following works without restarting Singular:: + + sage: a = singular(1) + sage: _ = singular._expect.sendline('while(1){};') + sage: singular.interrupt() + True + + We can still access a:: + + sage: 2*a + 2 + + Interrupting nothing or unfinished input also works:: + + sage: singular.interrupt() + True + sage: _ = singular._expect.sendline('1+') + sage: singular.interrupt() + True + sage: 3*a + 3 + + """ + # Work around for Singular bug + # http://www.singular.uni-kl.de:8002/trac/ticket/727 + time.sleep(0.1) + + E = self._expect + E.sendline(chr(3)) + # The following is needed so interrupt() works even when + # there is no computation going on. + for i in range(5): + try: + E.expect_upto(self._prompt, timeout=0.1) + return + except pexpect.TIMEOUT: + pass + E.sendline(";") + def _read_in_file_command(self, filename): r""" EXAMPLES:: @@ -1308,7 +1357,7 @@ def _keyboard_interrupt(self): @instancedoc -class SingularElement(ExtraTabCompletion, ExpectElement): +class SingularElement(ExtraTabCompletion, ExpectElement, sage.interfaces.abc.SingularElement): def __init__(self, parent, type, value, is_name=False): """ @@ -2284,16 +2333,24 @@ def _instancedoc_(self): def is_SingularElement(x): r""" - Returns True is x is of type ``SingularElement``. + Return True is ``x`` is of type :class:`SingularElement`. + + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.SingularElement`) instead. EXAMPLES:: sage: from sage.interfaces.singular import is_SingularElement sage: is_SingularElement(singular(2)) + doctest:...: DeprecationWarning: the function is_SingularElement is deprecated; use isinstance(x, sage.interfaces.abc.SingularElement) instead + See https://trac.sagemath.org/34804 for details. True sage: is_SingularElement(2) False """ + from sage.misc.superseded import deprecation + deprecation(34804, "the function is_SingularElement is deprecated; use isinstance(x, sage.interfaces.abc.SingularElement) instead") + return isinstance(x, SingularElement) @@ -2734,7 +2791,8 @@ def singular_gb_standard_options(func): sage: P.<x,y> = QQ[] sage: I = P*[x,y] sage: sage_getargspec(I.interreduced_basis) - ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getsourcelines(I.interreduced_basis) ([' @handle_AA_and_QQbar\n', ' @singular_gb_standard_options\n', diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index df893a488f6..cefd33aebb4 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -117,7 +117,7 @@ sage: l6sn.sage_link().is_isotopic(l6) # optional - snappy True -But observe that the name conversion to SnapPy does not distingiush orientation +But observe that the name conversion to SnapPy does not distinguish orientation types:: sage: L6b = KnotInfo.L6a1_1 @@ -188,12 +188,12 @@ True To see all the properties available in this interface you can use "tab-completion". -For example type ``K.items.`` and than hit the "tab-key". You can select the item +For example type ``K.items.`` and than hit the :kbd:`Tab` key. You can select the item you want from the list. If you know some first letters type them first to obtain a reduced selection list. In a similar way you may select the knots and links. Here you have to type ``KnotInfo.`` -or ``KnotInfo.L7`` before stroking the "tab-key". In the latter case the selection list +or ``KnotInfo.L7`` before stroking the :kbd:`Tab` key. In the latter case the selection list will be reduced to proper links with 7 crossings. Finally there is a method :meth:`Link.get_knotinfo` of class :class:`Link` to find an instance @@ -216,6 +216,7 @@ AUTHORS: - Sebastian Oehms August 2020: initial version +- Sebastian Oehms June 2022: add :meth:`conway_polynomial` and :meth:`khovanov_polynomial` (:trac:`33969`) Thanks to Chuck Livingston and Allison Moore for their support. For further acknowledgments see the correspondig hompages. """ @@ -374,25 +375,27 @@ def items(self): @cached_method def __getitem__(self, item): r""" - sage: from sage.knots.knotinfo import KnotInfo - sage: L = KnotInfo.L4a1_0 - sage: L[L.items.alternating] - 'Y' - sage: L[L.items.arc_notation] - '{{6, 4}, {3, 5}, {4, 2}, {1, 3}, {2, 6}, {5, 1}}' - sage: L[L.items.braid_notation] - '{3, {-2, -2, -1, 2, -1}}' - sage: L[0] - Traceback (most recent call last): - ... - KeyError: "Item must be an instance of <enum 'KnotInfoColumns'>" + EXAMPLES:: + + sage: from sage.knots.knotinfo import KnotInfo + sage: L = KnotInfo.L4a1_0 + sage: L[L.items.alternating] + 'Y' + sage: L[L.items.arc_notation] + '{{6, 4}, {3, 5}, {4, 2}, {1, 3}, {2, 6}, {5, 1}}' + sage: L[L.items.braid_notation] + '{3, {-2, -2, -1, 2, -1}}' + sage: L[0] + Traceback (most recent call last): + ... + KeyError: "Item must be an instance of <enum 'KnotInfoColumns'>" """ if not isinstance(item, KnotInfoColumns): - raise KeyError('Item must be an instance of %s' %(KnotInfoColumns)) + raise KeyError('Item must be an instance of %s' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyLinks and self.is_knot(): - raise KeyError('Item not available for knots' %(KnotInfoColumns)) + raise KeyError('Item not available for knots' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyKnots and not self.is_knot(): - raise KeyError('Item not available for links' %(KnotInfoColumns)) + raise KeyError('Item not available for links' % (KnotInfoColumns)) l = db.read(item) ind = db.read_row_dict()[self.name][0] @@ -400,7 +403,7 @@ def __getitem__(self, item): if item.column_type() == item.types.OnlyLinks: offset = self._offset_knots() - return l[ind-offset] + return l[ind - offset] def _offset_knots(self): r""" @@ -1620,6 +1623,161 @@ def alexander_polynomial(self, var='t', original=False, laurent_poly=False): exp = ap.exponents() return t ** ((-max(exp) - min(exp)) // 2) * ap + @cached_method + def conway_polynomial(self, var='t', original=False): + r""" + Return the Conway polynomial according to the value of column + ``conway_polynomial`` for this knot or link as an instance of + :class:`~sage.rings.polynomial.polynomial_element.Polynomial`. + + It is obtained from the Seifert matrix `V` of ``self`` by the following + formula (see the KnotInfo description web-page; to launch it see the + example below): + + .. MATH:: + + \nabla(L) = \det(t^{\frac{1}{2}} V -t^{\frac{-1}{2}} V^t) + + Here `V^t` stands for the transpose of `V`. + + + INPUT: + + - ``var`` -- (default: ``'t'``) the variable + - ``original`` -- boolean (optional, default ``False``) if set to + ``True`` the original table entry is returned as a string + + OUTPUT: + + A polynomial over the integers, more precisely an instance of + :class:`~sage.rings.polynomial.polynomial_element.Polynomial`. + If ``original`` is set to ``True`` then a string is returned. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import KnotInfo + sage: K = KnotInfo.K4_1 + sage: Kc = K.conway_polynomial(); Kc + -t^2 + 1 + sage: L = KnotInfo.L5a1_0 + sage: Lc = L.conway_polynomial(); Lc + t^3 + + Comparision to Sage's results:: + + sage: Kc == K.link().conway_polynomial() + True + sage: Lc == L.link().conway_polynomial() + True + + Launch the KnotInfo description web-page:: + + sage: K.items.conway_polynomial.description_webpage() # not tested + True + """ + conway_polynomial = self[self.items.conway_polynomial] + + if original: + return conway_polynomial + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(ZZ, var) + + if not conway_polynomial and self.crossing_number() == 0: + return R.one() + + t, = R.gens() + lc = {'z': t} + return R(eval_knotinfo(conway_polynomial, locals=lc)) + + @cached_method + def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False): + r""" + Return the Khovanov polynomial according to the value of column + ``khovanov_polynomial`` for this knot or link as an instance of + :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`. + + INPUT: + + - ``var1`` -- (default: ``'q'``) the first variable + - ``var2`` -- (default: ``'t'``) the second variable + - ``base_ring`` -- (default: ``ZZ``) the ring of the polynomial's + coefficients + - ``original`` -- boolean (optional, default ``False``) if set to + ``True`` the original table entry is returned as a string + + OUTPUT: + + A Laurent polynomial over the integers, more precisely an instance of + :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`. + If ``original`` is set to ``True`` then a string is returned. + + .. NOTE :: + + The Khovanov polynomial given in KnotInfo corresponds to the mirror + image of the given knot for a `list of 140 exceptions + <https://raw.githubusercontent.com/soehms/database_knotinfo/main/hints/list_of_mirrored_khovanov_polynonmial.txt>`__. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import KnotInfo + sage: K = KnotInfo.K6_3 + sage: Kk = K.khovanov_polynomial(); Kk + q^7*t^3 + q^5*t^2 + q^3*t^2 + q^3*t + q*t + 2*q + 2*q^-1 + q^-1*t^-1 + + q^-3*t^-1 + q^-3*t^-2 + q^-5*t^-2 + q^-7*t^-3 + sage: Kk2 = K.khovanov_polynomial(var1='p', base_ring=GF(2)); Kk2 + p^7*t^3 + p^5*t^3 + p^5*t^2 + p^3*t + p^-1 + p^-1*t^-1 + p^-3*t^-2 + p^-7*t^-3 + + sage: L = KnotInfo.L5a1_0 + sage: Lk = L.khovanov_polynomial(); Lk + q^4*t^2 + t + 2 + 2*q^-2 + q^-2*t^-1 + q^-4*t^-2 + q^-6*t^-2 + q^-8*t^-3 + + Comparision to Sage's results:: + + sage: Kk == K.link().khovanov_polynomial() + True + sage: Kk2 == K.link().khovanov_polynomial(var1='p', base_ring=GF(2)) + True + sage: Lk == L.link().khovanov_polynomial() + True + """ + khovanov_polynomial = self[self.items.khovanov_polynomial] + + if original: + return khovanov_polynomial + + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + var_names = [var1, var2] + R = LaurentPolynomialRing(base_ring, var_names) + + if not khovanov_polynomial and self.crossing_number() == 0: + return R({(1, 0): 1, (-1, 0): 1}) + + ch = base_ring.characteristic() + if ch == 2: + if not self.is_knot(): + raise NotImplementedError('Khovanov polynomial available only for knots in characteristic 2') + khovanov_torsion_polynomial = self[self.items.khovanov_torsion_polynomial] + khovanov_torsion_polynomial = khovanov_torsion_polynomial.replace('Q', 'q') + khovanov_polynomial = '%s + %s' % (khovanov_polynomial, khovanov_torsion_polynomial) + + if not khovanov_polynomial: + # given just for links with less than 12 crossings + raise NotImplementedError('Khovanov polynomial not available for this link') + + from sage.repl.preparse import implicit_mul + # since implicit_mul does not know about the choice of variable names + # we have to insert * between them separately + for i in ['q', 't',')']: + for j in ['q', 't', '(']: + khovanov_polynomial = khovanov_polynomial.replace('%s%s' % (i, j), '%s*%s' % (i, j)) + khovanov_polynomial = implicit_mul(khovanov_polynomial) + gens = R.gens_dict() + lc = {} + lc['q'] = gens[var1] + lc['t'] = gens[var2] + + return R(eval_knotinfo(khovanov_polynomial, locals=lc)) @cached_method def link(self, use_item=db.columns().pd_notation, snappy=False): diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 10035f729ef..12e159db251 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -1956,13 +1956,14 @@ def conway_polynomial(self): M = max(alex.exponents()) coeff = alex[M] alex -= coeff * binom**M - conway += coeff * t_poly**M + conway += coeff * t_poly**M return conway def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ): r""" - Return the Khovanov polynomial of ``self``. This is the Poincaré - polynomial of the Khovanov homology. + Return the Khovanov polynomial of ``self``. + + This is the Poincaré polynomial of the Khovanov homology. INPUT: diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index fec450d7bc0..c526b9321a4 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -1,12 +1,5 @@ """ -Dokchitser's L-functions Calculator - -AUTHORS: - -- Tim Dokchitser (2002): original PARI code and algorithm (and the - documentation below is based on Dokchitser's docs). - -- William Stein (2006-03-08): Sage interface +Dokchitser's `L`-functions calculator .. TODO:: @@ -15,6 +8,14 @@ - plug this code into number fields and modular forms code (elliptic curves are done). + +AUTHORS: + +- Tim Dokchitser (2002): original PARI code and algorithm (and the + documentation below is based on Dokchitser's docs). + +- William Stein (2006-03-08): Sage interface + """ # **************************************************************************** @@ -337,6 +338,7 @@ def _gp_eval(self, s): # After init_coeffs is called, future calls to this method should # return the full output for further parsing raise RuntimeError("unable to create L-series, due to precision or other limits in PARI") + t = t.replace(" *** _^_: Warning: normalizing a series with 0 leading term.\n", "") return t def __check_init(self): diff --git a/src/sage/lfunctions/lcalc.py b/src/sage/lfunctions/lcalc.py index b8da008b8c2..d791460039b 100644 --- a/src/sage/lfunctions/lcalc.py +++ b/src/sage/lfunctions/lcalc.py @@ -1,5 +1,5 @@ r""" -Rubinstein's `L`-function Calculator +Rubinstein's `L`-function calculator This interface provides complete access to Rubinstein's lcalc calculator with extra PARI diff --git a/src/sage/lfunctions/pari.py b/src/sage/lfunctions/pari.py index d2b20f1891b..f75b232e595 100644 --- a/src/sage/lfunctions/pari.py +++ b/src/sage/lfunctions/pari.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -L-functions from PARI +`L`-functions from PARI This is a wrapper around the general PARI L-functions functionality. @@ -108,7 +108,7 @@ def __init__(self, conductor, gammaV, weight, eps, poles=[], if args or kwds: self.init_coeffs(*args, **kwds) - def init_coeffs(self, v, cutoff=None, w=1, *args, **kwds): + def init_coeffs(self, v, cutoff=None, w=1): """ Set the coefficients `a_n` of the `L`-series. @@ -126,7 +126,7 @@ def init_coeffs(self, v, cutoff=None, w=1, *args, **kwds): EXAMPLES:: sage: from sage.lfunctions.pari import lfun_generic, LFunction - sage: lf = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) + sage: lf = lfun_generic(conductor=1, gammaV=[0, 1], weight=12, eps=1) sage: pari_coeffs = pari('k->vector(k,n,(5*sigma(n,3)+7*sigma(n,5))*n/12 - 35*sum(k=1,n-1,(6*k-4*(n-k))*sigma(k,3)*sigma(n-k,5)))') sage: lf.init_coeffs(pari_coeffs) @@ -144,7 +144,7 @@ def init_coeffs(self, v, cutoff=None, w=1, *args, **kwds): Illustrate that one can give a list of complex numbers for v (see :trac:`10937`):: - sage: l2 = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) + sage: l2 = lfun_generic(conductor=1, gammaV=[0, 1], weight=12, eps=1) sage: l2.init_coeffs(list(delta_qexp(1000))[1:]) sage: L2 = LFunction(l2) sage: L2(14) @@ -155,20 +155,9 @@ def init_coeffs(self, v, cutoff=None, w=1, *args, **kwds): Verify that setting the `w` parameter does not raise an error (see :trac:`10937`):: - sage: L2 = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) + sage: L2 = lfun_generic(conductor=1, gammaV=[0, 1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:], w=[1..1000]) - - Additional arguments are ignored for compatibility with the old - Dokchitser script:: - - sage: L2.init_coeffs(list(delta_qexp(1000))[1:], foo="bar") - doctest:...: DeprecationWarning: additional arguments for initializing an lfun_generic are ignored - See https://trac.sagemath.org/26098 for details. """ - if args or kwds: - from sage.misc.superseded import deprecation - deprecation(26098, "additional arguments for initializing an lfun_generic are ignored") - v = pari(v) if v.type() not in ('t_CLOSURE', 't_VEC'): raise TypeError("v (coefficients) must be a list or a function") @@ -250,12 +239,12 @@ def lfun_character(chi): Check the values:: - sage: chi = DirichletGroup(24)([1,-1,-1]); chi + sage: chi = DirichletGroup(24)([1, -1, -1]); chi Dirichlet character modulo 24 of conductor 24 mapping 7 |--> 1, 13 |--> -1, 17 |--> -1 sage: Lchi = lfun_character(chi) sage: v = [0] + Lchi.lfunan(30).sage() - sage: all(v[i] == chi(i) for i in (7,13,17)) + sage: all(v[i] == chi(i) for i in (7, 13, 17)) True """ if not chi.is_primitive(): @@ -285,7 +274,7 @@ def lfun_elliptic_curve(E): Over number fields:: sage: K.<a> = QuadraticField(2) - sage: E = EllipticCurve([1,a]) + sage: E = EllipticCurve([1, a]) sage: L = LFunction(lfun_elliptic_curve(E)) sage: L(3) 1.00412346717019 @@ -309,7 +298,7 @@ def lfun_number_field(K): sage: L(3) 1.20205690315959 - sage: K = NumberField(x**2-2, 'a') + sage: K = NumberField(x**2 - 2, 'a') sage: L = LFunction(lfun_number_field(K)) sage: L(3) 1.15202784126080 @@ -338,10 +327,10 @@ def lfun_eta_quotient(scalings, exponents): sage: L(1) 0.0374412812685155 - sage: lfun_eta_quotient([6],[4]) - [[Vecsmall([7]), [Vecsmall([6]), Vecsmall([4])]], 0, [0, 1], 2, 36, 1] + sage: lfun_eta_quotient([6], [4]) + [[Vecsmall([7]), [Vecsmall([6]), Vecsmall([4]), 0]], 0, [0, 1], 2, 36, 1] - sage: lfun_eta_quotient([2,1,4], [5,-2,-2]) + sage: lfun_eta_quotient([2, 1, 4], [5, -2, -2]) Traceback (most recent call last): ... PariError: sorry, noncuspidal eta quotient is not yet implemented @@ -377,7 +366,7 @@ def lfun_quadratic_form(qf): EXAMPLES:: sage: from sage.lfunctions.pari import lfun_quadratic_form, LFunction - sage: Q = QuadraticForm(ZZ,2,[2,3,4]) + sage: Q = QuadraticForm(ZZ, 2, [2, 3, 4]) sage: L = LFunction(lfun_quadratic_form(Q)) sage: L(3) 0.377597233183583 @@ -409,7 +398,7 @@ def lfun_genus2(C): sage: L(3) 0.965946926261520 - sage: C = HyperellipticCurve(x^2+x, x^3+x^2+1) + sage: C = HyperellipticCurve(x^2 + x, x^3 + x^2 + 1) sage: L = LFunction(lfun_genus2(C)) sage: L(2) 0.364286342944359 @@ -445,11 +434,11 @@ class LFunction(SageObject): 0.000000000000000 sage: L.derivative(1) 0.305999773834052 - sage: L.derivative(1,2) + sage: L.derivative(1, 2) 0.373095594536324 sage: L.num_coeffs() 50 - sage: L.taylor_series(1,4) + sage: L.taylor_series(1, 4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) sage: L.check_functional_equation() # abs tol 4e-19 1.08420217248550e-19 @@ -463,9 +452,9 @@ class LFunction(SageObject): sage: L = E.lseries().dokchitser(algorithm="pari") sage: L.num_coeffs() 163 - sage: L.derivative(1,E.rank()) + sage: L.derivative(1, E.rank()) 1.51863300057685 - sage: L.taylor_series(1,4) + sage: L.taylor_series(1, 4) ...e-19 + (...e-19)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) .. RUBRIC:: Number field @@ -481,7 +470,7 @@ class LFunction(SageObject): 348 sage: L(2) 1.10398438736918 - sage: L.taylor_series(2,3) + sage: L.taylor_series(2, 3) 1.10398438736918 - 0.215822638498759*z + 0.279836437522536*z^2 + O(z^3) .. RUBRIC:: Ramanujan `\Delta` L-function @@ -489,7 +478,7 @@ class LFunction(SageObject): The coefficients are given by Ramanujan's tau function:: sage: from sage.lfunctions.pari import lfun_generic, LFunction - sage: lf = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) + sage: lf = lfun_generic(conductor=1, gammaV=[0, 1], weight=12, eps=1) sage: tau = pari('k->vector(k,n,(5*sigma(n,3)+7*sigma(n,5))*n/12 - 35*sum(k=1,n-1,(6*k-4*(n-k))*sigma(k,3)*sigma(n-k,5)))') sage: lf.init_coeffs(tau) sage: L = LFunction(lf) @@ -498,7 +487,7 @@ class LFunction(SageObject): sage: L(1) 0.0374412812685155 - sage: L.taylor_series(1,3) + sage: L.taylor_series(1, 3) 0.0374412812685155 + 0.0709221123619322*z + 0.0380744761270520*z^2 + O(z^3) """ def __init__(self, lfun, prec=None): @@ -608,7 +597,7 @@ def Lambda(self, s): sage: L = LFunction(lfun_number_field(QQ)) sage: L.Lambda(2) 0.523598775598299 - sage: L.Lambda(1-2) + sage: L.Lambda(1 - 2) 0.523598775598299 """ s = self._CCin(s) @@ -630,7 +619,7 @@ def hardy(self, t): TESTS:: - sage: L.hardy(.4+.3*I) + sage: L.hardy(.4 + .3*I) Traceback (most recent call last): ... PariError: incorrect type in lfunhardy (t_COMPLEX) @@ -694,7 +683,7 @@ def taylor_series(self, s, k=6, var='z'): sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(200,algorithm="pari") - sage: L.taylor_series(1,3) + sage: L.taylor_series(1, 3) 2...e-63 + (...e-63)*z + 0.75931650028842677023019260789472201907809751649492435158581*z^2 + O(z^3) Check that :trac:`25402` is fixed:: @@ -757,7 +746,7 @@ def __call__(self, s): sage: L = E.lseries().dokchitser(100, algorithm="pari") sage: L(1) 0.00000000000000000000000000000 - sage: L(1+I) + sage: L(1 + I) -1.3085436607849493358323930438 + 0.81298000036784359634835412129*I """ s = self._CC(s) diff --git a/src/sage/lfunctions/sympow.py b/src/sage/lfunctions/sympow.py index 7af1940f62b..4cf14233aa2 100644 --- a/src/sage/lfunctions/sympow.py +++ b/src/sage/lfunctions/sympow.py @@ -1,5 +1,5 @@ r""" -Watkins Symmetric Power `L`-function Calculator +Watkins symmetric power `L`-function calculator SYMPOW is a package to compute special values of symmetric power elliptic curve L-functions. It can compute up to about 64 digits of diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index a67ad9d8ebb..2b0ca332b2a 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -1,5 +1,5 @@ r""" -Class file for computing sums over zeros of motivic L-functions. +Class for computing sums over zeros of motivic `L`-functions All computations are done to double precision. diff --git a/src/sage/libs/arb/arith.pyx b/src/sage/libs/arb/arith.pyx index 31db27d6bb6..d885f97b238 100644 --- a/src/sage/libs/arb/arith.pyx +++ b/src/sage/libs/arb/arith.pyx @@ -57,6 +57,7 @@ def bernoulli(n): fmpq_clear(x) return q + def hilbert_class_polynomial(D): """ Return the Hilbert class polynomial for discriminant ``D`` using ``arb``. @@ -82,4 +83,3 @@ def hilbert_class_polynomial(D): poly = PolynomialRing(ZZ, "x", implementation="FLINT")() acb_modular_hilbert_class_poly(poly.__poly, n) return poly - diff --git a/src/sage/libs/braiding.pyx b/src/sage/libs/braiding.pyx index 9178d259b13..e8f37dad588 100644 --- a/src/sage/libs/braiding.pyx +++ b/src/sage/libs/braiding.pyx @@ -384,7 +384,6 @@ def sliding_circuits(braid): [[[0], [2, 1], [1, 2], [2]]], [[[0], [1, 2], [2], [2, 1]]], [[[0], [2, 1], [1], [1, 2]]]] - """ nstrands = braid.parent().strands() l = braid.Tietze() @@ -392,4 +391,3 @@ def sliding_circuits(braid): cdef list[list[list[list[int]]]] rop = SlidingCircuits(nstrands, l) sig_off() return rop - diff --git a/src/sage/libs/coxeter3/coxeter.pyx b/src/sage/libs/coxeter3/coxeter.pyx index bc64f99d953..9ef1d075be5 100644 --- a/src/sage/libs/coxeter3/coxeter.pyx +++ b/src/sage/libs/coxeter3/coxeter.pyx @@ -980,7 +980,7 @@ cdef class CoxGroupElement: cdef Length i = 0 cdef CoxGroupElement res - for i from 0 <= i < list.size(): + for i in range(list.size()): res = self._new() res.word = list[i] coatoms.append(res) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 2cfac57f544..8f46570313d 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -429,16 +429,14 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ECL_NIL elif pyobj is None: return ECL_NIL - elif isinstance(pyobj,long): - if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: + elif isinstance(pyobj, int): + if MOST_NEGATIVE_FIXNUM <= pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: return python_to_ecl(Integer(pyobj), read_strings) - elif isinstance(pyobj,int): - return ecl_make_integer(pyobj) - elif isinstance(pyobj,float): + elif isinstance(pyobj, float): return ecl_make_doublefloat(pyobj) - elif isinstance(pyobj,unicode): + elif isinstance(pyobj, unicode): try: s = str_to_bytes(pyobj, 'ascii') except UnicodeEncodeError: @@ -452,22 +450,22 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ecl_safe_funcall(read_from_string_clobj, o) else: return o - elif isinstance(pyobj,bytes): + elif isinstance(pyobj, bytes): s=<bytes>pyobj if read_strings: return ecl_safe_read_string(s) else: return ecl_cstring_to_base_string_or_nil(s) - elif isinstance(pyobj,Integer): + elif isinstance(pyobj, Integer): if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: return ecl_bignum_from_mpz( (<Integer>pyobj).value ) - elif isinstance(pyobj,Rational): + elif isinstance(pyobj, Rational): return ecl_make_ratio( python_to_ecl( (<Rational>pyobj).numerator(), read_strings ), python_to_ecl( (<Rational>pyobj).denominator(), read_strings )) - elif isinstance(pyobj,EclObject): + elif isinstance(pyobj, EclObject): return (<EclObject>pyobj).obj elif isinstance(pyobj, list): L = ECL_NIL diff --git a/src/sage/libs/eclib/homspace.pyx b/src/sage/libs/eclib/homspace.pyx index 9868795ab20..3feea84fda7 100644 --- a/src/sage/libs/eclib/homspace.pyx +++ b/src/sage/libs/eclib/homspace.pyx @@ -283,6 +283,4 @@ cdef class ModularSymbols: inc(iter) MS = MatrixSpace(base_ring, n, sparse=True) # The next step is the bottleneck. - ans = MS(d) - return ans - + return MS(d) diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 119a68ba34d..0faee97661e 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -731,9 +731,9 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 7) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 @@ -741,10 +741,10 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 4 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) @@ -756,9 +756,9 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) @@ -911,10 +911,10 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 93 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 ] - Checking 2-saturation + Checking 2-saturation possible kernel vector = [1,0,0] This point may be in 2E(Q): [1547:-2967:343] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [-2:3:1] Reducing index bound from 93 to 46 Points have successfully been 2-saturated (max q used = 11) @@ -933,12 +933,12 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 46 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation possible kernel vector = [0,1,0] This point may be in 3E(Q): [2707496766203306:864581029138191:2969715140223272] - ...and it is! + ...and it is! Replacing old generator #2 with new generator [-14:25:8] Reducing index bound from 46 to 15 Points have successfully been 3-saturated (max q used = 13) @@ -957,14 +957,14 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 15 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 5 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) - Checking 5-saturation + Checking 5-saturation possible kernel vector = [0,0,1] This point may be in 5E(Q): [-13422227300:-49322830557:12167000000] - ...and it is! + ...and it is! Replacing old generator #3 with new generator [1:-1:1] Reducing index bound from 15 to 3 Points have successfully been 5-saturated (max q used = 71) @@ -981,9 +981,9 @@ def process(self, v, saturation_bound=0): saturating basis...Saturation index bound (for points of good reduction) = 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done (True, 1, '[ ]') @@ -1329,6 +1329,5 @@ def points(self): P4 = [12:35:27] = 1*P1 + -1*P2 + -1*P3 (mod torsion) sage: EQ.points() [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] - """ return self.__mw.getbasis() diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index 7df61a54f12..119d4d49dd4 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -83,7 +83,7 @@ mwrank_set_precision(150) def get_precision(): """ - Returns the working floating point bit precision of mwrank, which is + Return the working floating point bit precision of mwrank, which is equal to the global NTL real number precision. OUTPUT: @@ -119,7 +119,7 @@ def set_precision(n): This change is global and affects *all* future calls of eclib functions by Sage. - .. note:: + .. NOTE:: The minimal value to which the precision may be set is 53. Lower values will be increased to 53. @@ -335,7 +335,7 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class return string_sigoff(Curvedata_repr(self.x))[:-1] def silverman_bound(self): - """ + r""" The Silverman height bound for this elliptic curve. OUTPUT: @@ -345,7 +345,7 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class + B`, where `h(P)` is the naive height and `\hat{h}(P)` the canonical height. - .. note:: + .. NOTE:: Since eclib can compute this to arbitrary precision, we could return a Sage real, but this is only a bound and in @@ -364,7 +364,7 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class return Curvedata_silverman_bound(self.x) def cps_bound(self): - """ + r""" The Cremona-Prickett-Siksek height bound for this elliptic curve. OUTPUT: @@ -374,12 +374,11 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class + B`, where `h(P)` is the naive height and `\hat{h}(P)` the canonical height. - .. note:: + .. NOTE:: - Since eclib can compute this to arbitrary precision, we + Since ``eclib`` can compute this to arbitrary precision, we could return a Sage real, but this is only a bound and in - the contexts in which it is used extra precision is - irrelevant. + the contexts in which it is used extra precision is irrelevant. EXAMPLES:: @@ -399,7 +398,7 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class return x def height_constant(self): - """ + r""" A height bound for this elliptic curve. OUTPUT: @@ -410,12 +409,11 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class canonical height. This is the minimum of the Silverman and Cremona_Prickett-Siksek height bounds. - .. note:: + .. NOTE:: - Since eclib can compute this to arbitrary precision, we + Since ``eclib`` can compute this to arbitrary precision, we could return a Sage real, but this is only a bound and in - the contexts in which it is used extra precision is - irrelevant. + the contexts in which it is used extra precision is irrelevant. EXAMPLES:: @@ -542,7 +540,7 @@ cdef class _mw: cdef int verb def __init__(self, _Curvedata curve, verb=False, pp=1, maxr=999): - """ + r""" Constructor for mw class. INPUT: @@ -704,10 +702,10 @@ cdef class _mw: .. NOTE:: - The eclib function which implements this only carries out - any saturation if the rank of the points increases upon - adding the new point. This is because it is assumed that - one saturates as ones goes along. + The eclib function which implements this only carries out + any saturation if the rank of the points increases upon + adding the new point. This is because it is assumed that + one saturates as ones goes along. EXAMPLES:: @@ -753,7 +751,7 @@ cdef class _mw: def getbasis(self): """ - Returns the current basis of the mw structure. + Return the current basis of the mw structure. OUTPUT: @@ -778,7 +776,7 @@ cdef class _mw: def regulator(self): """ - Returns the regulator of the current basis of the mw group. + Return the regulator of the current basis of the mw group. OUTPUT: @@ -810,7 +808,7 @@ cdef class _mw: def rank(self): """ - Returns the rank of the current basis of the mw group. + Return the rank of the current basis of the mw group. OUTPUT: @@ -908,7 +906,7 @@ cdef class _mw: return ok, index, unsat def search(self, h_lim, int moduli_option=0, int verb=0): - """ + r""" Search for points in the mw group. INPUT: @@ -926,17 +924,16 @@ cdef class _mw: .. NOTE:: - The effect of the search is also governed by the class - options, notably whether the points found are processed: - meaning that linear relations are found and saturation is - carried out, with the result that the list of generators - will always contain a `\ZZ`-span of the saturation of the - points found, modulo torsion. + The effect of the search is also governed by the class + options, notably whether the points found are processed: + meaning that linear relations are found and saturation is + carried out, with the result that the list of generators + will always contain a `\ZZ`-span of the saturation of the + points found, modulo torsion. OUTPUT: - None. The effect of the search is to update the list of - generators. + None. The effect of the search is to update the list of generators. EXAMPLES:: @@ -1069,7 +1066,7 @@ cdef class _two_descent: def getrank(self): """ - Returns the rank (after doing a 2-descent). + Return the rank (after doing a 2-descent). OUTPUT: @@ -1102,7 +1099,7 @@ cdef class _two_descent: def getrankbound(self): """ - Returns the rank upper bound (after doing a 2-descent). + Return the rank upper bound (after doing a 2-descent). OUTPUT: @@ -1135,7 +1132,7 @@ cdef class _two_descent: def getselmer(self): """ - Returns the 2-Selmer rank (after doing a 2-descent). + Return the 2-Selmer rank (after doing a 2-descent). OUTPUT: @@ -1167,7 +1164,7 @@ cdef class _two_descent: def ok(self): """ - Returns the success flag (after doing a 2-descent). + Return the success flag (after doing a 2-descent). OUTPUT: @@ -1196,7 +1193,7 @@ cdef class _two_descent: def getcertain(self): """ - Returns the certainty flag (after doing a 2-descent). + Return the certainty flag (after doing a 2-descent). OUTPUT: @@ -1273,8 +1270,8 @@ cdef class _two_descent: sig_off() def getbasis(self): - """ - Returns the basis of points found by doing a 2-descent. + r""" + Return the basis of points found by doing a 2-descent. If the success and certain flags are 1, this will be a `\ZZ/2\ZZ`-basis for `E(\QQ)/2E(\QQ)` (modulo torsion), @@ -1282,7 +1279,8 @@ cdef class _two_descent: .. NOTE:: - You must call ``saturate()`` first, or a RunTimeError will be raised. + You must call ``saturate()`` first, or a ``RunTimeError`` + will be raised. OUTPUT: @@ -1320,7 +1318,7 @@ cdef class _two_descent: def regulator(self): """ - Returns the regulator of the points found by doing a 2-descent. + Return the regulator of the points found by doing a 2-descent. OUTPUT: diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index d30ac0e3758..2d35716c4db 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -22,7 +22,7 @@ from sage.modular.all import Cusp cdef class ECModularSymbol: - """ + r""" Modular symbol associated with an elliptic curve, using John Cremona's newforms class. EXAMPLES:: diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx deleted file mode 100644 index bf62c33e265..00000000000 --- a/src/sage/libs/fes.pyx +++ /dev/null @@ -1,343 +0,0 @@ -# distutils: language = c -# distutils: libraries = fes -# sage_setup: distribution = sagemath-fes - -""" -Binding for the FES library - -Finding solutions of systems of boolean equations by exhaustive -search, via the fes library. This is usually (much) faster than -computing a Groebner basis, except in special cases where the latter -is particularly easy. - -The FES library is presently only able to deal with polynomials in 64 -variables. Performing a full exhaustive search over 64 variables will -take a **long** time. The number of variables can be artificially -reduced to 64 by specializing some of them. - -Note that the FES library **requires** at least of the equations to be -non-linear. - -AUTHORS: - -- Charles Bouillaguet (2012-12-20) : initial version - -EXAMPLES: - -Random Degree-2 System:: - - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: n = 16 # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: solution = dict(zip(R.gens(), VectorSpace(GF(2), n).random_element())) # optional - FES - - sage: F = [ R.random_element() for i in range(n+10) ] # optional - FES - sage: G = [ f + f.subs(solution) for f in F] # optional - FES - sage: sols = exhaustive_search(G) # optional - FES - sage: solution in sols # optional - FES - True - sage: len(sols) # optional - FES - 1 - -Cyclic benchmark:: - - sage: from sage.rings.ideal import Cyclic # optional - FES - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: n = 10 # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: sols = exhaustive_search( Cyclic(R) ) # optional - FES - sage: len(sols) # optional - FES - 1 - sage: set(sols[0]) == set(R.gens()) # optional - FES - True - -REFERENCES: - -- [BCCCNSY2010]_ -""" - -#***************************************************************************** -# Copyright (C) 2012 Charles Bouillaguet <charles.bouillaguet@lifl.fr> -# -# 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 libc.stdint cimport uint64_t -from cysignals.memory cimport sig_calloc, sig_free -from cysignals.signals cimport sig_on, sig_off - -cdef extern from "fes_interface.h": - ctypedef int (*solution_callback_t)(void *, uint64_t) - - void exhaustive_search_wrapper(int n, int n_eqs, int degree, int ***coeffs, solution_callback_t callback, void* callback_state, int verbose) - - -from sage.rings.integer import Integer -from sage.rings.infinity import Infinity -from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - -from sage.structure.parent cimport Parent -from sage.structure.sequence import Sequence -from sage.rings.polynomial.multi_polynomial cimport MPolynomial -from sage.rings.polynomial.term_order import TermOrder -from sage.rings.polynomial.pbori.pbori import BooleanPolynomial, BooleanPolynomialRing -from sage.arith.all import binomial -from sage.combinat.subset import Subsets - -from sage.matrix.all import * -from sage.modules.all import * - - - -class InternalState: - verbose = False - sols = [] - max_sols = 0 - - -cdef int report_solution(void *_state, uint64_t i): -# This is the callback function which is invoked by the fes library -# each time a solution is found. ``i`` describes the solution (least -# significant bit gives the value of the first variable, most -# significant bit gives the value of the last variable). ``state`` is -# the pointer passed to the fes library initially. - cdef object state = <object> _state - state.sols.append(i) - if state.verbose: - print("fes: solution {0} / {1} found : {2:x}".format(len(state.sols), state.max_sols, i)) - if (state.max_sols > 0 and state.max_sols >= len(state.sols)): - return 1 #stop the library - return 0 # keep going - - -def exhaustive_search(eqs, max_sols=Infinity, verbose=False): - r""" - Invokes the fes library to solve a system of boolean equation by - exhaustive search. - - INPUT: - - - ``eqs`` -- list of boolean equations - - - ``max_sols`` -- stop after this many solutions are found - - - ``verbose`` -- whether the library should display information about - its work - - NOTE: - - Using this function requires the optional package FES to be installed. - - EXAMPLES: - - A very simple example:: - - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: R.<x,y,z> = BooleanPolynomialRing() # optional - FES - sage: exhaustive_search( [x*y + z + y + 1, x+y+z, x*y + y*z] ) # optional - FES - [{y: 0, z: 1, x: 1}] - - Another very simple example:: - - sage: R.<x,y,z,t,w> = BooleanPolynomialRing() # optional - FES - sage: f = x*y*z + z + y + 1 # optional - FES - sage: sorted( exhaustive_search( [f, x+y+z, x*y + y*z] ) ) # optional - FES - [{w: 0, t: 0, y: 0, z: 1, x: 1}, {w: 0, t: 1, y: 0, z: 1, x: 1}, {w: 1, t: 0, y: 0, z: 1, x: 1}, {w: 1, t: 1, y: 0, z: 1, x: 1}] - - An example with several solutions:: - - sage: R.<x,y,z, t> = BooleanPolynomialRing() # optional - FES - sage: eqs = [x*z + y*t + 1, x*y + y*z + z*t + t*x , x+y+z+1] # optional - FES - sage: sorted( exhaustive_search( eqs ) ) # optional - FES - [{t: 0, y: 1, z: 1, x: 1}, {t: 1, y: 1, z: 0, x: 0}] - - We voluntarily limit the number of solutions returned by the library:: - - sage: eqs = [x*z + y*t + 1, x*y + y*z + z*t + t*x , x+y+z+1] # optional - FES - sage: exhaustive_search(eqs, max_sols=1 ) # optional - FES - [{t: 0, y: 1, z: 1, x: 1}] - - .. SEEALSO:: - - This function should return the same solutions as - :meth:`~sage.rings.polynomial.pbori.BooleanPolynomialIdeal.variety` - on a :class:`~sage.rings.polynomial.pbori.BooleanPolynomialIdeal`. - - .. NOTE:: - - The order in which the solutions are returned is implementation - dependent ; it may vary accross machines, and may vary accross - calls to the function. - - """ - if eqs == []: - raise ValueError("No equations, no solutions") - - eqs = Sequence(eqs) - R = eqs.ring() - if R.base_ring() != GF(2): - raise ValueError("FES only deals with equations over GF(2)") - - degree = max( [f.degree() for f in eqs] ) - if degree <= 1: - raise ValueError("the FES library requires equations to be non-linear") - n = R.ngens() - - - # ------- initialize a data-structure to communicate the equations to the library - cdef int ***coeffs = <int ***> sig_calloc(len(eqs), sizeof(int **)) - for e,f in enumerate(eqs): - coeffs[e] = <int **> sig_calloc(degree+1, sizeof(int *)) - for d in range(degree+1): - coeffs[e][d] = <int *> sig_calloc(binomial(n,d), sizeof(int)) - - for m in f: # we enumerate the monomials of f - d = m.degree() - temp_n = n - int_idx = 0 - prev_var = -1 - for idx in sorted(m.iterindex()): # iterate over all the variables in the monomial - j = idx - prev_var - 1 - for k in range(j): - int_idx += binomial(temp_n-k-1, d-1) - prev_var = idx - d -= 1 - temp_n -= 1 + j - coeffs[e][ m.degree() ][int_idx] = 1 - - internal_state = InternalState() - internal_state.verbose = verbose - internal_state.sols = [] - if max_sols == Infinity: - internal_state.max_sols = 0 - else: - internal_state.max_sols = max_sols - - # ------- runs the library - sig_on() - exhaustive_search_wrapper(n, len(eqs), degree, coeffs, report_solution, <void *> internal_state, verbose) - sig_off() - - # ------- frees memory occupied by the dense representation of the equations - if coeffs != NULL: - for e in range(len(eqs)): - if coeffs[e] != NULL: - for d in range(degree+1): - if coeffs[e][d] != NULL: - sig_free(coeffs[e][d]) - sig_free(coeffs[e]) - sig_free(coeffs) - - # ------ convert (packed) solutions to suitable format - dict_sols = [] - for i in internal_state.sols: - sol = dict([]) - for x in R.gens(): - sol[x] = GF(2)(i & 1) - i = i >> 1 - dict_sols.append( sol ) - - return dict_sols - - -def find_coordinate_change(As, max_tries=64): - """ - Tries to find a linear change of coordinates such that certain - coefficients of the quadratic forms As become zero. This in turn - makes the exhaustive search faster - - EXAMPLES: - - Testing that the function works:: - - sage: from sage.libs.fes import find_coordinate_change # optional - FES - sage: n = 40 # optional - FES - sage: K = GF(2) # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: foo = [ MatrixSpace(GF(2), n, n).random_element() ] # optional - FES - sage: f = [ M + M.T for M in foo ] # optional - FES - sage: S = find_coordinate_change( f ) # optional - FES - sage: g = [ S.T*M*S for M in f ] # optional - FES - sage: vector ([ M[0,1] for M in g[:32]]).is_zero() # optional - FES - True - """ - n = As[0].nrows() - m = min(32, len(As)) - system = matrix(GF(2), m, n-2) - S = identity_matrix(GF(2), n) - Bs = As - - for foo in range(max_tries): - for i in range(m): - system[i] = Bs[i][0,2:] - RHS = vector(GF(2), m, [ Bs[i][0,1] for i in range(m) ]) - try: - solution = system.solve_right( RHS ) - S_prime = identity_matrix(GF(2), n) - S_prime[1,2:] = solution - return S*S_prime.T - except ValueError: - S.randomize() - while not S.is_invertible(): - S.randomize() - Bs = [ S.T*M*S for M in As ] - print("trying again...") - raise ValueError("Could not find suitable coordinate change") - - - - -def prepare_polynomials(f): - """ - Finds a linear combination of the equations that is faster to solve by FES - - INPUT: - - - ``f`` -- list of boolean equations - - EXAMPLES: - - We check that the function does what it is supposed to do:: - - sage: from sage.libs.fes import prepare_polynomials # optional - FES - sage: n = 35 # optional - FES - sage: K = GF(2) # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: f = [ sum( [K.random_element() * R.gen(i) * R.gen(j) for i in range(n) for j in range(i)] ) \ - + sum( [K.random_element() * R.gen(i) for i in range(n) ] ) \ - + K.random_element() for l in range(n) ] # optional - FES - sage: g = prepare_polynomials(f) # optional - FES - sage: [x.lm() for x in g] # optional - FES, random - 0 - """ - if f == []: - return [] - excess = len(f) - 32 - - if excess <= 0: - return f - - # now, try to cancel the `excess` first monomials - s = Sequence(f) - - # switch to degrevlex if not already there - R = s.ring() - if R.term_order() != 'degrevlex': - R2 = BooleanPolynomialRing( R.ngens(), R.variable_names(), order='degrevlex') - s = Sequence( [R2(g) for g in s] ) - - monomials_in_s = list( s.monomials() ) - monomials_in_s.sort(reverse=True) - - m = matrix(R.base_ring(), [ [ g.monomial_coefficient(m) for m in monomials_in_s[:excess] ] for g in s ]) - # now find the linear combinations of the equations that kills the first `excess` monomials in all but `excess` equations - # todo, this is very likely suboptimal, but m.echelonize() does not return the transformation... - P, L, U = m.LU() - S = (P*L).I - result = Sequence( S * vector(s) ) - result.reverse() - return result diff --git a/src/sage/libs/flint/flint_ntl_wrap.h b/src/sage/libs/flint/flint_ntl_wrap.h index 841d990817d..c0ac48af7aa 100644 --- a/src/sage/libs/flint/flint_ntl_wrap.h +++ b/src/sage/libs/flint/flint_ntl_wrap.h @@ -8,7 +8,7 @@ #include <gmp.h> -/* Save previous definition of ulong if any, as zn_poly and pari also use it */ +/* Save previous definition of ulong if any, as pari also uses it */ #pragma push_macro("ulong") #undef ulong diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index ae02e473a95..1bfb23d0182 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -1,8 +1,8 @@ #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H -/* Using flint headers together in the same module as headers from some other - * libraries (zn_poly, pari, possibly others) as it defines the macros ulong - * and slong all over the place. +/* Using flint headers together in the same module as headers from + * some other libraries (pari, possibly others) as it defines the + * macros ulong and slong all over the place. * * What's worse is they are defined to types from GMP (mp_limb_t and * mp_limb_signed_t respectively) which themselves can have system-dependent @@ -16,7 +16,7 @@ #include <gmp.h> -/* Save previous definition of ulong if any, as zn_poly and pari also use it */ +/* Save previous definition of ulong if any, as pari also uses it */ /* Should work on GCC, clang, MSVC */ #pragma push_macro("ulong") #undef ulong diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index d894478ba60..23df744b440 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -440,7 +440,6 @@ cdef class Fmpz_poly(SageObject): cdef long nn = n fmpz_poly_truncate(self.poly, nn) # mutating! - def _sage_(self, var='x'): """ Return self as an element of the sage ZZ[var]. @@ -456,4 +455,3 @@ cdef class Fmpz_poly(SageObject): """ from sage.rings.integer_ring import ZZ return ZZ[var](self.list()) - diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index be43c4c3ee0..c555ea0333c 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -264,7 +264,7 @@ cdef Obj make_gap_string(sage_string) except NULL: try: GAP_Enter() b = str_to_bytes(sage_string) - C_NEW_STRING(result, len(b), b) + result = MakeStringWithLen(b, len(b)) return result finally: GAP_Leave() @@ -1847,7 +1847,7 @@ cdef class GapElement_FiniteField(GapElement): if ring is None: from sage.rings.finite_rings.finite_field_constructor import GF ring = GF(char**deg, name=var) - elif not (ring.is_field() and ring.is_finite() and \ + elif not (ring.is_field() and ring.is_finite() and ring.characteristic() == char and ring.degree() % deg == 0): raise ValueError(('the given ring is incompatible (must be a ' 'finite field of characteristic {} and degree ' diff --git a/src/sage/libs/gap/gap_includes.pxd b/src/sage/libs/gap/gap_includes.pxd index 5a9ab486f77..6d22e32540b 100644 --- a/src/sage/libs/gap/gap_includes.pxd +++ b/src/sage/libs/gap/gap_includes.pxd @@ -182,4 +182,4 @@ cdef extern from "gap/stringobj.h" nogil: bint IS_STRING(Obj obj) bint IsStringConv(Obj obj) Obj NEW_STRING(Int) - void C_NEW_STRING(Obj new_gap_string, int length, char* c_string) + Obj MakeStringWithLen(const char* buf, size_t len) diff --git a/src/sage/libs/giac/giac.pyx b/src/sage/libs/giac/giac.pyx index 4e451dba5e7..ccad5169836 100644 --- a/src/sage/libs/giac/giac.pyx +++ b/src/sage/libs/giac/giac.pyx @@ -374,8 +374,7 @@ def _giac(s): sage: x = libgiac('x') sage: (1+2*sin(3*x)).solve(x).simplify() - Warning, argument is not an equation, solving 1+2*sin(3*x)=0 - list[-pi/18,7*pi/18] + ...list[-pi/18,7*pi/18] sage: libgiac.solve('sin(3*x)>2*sin(x)',x) Traceback (most recent call last): diff --git a/src/sage/libs/gmp/pylong.pyx b/src/sage/libs/gmp/pylong.pyx index 388be32c55e..2f7ed35be9d 100644 --- a/src/sage/libs/gmp/pylong.pyx +++ b/src/sage/libs/gmp/pylong.pyx @@ -32,7 +32,15 @@ from cpython.longintrepr cimport _PyLong_New, py_long, digit, PyLong_SHIFT from .mpz cimport * cdef extern from *: - Py_ssize_t* Py_SIZE_PTR "&Py_SIZE"(object) + """ + /* Compatibility for python 3.8, can be removed later */ + #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) + static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) + { ob->ob_size = size; } + #define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) + #endif + """ + void Py_SET_SIZE(object, Py_ssize_t) int hash_bits """ #ifdef _PyHASH_BITS _PyHASH_BITS /* Python 3 */ @@ -57,10 +65,8 @@ cdef mpz_get_pylong_large(mpz_srcptr z): mpz_export(L.ob_digit, NULL, -1, sizeof(digit), 0, PyLong_nails, z) if mpz_sgn(z) < 0: - # Set correct size (use a pointer to hack around Cython's - # non-support for lvalues). - sizeptr = Py_SIZE_PTR(L) - sizeptr[0] = -pylong_size + # Set correct size + Py_SET_SIZE(L, -pylong_size) return L diff --git a/src/sage/libs/homfly.pyx b/src/sage/libs/homfly.pyx index 0493908fae2..550c5f02a65 100644 --- a/src/sage/libs/homfly.pyx +++ b/src/sage/libs/homfly.pyx @@ -102,9 +102,8 @@ def homfly_polynomial_dict(link): cdef Poly* c_output = homfly(link) sig_off() cdef int l = c_output.len - d = dict() + d = {} for i in range(l): ter = c_output.term[i] d[(int(ter.l), int(ter.m))] = int(ter.coef) return d - diff --git a/src/sage/libs/linkages/__init__.py b/src/sage/libs/linkages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/linkages/padics/relaxed/flint.pxi b/src/sage/libs/linkages/padics/relaxed/flint.pxi index 3422f3ff328..5955ea23e18 100644 --- a/src/sage/libs/linkages/padics/relaxed/flint.pxi +++ b/src/sage/libs/linkages/padics/relaxed/flint.pxi @@ -259,7 +259,7 @@ cdef inline void digit_smallest(cdigit res, cdigit carry, cdigit a, PowComputer_ - ``a`` -- a ``cdigit``, the digit to reduce - ``prime_pow`` -- the PowComputer for the ring - NOTE:: + .. NOTE:: This function assumes that ``a`` is always reduced in the usual sense, that is belongs to the range `[0, p-1]`. @@ -383,7 +383,7 @@ cdef inline void element_get_slice(fmpz_poly_t res, fmpz_poly_t x, slong start, - ``start`` -- an integer, the start position of the slice - ``length`` -- an integer, the length of the slice - NOTE:: + .. NOTE:: The function only sets up a pointer to the requested slice (the slice is not copied). Hence any future modification diff --git a/src/sage/libs/mpmath/ext_impl.pyx b/src/sage/libs/mpmath/ext_impl.pyx index f9b0ff2d42c..15557561806 100644 --- a/src/sage/libs/mpmath/ext_impl.pyx +++ b/src/sage/libs/mpmath/ext_impl.pyx @@ -44,10 +44,8 @@ from sage.rings.integer cimport Integer from sage.libs.gmp.pylong cimport * cdef mpz_set_integer(mpz_t v, x): - if isinstance(x, long): + if isinstance(x, int): mpz_set_pylong(v, x) - elif isinstance(x, int): - mpz_set_si(v, PyInt_AS_LONG(x)) elif isinstance(x, Integer): mpz_set(v, (<Integer>x).value) else: diff --git a/src/sage/libs/ntl/ntl_GF2.pyx b/src/sage/libs/ntl/ntl_GF2.pyx index c88034ff5c4..d6ada6cb100 100644 --- a/src/sage/libs/ntl/ntl_GF2.pyx +++ b/src/sage/libs/ntl/ntl_GF2.pyx @@ -116,6 +116,8 @@ cdef class ntl_GF2(): def __mul__(self, other): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: o*o @@ -137,6 +139,8 @@ cdef class ntl_GF2(): def __truediv__(self, other): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: o/o @@ -159,6 +163,8 @@ cdef class ntl_GF2(): def __sub__(self, other): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: o-o @@ -180,6 +186,8 @@ cdef class ntl_GF2(): def __add__(self, other): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: o+o @@ -201,6 +209,8 @@ cdef class ntl_GF2(): def __neg__(ntl_GF2 self): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: -z @@ -214,6 +224,8 @@ cdef class ntl_GF2(): def __pow__(ntl_GF2 self, long e, ignored): """ + EXAMPLES:: + sage: o = ntl.GF2(1) sage: z = ntl.GF2(0) sage: z^2 @@ -254,6 +266,7 @@ def unpickle_class_value(cls, x): """ return cls(x) + def unpickle_class_args(cls, x): """ Here for unpickling. @@ -266,4 +279,3 @@ def unpickle_class_args(cls, x): <class 'sage.libs.ntl.ntl_GF2.ntl_GF2'> """ return cls(*x) - diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index bc6a32ce0d3..54c7eef492a 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # Copyright (C) 2007 Martin Albrecht <malb@informatik.uni-bremen.de> # @@ -18,8 +18,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.ext.cplusplus cimport ccrepr @@ -73,7 +73,7 @@ def ntl_GF2E_random(ntl_GF2EContext_class ctx): cdef class ntl_GF2E(): r""" - The \\class{GF2E} represents a finite extension field over GF(2) + The :class:`GF2E` represents a finite extension field over GF(2) using NTL. Elements are represented as polynomials over GF(2) modulo a modulus. @@ -164,6 +164,8 @@ cdef class ntl_GF2E(): def __reduce__(self): """ + EXAMPLES:: + sage: ctx = ntl.GF2EContext( ntl.GF2X([1,1,0,1,1,0,0,0,1]) ) sage: a = ntl.GF2E(ntl.ZZ_pX([1,1,3],2), ctx) sage: loads(dumps(a)) == a @@ -439,17 +441,20 @@ cdef class ntl_GF2E(): return l def _sage_(ntl_GF2E self, k=None): - """ - Returns a \class{FiniteFieldElement} representation - of this element. If a \class{FiniteField} k is provided - it is constructed in this field if possible. A \class{FiniteField} - will be constructed if none is provided. + r""" + Return a :class:`FiniteFieldElement` representation of this element. + + If a :class:`FiniteField` `k` is provided, it is constructed + in this field if possible. A :class:`FiniteField` will be + constructed if none is provided. INPUT: - k -- optional GF(2**deg) + + - `k` -- (optional) a field `\GF{2^d}` OUTPUT: - FiniteFieldElement over k + + :class:`FiniteFieldElement` over `k` EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index aea1fcf144e..d4581467952 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -160,7 +160,7 @@ cdef class ntl_GF2X(): if x.characteristic() == 2: x = list(x.modulus()) elif isinstance(x, FiniteField_givaroElement): - x = "0x"+hex(x.integer_representation())[2:][::-1] + x = "0x" + hex(x.to_integer())[2:][::-1] elif isinstance(x, FiniteField_ntl_gf2eElement): x = x.polynomial().list() s = str(x).replace(","," ") diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 39a98da2101..48a329f3055 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) @@ -17,8 +17,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.signals cimport sig_on, sig_off from sage.ext.cplusplus cimport ccrepr, ccreadstr @@ -85,11 +85,8 @@ cdef class ntl_ZZ(): """ if isinstance(v, ntl_ZZ): self.x = (<ntl_ZZ>v).x - elif isinstance(v, long): - # Note: This case should be first since on Python 3 long is int - PyLong_to_ZZ(&self.x, v) elif isinstance(v, int): - ZZ_conv_from_int(self.x, PyInt_AS_LONG(v)) + PyLong_to_ZZ(&self.x, v) elif isinstance(v, Integer): self.set_from_sage_int(v) elif v is not None: @@ -176,6 +173,8 @@ cdef class ntl_ZZ(): def __mul__(self, other): """ + EXAMPLES:: + sage: n=ntl.ZZ(2983)*ntl.ZZ(2) sage: n 5966 @@ -192,6 +191,8 @@ cdef class ntl_ZZ(): def __sub__(self, other): """ + EXAMPLES:: + sage: n=ntl.ZZ(2983)-ntl.ZZ(2) sage: n 2981 @@ -208,6 +209,8 @@ cdef class ntl_ZZ(): def __add__(self, other): """ + EXAMPLES:: + sage: n=ntl.ZZ(2983)+ntl.ZZ(2) sage: n 2985 @@ -224,6 +227,8 @@ cdef class ntl_ZZ(): def __neg__(ntl_ZZ self): """ + EXAMPLES:: + sage: x = ntl.ZZ(38) sage: -x -38 @@ -236,6 +241,8 @@ cdef class ntl_ZZ(): def __pow__(ntl_ZZ self, long e, ignored): """ + EXAMPLES:: + sage: ntl.ZZ(23)^50 122008981252869411022491112993141891091036959856659100591281395343249 """ @@ -266,11 +273,12 @@ cdef class ntl_ZZ(): cdef int get_as_int(ntl_ZZ self): r""" Returns value as C int. + Return value is only valid if the result fits into an int. AUTHOR: David Harvey (2006-08-05) """ - cdef int ans + cdef int ans = 0 ZZ_conv_to_int(ans, self.x) return ans @@ -278,12 +286,14 @@ cdef class ntl_ZZ(): r""" This method exists solely for automated testing of get_as_int(). - sage: x = ntl.ZZ(42) - sage: i = x.get_as_int_doctest() - sage: i - 42 - sage: type(i) - <... 'int'> + EXAMPLES:: + + sage: x = ntl.ZZ(42) + sage: i = x.get_as_int_doctest() + sage: i + 42 + sage: type(i) + <... 'int'> """ return self.get_as_int() @@ -291,9 +301,11 @@ cdef class ntl_ZZ(): r""" Gets the value as a sage int. - sage: n=ntl.ZZ(2983) - sage: type(n._integer_()) - <class 'sage.rings.integer.Integer'> + EXAMPLES:: + + sage: n=ntl.ZZ(2983) + sage: type(n._integer_()) + <class 'sage.rings.integer.Integer'> AUTHOR: Joel B. Mohler """ @@ -332,10 +344,12 @@ cdef class ntl_ZZ(): r""" This method exists solely for automated testing of set_from_int(). - sage: x = ntl.ZZ() - sage: x.set_from_int_doctest(42) - sage: x - 42 + EXAMPLES:: + + sage: x = ntl.ZZ() + sage: x.set_from_int_doctest(42) + sage: x + 42 """ self.set_from_int(int(value)) diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 8d38fcb7f8f..e369f7152e4 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) @@ -17,8 +17,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.signals cimport sig_on, sig_off @@ -144,10 +144,12 @@ cdef class ntl_ZZX(): def __reduce__(self): """ - sage: from sage.libs.ntl.ntl_ZZX import ntl_ZZX - sage: f = ntl_ZZX([1,2,0,4]) - sage: loads(dumps(f)) == f - True + EXAMPLES:: + + sage: from sage.libs.ntl.ntl_ZZX import ntl_ZZX + sage: f = ntl_ZZX([1,2,0,4]) + sage: loads(dumps(f)) == f + True """ return unpickle_class_value, (ntl_ZZX, self.list()) @@ -183,6 +185,8 @@ cdef class ntl_ZZX(): def __setitem__(self, long i, a): """ + EXAMPLES:: + sage: n=ntl.ZZX([1,2,3]) sage: n [1 2 3] @@ -692,12 +696,14 @@ cdef class ntl_ZZX(): return (self*other).quo_rem(g)[0] def xgcd(self, ntl_ZZX other, proof=None): - """ - If self and other are coprime over the rationals, return r, s, - t such that r = s*self + t*other. Otherwise return 0. This - is \emph{not} the same as the \sage function on polynomials - over the integers, since here the return value r is always an - integer. + r""" + If ``self`` and ``other`` are coprime over the rationals, + return ``r, s, t`` such that ``r = s*self + t*other``. + Otherwise return 0. + + This is \emph{not} the same as the \sage function on + polynomials over the integers, since here the return value r + is always an integer. Here r is the resultant of a and b; if r != 0, then this function computes s and t such that: a*s + b*t = r; otherwise @@ -709,7 +715,6 @@ cdef class ntl_ZZX(): randomized strategy that errors with probability no more than `2^{-80}`. - EXAMPLES:: sage: f = ntl.ZZX([1,2,3]) * ntl.ZZX([4,5])**2 @@ -717,7 +722,8 @@ cdef class ntl_ZZX(): sage: f.xgcd(g) # nothing since they are not coprime (0, [], []) - In this example the input quadratic polynomials have a common root modulo 13. + In this example the input quadratic polynomials have a common root modulo 13:: + sage: f = ntl.ZZX([5,0,1]) sage: g = ntl.ZZX([18,0,1]) sage: f.xgcd(g) @@ -805,7 +811,8 @@ cdef class ntl_ZZX(): sage: f == g True - Though f and g are equal, they are not the same objects in memory: + Though f and g are equal, they are not the same objects in memory:: + sage: f is g False """ diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pyx b/src/sage/libs/ntl/ntl_ZZ_p.pyx index 9b90383d50c..b5511abb891 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_p.pyx @@ -117,11 +117,9 @@ cdef class ntl_ZZ_p(): if v is not None: if isinstance(v, ntl_ZZ_p): self.x = (<ntl_ZZ_p>v).x - elif isinstance(v, long): + elif isinstance(v, int): PyLong_to_ZZ(&temp, v) self.x = ZZ_to_ZZ_p(temp) - elif isinstance(v, int): - self.x = int_to_ZZ_p(v) elif isinstance(v, Integer): mpz_to_ZZ(&temp, (<Integer>v).value) self.x = ZZ_to_ZZ_p(temp) @@ -166,9 +164,11 @@ cdef class ntl_ZZ_p(): def __reduce__(self): """ - sage: a = ntl.ZZ_p(4,7) - sage: loads(dumps(a)) == a - True + EXAMPLES:: + + sage: a = ntl.ZZ_p(4,7) + sage: loads(dumps(a)) == a + True """ return unpickle_class_args, (ntl_ZZ_p, (self.lift(), self.modulus_context())) diff --git a/src/sage/libs/ntl/ntl_ZZ_pE.pyx b/src/sage/libs/ntl/ntl_ZZ_pE.pyx index 97c658dffb0..ed45e34f0be 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pE.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pE.pyx @@ -117,11 +117,9 @@ cdef class ntl_ZZ_pE(): tmp_zzpx = <ntl_ZZ_pX>ntl_ZZ_pX(v, self.c.pc) self.c.restore_c() # allocating tmp_zzpx can change the current modulus trac #25790 self.x = ZZ_pX_to_ZZ_pE(tmp_zzpx.x) - elif isinstance(v, long): + elif isinstance(v, int): PyLong_to_ZZ(&temp, v) self.x = ZZ_to_ZZ_pE(temp) - elif isinstance(v, int): - self.x = long_to_ZZ_pE(v) elif isinstance(v, ntl_ZZ_p): self.x = ZZ_p_to_ZZ_pE((<ntl_ZZ_p>v).x) elif isinstance(v, ntl_ZZ): @@ -166,9 +164,11 @@ cdef class ntl_ZZ_pE(): def __reduce__(self): """ - sage: a = ntl.ZZ_pE([4],ntl.ZZ_pX([1,1,1],ntl.ZZ(7))) - sage: loads(dumps(a)) == a - True + EXAMPLES:: + + sage: a = ntl.ZZ_pE([4],ntl.ZZ_pX([1,1,1],ntl.ZZ(7))) + sage: loads(dumps(a)) == a + True """ return make_ZZ_pE, (self.get_as_ZZ_pX(), self.get_modulus_context()) diff --git a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx index bef581722bd..b6ff3c66b01 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx @@ -37,13 +37,15 @@ cdef class ntl_ZZ_pEContext_class(): """ EXAMPLES: - # You can construct contexts manually. + You can construct contexts manually:: + sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([4,1,6],25)) sage: n1=c.ZZ_pE([10,17,12]) sage: n1 [2 15] - # or You can construct contexts implicitly. + Or you can construct contexts implicitly:: + sage: n2=ntl.ZZ_pE(12, ntl.ZZ_pX([1,1,1],7)) sage: n2 [5] @@ -65,9 +67,11 @@ cdef class ntl_ZZ_pEContext_class(): def __reduce__(self): """ - sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1],7)) - sage: loads(dumps(c)) is c - True + EXAMPLES:: + + sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1],7)) + sage: loads(dumps(c)) is c + True """ return ntl_ZZ_pEContext, (self.f,) diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index f9d2e982343..d5f10218a77 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -5,14 +5,15 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Wrapper for NTL's polynomials over finite ring extensions of `\Z / p\Z.` AUTHORS: - -- David Roe (2007-10-10) + +- David Roe (2007-10-10) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein <wstein@gmail.com> # David Roe <roed@math.harvard.edu> # @@ -25,8 +26,8 @@ AUTHORS: # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.signals cimport sig_on, sig_off from sage.ext.cplusplus cimport ccrepr diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 95d77f727fa..73151b5d481 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -207,11 +207,13 @@ cdef class ntl_ZZ_pX(): def __setitem__(self, long i, a): r""" - sage: c = ntl.ZZ_pContext(23) - sage: x = ntl.ZZ_pX([2, 3, 4], c) - sage: x[1] = 5 - sage: x - [2 5 4] + EXAMPLES:: + + sage: c = ntl.ZZ_pContext(23) + sage: x = ntl.ZZ_pX([2, 3, 4], c) + sage: x[1] = 5 + sage: x + [2 5 4] """ if i < 0: raise IndexError("index (i=%s) must be >= 0" % i) @@ -276,7 +278,7 @@ cdef class ntl_ZZ_pX(): """ self.c.restore_c() cdef ZZ_p_c r - cdef long l + cdef long l = 0 sig_on() r = ZZ_pX_coeff( self.x, i) ZZ_conv_to_long(l, ZZ_p_rep(r)) @@ -1152,7 +1154,7 @@ cdef class ntl_ZZ_pX(): ZZ_pX_Modulus_build(mod, modulus.x) cdef ntl_ZZ_pX mod_prime cdef ntl_ZZ_pContext_class ctx - cdef long mini, minval + cdef long mini = 0, minval = 0 if Integer(modulus[0].lift()).valuation(p) == 1: eisenstein = True for c in modulus.list()[1:-1]: diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 26575dd99ec..983e3056404 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -139,7 +139,7 @@ cdef class ntl_zz_p(): self.c = <ntl_zz_pContext_class>modulus elif isinstance(modulus, Integer): self.c = <ntl_zz_pContext_class>ntl_zz_pContext(modulus) - elif isinstance(modulus, long): + elif isinstance(modulus, int): self.c = <ntl_zz_pContext_class>ntl_zz_pContext(modulus) else: try: diff --git a/src/sage/libs/ntl/ntl_lzz_pContext.pyx b/src/sage/libs/ntl/ntl_lzz_pContext.pyx index 64301157702..80ff9ac51bc 100644 --- a/src/sage/libs/ntl/ntl_lzz_pContext.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pContext.pyx @@ -60,9 +60,11 @@ cdef class ntl_zz_pContext_class(): def __reduce__(self): """ - sage: c=ntl.zz_pContext(13) - sage: loads(dumps(c)) is c - True + EXAMPLES:: + + sage: c=ntl.zz_pContext(13) + sage: loads(dumps(c)) is c + True """ return ntl_zz_pContext, (self.p,) diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index 50d9447b295..98bd0d58658 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -155,7 +155,7 @@ cdef class ntl_zz_pX(): self.c = <ntl_zz_pContext_class>modulus elif isinstance(modulus, Integer): self.c = <ntl_zz_pContext_class>ntl_zz_pContext(modulus) - elif isinstance(modulus, long): + elif isinstance(modulus, int): self.c = <ntl_zz_pContext_class>ntl_zz_pContext(modulus) else: try: diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index 833b7b9ea8e..ee90bf17fce 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Matrices over the `\GF{2}` via NTL This class is only provided to have a complete NTL interface and for @@ -13,11 +13,11 @@ comparison purposes. Sage's native matrices over `F_2` are much faster for many problems like matrix multiplication and Gaussian elimination. AUTHORS: - - Martin Albrecht <malb@informatik.uni-bremen.de> - 2008-09: initial version + +- Martin Albrecht <malb@informatik.uni-bremen.de> 2008-09: initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # Copyright (C) 2008 Martin Albrecht <malb@informatik.uni-bremen.de> # @@ -30,8 +30,8 @@ AUTHORS: # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.signals cimport sig_on, sig_off from sage.ext.cplusplus cimport ccrepr @@ -131,6 +131,8 @@ cdef class ntl_mat_GF2(): def __reduce__(self): """ + EXAMPLES:: + sage: A = random_matrix(GF(2),4,4) sage: B = ntl.mat_GF2(A) sage: loads(dumps(B)) == B # indirect doctest @@ -375,17 +377,18 @@ cdef class ntl_mat_GF2(): sig_off() return r - def gauss(self,ncols=-1): - """ - Performs unitary row operations so as to bring this matrix - into row echelon form (not reduced!). If the optional - argument \code{ncols} is supplied, stops when first ncols - columns are in echelon form. The return value is the rank (or - the rank of the first ncols columns). + def gauss(self, ncols=-1): + r""" + Perform unitary row operations so as to bring this matrix + into row echelon form (not reduced!). + + If the optional argument ``ncols`` is supplied, stops when + first ``ncols`` columns are in echelon form. The return value is + the rank (or the rank of the first ``ncols`` columns). INPUT: - ncols -- number of columns to process (default: all) + - ``ncols`` -- number of columns to process (default: all) EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index e18d150cbda..f88f20c63c6 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -78,7 +78,7 @@ cdef class ntl_mat_GF2E(): [0x0 0x0 0x0 0x0 0x0] [0x0 0x0 0x0 0x0 0x0] ] - sage: A= matrix(k,5,5,[k.fetch_int(_%(2^4)) for _ in range(25)]) + sage: A = matrix(k, 5, 5, [k.from_integer(i % 2^4) for i in range(25)]) sage: ntl.mat_GF2E(ctx, A) [[0x0 0x1 0x2 0x3 0x4] [0x5 0x6 0x7 0x8 0x9] @@ -205,7 +205,7 @@ cdef class ntl_mat_GF2E(): sage: ntl.GF2XHexOutput(1) sage: m = ntl.mat_GF2E(ctx, 5,5,[0..24]) sage: n = ntl.mat_GF2E(ctx, 5,5,[3..27]) - sage: m*n ## indirect doctest + sage: m*n # indirect doctest [[0x87 0x04 0xc4 0xc7 0x87] [0x32 0x84 0x17 0x63 0x73] [0xa1 0x46 0x25 0xcd 0x2f] @@ -216,7 +216,7 @@ cdef class ntl_mat_GF2E(): cdef ntl_mat_GF2E r = self._new() if not isinstance(other, ntl_mat_GF2E): other = ntl_mat_GF2E(other, self.c) - if not self.c is (<ntl_mat_GF2E>other).c: + if self.c is not (<ntl_mat_GF2E>other).c: raise ValueError("You cannot perform arithmetic with matrices over different fields.") sig_on() mat_GF2E_mul(r.x, self.x, (<ntl_mat_GF2E>other).x) @@ -231,7 +231,7 @@ cdef class ntl_mat_GF2E(): sage: m = ntl.mat_GF2E(ctx, 5,5,[0..24]) sage: n = ntl.mat_GF2E(ctx, 5,5,[3..27]) sage: ntl.GF2XHexOutput(0) - sage: m-n ## indirect doctest + sage: m-n # indirect doctest [[[1 1] [1 0 1] [1 1 1] [1 0 1] [1 1]] [[1 0 1 1] [1 1 1 1] [1 0 1 1] [1 1] [1 0 1]] [[1 1 1] [1 0 1] [1 1] [1 0 1 1 1] [1 1 1 1 1]] @@ -242,7 +242,7 @@ cdef class ntl_mat_GF2E(): cdef ntl_mat_GF2E r = self._new() if not isinstance(other, ntl_mat_GF2E): other = ntl_mat_GF2E(other, self.c) - if not self.c is (<ntl_mat_GF2E>other).c: + if self.c is not (<ntl_mat_GF2E>other).c: raise ValueError("You cannot perform arithmetic with matrices over different fields.") sig_on() mat_GF2E_sub(r.x, self.x, (<ntl_mat_GF2E>other).x) @@ -256,7 +256,7 @@ cdef class ntl_mat_GF2E(): sage: ctx = ntl.GF2EContext([1,1,0,1,1,0,0,0,1]) sage: m = ntl.mat_GF2E(ctx, 5,5,[0..24]) sage: n = ntl.mat_GF2E(ctx, 5,5,[3..27]) - sage: m+n ## indirect doctest + sage: m+n # indirect doctest [[[1 1] [1 0 1] [1 1 1] [1 0 1] [1 1]] [[1 0 1 1] [1 1 1 1] [1 0 1 1] [1 1] [1 0 1]] [[1 1 1] [1 0 1] [1 1] [1 0 1 1 1] [1 1 1 1 1]] @@ -267,7 +267,7 @@ cdef class ntl_mat_GF2E(): cdef ntl_mat_GF2E r = self._new() if not isinstance(other, ntl_mat_GF2E): other = ntl_mat_GF2E(other, self.c) - if not self.c is (<ntl_mat_GF2E>other).c: + if self.c is not (<ntl_mat_GF2E>other).c: raise ValueError("You cannot perform arithmetic with matrices over different fields.") sig_on() mat_GF2E_add(r.x, self.x, (<ntl_mat_GF2E>other).x) @@ -280,7 +280,7 @@ cdef class ntl_mat_GF2E(): sage: ctx = ntl.GF2EContext([1,1,0,1,1,0,0,0,1]) sage: m = ntl.mat_GF2E(ctx, 5,5,[0..24]) - sage: -m == m ## indirect doctest + sage: -m == m # indirect doctest True """ cdef ntl_mat_GF2E r = self._new() @@ -295,7 +295,7 @@ cdef class ntl_mat_GF2E(): sage: ctx = ntl.GF2EContext([1,1,0,1,1,0,0,0,1]) sage: m = ntl.mat_GF2E(ctx, 5,5,[0..24]) - sage: m**2 == m*m ## indirect doctest + sage: m**2 == m*m # indirect doctest True """ cdef ntl_mat_GF2E r = self._new() @@ -445,17 +445,18 @@ cdef class ntl_mat_GF2E(): sig_off() return r - def gauss(self,ncols=-1): - """ - Performs unitary row operations so as to bring this matrix - into row echelon form. If the optional argument \code{ncols} - is supplied, stops when first ncols columns are in echelon - form. The return value is the rank (or the rank of the first - ncols columns). + def gauss(self, ncols=-1): + r""" + Perform unitary row operations so as to bring this matrix + into row echelon form. + + If the optional argument ``ncols`` is supplied, stops when + first ``ncols`` columns are in echelon form. The return value + is the rank (or the rank of the first ``ncols`` columns). INPUT: - - ``ncols`` - number of columns to process (default: all) + - ``ncols`` -- number of columns to process (default: all) EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_mat_ZZ.pyx b/src/sage/libs/ntl/ntl_mat_ZZ.pyx index dac2ca79636..fb1769db352 100644 --- a/src/sage/libs/ntl/ntl_mat_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_mat_ZZ.pyx @@ -73,7 +73,7 @@ cdef class ntl_mat_ZZ(): The \class{mat_ZZ} class implements arithmetic with matrices over `\Z`. """ def __init__(self, nrows=0, ncols=0, v=None): - """ + r""" The \class{mat_ZZ} class implements arithmetic with matrices over `\Z`. EXAMPLES:: @@ -319,6 +319,8 @@ cdef class ntl_mat_ZZ(): def __getitem__(self, ij): """ + EXAMPLES:: + sage: m = ntl.mat_ZZ(3, 2, range(6)) sage: m[0,0] ## indirect doctest 0 diff --git a/src/sage/libs/pari/convert_gmp.pyx b/src/sage/libs/pari/convert_gmp.pyx index fe23a00cdc7..a39caa379b4 100644 --- a/src/sage/libs/pari/convert_gmp.pyx +++ b/src/sage/libs/pari/convert_gmp.pyx @@ -159,7 +159,7 @@ cdef Gen rational_matrix(mpq_t** B, long nr, long nc): """ Create a new PARI matrix of type ``t_MAT`` from a given array of GMP rationals ``mpq_t``. - + INPUT: - ``B`` -- a 2-dimensional array of ``mpq_t`` values. This array is diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index e5a2aa25172..97d1ec95893 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -356,7 +356,7 @@ [2, 4]~*x + [1, 3]~ sage: pari(3).Qfb(7, 1) - Qfb(3, 7, 1, 0.E-19) + Qfb(3, 7, 1) sage: pari(3).Qfb(7, 2) Traceback (most recent call last): ... @@ -512,7 +512,7 @@ sage: pari('sqrt(-2)').frac() Traceback (most recent call last): ... - PariError: incorrect type in gfloor (t_COMPLEX) + PariError: incorrect type in gfrac (t_COMPLEX) sage: pari('1+2*I').imag() 2 @@ -1767,7 +1767,7 @@ sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() 3.605463601432652085915820564207726774810268996598024745444380641429820491740 # 64-bit - 3.60546360143265208591582056420772677481026899659802474544 # 32-bit + 3.60546360143265208591582056420772677481026899659802474544 # 32-bit sage: from cypari2 import Pari sage: pari = Pari() diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 8e3ac314b67..747a6b1e2fb 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -574,7 +574,7 @@ cdef extern from "singular/Singular/libsingular.h": # gets a component out of a polynomial vector - poly *pTakeOutComp1(poly **, int) + poly *pTakeOutComp(poly **, int) # deep copy p diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index d7255b34440..8c4536f298a 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -459,10 +459,11 @@ cdef ring* access_singular_ring(r) except <ring*> -1: cdef poly* copy_sage_polynomial_into_singular_poly(p): return p_Copy(access_singular_poly(p), access_singular_ring(p.parent())) + def all_vectors(s): """ - Checks if a sequence ``s`` consists of free module - elements over a singular ring. + Check if a sequence ``s`` consists of free module elements + over a singular ring. EXAMPLES:: @@ -478,11 +479,9 @@ def all_vectors(s): sage: all_vectors([M(0), M((x,y)),(0,0)]) False """ - for p in s: - if not (isinstance(p, FreeModuleElement_generic_dense)\ - and is_sage_wrapper_for_singular_ring(p.parent().base_ring())): - return False - return True + return all(isinstance(p, FreeModuleElement_generic_dense) + and is_sage_wrapper_for_singular_ring(p.parent().base_ring()) + for p in s) cdef class Converter(SageObject): @@ -1200,8 +1199,8 @@ cdef class SingularFunction(SageObject): - ``args`` -- a list of arguments - ``ring`` -- a multivariate polynomial ring - - ``interruptible`` -- if ``True`` pressing Ctrl-C during the - execution of this function will interrupt the computation + - ``interruptible`` -- if ``True`` pressing :kbd:`Ctrl` + :kbd:`C` + during the execution of this function will interrupt the computation (default: ``True``) - ``attributes`` -- a dictionary of optional Singular @@ -1332,7 +1331,7 @@ INPUT: - ``args`` -- a list of arguments - ``ring`` -- a multivariate polynomial ring -- ``interruptible`` -- if ``True`` pressing Ctrl-C during the +- ``interruptible`` -- if ``True`` pressing :kbd:`Ctrl` + :kbd:`C` during the execution of this function will interrupt the computation (default: ``True``) - ``attributes`` -- a dictionary of optional Singular attributes @@ -1394,9 +1393,7 @@ The Singular documentation for '%s' is given below. ring2 = a.parent() elif is_sage_wrapper_for_singular_ring(a): ring2 = a - elif isinstance(a, int) or\ - isinstance(a, long) or\ - isinstance(a, basestring): + elif isinstance(a, (int, str)): continue elif isinstance(a, Matrix_integer_dense): continue diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 20751eafa71..e5807a7a9d4 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -536,7 +536,7 @@ cdef class NCGroebnerStrategy(SageObject): if unlikely(self._parent._ring != currRing): rChangeCurrRing(self._parent._ring) - cdef int max_ind + cdef int max_ind = 0 cdef poly *_p = redNF(p_Copy(p._poly, self._parent._ring), max_ind, 0, self._strat) if likely(_p!=NULL): _p = redtailBba(_p, max_ind, self._strat) diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index e012da4573c..b2efc7dfbcb 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -5,16 +5,15 @@ AUTHOR: - Martin Albrecht (2009-07): refactoring """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Martin Albrecht <malb@informatik.uni-bremen.de> # # 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 cysignals.signals cimport sig_on, sig_off @@ -22,7 +21,7 @@ cdef extern from *: # hack to get at cython macro int unlikely(int) import re -plusminus_pattern = re.compile("([^\(^])([\+\-])") +plusminus_pattern = re.compile(r"([^\(^])([\+\-])") parenthvar_pattern = re.compile(r"\(([a-zA-Z][a-zA-Z0-9]*)\)") from sage.cpython.string cimport bytes_to_str, str_to_bytes diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index d8ea7b07f3c..3a1271cd59f 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1726,20 +1726,21 @@ cdef int overflow_check(unsigned long e, ring *_ring) except -1: Whether an overflow occurs or not partially depends on the number of variables in the ring. See trac ticket - :trac:`11856`. With Singular 4, it is by default optimized - for at least 4 variables on 64-bit and 2 variables on 32-bit, - which in both cases makes a maximal default exponent of - 2^16-1. + :trac:`11856`. EXAMPLES:: sage: P.<x,y> = QQ[] - sage: y^(2^16-1) - y^65535 - sage: y^2^16 + sage: y^(2^30) + Traceback (most recent call last): # 32-bit + ... # 32-bit + OverflowError: exponent overflow (1073741824) # 32-bit + y^1073741824 # 64-bit + sage: y^2^32 Traceback (most recent call last): ... - OverflowError: exponent overflow (65536) + OverflowError: Python int too large to convert to C unsigned long # 32-bit + OverflowError: exponent overflow (4294967296) # 64-bit """ if unlikely(e > _ring.bitmask): raise OverflowError("exponent overflow (%d)"%(e)) diff --git a/src/sage/libs/singular/standard_options.py b/src/sage/libs/singular/standard_options.py index 6797cb05001..5d74da3ce3a 100644 --- a/src/sage/libs/singular/standard_options.py +++ b/src/sage/libs/singular/standard_options.py @@ -117,7 +117,8 @@ def libsingular_gb_standard_options(func): sage: P.<x,y> = QQ[] sage: I = P*[x,y] sage: sage_getargspec(I.interreduced_basis) - ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getsourcelines(I.interreduced_basis) ([' @handle_AA_and_QQbar\n', ' @singular_gb_standard_options\n', diff --git a/src/sage/libs/sirocco.pyx b/src/sage/libs/sirocco.pyx index 5bd4062394d..117d714c5d5 100644 --- a/src/sage/libs/sirocco.pyx +++ b/src/sage/libs/sirocco.pyx @@ -292,4 +292,3 @@ cpdef list[list] contpath_comps(int deg, list values, double y0r, double y0i, li free(c_otherdegrees) free(c_othercoefs) return l - diff --git a/src/sage/libs/symmetrica/sb.pxi b/src/sage/libs/symmetrica/sb.pxi index e61fc08fb98..9bde08effa7 100644 --- a/src/sage/libs/symmetrica/sb.pxi +++ b/src/sage/libs/symmetrica/sb.pxi @@ -22,7 +22,7 @@ cdef object _check_schubert(object a, OP ca): br = a.parent().base_ring() if (br == QQ or br == ZZ): _op_schubert_sp(a, ca) - return min([max(i.reduced_word()+[0]) for i in a.support()]) + return min(max(i.reduced_word(), default=0) for i in a.support()) else: raise ValueError("a must be a Schubert polynomial over ZZ or QQ") else: diff --git a/src/sage/logic/logic.py b/src/sage/logic/logic.py index 769fae29289..13a45b337ff 100644 --- a/src/sage/logic/logic.py +++ b/src/sage/logic/logic.py @@ -318,7 +318,7 @@ def combine(self, statement1, statement2): 'CPAREN', 'CPAREN'], {'a': 'False', 'b': 'False'}, - ['a', 'b', 'b']] + ['a', 'b', 'b']] """ toks = ['OPAREN'] + statement1[0] + ['OR'] + statement2[0] + ['CPAREN'] variables = dict(statement1[1]) diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index 4e10184fd3e..bb09ed8885d 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -4,7 +4,7 @@ A catalog of manifolds to rapidly create various simple manifolds. The current entries to the catalog are obtained by typing -``manifolds.<tab>``, where ``<tab>`` indicates pressing the tab key. +``manifolds.<tab>``, where ``<tab>`` indicates pressing the :kbd:`Tab` key. They are: - :class:`~sage.manifolds.differentiable.examples.euclidean.EuclideanSpace`: Euclidean space @@ -29,7 +29,7 @@ # 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/ # ***************************************************************************** # Lazy import from examples folders: @@ -262,11 +262,12 @@ def Torus(R=2, r=1, names=None): M.induced_metric() return M + def RealProjectiveSpace(dim=2): r""" Generate projective space of dimension ``dim`` over the reals. - This is the topological space of lines through the origin in + This is the topological space of lines through the origin in `\RR^{d+1}`. The standard atlas consists of `d+2` charts, which sends the set `U_i = \{[x_1, x_2, \ldots, x_{d+1}] : x_i \neq 0 \}` to `k^{d}` by dividing by `x_i` and omitting the `i`th coordinate @@ -364,9 +365,9 @@ def RealProjectiveSpace(dim=2): charts = {0: U0.chart(''.join(names[1:]))} # create the charts - for j in range(1, dim+1): + for j in range(1, dim + 1): U = P.open_subset(name=f'U{j}', latex_name=f'U_{j}') - + # The chart where we assert that x_i == 1 Cj = U.chart(''.join(names[:j] + names[j+1:])) gj = Cj[:] diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 69744a312a5..689760625da 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -271,14 +271,14 @@ """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2021 Michael Jung <m.jung at vu.nl> # # 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. # https://www.gnu.org/licenses/ -#****************************************************************************** +# ***************************************************************************** from sage.algebras.finite_gca import FiniteGCAlgebra from sage.combinat.free_module import IndexedFreeModuleElement @@ -693,10 +693,10 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): Algebra of characteristic cohomology classes of the Tangent bundle TM over the 8-dimensional differentiable manifold M sage: CR.gens() - [Characteristic cohomology class (p_1)(TM) of the Tangent bundle TM over + (Characteristic cohomology class (p_1)(TM) of the Tangent bundle TM over the 8-dimensional differentiable manifold M, Characteristic cohomology class (p_2)(TM) of the Tangent bundle TM - over the 8-dimensional differentiable manifold M] + over the 8-dimensional differentiable manifold M) The default base ring is `\QQ`:: @@ -712,12 +712,12 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): complex vector bundle E -> M of rank 2 over the base space 4-dimensional differentiable manifold M sage: CR_E.gens() - [Characteristic cohomology class (c_1)(E) of the Differentiable complex + (Characteristic cohomology class (c_1)(E) of the Differentiable complex vector bundle E -> M of rank 2 over the base space 4-dimensional differentiable manifold M, Characteristic cohomology class (c_2)(E) of the Differentiable complex vector bundle E -> M of rank 2 over the base space - 4-dimensional differentiable manifold M] + 4-dimensional differentiable manifold M) Characteristic cohomology class ring over an oriented manifold:: @@ -727,9 +727,9 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): True sage: CR = TS2.characteristic_cohomology_class_ring() sage: CR.gens() - [Characteristic cohomology class (e)(TS^2) of the Tangent bundle TS^2 + (Characteristic cohomology class (e)(TS^2) of the Tangent bundle TS^2 over the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean - space E^3] + space E^3,) """ Element = CharacteristicCohomologyClassRingElement @@ -1043,6 +1043,7 @@ def _repr_(self): repr = f'Algebra of characteristic cohomology classes of the {vbundle}' return repr + # ***************************************************************************** # ALGORITHMS # ***************************************************************************** @@ -1103,6 +1104,7 @@ def multiplicative_sequence(q, n=None): for p in Partitions(k)}) return Sym.e()(mon_pol) + def additive_sequence(q, k, n=None): r""" Turn the polynomial ``q`` into its additive sequence. @@ -1845,4 +1847,4 @@ def get_gen_pow(self, nab, i, n): return nab._domain._one_scalar_field # no computation necessary if i == 0: return fast_wedge_power(EulerAlgorithm().get(nab)[0], n) - return fast_wedge_power(PontryaginAlgorithm().get(nab)[i-1], n) + return fast_wedge_power(PontryaginAlgorithm().get(nab)[i - 1], n) diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 0aa86ce31d5..04d85a0817d 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -12,11 +12,14 @@ (in practice, not parallelizable) differentiable manifold `M` - :class:`DiffFormFreeModule` for differential forms with values on a parallelizable manifold `M` + (the subclass :class:`VectorFieldDualFreeModule` implements the special + case of differential 1-forms on a parallelizable manifold `M`) AUTHORS: - Eric Gourgoulhon (2015): initial version - Travis Scrimshaw (2016): review tweaks +- Matthias Koeppe (2022): :class:`VectorFieldDualFreeModule` REFERENCES: @@ -25,8 +28,10 @@ """ # ***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> -# Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# Copyright (C) 2015-2021 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# 2020 Michael Jung +# 2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -42,6 +47,7 @@ from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal +from sage.tensor.modules.reflexive_module import ReflexiveModule_abstract class DiffFormModule(UniqueRepresentation, Parent): @@ -529,6 +535,31 @@ def base_module(self): """ return self._vmodule + tensor = tensor_product = ReflexiveModule_abstract.tensor_product + + def tensor_type(self): + r""" + Return the tensor type of ``self`` if ``self`` is a module of 1-forms. + + In this case, the pair `(0, 1)` is returned, indicating that the module + is identified with the dual of the base module. + + For differential forms of other degrees, an exception is raised. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: M.diff_form_module(1).tensor_type() + (0, 1) + sage: M.diff_form_module(2).tensor_type() + Traceback (most recent call last): + ... + NotImplementedError + """ + if self._degree == 1: + return (0, 1) + raise NotImplementedError + def degree(self): r""" Return the degree of the differential forms in ``self``. @@ -575,6 +606,8 @@ class DiffFormFreeModule(ExtPowerDualFreeModule): is not parallelizable, the class :class:`DiffFormModule` must be used instead. + For the special case of 1-forms, use the class :class:`VectorFieldDualFreeModule`. + INPUT: - ``vector_field_module`` -- free module `\mathfrak{X}(U,\Phi)` of vector @@ -661,18 +694,12 @@ class DiffFormFreeModule(ExtPowerDualFreeModule): sage: L1 is XM.dual() True - Since any tensor field of type `(0,1)` is a 1-form, there is a coercion - map from the set `T^{(0,1)}(M)` of such tensors to `\Omega^1(M)`:: + Since any tensor field of type `(0,1)` is a 1-form, it is also equal to + the set `T^{(0,1)}(M)` of such tensors to `\Omega^1(M)`:: sage: T01 = M.tensor_field_module((0,1)) ; T01 - Free module T^(0,1)(M) of type-(0,1) tensors fields on the - 3-dimensional differentiable manifold M - sage: L1.has_coerce_map_from(T01) - True - - There is also a coercion map in the reverse direction:: - - sage: T01.has_coerce_map_from(L1) + Free module Omega^1(M) of 1-forms on the 3-dimensional differentiable manifold M + sage: L1 is T01 True For a degree `p \geq 2`, the coercion holds only in the direction @@ -686,26 +713,6 @@ class DiffFormFreeModule(ExtPowerDualFreeModule): sage: A.has_coerce_map_from(T02) False - The coercion map `T^{(0,1)}(M) \rightarrow \Omega^1(M)` in action:: - - sage: b = T01([-x,2,3*y], name='b'); b - Tensor field b of type (0,1) on the 3-dimensional differentiable - manifold M - sage: b.display() - b = -x dx + 2 dy + 3*y dz - sage: lb = L1(b) ; lb - 1-form b on the 3-dimensional differentiable manifold M - sage: lb.display() - b = -x dx + 2 dy + 3*y dz - - The coercion map `\Omega^1(M) \rightarrow T^{(0,1)}(M)` in action:: - - sage: tlb = T01(lb); tlb - Tensor field b of type (0,1) on - the 3-dimensional differentiable manifold M - sage: tlb == b - True - The coercion map `\Omega^2(M) \rightarrow T^{(0,2)}(M)` in action:: sage: T02 = M.tensor_field_module((0,2)) ; T02 @@ -905,3 +912,167 @@ def _repr_(self): description += "along the {} mapped into the {}".format( self._domain, self._ambient_domain) return description + + +class VectorFieldDualFreeModule(DiffFormFreeModule): + r""" + Free module of differential 1-forms along a differentiable manifold `U` + with values on a parallelizable manifold `M`. + + Given a differentiable manifold `U` and a differentiable map + `\Phi:\; U \rightarrow M` to a parallelizable manifold `M` of dimension + `n`, the set `\Omega^1(U, \Phi)` of 1-forms along `U` with values on `M` + is a free module of rank `n` over `C^k(U)`, the commutative + algebra of differentiable scalar fields on `U` (see + :class:`~sage.manifolds.differentiable.scalarfield_algebra.DiffScalarFieldAlgebra`). + The standard case of 1-forms *on* a differentiable manifold `M` + corresponds to `U = M` and `\Phi = \mathrm{Id}_M`. Other common cases are + `\Phi` being an immersion and `\Phi` being a curve in `M` (`U` is then an + open interval of `\RR`). + + .. NOTE:: + + This class implements `\Omega^1(U, \Phi)` in the case where `M` is + parallelizable; `\Omega^1(U, \Phi)` is then a *free* module. If `M` + is not parallelizable, the class :class:`DiffFormModule` must be used + instead. + + INPUT: + + - ``vector_field_module`` -- free module `\mathfrak{X}(U,\Phi)` of vector + fields along `U` associated with the map `\Phi: U \rightarrow V` + + EXAMPLES: + + Free module of 1-forms on a parallelizable 3-dimensional manifold:: + + sage: M = Manifold(3, 'M') + sage: X.<x,y,z> = M.chart() + sage: XM = M.vector_field_module() ; XM + Free module X(M) of vector fields on the 3-dimensional differentiable + manifold M + sage: A = M.diff_form_module(1) ; A + Free module Omega^1(M) of 1-forms on the 3-dimensional differentiable manifold M + sage: latex(A) + \Omega^{1}\left(M\right) + + ``A`` is nothing but the dual of ``XM`` (the free module of vector fields on `M`) + and thus also equal to the 1st exterior + power of the dual, i.e. we have `\Omega^{1}(M) = \Lambda^1(\mathfrak{X}(M)^*) + = \mathfrak{X}(M)^*` (See + :class:`~sage.tensor.modules.ext_pow_free_module.ExtPowerDualFreeModule`):: + + sage: A is XM.dual_exterior_power(1) + True + + `\Omega^{1}(M)` is a module over the algebra `C^k(M)` of (differentiable) + scalar fields on `M`:: + + sage: A.category() + Category of finite dimensional modules over Algebra of differentiable + scalar fields on the 3-dimensional differentiable manifold M + sage: CM = M.scalar_field_algebra() ; CM + Algebra of differentiable scalar fields on the 3-dimensional + differentiable manifold M + sage: A in Modules(CM) + True + sage: A.base_ring() + Algebra of differentiable scalar fields on + the 3-dimensional differentiable manifold M + sage: A.base_module() + Free module X(M) of vector fields on + the 3-dimensional differentiable manifold M + sage: A.base_module() is XM + True + sage: A.rank() + 3 + + Elements can be constructed from `A`. In particular, ``0`` yields + the zero element of `A`:: + + sage: A(0) + 1-form zero on the 3-dimensional differentiable manifold M + sage: A(0) is A.zero() + True + + while non-zero elements are constructed by providing their components + in a given vector frame:: + + sage: comp = [3*x,-z,4] + sage: a = A(comp, frame=X.frame(), name='a') ; a + 1-form a on the 3-dimensional differentiable manifold M + sage: a.display() + a = 3*x dx - z dy + 4 dz + + An alternative is to construct the 1-form from an empty list of + components and to set the nonzero nonredundant components afterwards:: + + sage: a = A([], name='a') + sage: a[0] = 3*x # component in the manifold's default frame + sage: a[1] = -z + sage: a[2] = 4 + sage: a.display() + a = 3*x dx - z dy + 4 dz + + Since any tensor field of type `(0,1)` is a 1-form, there is a coercion + map from the set `T^{(0,1)}(M)` of such tensors to `\Omega^1(M)`:: + + sage: T01 = M.tensor_field_module((0,1)) ; T01 + Free module Omega^1(M) of 1-forms on the 3-dimensional differentiable manifold M + sage: A.has_coerce_map_from(T01) + True + + There is also a coercion map in the reverse direction:: + + sage: T01.has_coerce_map_from(A) + True + + The coercion map `T^{(0,1)}(M) \rightarrow \Omega^1(M)` in action:: + + sage: b = T01([-x,2,3*y], name='b'); b + 1-form b on the 3-dimensional differentiable manifold M + sage: b.display() + b = -x dx + 2 dy + 3*y dz + sage: lb = A(b) ; lb + 1-form b on the 3-dimensional differentiable manifold M + sage: lb.display() + b = -x dx + 2 dy + 3*y dz + + The coercion map `\Omega^1(M) \rightarrow T^{(0,1)}(M)` in action:: + + sage: tlb = T01(lb); tlb + 1-form b on the 3-dimensional differentiable manifold M + sage: tlb == b + True + """ + + def __init__(self, vector_field_module): + r""" + Construct a free module of differential 1-forms. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X.<x,y,z> = M.chart() + sage: A = M.vector_field_module().dual(); A + Free module Omega^1(M) of 1-forms on the 3-dimensional differentiable manifold M + sage: TestSuite(A).run() + + """ + DiffFormFreeModule.__init__(self, vector_field_module, 1) + + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: X.<x,y,z> = M.chart() + sage: A = M.vector_field_module().dual(); A + Free module Omega^1(M) of 1-forms on the 3-dimensional differentiable manifold M + sage: A.tensor_type() + (0, 1) + + """ + return (0, 1) diff --git a/src/sage/manifolds/differentiable/examples/euclidean.py b/src/sage/manifolds/differentiable/examples/euclidean.py index 8b3cb23531d..9528d5a5f8a 100644 --- a/src/sage/manifolds/differentiable/examples/euclidean.py +++ b/src/sage/manifolds/differentiable/examples/euclidean.py @@ -717,7 +717,7 @@ def __classcall_private__(cls, n=None, name=None, latex_name=None, start_index=start_index, unique_tag=unique_tag) - return super(cls, EuclideanSpace).__classcall__(cls, + return super().__classcall__(cls, n, name=name, latex_name=latex_name, coordinates=coordinates, symbols=symbols, metric_name=metric_name, diff --git a/src/sage/manifolds/differentiable/examples/real_line.py b/src/sage/manifolds/differentiable/examples/real_line.py index 35636dc0bd7..10a44c7e149 100644 --- a/src/sage/manifolds/differentiable/examples/real_line.py +++ b/src/sage/manifolds/differentiable/examples/real_line.py @@ -320,10 +320,10 @@ def __classcall_private__(cls, lower, upper, ambient_interval=None, coordinate = None names = None start_index = 0 - return super(cls, OpenInterval).__classcall__(cls, lower, upper, - ambient_interval=ambient_interval, name=name, - latex_name=latex_name, coordinate=coordinate, - names=names, start_index=start_index) + return super().__classcall__(cls, lower, upper, + ambient_interval=ambient_interval, name=name, + latex_name=latex_name, coordinate=coordinate, + names=names, start_index=start_index) def __init__(self, lower, upper, ambient_interval=None, name=None, latex_name=None, @@ -495,9 +495,9 @@ def _element_constructor_(self, coords=None, chart=None, name=None, """ if coords in SR: coords = (coords,) - return super(OpenInterval, self)._element_constructor_(coords=coords, - chart=chart, name=name, latex_name=latex_name, - check_coords=check_coords) + return super()._element_constructor_(coords=coords, + chart=chart, name=name, latex_name=latex_name, + check_coords=check_coords) def _Hom_(self, other, category=None): r""" @@ -879,10 +879,10 @@ def __classcall__(cls, name=unicode_mathbbR, latex_name=r'\Bold{R}', True """ - return super(cls, RealLine).__classcall__(cls, name=name, - latex_name=latex_name, - coordinate=coordinate, - names=names, start_index=start_index) + return super().__classcall__(cls, name=name, + latex_name=latex_name, + coordinate=coordinate, + names=names, start_index=start_index) def __init__(self, name=unicode_mathbbR, latex_name=r'\Bold{R}', coordinate=None, names=None, start_index=0): diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index 1183448251a..4ccc6f7440b 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -320,14 +320,14 @@ def __classcall_private__(cls, n=None, radius=1, ambient_space=None, from sage.misc.prandom import getrandbits from time import time if unique_tag is None: - unique_tag = getrandbits(128)*time() - - return super(cls, Sphere).__classcall__(cls, n, radius=radius, - ambient_space=ambient_space, - center=center, - name=name, latex_name=latex_name, - coordinates=coordinates, names=names, - unique_tag=unique_tag) + unique_tag = getrandbits(128) * time() + + return super().__classcall__(cls, n, radius=radius, + ambient_space=ambient_space, + center=center, + name=name, latex_name=latex_name, + coordinates=coordinates, names=names, + unique_tag=unique_tag) def __init__(self, n, radius=1, ambient_space=None, center=None, name=None, latex_name=None, coordinates='spherical', names=None, diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 7fa589be987..e674ad8db05 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -1438,9 +1438,9 @@ def tensor_field_module(self, tensor_type, dest_map=None): Free module T^(2,1)(U) of type-(2,1) tensors fields on the Open subset U of the 3-dimensional differentiable manifold M sage: TU.category() - Category of finite dimensional modules over Algebra of - differentiable scalar fields on the Open subset U of the - 3-dimensional differentiable manifold M + Category of tensor products of finite dimensional modules + over Algebra of differentiable scalar fields + on the Open subset U of the 3-dimensional differentiable manifold M sage: TU.base_ring() Algebra of differentiable scalar fields on the Open subset U of the 3-dimensional differentiable manifold M @@ -3957,7 +3957,7 @@ def affine_connection(self, name, latex_name=None): AffineConnection return AffineConnection(self, name, latex_name) - def metric(self, name: str, signature: Optional[int] = None, + def metric(self, name: str, signature: Optional[int] = None, latex_name: Optional[str] = None, dest_map: Optional[DiffMap] = None) -> PseudoRiemannianMetric: r""" diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index 482f9527ba0..c72f50b70fa 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -498,7 +498,7 @@ def poisson_bracket( def volume_form(self, contra: int = 0) -> TensorField: r""" - Liouville volume form `\frac{1}{n!}\omega^n` associated with the + Liouville volume form `\frac{1}{n!}\omega^n` associated with the symplectic form `\omega`, where `2n` is the dimension of the manifold. INPUT: diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index a2aad4d4937..db4a2a05aa1 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -176,8 +176,8 @@ class TensorField(ModuleElementWithMutability): Module T^(0,2)(S^2) of type-(0,2) tensors fields on the 2-dimensional differentiable manifold S^2 sage: t.parent().category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold S^2 + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold S^2 The parent of `t` is not a free module, for the sphere `S^2` is not parallelizable:: diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index d68264a472c..6347189848f 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -27,9 +27,11 @@ """ # ***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> -# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl> -# Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# Copyright (C) 2015-2018 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# 2015 Michal Bejger <bejger@camk.edu.pl> +# 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# 2020 Michael Jung +# 2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -41,6 +43,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.categories.modules import Modules +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor from sage.tensor.modules.tensor_free_module import TensorFreeModule from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal @@ -51,7 +54,8 @@ from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) -class TensorFieldModule(UniqueRepresentation, Parent): + +class TensorFieldModule(UniqueRepresentation, ReflexiveModule_tensor): r""" Module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a differentiable manifold `M`, via a @@ -123,8 +127,8 @@ class TensorFieldModule(UniqueRepresentation, Parent): `T^{(2,0)}(M)` is a module over the algebra `C^k(M)`:: sage: T20.category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold M + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold M sage: T20.base_ring() is M.scalar_field_algebra() True @@ -226,10 +230,16 @@ class TensorFieldModule(UniqueRepresentation, Parent): [1 0] [0 1] + TESTS:: + + sage: T11.tensor_factors() + [Module X(M) of vector fields on the 2-dimensional differentiable manifold M, + Module Omega^1(M) of 1-forms on the 2-dimensional differentiable manifold M] + """ Element = TensorField - def __init__(self, vector_field_module, tensor_type): + def __init__(self, vector_field_module, tensor_type, category=None): r""" Construct a module of tensor fields taking values on a (a priori) not parallelizable differentiable manifold. @@ -281,7 +291,8 @@ def __init__(self, vector_field_module, tensor_type): # the member self._ring is created for efficiency (to avoid calls to # self.base_ring()): self._ring = domain.scalar_field_algebra() - Parent.__init__(self, base=self._ring, category=Modules(self._ring)) + category = Modules(self._ring).TensorProducts().or_subcategory(category) + Parent.__init__(self, base=self._ring, category=category) self._domain = domain self._dest_map = dest_map self._ambient_domain = vector_field_module._ambient_domain @@ -652,8 +663,8 @@ class TensorFieldFreeModule(TensorFreeModule): `T^{(2,0)}(\RR^3)` is a module over the algebra `C^k(\RR^3)`:: sage: T20.category() - Category of finite dimensional modules over Algebra of differentiable - scalar fields on the 3-dimensional differentiable manifold R^3 + Category of tensor products of finite dimensional modules over + Algebra of differentiable scalar fields on the 3-dimensional differentiable manifold R^3 sage: T20.base_ring() is M.scalar_field_algebra() True diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 1e097907967..ac8e7dc1290 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -28,9 +28,14 @@ """ # ****************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> -# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl> -# Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# Copyright (C) 2015-2021 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# 2015 Michal Bejger <bejger@camk.edu.pl> +# 2016 Travis Scrimshaw <tscrimsh@umn.edu> +# 2018 Florentin Jaffredo +# 2019 Hans Fotsing Tetsing +# 2020 Michael Jung +# 2020-2022 Matthias Koeppe +# 2021-2022 Tobias Diez # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -49,13 +54,14 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule +from sage.tensor.modules.reflexive_module import ReflexiveModule_base if TYPE_CHECKING: from sage.manifolds.differentiable.diff_map import DiffMap from sage.manifolds.differentiable.manifold import DifferentiableManifold -class VectorFieldModule(UniqueRepresentation, Parent): +class VectorFieldModule(UniqueRepresentation, ReflexiveModule_base): r""" Module of vector fields along a differentiable manifold `U` with values on a differentiable manifold `M`, via a differentiable @@ -501,7 +507,7 @@ def destination_map(self): """ return self._dest_map - def tensor_module(self, k, l): + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the module of type-`(k,l)` tensors on ``self``. @@ -547,11 +553,16 @@ def tensor_module(self, k, l): for more examples and documentation. """ - from sage.manifolds.differentiable.tensorfield_module import \ + if sym or antisym: + raise NotImplementedError + try: + return self._tensor_modules[(k,l)] + except KeyError: + from sage.manifolds.differentiable.tensorfield_module import \ TensorFieldModule - if (k,l) not in self._tensor_modules: - self._tensor_modules[(k,l)] = TensorFieldModule(self, (k,l)) - return self._tensor_modules[(k,l)] + T = TensorFieldModule(self, (k,l)) + self._tensor_modules[(k,l)] = T + return T def exterior_power(self, p): r""" @@ -600,13 +611,17 @@ def exterior_power(self, p): for more examples and documentation. """ - from sage.manifolds.differentiable.multivector_module import \ + try: + return self._exterior_powers[p] + except KeyError: + if p == 0: + L = self._ring + else: + from sage.manifolds.differentiable.multivector_module import \ MultivectorModule - if p == 0: - return self._ring - if p not in self._exterior_powers: - self._exterior_powers[p] = MultivectorModule(self, p) - return self._exterior_powers[p] + L = MultivectorModule(self, p) + self._exterior_powers[p] = L + return L def dual_exterior_power(self, p): r""" @@ -654,13 +669,17 @@ def dual_exterior_power(self, p): for more examples and documentation. """ - from sage.manifolds.differentiable.diff_form_module import \ + try: + return self._dual_exterior_powers[p] + except KeyError: + if p == 0: + L = self._ring + else: + from sage.manifolds.differentiable.diff_form_module import \ DiffFormModule - if p == 0: - return self._ring - if p not in self._dual_exterior_powers: - self._dual_exterior_powers[p] = DiffFormModule(self, p) - return self._dual_exterior_powers[p] + L = DiffFormModule(self, p) + self._dual_exterior_powers[p] = L + return L def dual(self): r""" @@ -713,8 +732,8 @@ def general_linear_group(self): self._general_linear_group = AutomorphismFieldGroup(self) return self._general_linear_group - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - antisym=None, specific_type=None): + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, + antisym=None, specific_type=None): r""" Construct a tensor on ``self``. @@ -758,10 +777,6 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, sage: XM.tensor((1,2), name='t') Tensor field t of type (1,2) on the 2-dimensional differentiable manifold M - sage: XM.tensor((1,0), name='a') - Vector field a on the 2-dimensional differentiable manifold M - sage: XM.tensor((0,2), name='a', antisym=(0,1)) - 2-form a on the 2-dimensional differentiable manifold M .. SEEALSO:: @@ -806,6 +821,82 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, self, tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) + def tensor(self, *args, **kwds): + r""" + Construct a tensor field on the domain of ``self`` or a tensor product of ``self`` with other modules. + + If ``args`` consist of other parents, just delegate to :meth:`tensor_product`. + + Otherwise, construct a tensor (i.e., a tensor field on the domain of + the vector field module) from the following input. + + INPUT: + + - ``tensor_type`` -- pair (k,l) with k being the contravariant rank + and l the covariant rank + - ``name`` -- (string; default: ``None``) name given to the tensor + - ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote + the tensor; if none is provided, the LaTeX symbol is set to ``name`` + - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention position=0 for the first argument; for instance: + + * ``sym=(0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym=[(0,2),(1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments + + - ``antisym`` -- (default: ``None``) antisymmetry or list of + antisymmetries among the arguments, with the same convention + as for ``sym`` + - ``specific_type`` -- (default: ``None``) specific subclass of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` for + the output + + OUTPUT: + + - instance of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + representing the tensor defined on the vector field module with the + provided characteristics + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor((1,2), name='t') + Tensor field t of type (1,2) on the 2-dimensional differentiable + manifold M + sage: XM.tensor((1,0), name='a') + Vector field a on the 2-dimensional differentiable manifold M + sage: XM.tensor((0,2), name='a', antisym=(0,1)) + 2-form a on the 2-dimensional differentiable manifold M + + Delegation to :meth:`tensor_product`:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor(XM) + Module T^(2,0)(M) of type-(2,0) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM, XM.dual(), XM) + Module T^(3,1)(M) of type-(3,1) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM).tensor(XM.dual().tensor(XM.dual())) + Traceback (most recent call last): + ... + AttributeError: 'TensorFieldModule_with_category' object has no attribute '_basis_sym' + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + for more examples and documentation. + """ + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so this method + # also needs to double as the tensor product construction + if isinstance(args[0], Parent): + return self.tensor_product(*args, **kwds) + return self._tensor(*args, **kwds) + def alternating_contravariant_tensor(self, degree, name=None, latex_name=None): r""" @@ -1715,7 +1806,7 @@ def destination_map(self) -> DiffMap: """ return self._dest_map - def tensor_module(self, k, l): + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the free module of all tensors of type `(k, l)` defined on ``self``. @@ -1764,11 +1855,15 @@ def tensor_module(self, k, l): for more examples and documentation. """ + if sym or antisym: + raise NotImplementedError try: return self._tensor_modules[(k,l)] except KeyError: if (k, l) == (1, 0): T = self + elif (k, l) == (0, 1): + T = self.dual() else: from sage.manifolds.differentiable.tensorfield_module import \ TensorFieldFreeModule @@ -1868,8 +1963,7 @@ def dual_exterior_power(self, p): Free module Omega^2(M) of 2-forms on the 2-dimensional differentiable manifold M sage: XM.dual_exterior_power(1) - Free module Omega^1(M) of 1-forms on the 2-dimensional - differentiable manifold M + Free module Omega^1(M) of 1-forms on the 2-dimensional differentiable manifold M sage: XM.dual_exterior_power(1) is XM.dual() True sage: XM.dual_exterior_power(0) @@ -1889,6 +1983,10 @@ def dual_exterior_power(self, p): except KeyError: if p == 0: L = self._ring + elif p == 1: + from sage.manifolds.differentiable.diff_form_module import \ + VectorFieldDualFreeModule + L = VectorFieldDualFreeModule(self) else: from sage.manifolds.differentiable.diff_form_module import \ DiffFormFreeModule @@ -2010,7 +2108,7 @@ def basis(self, symbol=None, latex_symbol=None, from_frame=None, symbol_dual=symbol_dual, latex_symbol_dual=latex_symbol_dual) - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None, specific_type=None): r""" Construct a tensor on ``self``. diff --git a/src/sage/manifolds/differentiable/vectorframe.py b/src/sage/manifolds/differentiable/vectorframe.py index 6df9c54c0d1..8f8c26c0c61 100644 --- a/src/sage/manifolds/differentiable/vectorframe.py +++ b/src/sage/manifolds/differentiable/vectorframe.py @@ -452,10 +452,10 @@ def set_name(self, symbol, latex_symbol=None, indices=None, \left(M, \left(e^{\xi},e^{\zeta}\right)\right) """ - super(CoFrame, self).set_name(symbol, latex_symbol=latex_symbol, - indices=indices, - latex_indices=latex_indices, - index_position=index_position) + super().set_name(symbol, latex_symbol=latex_symbol, + indices=indices, + latex_indices=latex_indices, + index_position=index_position) if include_domain: # Redefinition of the name and the LaTeX name to include the domain self._name = "({}, {})".format(self._domain._name, self._name) @@ -672,12 +672,12 @@ def __classcall_private__(cls, vector_field_module, symbol, symbol_dual = tuple(symbol_dual) if isinstance(latex_symbol_dual, list): latex_symbol_dual = tuple(latex_symbol_dual) - return super(VectorFrame, cls).__classcall__(cls, vector_field_module, - symbol, latex_symbol=latex_symbol, - from_frame=from_frame, indices=indices, - latex_indices=latex_indices, - symbol_dual=symbol_dual, - latex_symbol_dual=latex_symbol_dual) + return super().__classcall__(cls, vector_field_module, + symbol, latex_symbol=latex_symbol, + from_frame=from_frame, indices=indices, + latex_indices=latex_indices, + symbol_dual=symbol_dual, + latex_symbol_dual=latex_symbol_dual) def __init__(self, vector_field_module, symbol, latex_symbol=None, from_frame=None, indices=None, latex_indices=None, @@ -1570,10 +1570,10 @@ def set_name(self, symbol, latex_symbol=None, indices=None, \left(M, \left(E_{\alpha},E_{\beta}\right)\right) """ - super(VectorFrame, self).set_name(symbol, latex_symbol=latex_symbol, - indices=indices, - latex_indices=latex_indices, - index_position=index_position) + super().set_name(symbol, latex_symbol=latex_symbol, + indices=indices, + latex_indices=latex_indices, + index_position=index_position) if include_domain: # Redefinition of the name and the LaTeX name to include the domain self._name = "({}, {})".format(self._domain._name, self._name) diff --git a/src/sage/manifolds/local_frame.py b/src/sage/manifolds/local_frame.py index 4d7db5c7d55..fffe607efda 100644 --- a/src/sage/manifolds/local_frame.py +++ b/src/sage/manifolds/local_frame.py @@ -162,20 +162,21 @@ """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2013-2018 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> # Copyright (C) 2019 Michael Jung <micjung@uni-potsdam.de> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.tensor.modules.free_module_basis import (FreeModuleBasis, FreeModuleCoBasis) from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule + class LocalCoFrame(FreeModuleCoBasis): r""" Local coframe on a vector bundle. @@ -398,10 +399,10 @@ def set_name(self, symbol, latex_symbol=None, indices=None, \left(E|_{M}, \left(e^{\xi},e^{\zeta}\right)\right) """ - super(LocalCoFrame, self).set_name(symbol, latex_symbol=latex_symbol, - indices=indices, - latex_indices=latex_indices, - index_position=index_position) + super().set_name(symbol, latex_symbol=latex_symbol, + indices=indices, + latex_indices=latex_indices, + index_position=index_position) if include_domain: # Redefinition of the name and the LaTeX name to include the domain self._name = "({}|_{}, {})".format(self._vbundle._name, @@ -597,12 +598,12 @@ def __classcall_private__(cls, section_module, symbol, symbol_dual = tuple(symbol_dual) if isinstance(latex_symbol_dual, list): latex_symbol_dual = tuple(latex_symbol_dual) - return super(LocalFrame, cls).__classcall__(cls, section_module, - symbol, latex_symbol=latex_symbol, - indices=indices, - latex_indices=latex_indices, - symbol_dual=symbol_dual, - latex_symbol_dual=latex_symbol_dual) + return super().__classcall__(cls, section_module, + symbol, latex_symbol=latex_symbol, + indices=indices, + latex_indices=latex_indices, + symbol_dual=symbol_dual, + latex_symbol_dual=latex_symbol_dual) def __init__(self, section_module, symbol, latex_symbol=None, indices=None, latex_indices=None, symbol_dual=None, latex_symbol_dual=None): @@ -1239,10 +1240,10 @@ def set_name(self, symbol, latex_symbol=None, indices=None, \left(E|_{M}, \left(E_{\alpha},E_{\beta}\right)\right) """ - super(LocalFrame, self).set_name(symbol, latex_symbol=latex_symbol, - indices=indices, - latex_indices=latex_indices, - index_position=index_position) + super().set_name(symbol, latex_symbol=latex_symbol, + indices=indices, + latex_indices=latex_indices, + index_position=index_position) if include_domain: # Redefinition of the name and the LaTeX name to include the domain self._name = "({}|_{}, {})".format(self._vbundle._name, diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 09b040fb3fc..bb3f6cc42f5 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -2206,7 +2206,7 @@ class options(GlobalOptions): sage: M.options._reset() """ NAME = 'manifolds' - module = 'sage.manifolds' + module = 'sage.manifolds.manifold' option_class = 'TopologicalManifold' textbook_output = dict(default=True, description='textbook-like output instead of the Pynac output for derivatives', diff --git a/src/sage/manifolds/topological_submanifold.py b/src/sage/manifolds/topological_submanifold.py index 828b97703f5..314a6a16a1b 100644 --- a/src/sage/manifolds/topological_submanifold.py +++ b/src/sage/manifolds/topological_submanifold.py @@ -294,8 +294,7 @@ def open_subset(self, name, latex_name=None, coord_def={}, supersets=None): OUTPUT: - - the open subset, as an instance of - :class:`~sage.manifolds.manifold.topological_submanifold.TopologicalSubmanifold` + - the open subset, as an instance of :class:`TopologicalSubmanifold` EXAMPLES:: diff --git a/src/sage/manifolds/utilities.py b/src/sage/manifolds/utilities.py index 75a07519b0d..de83d63326f 100644 --- a/src/sage/manifolds/utilities.py +++ b/src/sage/manifolds/utilities.py @@ -204,7 +204,8 @@ def arithmetic(self, ex, operator): simpl = SR(1)/simpl return simpl # If operator is not a square root, we default to ExpressionTreeWalker: - return super(SimplifySqrtReal, self).arithmetic(ex, operator) + return super().arithmetic(ex, operator) + class SimplifyAbsTrig(ExpressionTreeWalker): r""" @@ -340,7 +341,7 @@ def composition(self, ex, operator): ex = -cos(x) return ex # If no pattern is found, we default to ExpressionTreeWalker: - return super(SimplifyAbsTrig, self).composition(ex, operator) + return super().composition(ex, operator) def simplify_sqrt_real(expr): diff --git a/src/sage/matrix/action.pyx b/src/sage/matrix/action.pyx index 6d4d7853b5f..dc057d1dce5 100644 --- a/src/sage/matrix/action.pyx +++ b/src/sage/matrix/action.pyx @@ -580,4 +580,3 @@ cdef class MatrixSchemePointAction(MatrixMulAction): (2 : 1) """ return P._matrix_times_point_(mat, self._codomain) - diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 9b7ce991295..9e738312c27 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -1223,7 +1223,7 @@ cdef class MatrixArgs: return self.sequence_type() if isinstance(self.entries, dict): return MA_ENTRIES_MAPPING - if isinstance(self.entries, (int, long, float, complex)): + if isinstance(self.entries, (int, float, complex)): return MA_ENTRIES_SCALAR # Note: some objects are callable, iterable and act like a @@ -1296,7 +1296,7 @@ cdef class MatrixArgs: return MA_ENTRIES_SEQ_SEQ else: return MA_ENTRIES_SEQ_FLAT - if isinstance(x, (int, long, float, complex)): + if isinstance(x, (int, float, complex)): return MA_ENTRIES_SEQ_FLAT if isinstance(x, Element) and element_is_scalar(<Element>x): return MA_ENTRIES_SEQ_FLAT diff --git a/src/sage/matrix/benchmark.py b/src/sage/matrix/benchmark.py index 86d09a63a49..811a1cbfc98 100644 --- a/src/sage/matrix/benchmark.py +++ b/src/sage/matrix/benchmark.py @@ -1229,12 +1229,10 @@ def nullspace_RDF(n=300, min=0, max=10, system='sage'): t := Cputime(); K := Kernel(A); s := Cputime(t); -"""%(n,min,max) +""" % (n, min, max) if verbose: print(code) magma.eval(code) return float(magma.eval('s')) else: - raise ValueError('unknown system "%s"'%system) - - + raise ValueError('unknown system "%s"' % system) diff --git a/src/sage/matrix/change_ring.pyx b/src/sage/matrix/change_ring.pyx index 33f962aebc0..c14d6540849 100644 --- a/src/sage/matrix/change_ring.pyx +++ b/src/sage/matrix/change_ring.pyx @@ -38,8 +38,5 @@ def integer_to_real_double_dense(Matrix_integer_dense A): S, None, None, None) for i from 0 <= i < A._nrows: for j from 0 <= j < A._ncols: - M.set_unsafe_double(i,j,A.get_unsafe_double(i,j)) + M.set_unsafe_double(i, j, A.get_unsafe_double(i, j)) return M - - - diff --git a/src/sage/matrix/echelon_matrix.pyx b/src/sage/matrix/echelon_matrix.pyx index 1b329b6a423..3fc43b485ca 100644 --- a/src/sage/matrix/echelon_matrix.pyx +++ b/src/sage/matrix/echelon_matrix.pyx @@ -151,4 +151,3 @@ def reduced_echelon_matrix_iterator(K, k, n, bint sparse=False, bint copy=True, yield m del v # hack: Python itertools reuses the tuple if nobody else uses it del pivots # hack: Python itertools reuses the tuple if nobody else uses it - diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 372ba91ec65..608e0078a8a 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -204,7 +204,7 @@ cdef class Matrix(sage.structure.element.Matrix): cdef Py_ssize_t i, j x = self.fetch('list') - if not x is None: + if x is not None: return x x = [] for i from 0 <= i < self._nrows: @@ -309,7 +309,7 @@ cdef class Matrix(sage.structure.element.Matrix): [ 3 4 10] """ d = self.fetch('dict') - if not d is None: + if d is not None: return d cdef Py_ssize_t i, j @@ -4612,7 +4612,8 @@ cdef class Matrix(sage.structure.element.Matrix): (0, 1) """ x = self.fetch('pivots') - if not x is None: return tuple(x) + if x is not None: + return tuple(x) self.echelon_form() x = self.fetch('pivots') if x is None: @@ -4650,10 +4651,10 @@ cdef class Matrix(sage.structure.element.Matrix): ....: [8*x^2 + 12*x + 15, 8*x^2 + 9*x + 16] ]) sage: m.rank() 2 - """ x = self.fetch('rank') - if not x is None: return x + if x is not None: + return x if self._nrows == 0 or self._ncols == 0: return 0 r = len(self.pivots()) @@ -4681,12 +4682,13 @@ cdef class Matrix(sage.structure.element.Matrix): (2,) """ x = self.fetch('nonpivots') - if not x is None: return tuple(x) + if x is not None: + return tuple(x) X = set(self.pivots()) np = [] for j in xrange(self.ncols()): - if not (j in X): + if j not in X: np.append(j) np = tuple(np) self.cache('nonpivots',np) @@ -4777,7 +4779,7 @@ cdef class Matrix(sage.structure.element.Matrix): [(0, 0), (1, 0), (1, 1), (0, 2)] """ x = self.fetch('nonzero_positions_by_column') - if not x is None: + if x is not None: if copy: return list(x) return x diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ad0f7e58e15..cc1343b1538 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -63,6 +63,8 @@ AUTHORS: Pfaffian - Moritz Firsching(2020-10-05): added ``quantum_determinant`` + +- Dima Pasechnik (2022-11-08): fixed ``echelonize`` for inexact matrices """ # **************************************************************************** @@ -797,7 +799,7 @@ cdef class Matrix(Matrix1): sage: RF = RealField(52) sage: B = matrix(RF, 2, 2, 1) sage: A = matrix(RF, [[0.24, 1, 0], [1, 0, 0]]) - sage: 0 < (A * A.solve_right(B) - B).norm() < 1e-14 + sage: 0 <= (A * A.solve_right(B) - B).norm() < 1e-14 True Over the inexact ring ``SR``, we can still verify the solution @@ -1534,19 +1536,15 @@ cdef class Matrix(Matrix1): Beware that the ``exact`` algorithm is not numerically stable, but the default ``numpy`` algorithm is:: - sage: M = matrix(RR, 3, 3, [1,2,3,1/3,2/3,3/3,1/5,2/5,3/5]) - sage: M.pseudoinverse() # tol 1e-15 - [0.0620518477661335 0.0206839492553778 0.0124103695532267] - [ 0.124103695532267 0.0413678985107557 0.0248207391064534] - [ 0.186155543298400 0.0620518477661335 0.0372311086596801] - sage: M.pseudoinverse(algorithm="numpy") # tol 1e-15 - [0.0620518477661335 0.0206839492553778 0.0124103695532267] - [ 0.124103695532267 0.0413678985107557 0.0248207391064534] - [ 0.186155543298400 0.0620518477661335 0.0372311086596801] - sage: M.pseudoinverse(algorithm="exact") - [ 0.125000000000000 0.0625000000000000 0.0312500000000000] - [ 0.250000000000000 0.125000000000000 0.0625000000000000] - [ 0.000000000000000 0.000000000000000 0.0625000000000000] + sage: M = matrix.hilbert(12,ring=RR) + sage: (~M*M).norm() # a considerable error + 1.3... + sage: Mx = M.pseudoinverse(algorithm="exact") + sage: (Mx*M).norm() # huge error + 11.5... + sage: Mx = M.pseudoinverse(algorithm="numpy") + sage: (Mx*M).norm() # still OK + 1.00... When multiplying the given matrix with the pseudoinverse, the result is symmetric for the ``exact`` algorithm or hermitian @@ -1580,6 +1578,11 @@ cdef class Matrix(Matrix1): [-1286742750677287/643371375338643 1000799917193445/1000799917193444] [ 519646110850445/346430740566963 -300239975158034/600479950316067] + Although it is not too far off:: + + sage: (~M-M.pseudoinverse(algorithm="numpy")).norm() < 1e-14 + True + TESTS:: sage: M.pseudoinverse(algorithm="exact") @@ -2795,7 +2798,7 @@ cdef class Matrix(Matrix1): (y + 1) * (y + 2)^2 """ f = self.fetch('minpoly') - if not f is None: + if f is not None: return f.change_variable_name(var) f = self.charpoly(var=var, **kwds) try: @@ -3415,7 +3418,7 @@ cdef class Matrix(Matrix1): TypeError: Hessenbergize only possible for matrices over a field """ X = self.fetch('hessenberg_form') - if not X is None: + if X is not None: return X R = self._base_ring if R not in _Fields: @@ -4437,7 +4440,7 @@ cdef class Matrix(Matrix1): # Determine proof keyword for integer matrices proof = kwds.pop('proof', None) - if not (proof in [None, True, False]): + if proof not in [None, True, False]: raise ValueError("'proof' must be one of True, False or None, not %s" % proof) if not (proof is None or is_IntegerRing(R)): raise ValueError("'proof' flag only valid for matrices over the integers") @@ -4848,7 +4851,7 @@ cdef class Matrix(Matrix1): True """ K = self.fetch('right_kernel') - if not K is None: + if K is not None: verbose("retrieving cached right kernel for %sx%s matrix" % (self.nrows(), self.ncols()),level=1) return K @@ -5024,7 +5027,7 @@ cdef class Matrix(Matrix1): True """ K = self.fetch('left_kernel') - if not K is None: + if K is not None: verbose("retrieving cached left kernel for %sx%s matrix" % (self.nrows(), self.ncols()),level=1) return K @@ -5121,7 +5124,7 @@ cdef class Matrix(Matrix1): True """ A = self.restrict(V, check=check) - if not poly is None: + if poly is not None: A = poly(A) W = A.kernel() if V.is_ambient(): @@ -6263,7 +6266,7 @@ cdef class Matrix(Matrix1): key = 'eigenspaces_left_' + format + '{0}'.format(var) x = self.fetch(key) - if not x is None: + if x is not None: if algebraic_multiplicity: return x else: @@ -6505,7 +6508,7 @@ cdef class Matrix(Matrix1): key = 'eigenspaces_right_' + format + '{0}'.format(var) x = self.fetch(key) - if not x is None: + if x is not None: if algebraic_multiplicity: return x else: @@ -6743,7 +6746,7 @@ cdef class Matrix(Matrix1): % self.base_ring()) x = self.fetch('eigenvectors_left') - if not x is None: + if x is not None: return x if not self.base_ring().is_exact(): @@ -7599,8 +7602,18 @@ cdef class Matrix(Matrix1): sage: transformation_matrix = m.echelonize(transformation=True) sage: m == transformation_matrix * m_original True + + TESTS:: + + Check that :trac:`34724` is fixed (indirect doctest):: + + sage: a=6.12323399573677e-17 + sage: m=matrix(RR,[[-a, -1.72508242466029], [ 0.579682446302195, a]]) + sage: (~m*m).norm() + 1.0 """ self.check_mutability() + basring = self.base_ring() if algorithm == 'default': from sage.categories.discrete_valuation import DiscreteValuationFields @@ -7610,16 +7623,22 @@ cdef class Matrix(Matrix1): # In general, we would like to do so in any rank one valuation ring, # but this should be done by introducing a category of general valuation rings and fields, # which we don't have at the moment - elif self.base_ring() in DiscreteValuationFields(): + elif basring in DiscreteValuationFields(): try: - self.base_ring().one().abs() + basring.one().abs() algorithm = 'scaled_partial_pivoting' except (AttributeError, TypeError): algorithm = 'scaled_partial_pivoting_valuation' - else: + elif basring.is_exact(): algorithm = 'classical' + else: + try: + (basring(0.42)).abs() + algorithm = 'scaled_partial_pivoting' + except (AttributeError, ArithmeticError, TypeError): + algorithm = 'classical' try: - if self.base_ring() in _Fields: + if basring in _Fields: if algorithm in ['classical', 'partial_pivoting', 'scaled_partial_pivoting', 'scaled_partial_pivoting_valuation']: self._echelon_in_place(algorithm) elif algorithm == 'strassen': @@ -7631,7 +7650,7 @@ cdef class Matrix(Matrix1): kwds['algorithm'] = algorithm return self._echelonize_ring(**kwds) except ArithmeticError as msg: - raise NotImplementedError("%s\nEchelon form not implemented over '%s'."%(msg,self.base_ring())) + raise NotImplementedError("%s\nEchelon form not implemented over '%s'."%(msg,basring)) def echelon_form(self, algorithm="default", cutoff=0, **kwds): r""" @@ -7786,7 +7805,7 @@ cdef class Matrix(Matrix1): [0.000 1.00 1.00] """ E = self.fetch('echelon_' + algorithm) - if not E is None: + if E is not None: return E E = self.__copy__() E._echelon_in_place(algorithm) @@ -8572,7 +8591,7 @@ cdef class Matrix(Matrix1): """ if self._ncols != right._nrows: raise ArithmeticError("Number of columns of self must equal number of rows of right.") - if not self._base_ring is right.base_ring(): + if self._base_ring is not right.base_ring(): raise TypeError("Base rings must be the same.") if cutoff == 0: @@ -8676,8 +8695,8 @@ cdef class Matrix(Matrix1): nrows = self._nrows - row if ncols == -1: ncols = self._ncols - col - if check and (row < 0 or col < 0 or row + nrows > self._nrows or \ - col + ncols > self._ncols): + if check and (row < 0 or col < 0 or row + nrows > self._nrows or + col + ncols > self._ncols): raise IndexError("matrix window index out of range") return matrix_window.MatrixWindow(self, row, col, nrows, ncols) @@ -9251,7 +9270,7 @@ cdef class Matrix(Matrix1): if density >= 1: for i from 0 <= i < self._nrows: for j from 0 <= j < self._ncols: - self.set_unsafe(i, j, R._random_nonzero_element(*args,\ + self.set_unsafe(i, j, R._random_nonzero_element(*args, **kwds)) else: num = int(self._nrows * self._ncols * density) @@ -9624,7 +9643,7 @@ cdef class Matrix(Matrix1): """ key = 'normal' n = self.fetch(key) - if not n is None: + if n is not None: return n if not self.is_square(): self.cache(key, False) @@ -9932,12 +9951,11 @@ cdef class Matrix(Matrix1): The result is cached. """ - if self._nrows != self._ncols: raise ValueError("must be a square matrix") X = self.fetch('adjugate') - if not X is None: + if X is not None: return X X = self._adjugate() @@ -11243,16 +11261,17 @@ cdef class Matrix(Matrix1): while ranks[i] > ranks[i+1] and ranks[i+1] > n-mult: C = B*C ranks.append(C.rank()) - i = i+1 + i += 1 diagram = [ranks[i]-ranks[i+1] for i in xrange(len(ranks)-1)] - blocks.extend([(eval, i) \ - for i in Partition(diagram).conjugate()]) + blocks.extend([(eval, i) + for i in Partition(diagram).conjugate()]) # ``J`` is the matrix in Jordan canonical form. Note that the blocks # are ordered firstly by the eigenvalues, in the same order as obeyed # by ``.roots()``, and secondly by size from greatest to smallest. - J = block_diagonal_matrix([jordan_block(eval, size, sparse=sparse) \ - for (eval, size) in blocks], subdivide=subdivide) + J = block_diagonal_matrix([jordan_block(eval, size, sparse=sparse) + for (eval, size) in blocks], + subdivide=subdivide) if transformation: from itertools import groupby @@ -11268,15 +11287,15 @@ cdef class Matrix(Matrix1): # Let B be the matrix `A - eval Id`. B = A - eval - block_sizes = [size for e,size in blocks if e == eval] - block_size_pairs = [(val,len(list(c))) \ - for val,c in groupby(block_sizes)] + block_sizes = [size for e, size in blocks if e == eval] + block_size_pairs = [(val, len(list(c))) + for val, c in groupby(block_sizes)] # Y is a list of vectors, spanning everything that we have # covered by the Jordan chains we developed so far. Y = [] - for l,count in block_size_pairs: + for l, count in block_size_pairs: # There are ``count`` Jordan blocks of size ``l`` # associated to this eigenvalue. @@ -12382,7 +12401,7 @@ cdef class Matrix(Matrix1): iterates, poly, augmented, pivots = self._cyclic_subspace(v) k = len(pivots) - polynomial = not var is None + polynomial = (var is not None) if polynomial: x = sage.rings.polynomial.polynomial_ring.polygen(R, var) poly = sum([poly[i] * x**i for i in range(len(poly))]) diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index 47c279254c2..55e1b7d739e 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1058,7 +1058,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): mpq_clear(tmp) sig_off() - def randomize(self, density=1, num_bound=2, den_bound=2, \ + def randomize(self, density=1, num_bound=2, den_bound=2, distribution=None, nonzero=False, *args, **kwds): r""" Randomize the entries of ``self``. @@ -1118,7 +1118,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): for col in range(self._matrix._ncols): col_is_zero = True while col_is_zero: - self._randomize_rational_column_unsafe(col, B.value, \ + self._randomize_rational_column_unsafe(col, B.value, C.value, distribution) # Check whether the new column is non-zero for i in range(self._degree): @@ -1131,7 +1131,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): col = rstate.c_random() % self._matrix._ncols col_is_zero = True while col_is_zero: - self._randomize_rational_column_unsafe(col, B.value, \ + self._randomize_rational_column_unsafe(col, B.value, C.value, distribution) # Check whether the new column is non-zero for i in range(self._degree): @@ -1141,25 +1141,27 @@ cdef class Matrix_cyclo_dense(Matrix_dense): else: if density >= 1: for col in range(self._matrix._ncols): - self._randomize_rational_column_unsafe(col, B.value, \ + self._randomize_rational_column_unsafe(col, B.value, C.value, distribution) else: num = int(self._nrows * self._ncols * density) for k in range(num): col = rstate.c_random() % self._matrix._ncols - self._randomize_rational_column_unsafe(col, B.value, \ + self._randomize_rational_column_unsafe(col, B.value, C.value, distribution) def _charpoly_bound(self): """ Determine a bound for the coefficients of the characteristic - polynomial of self. We use the bound in Lemma 2.2 of: + polynomial of ``self``. + + We use the bound in Lemma 2.2 of: Dumas, J-G. "Bounds on the coefficients of characteristic and minimal polynomials." J. Inequal. Pure Appl. Math. 8 (2007), no. 2. - This bound only applies for `self._nrows >= 4`, so in all + This bound only applies for ``self._nrows >= 4``, so in all smaller cases, we just use a naive bound. EXAMPLES:: @@ -1961,4 +1963,3 @@ cdef class Matrix_cyclo_dense(Matrix_dense): if subdivide: M.subdivide([Anr*i for i in range(1,nr)], [Anc*i for i in range(1,nc)]) return M - diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index d9c89a6d38d..b0f54d7f32e 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -1031,7 +1031,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): return P, L, U PLU = self.fetch('PLU_factors') - if not PLU is None: + if PLU is not None: return PLU if scipy is None: import scipy @@ -2351,7 +2351,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm)) key = 'unitary_{0}_{1}'.format(algorithm, tol) b = self.fetch(key) - if not b is None: + if b is not None: return b if not self.is_square(): self.cache(key, False) @@ -2479,7 +2479,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): key = ("_is_hermitian_orthonormal", tol, skew) h = self.fetch(key) - if not h is None: + if h is not None: return h if not self.is_square(): self.cache(key, False) @@ -2910,7 +2910,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): key = 'normal_{0}_{1}'.format(algorithm, tol) b = self.fetch(key) - if not b is None: + if b is not None: return b if not self.is_square(): self.cache(key, False) @@ -3214,7 +3214,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): format = 'real' schur = self.fetch('schur_factors_' + format) - if not schur is None: + if schur is not None: return schur if scipy is None: import scipy diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx index 0102d045e6c..8485760e9cb 100644 --- a/src/sage/matrix/matrix_gap.pyx +++ b/src/sage/matrix/matrix_gap.pyx @@ -327,7 +327,7 @@ cdef class Matrix_gap(Matrix_dense): def transpose(self): r""" Return the transpose of this matrix. - + EXAMPLES:: sage: M = MatrixSpace(QQ, 2, implementation='gap') diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index a35a149db56..922f6288696 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -129,7 +129,7 @@ cdef class M4RIE_finite_field: gf2e_free(self.ff) cdef m4ri_word poly_to_word(f): - return f.integer_representation() + return f.to_integer() cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): @@ -903,7 +903,8 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): full = int(reduced) x = self.fetch('in_echelon_form') - if not x is None: return # already known to be in echelon form + if x is not None: + return # already known to be in echelon form self.check_mutability() self.clear_cache() @@ -1335,7 +1336,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): 0 """ x = self.fetch('rank') - if not x is None: + if x is not None: return x if self._nrows == 0 or self._ncols == 0: return 0 @@ -1517,6 +1518,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): mzed_cling(self._entries, v) mzd_slice_free(v) + def unpickle_matrix_gf2e_dense_v0(Matrix_mod2_dense a, base_ring, nrows, ncols): r""" EXAMPLES:: diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index e5fdcca4e3f..14dcd5b589d 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -25,19 +25,18 @@ AUTHORS: """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2015 Simon King <simon.king@uni-jena.de> # # 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 cysignals.memory cimport check_realloc, check_malloc, sig_free from cpython.bytes cimport PyBytes_AsString, PyBytes_FromStringAndSize -from sage.cpython.string cimport str_to_bytes from cysignals.signals cimport sig_on, sig_off, sig_check cimport cython @@ -105,11 +104,11 @@ cdef class FieldConverter_class: sage: C = M._converter sage: C.fel_to_field(15) 3*y - sage: F.fetch_int(15) + sage: F.from_integer(15) 3*y sage: C.field_to_fel(y) 5 - sage: y.integer_representation() + sage: y.to_integer() 5 """ def __init__(self, field): @@ -139,7 +138,7 @@ cdef class FieldConverter_class: sage: C = FieldConverter_class(F) sage: C.fel_to_field(15) 3*y - sage: F.fetch_int(15) + sage: F.from_integer(15) 3*y """ return self.field(FfToInt(x)) @@ -155,7 +154,7 @@ cdef class FieldConverter_class: sage: C = FieldConverter_class(F) sage: C.field_to_fel(y) 5 - sage: y.integer_representation() + sage: y.to_integer() 5 TESTS: @@ -165,9 +164,9 @@ cdef class FieldConverter_class: sage: C.field_to_fel('foo') Traceback (most recent call last): ... - AttributeError: 'str' object has no attribute 'integer_representation' + AttributeError: 'str' object has no attribute 'to_integer' """ - return FfFromInt(x.integer_representation()) + return FfFromInt(x.to_integer()) cdef class PrimeFieldConverter_class(FieldConverter_class): @@ -1937,7 +1936,7 @@ def mtx_unpickle(f, int nr, int nc, data, bint m): # in the following line, we use a helper function that would return bytes, # regardless whether the input is bytes or str. cdef bytes Data = str_to_bytes(data, encoding='latin1') - if isinstance(f, (int, long)): + if isinstance(f, int): # This is for old pickles created with the group cohomology spkg MS = MatrixSpace(GF(f, 'z'), nr, nc, implementation=Matrix_gfpn_dense) else: diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 3b8db1ec73f..b9f9d5791b4 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1639,7 +1639,7 @@ cdef class Matrix_integer_dense(Matrix_dense): from .matrix_modn_dense_float import MAX_MODULUS as MAX_MODULUS_FLOAT from .matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_DOUBLE - if isinstance(moduli, (int, long, Integer)): + if isinstance(moduli, (int, Integer)): return self._mod_int(moduli) elif isinstance(moduli, list): moduli = MultiModularBasis(moduli) @@ -2269,7 +2269,7 @@ cdef class Matrix_integer_dense(Matrix_dense): [ 0 0 0] """ p = self.fetch('pivots') - if not p is None: + if p is not None: return tuple(p) cdef Matrix_integer_dense E @@ -3416,7 +3416,7 @@ cdef class Matrix_integer_dense(Matrix_dense): from .misc import matrix_integer_dense_rational_reconstruction return matrix_integer_dense_rational_reconstruction(self, N) - def randomize(self, density=1, x=None, y=None, distribution=None, \ + def randomize(self, density=1, x=None, y=None, distribution=None, nonzero=False): """ Randomize ``density`` proportion of the entries of this matrix, @@ -3487,8 +3487,8 @@ cdef class Matrix_integer_dense(Matrix_dense): if density == 1: for i from 0 <= i < self._nrows: for j from 0 <= j < self._ncols: - the_integer_ring._randomize_mpz(tmp, x, y, \ - distribution) + the_integer_ring._randomize_mpz(tmp, x, y, + distribution) self.set_unsafe_mpz(i,j,tmp) else: nc = self._ncols @@ -3496,7 +3496,7 @@ cdef class Matrix_integer_dense(Matrix_dense): for i from 0 <= i < self._nrows: for j from 0 <= j < num_per_row: k = rstate.c_random()%nc - the_integer_ring._randomize_mpz(tmp, \ + the_integer_ring._randomize_mpz(tmp, x, y, distribution) self.set_unsafe_mpz(i,k,tmp) sig_off() @@ -3509,7 +3509,7 @@ cdef class Matrix_integer_dense(Matrix_dense): for i from 0 <= i < self._nrows: for j from 0 <= j < self._ncols: while fmpz_sgn(fmpz_mat_entry(self._matrix,i,j)) == 0: - the_integer_ring._randomize_mpz(tmp, \ + the_integer_ring._randomize_mpz(tmp, x, y, distribution) self.set_unsafe_mpz(i,j,tmp) else: @@ -3519,7 +3519,7 @@ cdef class Matrix_integer_dense(Matrix_dense): for j from 0 <= j < num_per_row: k = rstate.c_random() % nc while fmpz_sgn(fmpz_mat_entry(self._matrix,i,k)) == 0: - the_integer_ring._randomize_mpz(tmp,\ + the_integer_ring._randomize_mpz(tmp, x, y, distribution) self.set_unsafe_mpz(i,k,tmp) @@ -3582,7 +3582,7 @@ cdef class Matrix_integer_dense(Matrix_dense): "or 'linbox'") r = self.fetch('rank') - if not r is None: + if r is not None: return r if algorithm == 'flint' or (self._nrows <= 6 and self._ncols <= 6 diff --git a/src/sage/matrix/matrix_integer_dense_saturation.py b/src/sage/matrix/matrix_integer_dense_saturation.py index 9ac854ea2d1..a7ce81fc86a 100644 --- a/src/sage/matrix/matrix_integer_dense_saturation.py +++ b/src/sage/matrix/matrix_integer_dense_saturation.py @@ -103,12 +103,12 @@ def random_sublist_of_size(k, n): w = random_sublist_of_size(k, k - n) m = set(w) w = [z for z in range(k) if z not in m] - assert(len(w) == n) + assert len(w) == n return w randrange = current_randstate().python_random().randrange - w = set([]) + w = set() while len(w) < n: z = randrange(k) if z not in w: @@ -339,13 +339,11 @@ def index_in_saturation(A, proof=True): """ r = A.rank() if r == 0: - return ZZ(1) + return ZZ.one() if r < A.nrows(): A = A.hermite_form(proof=proof, include_zero_rows=False) if A.is_square(): return abs(A.determinant(proof=proof)) A = A.transpose() - A = A.hermite_form(proof=proof,include_zero_rows=False) + A = A.hermite_form(proof=proof, include_zero_rows=False) return abs(A.determinant(proof=proof)) - - diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index 79894a27f7e..a73432e588c 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -219,7 +219,7 @@ cdef class Matrix_integer_sparse(Matrix_sparse): be dangerous if you change entries of the returned dict. """ d = self.fetch('dict') - if not d is None: + if d is not None: return d cdef Py_ssize_t i, j, k @@ -311,10 +311,9 @@ cdef class Matrix_integer_sparse(Matrix_sparse): [0 1 0 0 0 0 1 0] sage: M._nonzero_positions_by_row() [(0, 3), (1, 1), (1, 6)] - """ x = self.fetch('nonzero_positions') - if not x is None: + if x is not None: if copy: return list(x) return x @@ -343,10 +342,9 @@ cdef class Matrix_integer_sparse(Matrix_sparse): [0 1 0 0 0 0 1 0] sage: M._nonzero_positions_by_column() [(1, 1), (0, 3), (1, 6)] - """ x = self.fetch('nonzero_positions_by_column') - if not x is None: + if x is not None: if copy: return list(x) return x diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index e68ba3ad0bc..84ddd3819c7 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -39,10 +39,10 @@ def prm_mul(p1, p2, mask_free, prec): - `p1,p2` -- polynomials as dictionaries - - `mask_free` -- an integer mask that give the list of free variables + - ``mask_free`` -- an integer mask that give the list of free variables (the `i`-th variable is free if the `i`-th bit of ``mask_free`` is `1`) - - `prec` -- if `prec` is not None, truncate the product at precision `prec` + - ``prec`` -- if ``prec`` is not ``None``, truncate the product at precision ``prec`` EXAMPLES:: @@ -287,7 +287,7 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None): a = A[i] # the i-th row of A for j in range(len(a)): if a[j]: - p1[1<<j] = a[j] * t + p1[1 << j] = a[j] * t # make the product with the preceding polynomials, taking care of # variables that can be integrated diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 12f5530cc92..5ac8832ffe9 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1040,7 +1040,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse full = int(reduced) x = self.fetch('in_echelon_form') - if not x is None: return # already known to be in echelon form + if x is not None: + return # already known to be in echelon form if algorithm == 'heuristic': @@ -1839,7 +1840,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse 0 """ x = self.fetch('rank') - if not x is None: + if x is not None: return x if self._nrows == 0 or self._ncols == 0: return 0 diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 7521b863668..73a8f97c740 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -278,7 +278,7 @@ cdef inline celement linbox_det(celement modulus, celement* entries, Py_ssize_t cdef ModField *F = new ModField(<long>modulus) cdef celement *cpy = linbox_copy(modulus, entries, n, n) - cdef celement d + cdef celement d = 0 cdef size_t nbthreads nbthreads = Parallelism().get('linbox') @@ -299,7 +299,7 @@ cdef inline celement linbox_matrix_matrix_multiply(celement modulus, celement* a C = A*B """ cdef ModField *F = new ModField(<long>modulus) - cdef ModField.Element one, zero + cdef ModField.Element one = 0, zero = 0 F[0].init(one, <int>1) F[0].init(zero, <int>0) @@ -327,7 +327,7 @@ cdef inline int linbox_matrix_vector_multiply(celement modulus, celement* C, cel C = A*v """ cdef ModField *F = new ModField(<long>modulus) - cdef ModField.Element one, zero + cdef ModField.Element one = 0, zero = 0 F.init(one, <int>1) F.init(zero, <int>0) diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 24f2f207f33..c5b85660f5f 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -231,7 +231,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): Finite Field of size 13 """ d = self.fetch('dict') - if not d is None: + if d is not None: return d cdef Py_ssize_t i, j, k @@ -427,7 +427,8 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): """ from sage.misc.verbose import verbose, get_verbose x = self.fetch('in_echelon_form') - if not x is None and x: return # already known to be in echelon form + if x is not None and x: + return # already known to be in echelon form self.check_mutability() cdef Py_ssize_t i, r, c, min, min_row, start_row @@ -501,7 +502,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): [(0, 3), (1, 1), (1, 6)] """ x = self.fetch('nonzero_positions') - if not x is None: + if x is not None: if copy: return list(x) return x diff --git a/src/sage/matrix/matrix_mpolynomial_dense.pyx b/src/sage/matrix/matrix_mpolynomial_dense.pyx index 91a77ff60c2..1fd54027d2d 100644 --- a/src/sage/matrix/matrix_mpolynomial_dense.pyx +++ b/src/sage/matrix/matrix_mpolynomial_dense.pyx @@ -150,7 +150,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): 2 """ x = self.fetch('pivots') - if not x is None: + if x is not None: return x self.echelon_form('frac') @@ -218,7 +218,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): return x = self.fetch('in_echelon_form_'+algorithm) - if not x is None: + if x is not None: return # already known to be in echelon form if algorithm == 'bareiss': @@ -256,7 +256,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): cdef R = self.base_ring() x = self.fetch('in_echelon_form_bareiss') - if not x is None: + if x is not None: return # already known to be in echelon form if isinstance(self.base_ring(), MPolynomialRing_libsingular): @@ -387,7 +387,8 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): cdef int c, r, i, j, rc, start_row, nr, nc x = self.fetch('in_echelon_form_row_reduction') - if not x is None: return # already known to be in echelon form + if x is not None: + return # already known to be in echelon form nr,nc = self.nrows(),self.ncols() F = self.base_ring().base_ring() @@ -525,7 +526,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): d = self.fetch('det') - if not d is None: + if d is not None: return d if self._nrows == 0: @@ -537,7 +538,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): elif self.fetch('charpoly') is not None: # if charpoly known, then det is easy. D = self.fetch('charpoly') - if not D is None: + if D is not None: c = D[0] if self._nrows % 2: c = -c diff --git a/src/sage/matrix/matrix_numpy_dense.pyx b/src/sage/matrix/matrix_numpy_dense.pyx index 215e8f46d76..6a3dafbb07a 100644 --- a/src/sage/matrix/matrix_numpy_dense.pyx +++ b/src/sage/matrix/matrix_numpy_dense.pyx @@ -314,9 +314,9 @@ cdef class Matrix_numpy_dense(Matrix_dense): """ cdef Py_ssize_t i, j tol = float(tol) - key = 'symmetric_%s'%tol + key = 'symmetric_%s' % tol b = self.fetch(key) - if not b is None: + if b is not None: return b if self._nrows != self._ncols: self.cache(key, False) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 0c7ad5e4aa8..92502d9632b 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -370,11 +370,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # raise an error if d does not have the right length if row_wise and len(d) != m: - raise ValueError("length of input degree list should be the " \ - + "row dimension of the input matrix") + raise ValueError("length of input degree list should be the " + "row dimension of the input matrix") elif (not row_wise) and len(d) != n: - raise ValueError("length of input degree list should be the " \ - + "column dimension of the input matrix") + raise ValueError("length of input degree list should be the " + "column dimension of the input matrix") from sage.matrix.constructor import matrix return matrix(self.base_ring().base_ring(), m, n, @@ -464,11 +464,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # raise an error if d does not have the right length if row_wise and len(d) != m: - raise ValueError("length of input precision list should be the " \ - + "row dimension of the input matrix") + raise ValueError("length of input precision list should be the " + "row dimension of the input matrix") elif (not row_wise) and len(d) != n: - raise ValueError("length of input precision list should be the " \ - + "column dimension of the input matrix") + raise ValueError("length of input precision list should be the " + "column dimension of the input matrix") return matrix(self.base_ring(), m, n, [[self[i,j].truncate(d[i]) if row_wise else self[i,j].truncate(d[j]) @@ -556,11 +556,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # raise an error if d does not have the right length if row_wise and len(d) != m: - raise ValueError("length of input shift list should be the " \ - + "row dimension of the input matrix") + raise ValueError("length of input shift list should be the " + "row dimension of the input matrix") elif (not row_wise) and len(d) != n: - raise ValueError("length of input shift list should be the " \ - + "column dimension of the input matrix") + raise ValueError("length of input shift list should be the " + "column dimension of the input matrix") return matrix(self.base_ring(), m, n, [[self[i,j].shift(d[i]) if row_wise else self[i,j].shift(d[j]) @@ -701,11 +701,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # raise an error if degree does not have the right length if row_wise and len(degree) != m: - raise ValueError("length of input degree list should be the " \ - + "row dimension of the input matrix") + raise ValueError("length of input degree list should be the " + "row dimension of the input matrix") elif (not row_wise) and len(degree) != n: - raise ValueError("length of input degree list should be the " \ - + "column dimension of the input matrix") + raise ValueError("length of input degree list should be the " + "column dimension of the input matrix") return matrix(self.base_ring(), m, n, [[self[i,j].reverse(degree[i]) if row_wise else self[i,j].reverse(degree[j]) @@ -788,8 +788,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): try: inv_trunc = self.constant_matrix().inverse() except ZeroDivisionError: - raise ZeroDivisionError("the constant matrix term self(0)" \ - " must be invertible") + raise ZeroDivisionError("the constant matrix term self(0)" + " must be invertible") except ArithmeticError: raise ArithmeticError("the input matrix must be square") @@ -2680,8 +2680,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): :meth:`reduce` . """ if self.nrows() != B.nrows(): - raise ValueError("row dimension of self should be the" \ - + " row dimension of the input matrix") + raise ValueError("row dimension of self should be the" + " row dimension of the input matrix") (Q,R) = self.T.right_quo_rem(B.T) return (Q.T,R.T) @@ -2839,8 +2839,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): :meth:`reduce` . """ if self.ncols() != B.ncols(): - raise ValueError("column dimension of self should be the" \ - + " column dimension of the input matrix") + raise ValueError("column dimension of self should be the" + " column dimension of the input matrix") if B.is_square() and \ B.is_reduced(row_wise=False,include_zero_vectors=False): # case of B column reduced (without zero columns): @@ -2859,10 +2859,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): (Q,R) = self.reduce(B,shifts=s,return_quotient=True) cdeg = R.column_degrees() if all([cdeg[i] + s[i] < 0 for i in range(B.ncols())]): - return (Q,R) - else: - raise ValueError("division of these matrices does not admit" \ - + " a remainder with the required degree property") + return (Q, R) + raise ValueError("division of these matrices does not admit a " + "remainder with the required degree property") def _right_quo_rem_reduced(self, B): r""" @@ -3194,11 +3193,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): :meth:`right_quo_rem` . """ if row_wise and self.ncols() != B.ncols(): - raise ValueError("column dimension of self should be the" \ - + " column dimension of the input matrix") + raise ValueError("column dimension of self should be the" + " column dimension of the input matrix") if not row_wise and (self.nrows() != B.nrows()): - raise ValueError("row dimension of self should be the" \ - + " row dimension of the input matrix") + raise ValueError("row dimension of self should be the" + " row dimension of the input matrix") # note: is_popov calls B._check_shift_dimension(shifts,row_wise) # --> no need to check here again if B.is_popov(shifts,row_wise,False,False): @@ -3355,30 +3354,30 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): if shifts is None: shifts = [0] * m if row_wise else [0] * n elif row_wise and len(shifts) != m: - raise ValueError('shifts length should be the row dimension of' \ - + ' the input matrix') + raise ValueError('shifts length should be the row dimension of' + ' the input matrix') elif (not row_wise) and len(shifts) != n: - raise ValueError('shifts length should be the column dimension' \ - + ' of the input matrix') + raise ValueError('shifts length should be the column dimension' + ' of the input matrix') # set default order / check order dimension if not isinstance(order,list): order = [order]*n if row_wise else [order]*m if row_wise and len(order) != n: - raise ValueError("order length should be the column dimension" \ - + " of the input matrix") + raise ValueError("order length should be the column dimension" + " of the input matrix") elif (not row_wise) and len(order) != m: - raise ValueError("order length should be the row dimension of" \ - + " the input matrix") + raise ValueError("order length should be the row dimension of" + " the input matrix") # raise an error if self does not have the right dimension if row_wise and self.ncols() != m: - raise ValueError("column dimension should be the row dimension" \ - + " of the input matrix") + raise ValueError("column dimension should be the row dimension" + " of the input matrix") elif (not row_wise) and self.nrows() != n: - raise ValueError("row dimension should be the column dimension" \ - + " of the input matrix") + raise ValueError("row dimension should be the column dimension" + " of the input matrix") # check square if not self.is_square(): @@ -3599,11 +3598,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # (since weak Popov, pivot degree is rdeg-shifts entrywise) degree_shifts = [shifts[i] - rdeg[i] for i in range(n)] # compute approximant basis with that list as shifts - P,rdeg = self.transpose()._approximant_basis_iterative( \ - order, degree_shifts) + P, rdeg = self.transpose()._approximant_basis_iterative( + order, degree_shifts) P = P.transpose() # right-multiply by inverse of leading matrix - lmat = P.leading_matrix(shifts=degree_shifts,row_wise=False) + lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False) P = P * lmat.inverse() else: P = P.transpose() @@ -3824,19 +3823,19 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): if shifts is None: shifts = [0] * m if row_wise else [0] * n elif row_wise and len(shifts) != m: - raise ValueError('shifts length should be the row dimension of' \ - + ' the input matrix') + raise ValueError('shifts length should be the row dimension of' + ' the input matrix') elif (not row_wise) and len(shifts) != n: - raise ValueError('shifts length should be the column dimension' \ - + ' of the input matrix') + raise ValueError('shifts length should be the column dimension' + ' of the input matrix') # raise an error if self does not have the right dimension if row_wise and self.ncols() != m: - raise ValueError("column dimension should be the row dimension" \ - + " of the input matrix") + raise ValueError("column dimension should be the row dimension" + " of the input matrix") elif (not row_wise) and self.nrows() != n: - raise ValueError("row dimension should be the column dimension" \ - + " of the input matrix") + raise ValueError("row dimension should be the column dimension" + " of the input matrix") # check full rank and shifts-(ordered weak) Popov form if normal_form and (not self.is_popov(shifts, row_wise, False, False)): diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index b86023f5ef9..93efb95781f 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -2210,7 +2210,7 @@ cdef class Matrix_rational_dense(Matrix_dense): mpq_clear(tmp2) return QA - def randomize(self, density=1, num_bound=2, den_bound=2, \ + def randomize(self, density=1, num_bound=2, den_bound=2, distribution=None, nonzero=False): """ Randomize ``density`` proportion of the entries of this matrix, leaving diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index da3270d470d..c7cd866b578 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -301,7 +301,7 @@ cdef class Matrix_rational_sparse(Matrix_sparse): be dangerous if you change entries of the returned dict. """ d = self.fetch('dict') - if not d is None: + if d is not None: return d cdef Py_ssize_t i, j, k @@ -337,10 +337,9 @@ cdef class Matrix_rational_sparse(Matrix_sparse): [0 1 0 0 0 0 1 0] sage: M.nonzero_positions() [(0, 3), (1, 1), (1, 6)] - """ x = self.fetch('nonzero_positions') - if not x is None: + if x is not None: if copy: return list(x) return x @@ -520,7 +519,8 @@ cdef class Matrix_rational_sparse(Matrix_sparse): """ x = self.fetch('in_echelon_form') - if not x is None: return # already known to be in echelon form + if x is not None: + return # already known to be in echelon form self.check_mutability() pivots = self._echelonize_multimodular(height_guess, proof, **kwds) @@ -557,9 +557,10 @@ cdef class Matrix_rational_sparse(Matrix_sparse): """ label = 'echelon_form_%s'%algorithm x = self.fetch(label) - if not x is None: + if x is not None: return x - if self.fetch('in_echelon_form'): return self + if self.fetch('in_echelon_form'): + return self E, pivots = self._echelon_form_multimodular(height_guess, proof=proof) @@ -805,4 +806,3 @@ cdef class Matrix_rational_sparse(Matrix_sparse): cdef mpq_t minus_one mpq_init(minus_one) mpq_set_si(minus_one, -1,1) - diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 26f058d3aeb..e889cee7905 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -49,7 +49,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.superseded import deprecated_function_alias - +from sage.misc.persist import register_unpickle_override from sage.categories.rings import Rings from sage.categories.fields import Fields from sage.categories.enumerated_sets import EnumeratedSets @@ -406,7 +406,6 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): return Matrix_generic_sparse - class MatrixSpace(UniqueRepresentation, Parent): """ The space of matrices of given size and base ring @@ -544,10 +543,10 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio sage: class MyMatrixSpace(MatrixSpace): ....: @staticmethod ....: def __classcall__(cls, base_ring, nrows, ncols=None, my_option=True, sparse=False, implementation=None): - ....: return super(MyMatrixSpace, cls).__classcall__(cls, base_ring, nrows, ncols=ncols, my_option=my_option, sparse=sparse, implementation=implementation) + ....: return super().__classcall__(cls, base_ring, nrows, ncols=ncols, my_option=my_option, sparse=sparse, implementation=implementation) ....: ....: def __init__(self, base_ring, nrows, ncols, sparse, implementation, my_option=True): - ....: super(MyMatrixSpace, self).__init__(base_ring, nrows, ncols, sparse, implementation) + ....: super().__init__(base_ring, nrows, ncols, sparse, implementation) ....: self._my_option = my_option sage: MS1 = MyMatrixSpace(ZZ, 2) @@ -558,7 +557,7 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio False """ if base_ring not in _Rings: - raise TypeError("base_ring (=%s) must be a ring"%base_ring) + raise TypeError("base_ring (=%s) must be a ring" % base_ring) nrows = int(nrows) if ncols is None: ncols = nrows @@ -574,8 +573,8 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio raise OverflowError("number of rows and columns may be at most %s" % sys.maxsize) matrix_cls = get_matrix_class(base_ring, nrows, ncols, sparse, implementation) - return super(MatrixSpace, cls).__classcall__( - cls, base_ring, nrows, ncols, sparse, matrix_cls, **kwds) + return super().__classcall__(cls, base_ring, nrows, + ncols, sparse, matrix_cls, **kwds) def __init__(self, base_ring, nrows, ncols, sparse, implementation): r""" @@ -1261,8 +1260,8 @@ def _repr_(self): s = "sparse" else: s = "dense" - s = "Full MatrixSpace of %s by %s %s matrices over %s"%( - self.__nrows, self.__ncols, s, self.base_ring()) + s = "Full MatrixSpace of %s by %s %s matrices over %s" % ( + self.__nrows, self.__ncols, s, self.base_ring()) if not self._has_default_implementation(): s += " (using {})".format(self.Element.__name__) @@ -1283,7 +1282,7 @@ def _repr_option(self, key): """ if key == 'element_ascii_art': return self.__nrows > 1 - return super(MatrixSpace, self)._repr_option(key) + return super()._repr_option(key) def _latex_(self): r""" @@ -1295,8 +1294,8 @@ def _latex_(self): sage: latex(MS3) \mathrm{Mat}_{6\times 6}(\Bold{Q}) """ - return "\\mathrm{Mat}_{%s\\times %s}(%s)"%(self.nrows(), self.ncols(), - latex.latex(self.base_ring())) + return "\\mathrm{Mat}_{%s\\times %s}(%s)" % (self.nrows(), self.ncols(), + latex.latex(self.base_ring())) def __len__(self): """ @@ -1504,14 +1503,14 @@ def __iter__(self): ... NotImplementedError: len() of an infinite set """ - #Make sure that we can iterate over the base ring + # Make sure that we can iterate over the base ring base_ring = self.base_ring() base_iter = iter(base_ring) - number_of_entries = (self.__nrows*self.__ncols) + number_of_entries = (self.__nrows * self.__ncols) - #If the number of entries is zero, then just - #yield the empty matrix in that case and return + # If the number of entries is zero, then just + # yield the empty matrix in that case and return if number_of_entries == 0: yield self(0) return @@ -1519,11 +1518,11 @@ def __iter__(self): import sage.combinat.integer_vector if not base_ring.is_finite(): - #When the base ring is not finite, then we should go - #through and yield the matrices by "weight", which is - #the total number of iterations that need to be done - #on the base ring to reach the matrix. - base_elements = [ next(base_iter) ] + # When the base ring is not finite, then we should go + # through and yield the matrices by "weight", which is + # the total number of iterations that need to be done + # on the base ring to reach the matrix. + base_elements = [next(base_iter)] weight = 0 while True: for iv in sage.combinat.integer_vector.IntegerVectors(weight, number_of_entries): @@ -1579,7 +1578,7 @@ def __getitem__(self, x): """ if isinstance(x, (integer.Integer, int)): return self.list()[x] - return super(MatrixSpace, self).__getitem__(x) + return super().__getitem__(x) def basis(self): """ @@ -1639,7 +1638,6 @@ def dims(self): """ return (self.__nrows, self.__ncols) - def submodule(self, gens, check=True, already_echelonized=False, unitriangular=False, support_order=None, category=None, *args, **opts): @@ -1733,6 +1731,7 @@ def submodule(self, gens, check=True, already_echelonized=False, category=category, *args, **opts) from sage.misc.cachefunc import cached_method + @cached_method def identity_matrix(self): """ @@ -1903,7 +1902,7 @@ def gen(self, n): r = n // self.__ncols c = n - (r * self.__ncols) z = self.zero_matrix().__copy__() - z[r,c] = 1 + z[r, c] = 1 return z @cached_method @@ -2208,10 +2207,10 @@ def random_element(self, density=None, *args, **kwds): """ Z = self.zero_matrix().__copy__() if density is None: - Z.randomize(density=float(1), nonzero=kwds.pop('nonzero', False), \ + Z.randomize(density=float(1), nonzero=kwds.pop('nonzero', False), *args, **kwds) else: - Z.randomize(density=density, nonzero=kwds.pop('nonzero', True), \ + Z.randomize(density=density, nonzero=kwds.pop('nonzero', True), *args, **kwds) return Z @@ -2328,10 +2327,8 @@ def _magma_init_(self, magma): """ K = magma(self.base_ring()) if self.__nrows == self.__ncols: - s = 'MatrixAlgebra(%s,%s)'%(K.name(), self.__nrows) - else: - s = 'RMatrixSpace(%s,%s,%s)'%(K.name(), self.__nrows, self.__ncols) - return s + return 'MatrixAlgebra(%s,%s)' % (K.name(), self.__nrows) + return 'RMatrixSpace(%s,%s,%s)' % (K.name(), self.__nrows, self.__ncols) def _polymake_init_(self): r""" @@ -2384,6 +2381,7 @@ def _random_nonzero_element(self, *args, **kwds): rand_matrix = self.random_element(*args, **kwds) return rand_matrix + def dict_to_list(entries, nrows, ncols): r""" Given a dictionary of coordinate tuples, return the list given by @@ -2468,59 +2466,59 @@ def _test_trivial_matrices_inverse(ring, sparse=True, implementation=None, check """ # Check that the empty 0x0 matrix is it's own inverse with det=1. ms00 = MatrixSpace(ring, 0, 0, sparse=sparse) - m00 = ms00(0) - assert(m00.determinant() == ring(1)) - assert(m00.is_invertible()) - assert(m00.inverse() == m00) + m00 = ms00(0) + assert m00.determinant() == ring(1) + assert m00.is_invertible() + assert m00.inverse() == m00 if checkrank: - assert(m00.rank() == 0) + assert m00.rank() == 0 # Check that the empty 0x3 and 3x0 matrices are not invertible and that # computing the determinant raise the proper exception. for ms0 in [MatrixSpace(ring, 0, 3, sparse=sparse), MatrixSpace(ring, 3, 0, sparse=sparse)]: - mn0 = ms0(0) - assert(not mn0.is_invertible()) + mn0 = ms0(0) + assert not mn0.is_invertible() try: d = mn0.determinant() print(d) res = False except ValueError: res = True - assert(res) + assert res try: mn0.inverse() res = False except ArithmeticError: res = True - assert(res) + assert res if checkrank: - assert(mn0.rank() == 0) + assert mn0.rank() == 0 # Check that the null 1x1 matrix is not invertible and that det=0 ms1 = MatrixSpace(ring, 1, 1, sparse=sparse) - m0 = ms1(0) - assert(not m0.is_invertible()) - assert(m0.determinant() == ring(0)) + m0 = ms1(0) + assert not m0.is_invertible() + assert m0.determinant() == ring(0) try: m0.inverse() res = False except (ZeroDivisionError, RuntimeError): - #FIXME: Make pynac throw a ZeroDivisionError on division by - #zero instead of a runtime Error + # FIXME: Make pynac throw a ZeroDivisionError on division by + # zero instead of a runtime Error res = True - assert(res) + assert res if checkrank: - assert(m0.rank() == 0) + assert m0.rank() == 0 # Check that the identity 1x1 matrix is its own inverse with det=1 - m1 = ms1(1) - assert(m1.is_invertible()) - assert(m1.determinant() == ring(1)) + m1 = ms1(1) + assert m1.is_invertible() + assert m1.determinant() == ring(1) inv = m1.inverse() - assert(inv == m1) + assert inv == m1 if checkrank: - assert(m1.rank() == 1) + assert m1.rank() == 1 test_trivial_matrices_inverse = deprecated_function_alias(33612, _test_trivial_matrices_inverse) @@ -2529,10 +2527,13 @@ def _test_trivial_matrices_inverse(ring, sparse=True, implementation=None, check # Fix unpickling Matrix_modn_dense and Matrix_integer_2x2 lazy_import('sage.matrix.matrix_modn_dense_double', 'Matrix_modn_dense_double') lazy_import('sage.matrix.matrix_integer_dense', 'Matrix_integer_dense') -from sage.misc.persist import register_unpickle_override + + def _MatrixSpace_ZZ_2x2(): from sage.rings.integer_ring import ZZ - return MatrixSpace(ZZ,2) + return MatrixSpace(ZZ, 2) + + register_unpickle_override('sage.matrix.matrix_modn_dense', 'Matrix_modn_dense', Matrix_modn_dense_double) register_unpickle_override('sage.matrix.matrix_integer_2x2', diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index cd5daef1903..dcd9c2e1550 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -1005,7 +1005,7 @@ cdef class Matrix_sparse(matrix.Matrix): [ 0 1 2 3] [ 4 5 6 7] - TESTS:: + TESTS: One can stack matrices over different rings (:trac:`16399`). :: diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 9409ff5c1e8..8774848f2d7 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -166,6 +166,21 @@ cdef maxima from sage.calculus.calculus import symbolic_expression_from_maxima_string, maxima cdef class Matrix_symbolic_dense(Matrix_generic_dense): + def echelonize(self, **kwds): + """ + Echelonize using the classical algorithm. + + + TESTS:: + + sage: m = matrix([[cos(pi/5), sin(pi/5)], [-sin(pi/5), cos(pi/5)]]) + sage: m.echelonize(); m + [1 0] + [0 1] + """ + + return super().echelonize(algorithm="classical", **kwds) + def eigenvalues(self, extend=True): """ Compute the eigenvalues by solving the characteristic diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index ff22a20d7c1..9b59fa8dcee 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -4,18 +4,23 @@ This module implements general operation tables, which are very matrix-like. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Rob Beezer <beezer at ups.edu> # # 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.sage_object import SageObject +from matplotlib.cm import gist_rainbow, Greys +from sage.plot.matrix_plot import matrix_plot +from sage.matrix.constructor import Matrix +from sage.plot.text import text +from copy import copy + class OperationTable(SageObject): r""" @@ -374,15 +379,12 @@ class OperationTable(SageObject): k| k i g f d e l b h a j c l| l c j i g a h e k b d f - .. TODO:: - - Provide color and grayscale graphical representations of tables. - See commented-out stubs in source code. - - AUTHOR: + AUTHORS: - Rob Beezer (2010-03-15) + - Bruno Edwards (2022-10-31) """ + def __init__(self, S, operation, names='letters', elements=None): r""" TESTS:: @@ -397,7 +399,7 @@ def __init__(self, S, operation, names='letters', elements=None): # Note: there exist listable infinite objects (like ZZ) if (elements is None): if hasattr(S, 'is_finite'): - if not(S.is_finite()): + if not S.is_finite(): raise ValueError('%s is infinite' % S) try: try: @@ -411,7 +413,7 @@ def __init__(self, S, operation, names='letters', elements=None): try: for e in elements: coerced = S(e) - if not(coerced in elems): + if coerced not in elems: elems.append(coerced) except Exception: raise TypeError('unable to coerce %s into %s' % (e, S)) @@ -432,7 +434,7 @@ def __init__(self, S, operation, names='letters', elements=None): supported = { add: (add, '+', '+'), mul: (mul, '*', '\\ast') - } + } # default symbols for upper-left-hand-corner of table self._ascii_symbol = '.' self._latex_symbol = '\\cdot' @@ -451,7 +453,7 @@ def __init__(self, S, operation, names='letters', elements=None): # the elements might not be hashable. But if they are it is much # faster to lookup in a hash table rather than in a list! try: - get_row = {e: i for i,e in enumerate(self._elts)}.__getitem__ + get_row = {e: i for i, e in enumerate(self._elts)}.__getitem__ except TypeError: get_row = self._elts.index @@ -461,7 +463,8 @@ def __init__(self, S, operation, names='letters', elements=None): try: result = self._operation(g, h) except Exception: - raise TypeError('elements %s and %s of %s are incompatible with operation: %s' % (g,h,S,self._operation)) + raise TypeError('elements %s and %s of %s are incompatible with operation: %s' % ( + g, h, S, self._operation)) try: r = get_row(result) @@ -477,7 +480,8 @@ def __init__(self, S, operation, names='letters', elements=None): except (KeyError, ValueError): failed = True if failed: - raise ValueError('%s%s%s=%s, and so the set is not closed' % (g, self._ascii_symbol, h, result)) + raise ValueError('%s%s%s=%s, and so the set is not closed' % ( + g, self._ascii_symbol, h, result)) row.append(r) self._table.append(row) @@ -558,7 +562,8 @@ def _name_maker(self, names): else: width = int(log(self._n - 1, base)) + 1 for i in range(self._n): - places = Integer(i).digits(base=base, digits=letters, padto=width) + places = Integer(i).digits( + base=base, digits=letters, padto=width) places.reverse() name_list.append(''.join(places)) elif names == 'elements': @@ -570,19 +575,22 @@ def _name_maker(self, names): name_list.append(estr) elif isinstance(names, list): if len(names) != self._n: - raise ValueError('list of element names must be the same size as the set, %s != %s'%(len(names), self._n)) + raise ValueError('list of element names must be the same size as the set, %s != %s' % ( + len(names), self._n)) width = 0 for name in names: if not isinstance(name, str): - raise ValueError('list of element names must only contain strings, not %s' % name) + raise ValueError( + 'list of element names must only contain strings, not %s' % name) if len(name) > width: width = len(name) name_list.append(name) else: - raise ValueError("element names must be a list, or one of the keywords: 'letters', 'digits', 'elements'") + raise ValueError( + "element names must be a list, or one of the keywords: 'letters', 'digits', 'elements'") name_dict = {} for i in range(self._n): - name_dict[name_list[i]]=self._elts[i] + name_dict[name_list[i]] = self._elts[i] return width, name_list, name_dict def __getitem__(self, pair): @@ -632,13 +640,15 @@ def __getitem__(self, pair): IndexError: invalid indices of operation table: ((1,512), (1,3,2,4)(5,7)) """ if not (isinstance(pair, tuple) and len(pair) == 2): - raise TypeError('indexing into an operation table requires exactly two elements') + raise TypeError( + 'indexing into an operation table requires exactly two elements') g, h = pair try: row = self._elts.index(g) col = self._elts.index(h) except ValueError: - raise IndexError('invalid indices of operation table: (%s, %s)' % (g, h)) + raise IndexError( + 'invalid indices of operation table: (%s, %s)' % (g, h)) return self._elts[self._table[row][col]] def __eq__(self, other): @@ -747,8 +757,9 @@ def set_print_symbols(self, ascii, latex): ... ValueError: ASCII symbol should be a single character, not 5 """ - if not isinstance(ascii, str) or not len(ascii)==1: - raise ValueError('ASCII symbol should be a single character, not %s' % ascii) + if not isinstance(ascii, str) or not len(ascii) == 1: + raise ValueError( + 'ASCII symbol should be a single character, not %s' % ascii) if not isinstance(latex, str): raise ValueError('LaTeX symbol must be a string, not %s' % latex) self._ascii_symbol = ascii @@ -935,22 +946,79 @@ def matrix_of_variables(self): from sage.rings.rational_field import QQ R = PolynomialRing(QQ, 'x', self._n) MS = MatrixSpace(R, self._n, self._n) - entries = [R('x'+str(self._table[i][j])) for i in range(self._n) for j in range(self._n)] - return MS( entries ) - - #def color_table(): - #r""" - #Returns a graphic image as a square grid where entries are color coded. - #""" - #pass - #return None - - #def gray_table(): - #r""" - #Returns a graphic image as a square grid where entries are coded as grayscale values. - #""" - #pass - #return None + entries = [R('x'+str(self._table[i][j])) + for i in range(self._n) for j in range(self._n)] + return MS(entries) + + # documentation hack + # makes the cmap default argument look nice in the docs + # by copying the gist_rainbow object and overriding __repr__ + gist_rainbow_copy=copy(gist_rainbow) + class ReprOverrideLinearSegmentedColormap(gist_rainbow_copy.__class__): + def __repr__(self): + return "gist_rainbow" + gist_rainbow_copy.__class__=ReprOverrideLinearSegmentedColormap + + + def color_table(self, element_names=True, cmap=gist_rainbow_copy, **options): + r""" + Returns a graphic image as a square grid where entries are color coded. + + INPUT: + + - ``element_names`` - (default : ``True``) Whether to display text with element names on the image + + - ``cmap`` - (default : ``gist_rainbow``) colour map for plot, see matplotlib.cm + + - ``**options`` - passed on to matrix_plot call + + EXAMPLES:: + + sage: from sage.matrix.operation_table import OperationTable + sage: OTa = OperationTable(SymmetricGroup(3), operation=operator.mul) + sage: OTa.color_table() + Graphics object consisting of 37 graphics primitives + + .. PLOT:: + + from sage.matrix.operation_table import OperationTable + OTa = OperationTable(SymmetricGroup(3), operation=operator.mul) + sphinx_plot(OTa.color_table(), figsize=(3.0,3.0)) + """ + + # Base matrix plot object, without text + plot = matrix_plot(Matrix(self._table), cmap=cmap, + frame=False, **options) + + if element_names: + + # adapted from ._ascii_table() + # prepare widenames[] list for labelling on image + n = self._n + width = self._width + + widenames = [] + for name in self._names: + widenames.append("{0: >{1}s}".format(name, width)) + + # iterate through each element + for g in range(n): + for h in range(n): + + # add text to the plot + tPos = (g, h) + tText = widenames[self._table[g][h]] + t = text(tText, tPos, rgbcolor=(0, 0, 0)) + plot = plot + t + + # https://moyix.blogspot.com/2022/09/someones-been-messing-with-my-subnormals.html + import warnings + warnings.filterwarnings("ignore", message="The value of the smallest subnormal for") + + return plot + + def gray_table(self, **options): + return self.color_table(cmap=Greys, **options) def _ascii_table(self): r""" @@ -1031,7 +1099,7 @@ def _ascii_table(self): widenames.append('{0: >{1}s}'.format(name, width)) # Headers - table = ['{0: >{1}s} '.format(self._ascii_symbol,width)] + table = ['{0: >{1}s} '.format(self._ascii_symbol, width)] table += [' '+widenames[i] for i in range(n)]+['\n'] table += [' ']*width + ['+'] + ['-']*(n*(width+1))+['\n'] @@ -1068,7 +1136,8 @@ def _latex_(self): # Row label and body of table for g in range(n): - table.append('{}') # Interrupts newline and [], so not line spacing + # Interrupts newline and [], so not line spacing + table.append('{}') table.append(names[g]) for h in range(n): table.append('&'+names[self._table[g][h]]) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 619d5d7c892..5d3bb13445b 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -67,6 +67,7 @@ import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import vector from sage.structure.element import is_Matrix +from sage.structure.sequence import Sequence from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer @@ -2332,6 +2333,9 @@ def companion_matrix(poly, format='right'): ... ValueError: polynomial cannot be specified by an empty list + sage: companion_matrix([QQ.one()]).parent() + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + AUTHOR: - Rob Beezer (2011-05-19) @@ -2340,7 +2344,7 @@ def companion_matrix(poly, format='right'): if format not in ['right', 'left', 'top', 'bottom']: raise ValueError("format must be 'right', 'left', 'top' or 'bottom', not {0}".format(format)) try: - poly = list(poly) + poly = Sequence(poly) except TypeError: raise TypeError('input must be a polynomial (not a symbolic expression, see docstring), or other iterable, not {0}'.format(poly)) n = len(poly) - 1 @@ -2348,31 +2352,30 @@ def companion_matrix(poly, format='right'): raise ValueError('polynomial cannot be specified by an empty list') if not poly[n] == 1: raise ValueError('polynomial (or the polynomial implied by coefficients) must be monic, not a leading coefficient of {0}'.format(poly[n])) - entries = [0] * (n * n) + try: + M = sage.matrix.constructor.matrix(poly.universe(), n, n) + except TypeError: + raise TypeError("unable to find common ring for coefficients from polynomial") # 1's below diagonal, or above diagonal if format in ['right', 'top']: for i in range(n - 1): - entries[(i+1)*n + i] = 1 + M[i+1, i] = 1 else: for i in range(n-1): - entries[i*n + i+1] = 1 + M[i, i+1] = 1 # right side, left side (reversed), bottom edge, top edge (reversed) if format == 'right': for i in range(n): - entries[i*n + n-1] = -poly[i] + M[i, n-1] = -poly[i] elif format == 'left': for i in range(n): - entries[(n-1-i)*n + 0] = -poly[i] + M[n-1-i, 0] = -poly[i] elif format == 'bottom': for i in range(n): - entries[(n-1)*n + i] = -poly[i] + M[n-1, i] = -poly[i] elif format == 'top': for i in range(n): - entries[0*n + n-1-i] = -poly[i] - try: - M = sage.matrix.constructor.matrix(n, n, entries) - except TypeError: - raise TypeError("unable to find common ring for coefficients from polynomial") + M[0, n-1-i] = -poly[i] return M diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index cd343bd92e8..ab392d66cb1 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -556,7 +556,7 @@ class int_range: if indices is None: self._intervals = [] return - elif not range is None: + elif range is not None: self._intervals = [(int(indices), int(range))] else: self._intervals = [] @@ -867,4 +867,3 @@ def test(n, m, R, c=2): ## sage: C == D ## True - diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index 7b72cd245df..5679814d32b 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -2115,7 +2115,7 @@ cdef class BasisExchangeMatroid(Matroid): return EQ[0] cpdef _is_isomorphism(self, other, morphism): - """ + r""" Version of is_isomorphism() that does no type checking. INPUT: diff --git a/src/sage/matroids/basis_matroid.pyx b/src/sage/matroids/basis_matroid.pyx index b8fd1cdc342..ff70add1ce2 100644 --- a/src/sage/matroids/basis_matroid.pyx +++ b/src/sage/matroids/basis_matroid.pyx @@ -1280,8 +1280,9 @@ cdef long set_to_index(bitset_t S): s = bitset_next(S, s + 1) return index + cdef index_to_set(bitset_t S, long index, long k, long n): - """ + r""" Compute the k-subset of `\{0, ..., n-1\}` of rank index """ bitset_clear(S) diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index 11c1ae4bbbc..1fb2cd2b93e 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -34,9 +34,9 @@ For built-in matroids, do the following: -* Within a Sage session, type ``matroids.`` (Do not press "Enter", and do not - forget the final period ".") -* Hit "tab". +* Within a Sage session, type ``matroids.`` (Do not press :kbd:`Enter`, + and do not forget the final period ".") +* Hit :kbd:`Tab`. You will see a list of methods which will construct matroids. For example:: @@ -140,9 +140,9 @@ def Matroid(groundset=None, data=None, **kwds): There are two main entry points to Sage's matroid functionality. For built-in matroids, do the following: - * Within a Sage session, type "matroids." (Do not press "Enter", and do + * Within a Sage session, type "matroids." (Do not press :kbd:`Enter`, and do not forget the final period ".") - * Hit "tab". + * Hit :kbd:`Tab`. You will see a list of methods which will construct matroids. For example:: diff --git a/src/sage/matroids/extension.pyx b/src/sage/matroids/extension.pyx index f4f9d44516b..0408e60f86e 100644 --- a/src/sage/matroids/extension.pyx +++ b/src/sage/matroids/extension.pyx @@ -248,7 +248,7 @@ cdef class LinearSubclassesIter: cdef class LinearSubclasses: - """ + r""" An iterable set of linear subclasses of a matroid. Enumerate linear subclasses of a given matroid. A *linear subclass* is a @@ -412,7 +412,7 @@ cdef class LinearSubclasses: cdef class MatroidExtensions(LinearSubclasses): - """ + r""" An iterable set of single-element extensions of a given matroid. INPUT: diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index 01aaefc2f27..6b3cf27bcf2 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -3682,4 +3682,3 @@ cdef class RationalMatrix(LeanMatrix): version = 0 data = (self.nrows(), self.ncols(), entries) return unpickle_rational_matrix, (version, data) - diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 917017f6afe..0c9b0a1baff 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -761,7 +761,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): return {e: R[self._idx[e]] for e in self.groundset()} cpdef LeanMatrix _reduced_representation(self, B=None): - """ + r""" Return a reduced representation of the matroid, i.e. a matrix `R` such that `[I\ \ R]` represents the matroid. @@ -800,7 +800,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): # (field) isomorphism cpdef bint _is_field_isomorphism(self, LinearMatroid other, morphism): # not safe if self == other - """ + r""" Version of :meth:`<LinearMatroid.is_field_isomorphism>` that does no type checking. @@ -966,7 +966,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): return self._is_field_isomorphism(other, morphism) cpdef is_field_isomorphism(self, other, morphism): - """ + r""" Test if a provided morphism induces a bijection between represented matroids. @@ -1322,7 +1322,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): return type(self)(reduced_matrix=M, groundset=rows + cols) cpdef dual(self): - """ + r""" Return the dual of the matroid. Let `M` be a matroid with ground set `E`. If `B` is the set of bases @@ -1354,7 +1354,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): return type(self)(reduced_matrix=R, groundset=cols + rows) cpdef has_line_minor(self, k, hyperlines=None, certificate=False): - """ + r""" Test if the matroid has a `U_{2, k}`-minor. The matroid `U_{2, k}` is a matroid on `k` elements in which every @@ -3130,7 +3130,7 @@ cdef class BinaryMatroid(LinearMatroid): self._one = GF2_one cpdef base_ring(self): - """ + r""" Return the base ring of the matrix representing the matroid, in this case `\GF{2}`. @@ -3308,7 +3308,7 @@ cdef class BinaryMatroid(LinearMatroid): return self._A.copy() # Deprecated Sage matrix operation cpdef LeanMatrix _reduced_representation(self, B=None): - """ + r""" Return a reduced representation of the matroid, i.e. a matrix `R` such that `[I\ \ R]` represents the matroid. @@ -4197,7 +4197,7 @@ cdef class TernaryMatroid(LinearMatroid): self._two = GF3_minus_one cpdef base_ring(self): - """ + r""" Return the base ring of the matrix representing the matroid, in this case `\GF{3}`. @@ -4381,7 +4381,7 @@ cdef class TernaryMatroid(LinearMatroid): return self._A.copy() # Deprecated Sage matrix operation cpdef LeanMatrix _reduced_representation(self, B=None): - """ + r""" Return a reduced representation of the matroid, i.e. a matrix `R` such that `[I\ \ R]` represents the matroid. @@ -4563,7 +4563,7 @@ cdef class TernaryMatroid(LinearMatroid): return self._t_invariant cpdef bicycle_dimension(self): - """ + r""" Return the bicycle dimension of the ternary matroid. The bicycle dimension of a linear subspace `V` is @@ -5096,7 +5096,7 @@ cdef class QuaternaryMatroid(LinearMatroid): self._x_one = (<QuaternaryMatrix>self._A)._x_one cpdef base_ring(self): - """ + r""" Return the base ring of the matrix representing the matroid, in this case `\GF{4}`. @@ -5273,7 +5273,7 @@ cdef class QuaternaryMatroid(LinearMatroid): return self._A.copy() # Deprecated Sage matrix operation cpdef LeanMatrix _reduced_representation(self, B=None): - """ + r""" Return a reduced representation of the matroid, i.e. a matrix `R` such that `[I\ \ R]` represents the matroid. @@ -5413,7 +5413,7 @@ cdef class QuaternaryMatroid(LinearMatroid): return self._q_invariant cpdef bicycle_dimension(self): - """ + r""" Return the bicycle dimension of the quaternary matroid. The bicycle dimension of a linear subspace `V` is @@ -5810,7 +5810,7 @@ cdef class RegularMatroid(LinearMatroid): return P cpdef base_ring(self): - """ + r""" Return the base ring of the matrix representing the matroid, in this case `\ZZ`. @@ -6230,7 +6230,7 @@ cdef class RegularMatroid(LinearMatroid): return {e:idx[m[str(e)]] for e in self.groundset() if str(e) in m} cpdef has_line_minor(self, k, hyperlines=None, certificate=False): - """ + r""" Test if the matroid has a `U_{2, k}`-minor. The matroid `U_{2, k}` is a matroid on `k` elements in which every diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 8263860cbec..6c43b0d896d 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -3150,7 +3150,7 @@ cdef class Matroid(SageObject): return Polyhedron(vertices) def independence_matroid_polytope(self): - """ + r""" Return the independence matroid polytope of ``self``. This is defined as the convex hull of the vertices @@ -3430,7 +3430,7 @@ cdef class Matroid(SageObject): return self._is_isomorphism(other, morphism) cpdef is_isomorphism(self, other, morphism): - """ + r""" Test if a provided morphism induces a matroid isomorphism. A *morphism* is a map from the groundset of ``self`` to the groundset @@ -3553,7 +3553,7 @@ cdef class Matroid(SageObject): return self._is_isomorphism(other, mf) cpdef _is_isomorphism(self, other, morphism): - """ + r""" Version of is_isomorphism() that does no type checking. INPUT: @@ -3942,7 +3942,7 @@ cdef class Matroid(SageObject): return self.delete(X) cpdef dual(self): - """ + r""" Return the dual of the matroid. Let `M` be a matroid with ground set `E`. If `B` is the set of bases @@ -4054,7 +4054,7 @@ cdef class Matroid(SageObject): return self._has_minor(N, certificate) cpdef has_line_minor(self, k, hyperlines=None, certificate=False): - """ + r""" Test if the matroid has a `U_{2, k}`-minor. The matroid `U_{2, k}` is a matroid on `k` elements in which every @@ -4125,7 +4125,7 @@ cdef class Matroid(SageObject): return self._has_line_minor(k, hyperlines, certificate) cpdef _has_line_minor(self, k, hyperlines, certificate=False): - """ + r""" Test if the matroid has a `U_{2, k}`-minor. Internal version that does no input checking. @@ -4313,7 +4313,7 @@ cdef class Matroid(SageObject): return self.dual().extension(element, subsets).dual() cpdef modular_cut(self, subsets): - """ + r""" Compute the modular cut generated by ``subsets``. A *modular cut* is a collection `C` of flats such that @@ -4403,7 +4403,7 @@ cdef class Matroid(SageObject): return final_list cpdef linear_subclasses(self, line_length=None, subsets=None): - """ + r""" Return an iterable set of linear subclasses of the matroid. A *linear subclass* is a set of hyperplanes (i.e. closed sets of rank @@ -4714,7 +4714,7 @@ cdef class Matroid(SageObject): return True cpdef is_cosimple(self): - """ + r""" Test if the matroid is cosimple. A matroid is *cosimple* if it contains no cocircuits of length 1 or 2. @@ -4791,7 +4791,7 @@ cdef class Matroid(SageObject): return components cpdef is_connected(self, certificate=False): - """ + r""" Test if the matroid is connected. A *separation* in a matroid is a partition `(X, Y)` of the @@ -7480,7 +7480,7 @@ cdef class Matroid(SageObject): return A cpdef tutte_polynomial(self, x=None, y=None): - """ + r""" Return the Tutte polynomial of the matroid. The *Tutte polynomial* of a matroid is the polynomial @@ -7985,8 +7985,8 @@ cdef class Matroid(SageObject): for c in LM.chains(exclude=LM.maximal_elements()): if c: # the facets of IM are already present # get the cardinality of intersection of facet with IM - r = self.rank() - len(c) - + r = self.rank() - len(c) + # get candidate independent_sets for I in self.independent_r_sets(r): if I.issubset(c[0]): @@ -8012,7 +8012,7 @@ cdef class Matroid(SageObject): OUTPUT: - An instance of + An instance of :class:`MatroidUnion <sage.matroids.union_matroid.MatroidUnion>`. EXAMPLES:: diff --git a/src/sage/matroids/matroids_catalog.py b/src/sage/matroids/matroids_catalog.py index 2c9e607cbbe..1fc55a58cf2 100644 --- a/src/sage/matroids/matroids_catalog.py +++ b/src/sage/matroids/matroids_catalog.py @@ -3,8 +3,8 @@ A module containing constructors for several common matroids. -A list of all matroids in this module is available via tab -completion. Let ``<tab>`` indicate pressing the tab key. So begin by typing +A list of all matroids in this module is available via tab completion. +Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing ``matroids.<tab>`` to see the various constructions available. Many special matroids can be accessed from the submenu ``matroids.named_matroids.<tab>``. diff --git a/src/sage/media/channels.pyx b/src/sage/media/channels.pyx index 5caddeb2585..d2832abf8ef 100644 --- a/src/sage/media/channels.pyx +++ b/src/sage/media/channels.pyx @@ -17,12 +17,12 @@ def _separate_channels(_data, _width, _nchannels): if width == 1: # handle the one byte case - for n from 0 <= n < l: + for n in range(l): channel_data[n % nchannels].append(ord(data[n])-127) elif width == 2: a = 32768 - for n from 0 <= n < l: + for n in range(l): # compute the value as an integer x = <int> (data[2*n]) + 256 * <int>(data[2*n + 1]) #x -= 65536*(x > a) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 9fa967ce737..78c70a2fb69 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -69,7 +69,7 @@ approach is still needed for cpdef methods:: sage: cython_code = ['cpdef test_meth(self,x):', ....: ' "some doc for a wrapped cython method"', ....: ' return -x', - ....: 'from sage.all import cached_method', + ....: 'from sage.misc.cachefunc import cached_method', ....: 'from sage.structure.parent cimport Parent', ....: 'cdef class MyClass(Parent):', ....: ' @cached_method', @@ -107,7 +107,10 @@ By :trac:`11115`, even if a parent does not allow attribute assignment, it can inherit a cached method from the parent class of a category (previously, the cache would have been broken):: - sage: cython_code = ["from sage.all import cached_method, cached_in_parent_method, Category, Objects", + sage: cython_code = ["from sage.misc.cachefunc import cached_method", + ....: "from sage.misc.cachefunc import cached_in_parent_method", + ....: "from sage.categories.category import Category", + ....: "from sage.categories.objects import Objects", ....: "class MyCategory(Category):", ....: " @cached_method", ....: " def super_categories(self):", @@ -931,9 +934,9 @@ cdef class CachedFunction(): sage: I = P*[x,y] sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(I.groebner_basis) # indirect doctest - ArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'], - varargs='args', keywords='kwds', defaults=('', None, None, - False)) + FullArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'], + varargs='args', varkw='kwds', defaults=('', None, None, False), + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ return sage_getargspec(self.f) @@ -2818,7 +2821,7 @@ cdef class CachedMethod(): except Exception: pass if self.nargs == 0: - args, varargs, keywords, defaults = sage_getargspec(f) + args, varargs, keywords, defaults, kwonlyargs, kwonlydefaults, annotations = sage_getargspec(f) if varargs is None and keywords is None and len(args)<=1: self.nargs = 1 else: @@ -2954,7 +2957,7 @@ cdef class CachedSpecialMethod(CachedMethod): # we need to analyse the argspec f = self._cachedfunc.f if self.nargs == 0: - args, varargs, keywords, defaults = sage_getargspec(f) + args, varargs, keywords, defaults, kwonlyargs, kwonlydefaults, annotations = sage_getargspec(f) if varargs is None and keywords is None and len(args)<=1: self.nargs = 1 Caller = CachedMethodCallerNoArgs(inst, f, name=name, do_pickle=self._cachedfunc.do_pickle) diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 28c52448813..dd9123f5004 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -32,7 +32,8 @@ from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines, sage_getargspec) -from inspect import ArgSpec + +from inspect import FullArgSpec def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): @@ -92,7 +93,8 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): 5 sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(g) - ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) Demonstrate that it correctly gets the source lines and the source file, which is essential for interactive code edition; note that we @@ -391,7 +393,8 @@ def __call__(self, func): sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(f) - ArgSpec(args=['arrow_size'], varargs='args', keywords='kwds', defaults=(2,)) + FullArgSpec(args=['arrow_size'], varargs='args', varkw='kwds', defaults=(2,), + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ @sage_wraps(func) def wrapper(*args, **kwds): @@ -422,7 +425,8 @@ def listForNone(l): defaults = (argspec.defaults if argspec.defaults is not None else ()) \ + tuple(self.options.values()) # Note: argspec.defaults is not always a tuple for some reason - return ArgSpec(args, argspec.varargs, argspec.keywords, defaults) + return FullArgSpec(args, argspec.varargs, argspec.varkw, defaults, + kwonlyargs=[], kwonlydefaults=None, annotations={}) wrapper._sage_argspec_ = argspec return wrapper @@ -458,7 +462,8 @@ def __init__(self, **options): sage: f1 = o(f) sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(f1) - ArgSpec(args=['rgbcolor'], varargs='args', keywords='kwds', defaults=((0, 0, 1),)) + FullArgSpec(args=['rgbcolor'], varargs='args', varkw='kwds', defaults=((0, 0, 1),), + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ self.options = options self.original_opts = options.pop('__original_opts', False) @@ -499,7 +504,8 @@ def argspec(): list(self.options)) defaults = (argspec.defaults or ()) + tuple(self.options.values()) # Note: argspec.defaults is not always a tuple for some reason - return ArgSpec(args, argspec.varargs, argspec.keywords, defaults) + return FullArgSpec(args, argspec.varargs, argspec.varkw, defaults, + kwonlyargs=[], kwonlydefaults=None, annotations={}) wrapper._sage_argspec_ = argspec diff --git a/src/sage/misc/derivative.pyx b/src/sage/misc/derivative.pyx index 60bb7c1e09b..45ee7a918c5 100644 --- a/src/sage/misc/derivative.pyx +++ b/src/sage/misc/derivative.pyx @@ -221,4 +221,3 @@ def multi_derivative(F, args): for arg in derivative_parse(args): F = F._derivative(arg) return F - diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 5eed16a8565..8d2c2f080d5 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -525,12 +525,12 @@ def import_statements(*objects, **kwds): if kwds: raise TypeError("Unexpected '{}' argument".format(next(iter(kwds)))) - def expand_comma_separated_names(object): - if isinstance(object, str): - for w in object.strip('()').split(','): + def expand_comma_separated_names(obj): + if isinstance(obj, str): + for w in obj.strip('()').split(','): yield w.strip() else: - yield object + yield obj for obj in itertools.chain.from_iterable(expand_comma_separated_names(object) for object in objects): diff --git a/src/sage/misc/fast_methods.pyx b/src/sage/misc/fast_methods.pyx index eb73cb6bc3e..d3fd260f55f 100644 --- a/src/sage/misc/fast_methods.pyx +++ b/src/sage/misc/fast_methods.pyx @@ -311,7 +311,7 @@ class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): EXAMPLES:: sage: from sage.misc.fast_methods import Singleton - sage: class C(Singleton, Parent): + sage: class C(Singleton, Parent): ....: def __init__(self): ....: print("creating singleton") ....: Parent.__init__(self, base=ZZ, category=Rings()) @@ -321,7 +321,7 @@ class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): sage: __main__.C = C # ... in doctests sage: loads(dumps(c)) is copy(c) is C() # indirect doctest True - """ + """ return self def __reduce__(self): @@ -332,7 +332,7 @@ class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): EXAMPLES:: sage: from sage.misc.fast_methods import Singleton - sage: class C(Singleton, Parent): + sage: class C(Singleton, Parent): ....: def __init__(self): ....: print("creating singleton") ....: Parent.__init__(self, base=ZZ, category=Rings()) @@ -343,11 +343,11 @@ class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): sage: __main__.C = C # ... in doctests sage: loads(dumps(c)) is copy(c) is C() # indirect doctest True - + The pickle data mainly consist of the class of the unique instance, which may be a subclass of the original class used to create the instance.If the class is replaced by a sub-sub-class after creation of the instance, pickling fails. See the doctest in :class:`Singleton`. - """ + """ return self.__class__, () diff --git a/src/sage/misc/fpickle.pyx b/src/sage/misc/fpickle.pyx index 502080e2c10..793041d1422 100644 --- a/src/sage/misc/fpickle.pyx +++ b/src/sage/misc/fpickle.pyx @@ -34,6 +34,12 @@ def reduce_code(co): sage: def foo(N): return N+1 sage: sage.misc.fpickle.reduce_code(foo.__code__) (<cyfunction code_ctor at ...>, ...) + + Test that the constructed code matches the original code:: + + sage: ctor, args = sage.misc.fpickle.reduce_code(foo.__code__) + sage: ctor(*args) == foo.__code__ + True """ if co.co_freevars or co.co_cellvars: raise ValueError("Cannot pickle code objects from closures") @@ -44,7 +50,12 @@ def reduce_code(co): co_args += (co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, - co.co_name, co.co_firstlineno, co.co_lnotab) + co.co_name) + if sys.version_info.minor >= 11: + co_args += (co.co_qualname, co.co_firstlineno, + co.co_linetable, co.co_exceptiontable) + else: + co_args += (co.co_firstlineno, co.co_lnotab) return (code_ctor, co_args) diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx index 0ac03cf0715..e1bb7978953 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -116,7 +116,7 @@ cdef class ArgumentFixer: """ def __init__(self, f, classmethod = False): try: - arg_names, varargs, varkw, defaults = sage_getargspec(f) + arg_names, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations = sage_getargspec(f) except AttributeError: # This error occurs if f is defined in a Cython file and the # source file has gone. diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 7d2ee692e13..40683070269 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1822,7 +1822,7 @@ def squarefree_part(x): return x.squarefree_part() except AttributeError: pass - from sage.arith.all import factor + from sage.arith.misc import factor from sage.structure.all import parent F = factor(x) n = parent(x)(1) diff --git a/src/sage/misc/instancedoc.pyx b/src/sage/misc/instancedoc.pyx index 574287fefe9..360d3f768b3 100644 --- a/src/sage/misc/instancedoc.pyx +++ b/src/sage/misc/instancedoc.pyx @@ -327,4 +327,3 @@ def instancedoc(cls): tp.tp_doc = NULL PyType_Modified(tp) return cls - diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 3ec3a3e9edf..5dc3fbe5ecd 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -409,7 +409,7 @@ def float_function(x): sage: latex(float(2e-13)) 2 \times 10^{-13} """ - from sage.all import RDF + from sage.rings.real_double import RDF return latex(RDF(x)) @@ -1859,7 +1859,7 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, sage: with NamedTemporaryFile(mode="w+t", suffix=".tex") as f: # optional - latex latex_package_tkz_graph ....: _ = f.write(_latex_file_(g)) ....: f.flush() - ....: _run_latex_(file, engine="pdflatex") + ....: _run_latex_(f.name, engine="pdflatex") 'pdf' sage: view(4, margin=5, debug=True) # not tested diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index cdfdd6b6cbb..2e6874dde77 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -346,7 +346,7 @@ def _repr_(self): It contains the first few and last few lines of the content. - NOTE:: + .. NOTE:: Use ``print(t)`` or ``str(t)`` to show or get the full content. diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index d5610294a68..23a7098564b 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -506,6 +506,7 @@ class lazy_attribute(_lazy_attribute): if hasattr(f, "__module__"): self.__module__ = f.__module__ + class lazy_class_attribute(lazy_attribute): """ A lazy class attribute for an class is like a usual class attribute, @@ -601,7 +602,6 @@ class lazy_class_attribute(lazy_attribute): """ result = self.f(cls) if result is NotImplemented: - return getattr(super(cls, cls),self.__name__) + return getattr(super(cls, cls), self.__name__) setattr(cls, self.__name__, result) return result - diff --git a/src/sage/misc/lazy_format.py b/src/sage/misc/lazy_format.py index e3050695b25..b58ea155862 100644 --- a/src/sage/misc/lazy_format.py +++ b/src/sage/misc/lazy_format.py @@ -78,7 +78,7 @@ class LazyFormat(str): ....: LazyFormat("%s is wrong")%IDontLikeBeingPrinted()) Traceback (most recent call last): ... - AssertionError: <unprintable AssertionError object> + AssertionError: ... """ def __mod__(self, args): diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index 35f09ba3063..a60c26e1b90 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -9,7 +9,7 @@ use. EXAMPLES:: - sage: lazy_import('sage.rings.all', 'ZZ') + sage: lazy_import('sage.rings.integer_ring', 'ZZ') sage: type(ZZ) <class 'sage.misc.lazy_import.LazyImport'> sage: ZZ(4.0) @@ -20,7 +20,7 @@ during Sage's startup. In case a lazy import's sole purpose is to break a circular reference and it is known to be resolved at startup time, one can use the ``at_startup`` option:: - sage: lazy_import('sage.rings.all', 'ZZ', at_startup=True) + sage: lazy_import('sage.rings.integer_ring', 'ZZ', at_startup=True) This option can also be used as an intermediate step toward not importing by default a module that is used in several places, some of @@ -68,7 +68,16 @@ from warnings import warn import inspect from . import sageinspect -from sage.features import FeatureNotPresentError + +# LazyImport.__repr__ uses try... except FeatureNotPresentError. +# This is defined in sage.features, provided by the distribution sagemath-environment. +try: + from sage.features import FeatureNotPresentError +except ImportError: + # If sage.features cannot be imported, then FeatureNotPresentError cannot + # be raised. In this case, use the empty tuple as the exception specification. + FeatureNotPresentError = () + cdef inline obj(x): if type(x) is LazyImport: @@ -147,7 +156,7 @@ cpdef test_fake_startup(): EXAMPLES:: sage: sage.misc.lazy_import.test_fake_startup() - sage: lazy_import('sage.rings.all', 'ZZ', 'my_ZZ') + sage: lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') sage: my_ZZ(123) doctest:warning... UserWarning: Resolving lazy import ZZ during startup @@ -165,7 +174,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_integer = LazyImport('sage.rings.all', 'Integer') + sage: my_integer = LazyImport('sage.rings.integer', 'Integer') sage: my_integer(4) 4 sage: my_integer('101', base=2) @@ -190,7 +199,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: type(lazy_ZZ) <class 'sage.misc.lazy_import.LazyImport'> sage: lazy_ZZ._get_object() is ZZ @@ -226,7 +235,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_integer_ring = LazyImport('sage.rings.all', 'ZZ') + sage: my_integer_ring = LazyImport('sage.rings.integer_ring', 'ZZ') sage: my_integer_ring._object is None True sage: my_integer_ring._get_object() @@ -252,6 +261,8 @@ cdef class LazyImport(): self._object = getattr(__import__(self._module, {}, {}, [self._name]), self._name) except ImportError as e: if self._feature: + # Avoid warnings from static type checkers by explicitly importing FeatureNotPresentError. + from sage.features import FeatureNotPresentError raise FeatureNotPresentError(self._feature, reason=f'Importing {self._name} failed: {e}') raise @@ -305,7 +316,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_isprime = LazyImport('sage.all', 'is_prime') + sage: my_isprime = LazyImport('sage.arith.misc', 'is_prime') sage: my_isprime.__doc__ is is_prime.__doc__ True @@ -325,7 +336,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_isprime = LazyImport('sage.all', 'is_prime') + sage: my_isprime = LazyImport('sage.arith.misc', 'is_prime') sage: 'def is_prime(' in my_isprime._sage_src_() True """ @@ -338,9 +349,11 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: rm = LazyImport('sage.all', 'random_matrix') + sage: rm = LazyImport('sage.matrix.special', 'random_matrix') sage: rm._sage_argspec_() - ArgSpec(args=['ring', 'nrows', 'ncols', 'algorithm', 'implementation'], varargs='args', keywords='kwds', defaults=(None, 'randomize', None)) + FullArgSpec(args=['ring', 'nrows', 'ncols', 'algorithm', 'implementation'], + varargs='args', varkw='kwds', defaults=(None, 'randomize', None), + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ return sageinspect.sage_getargspec(self.get_object()) @@ -352,7 +365,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_integer = LazyImport('sage.rings.all', 'Integer') + sage: my_integer = LazyImport('sage.rings.integer', 'Integer') sage: my_integer.sqrt is Integer.sqrt True """ @@ -369,7 +382,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: dir(lazy_ZZ) == dir(ZZ) True """ @@ -382,7 +395,7 @@ cdef class LazyImport(): EXAMPLES:: sage: from sage.misc.lazy_import import LazyImport - sage: my_isprime = LazyImport('sage.all', 'is_prime') + sage: my_isprime = LazyImport('sage.arith.misc', 'is_prime') sage: is_prime(12) == my_isprime(12) True sage: is_prime(13) == my_isprime(13) @@ -395,7 +408,7 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: repr(lazy_ZZ) == repr(ZZ) True """ @@ -410,7 +423,7 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: str(lazy_ZZ) == str(ZZ) True """ @@ -421,7 +434,7 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: bool(lazy_ZZ) == bool(ZZ) True """ @@ -432,7 +445,7 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: hash(lazy_ZZ) == hash(ZZ) True """ @@ -443,7 +456,7 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: lazy_ZZ = LazyImport('sage.rings.all', 'ZZ') + sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ') sage: lazy_ZZ == ZZ True sage: lazy_ZZ == RR @@ -469,16 +482,17 @@ cdef class LazyImport(): Here we show how to take a function in a module, and lazy import it as a method of a class. For the sake of this - example, we add manually a function in sage.all:: + example, we add manually a function in :mod:`sage.all__sagemath_objects`:: sage: def my_method(self): return self - sage: sage.all.my_method = my_method + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.my_method = my_method Now we lazy import it as a method of a new class ``Foo``:: sage: from sage.misc.lazy_import import LazyImport sage: class Foo(): - ....: my_method = LazyImport('sage.all', 'my_method') + ....: my_method = LazyImport('sage.all__sagemath_objects', 'my_method') Now we can use it as a usual method:: @@ -504,7 +518,7 @@ cdef class LazyImport(): ``__get__`` needs to manually modify the class dict:: sage: class Foo(): - ....: lazy_import('sage.all', 'plot') + ....: lazy_import('sage.plot.plot', 'plot') sage: class Bar(Foo): ....: pass sage: type(Foo.__dict__['plot']) @@ -555,12 +569,13 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: sage.all.foo = list(range(10)) - sage: lazy_foo = LazyImport('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = list(range(10)) + sage: lazy_foo = LazyImport('sage.all__sagemath_objects', 'foo') sage: lazy_foo[1] = 100 sage: print(lazy_foo) [0, 100, 2, 3, 4, 5, 6, 7, 8, 9] - sage: sage.all.foo + sage: sage.all__sagemath_objects.foo [0, 100, 2, 3, 4, 5, 6, 7, 8, 9] """ self.get_object()[key] = value @@ -570,12 +585,13 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: sage.all.foo = list(range(10)) - sage: lazy_foo = LazyImport('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = list(range(10)) + sage: lazy_foo = LazyImport('sage.all__sagemath_objects', 'foo') sage: del lazy_foo[1] sage: print(lazy_foo) [0, 2, 3, 4, 5, 6, 7, 8, 9] - sage: print(sage.all.foo) + sage: print(sage.all__sagemath_objects.foo) [0, 2, 3, 4, 5, 6, 7, 8, 9] """ del self.get_object()[key] @@ -610,8 +626,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo + 1 11 """ @@ -621,8 +638,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo - 1 9 """ @@ -632,8 +650,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo * 2 20 """ @@ -644,8 +663,9 @@ cdef class LazyImport(): TESTS:: sage: from sympy import Matrix - sage: sage.all.foo = Matrix([[1,1],[0,1]]) - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = Matrix([[1,1],[0,1]]) + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo.__matmul__(foo) Matrix([ [1, 2], @@ -657,8 +677,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo // 3 3 """ @@ -668,8 +689,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: operator.truediv(foo, 3) 10/3 """ @@ -679,8 +701,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo ** 2 100 """ @@ -690,8 +713,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo % 7 3 """ @@ -701,8 +725,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo << 3 80 """ @@ -712,8 +737,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo >> 2 2 """ @@ -723,8 +749,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo & 7 2 """ @@ -734,8 +761,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo | 7 15 """ @@ -745,8 +773,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: foo ^^ 7 13 """ @@ -756,8 +785,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: -foo -10 """ @@ -767,8 +797,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: +foo 10 """ @@ -778,8 +809,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = -1000 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = -1000 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: abs(foo) 1000 """ @@ -789,8 +821,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: ~foo 1/10 """ @@ -800,8 +833,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: complex(foo) (10+0j) """ @@ -811,8 +845,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: int(foo) 10 """ @@ -822,8 +857,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: float(foo) 10.0 """ @@ -833,8 +869,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: oct(foo) '0o12' """ @@ -844,8 +881,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: hex(foo) '0xa' """ @@ -855,8 +893,9 @@ cdef class LazyImport(): """ TESTS:: - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = 10 + sage: lazy_import('sage.all__sagemath_objects', 'foo') sage: list(range(100))[foo] 10 """ @@ -868,14 +907,14 @@ cdef class LazyImport(): TESTS:: - sage: from sage.misc.lazy_import import LazyImport - sage: sage.all.foo = [[1,2], 3] - sage: lazy_foo = LazyImport('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = [[1,2], 3] + sage: lazy_foo = LazyImport('sage.all__sagemath_objects', 'foo') sage: a = copy(lazy_foo) - sage: a is sage.all.foo # copy + sage: a is sage.all__sagemath_objects.foo # copy False - sage: a[0] is sage.all.foo[0] # copy but not deep + sage: a[0] is sage.all__sagemath_objects.foo[0] # copy but not deep True sage: type(lazy_foo) is LazyImport True @@ -890,12 +929,13 @@ cdef class LazyImport(): TESTS:: sage: from sage.misc.lazy_import import LazyImport - sage: sage.all.foo = [[1,2], 3] - sage: lazy_foo = LazyImport('sage.all', 'foo') + sage: import sage.all__sagemath_objects + sage: sage.all__sagemath_objects.foo = [[1,2], 3] + sage: lazy_foo = LazyImport('sage.all__sagemath_objects', 'foo') sage: a = deepcopy(lazy_foo) - sage: a is sage.all.foo # copy + sage: a is sage.all__sagemath_objects.foo # copy False - sage: a[0] is sage.all.foo[0] # deep copy + sage: a[0] is sage.all__sagemath_objects.foo[0] # deep copy False sage: type(lazy_foo) is LazyImport True @@ -983,27 +1023,27 @@ def lazy_import(module, names, as_=None, *, EXAMPLES:: - sage: lazy_import('sage.rings.all', 'ZZ') + sage: lazy_import('sage.rings.integer_ring', 'ZZ') sage: type(ZZ) <class 'sage.misc.lazy_import.LazyImport'> sage: ZZ(4.0) 4 - sage: lazy_import('sage.rings.all', 'RDF', 'my_RDF') + sage: lazy_import('sage.rings.real_double', 'RDF', 'my_RDF') sage: my_RDF._get_object() is RDF True sage: my_RDF(1/2) 0.5 - sage: lazy_import('sage.all', ['QQ', 'RR'], ['my_QQ', 'my_RR']) + sage: lazy_import('sage.rings.rational_field', ['QQ', 'frac'], ['my_QQ', 'my_frac']) sage: my_QQ._get_object() is QQ True - sage: my_RR._get_object() is RR + sage: my_frac._get_object() is sage.rings.rational_field.frac True Upon the first use, the object is injected directly into the calling namespace:: - sage: lazy_import('sage.all', 'ZZ', 'my_ZZ') + sage: lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') sage: my_ZZ is ZZ False sage: my_ZZ(37) @@ -1014,7 +1054,7 @@ def lazy_import(module, names, as_=None, *, We check that :func:`lazy_import` also works for methods:: sage: class Foo(): - ....: lazy_import('sage.all', 'plot') + ....: lazy_import('sage.plot.plot', 'plot') sage: class Bar(Foo): ....: pass sage: type(Foo.__dict__['plot']) @@ -1026,16 +1066,16 @@ def lazy_import(module, names, as_=None, *, If deprecated then a deprecation warning is issued:: - sage: lazy_import('sage.all', 'Qp', 'my_Qp', deprecation=14275) + sage: lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp', deprecation=14275) sage: my_Qp(5) doctest:...: DeprecationWarning: - Importing my_Qp from here is deprecated; please use "from sage.all import Qp as my_Qp" instead. + Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead. See http://trac.sagemath.org/14275 for details. 5-adic Field with capped relative precision 20 An example of deprecation with a message:: - sage: lazy_import('sage.all', 'Qp', 'my_Qp_msg', deprecation=(14275, "This is an example.")) + sage: lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp_msg', deprecation=(14275, "This is an example.")) sage: my_Qp_msg(5) doctest:...: DeprecationWarning: This is an example. See http://trac.sagemath.org/14275 for details. @@ -1153,6 +1193,78 @@ def get_star_imports(module_name): return all +def attributes(a): + """ + Return the private attributes of a :class:`LazyImport` object in a dictionary. + + This is for debugging and doctesting purposes only. + + EXAMPLES:: + + sage: from sage.misc.lazy_import import attributes + sage: lazy_import("sage.structure.unique_representation", "foo") + sage: attributes(foo)['_namespace'] is globals() + True + sage: D = attributes(foo) + sage: del D['_namespace'] + sage: D + {'_as_name': 'foo', + '_at_startup': False, + '_deprecation': None, + '_module': 'sage.structure.unique_representation', + '_name': 'foo', + '_object': None} + """ + cdef LazyImport b + b = a + return {"_object": b._object, + "_module": b._module, + "_name": b._name, + "_as_name": b._as_name, + "_namespace": b._namespace, + "_at_startup": b._at_startup, + "_deprecation": b._deprecation} + + +def clean_namespace(namespace=None): + """ + Adjust :class:`LazyImport` bindings in given namespace to refer to this actual namespace. + + When :class:`LazyImport` objects are imported into other namespaces via normal ``import`` + instructions, the data stored on a :class:`LazyImport` object that helps it to adjust the + binding in the namespace to the actual imported object upon access is not adjusted. + This routine fixes that. + + INPUT: + + - ``namespace`` -- the namespace where importing the names; by default, + import the names to current namespace + + EXAMPLES:: + + sage: from sage.misc.lazy_import import attributes, clean_namespace + sage: from sage.calculus.calculus import maxima as C + sage: attributes(C)['_as_name'] + 'maxima' + sage: attributes(C)['_namespace'] is sage.calculus.calculus.__dict__ + True + sage: clean_namespace(globals()) + sage: attributes(C)['_as_name'] + 'C' + sage: attributes(C)['_namespace'] is globals() + True + """ + cdef LazyImport w + if namespace is None: + namespace = inspect.currentframe().f_locals + for k, v in namespace.items(): + if type(v) is LazyImport: + w = v + if w._namespace is not None and (w._namespace is not namespace or w._as_name != k): + namespace[k] = LazyImport(w._module, w._name, as_name=k, at_startup=w._at_startup, + namespace=namespace, deprecation=w._deprecation) + + # Add support for _instancedoc_ from sage.misc.instancedoc import instancedoc instancedoc(LazyImport) diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index c2c440daffb..bae968c42f3 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -548,7 +548,7 @@ def union(x, y=None): True """ from sage.misc.superseded import deprecation - deprecation(32096, "sage.misc.misc.union is deprecated, use 'list(set(x).union(y)' or a more suitable replacement") + deprecation(32096, "sage.misc.misc.union is deprecated, use 'list(set(x).union(y))' or a more suitable replacement") if y is None: return list(set(x)) return list(set(x).union(y)) diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index 427dd8374e2..b601d6f7276 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -386,4 +386,3 @@ class A1: class A2: class A3: pass - diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 849ee71f3fb..975ddc50766 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -26,8 +26,7 @@ 'alabaster', 'arb', ... - 'zlib', - 'zn_poly'] + 'zlib'] Functions --------- @@ -265,7 +264,7 @@ def list_packages(*pkg_types: str, pkg_sources: List[str] = ['normal', 'pip', 's 'arb', 'babel', ... - 'zn_poly'] + 'zlib'] sage: sage_conf_info = L['sage_conf'] # optional - sage_spkg sage: sage_conf_info.type # optional - sage_spkg 'standard' @@ -498,8 +497,8 @@ def package_versions(package_type, local=False): sage: std = package_versions('standard', local=True) # optional - sage_spkg sage: 'gap' in std # optional - sage_spkg True - sage: std['zn_poly'] # optional - sage_spkg, random - ('0.9.p12', '0.9.p12') + sage: std['zlib'] # optional - sage_spkg, random + ('1.2.11.p0', '1.2.11.p0') """ return {pkg.name: (pkg.installed_version, pkg.remote_version) for pkg in list_packages(package_type, local=local).values()} diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 8b5474972f2..17e0ce7b72c 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -1,22 +1,154 @@ +# sage_setup: distribution = sagemath-environment """ Recognizing package directories """ +# **************************************************************************** +# Copyright (C) 2020-2022 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + import os import glob from contextlib import contextmanager -def is_package_or_sage_namespace_package_dir(path): +class SourceDistributionFilter: + r""" + A :class:`collections.abc.Container` for source files in distributions. + + INPUT: + + - ``include_distributions`` -- (default: ``None``) if not ``None``, + should be a sequence or set of strings: include files whose + ``distribution`` (from a ``# sage_setup: distribution = PACKAGE`` + directive in the source file) is an element of ``distributions``. + + - ``exclude_distributions`` -- (default: ``None``) if not ``None``, + should be a sequence or set of strings: exclude files whose + ``distribution`` (from a ``# sage_setup: distribution = PACKAGE`` + directive in the module source file) is in ``exclude_distributions``. + + EXAMPLES:: + + sage: from sage.misc.package_dir import SourceDistributionFilter + sage: F = SourceDistributionFilter() + sage: sage.misc.package_dir.__file__ in F + True + sage: F = SourceDistributionFilter(include_distributions=['sagemath-environment']) + sage: sage.misc.package_dir.__file__ in F + True + sage: F = SourceDistributionFilter(exclude_distributions=['sagemath-environment']) + sage: sage.misc.package_dir.__file__ in F + False + """ + def __init__(self, include_distributions=None, exclude_distributions=None): + r""" + TESTS: + + ``exclude_distributions=None`` is normalized to the empty tuple:: + + sage: from sage.misc.package_dir import SourceDistributionFilter + sage: F = SourceDistributionFilter() + sage: F._exclude_distributions + () + """ + self._include_distributions = include_distributions + if exclude_distributions is None: + exclude_distributions = () + self._exclude_distributions = exclude_distributions + + def __contains__(self, filename): + r""" + TESTS: + + No file access is used when neither ``include_distributions`` nor + ``exclude_distributions`` is given:: + + sage: from sage.misc.package_dir import SourceDistributionFilter + sage: F = SourceDistributionFilter() + sage: '/doesnotexist' in F + True + + ``exclude_distributions`` can also be an empty container:: + + sage: F = SourceDistributionFilter(exclude_distributions=()) + sage: '/doesnotexist' in F + True + """ + if self._include_distributions is None and not self._exclude_distributions: + return True + distribution = read_distribution(filename) + if self._include_distributions is not None: + if distribution not in self._include_distributions: + return False + return distribution not in self._exclude_distributions + + +def read_distribution(src_file): + """ + Parse ``src_file`` for a ``# sage_setup: distribution = PKG`` directive. + + INPUT: + + - ``src_file`` -- file name of a Python or Cython source file + + OUTPUT: + + - a string, the name of the distribution package (``PKG``); or the empty + string if no directive was found. + + EXAMPLES:: + + sage: from sage.env import SAGE_SRC + sage: from sage.misc.package_dir import read_distribution + sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'tdlib.pyx')) + 'sagemath-tdlib' + sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py')) + '' + """ + from Cython.Utils import open_source_file + with open_source_file(src_file, error_handling='ignore') as fh: + for line in fh: + # Adapted from Cython's Build/Dependencies.py + line = line.lstrip() + if not line: + continue + if line[0] != '#': + break + line = line[1:].lstrip() + kind = "sage_setup:" + if line.startswith(kind): + key, _, value = [s.strip() for s in line[len(kind):].partition('=')] + if key == "distribution": + return value + return '' + + +def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): r""" Return whether ``path`` is a directory that contains a Python package. - Ordinary Python packages are recognized by the presence of `__init__.py`. + Ordinary Python packages are recognized by the presence of ``__init__.py``. Implicit namespace packages (PEP 420) are only recognized if they follow the conventions of the Sage library, i.e., the directory contains a file ``all.py`` or a file matching the pattern ``all__*.py`` such as ``all__sagemath_categories.py``. + INPUT: + + - ``path`` -- a directory name. + + - ``distribution_filter`` -- (optional, default: ``None``) + only consider ``all*.py`` files whose distribution (from a + ``# sage_setup: distribution = PACKAGE`` directive in the source file) + is an element of ``distribution_filter``. + EXAMPLES: :mod:`sage.cpython` is an ordinary package:: @@ -49,14 +181,17 @@ def is_package_or_sage_namespace_package_dir(path): sage: is_package_or_sage_namespace_package_dir(directory) False """ - if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package - return True - if os.path.exists(os.path.join(path, '__init__.pxd')): # for consistency with Cython + if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package return True - if os.path.exists(os.path.join(path, 'all.py')): # complete namespace package + if os.path.exists(os.path.join(path, '__init__.pxd')): # for consistency with Cython return True - for _ in glob.iglob(os.path.join(path, 'all__*.py')): - return True # partial namespace package + fname = os.path.join(path, 'all.py') + if os.path.exists(fname): + if distribution_filter is None or fname in distribution_filter: # complete namespace package + return True + for fname in glob.iglob(os.path.join(path, 'all__*.py')): + if distribution_filter is None or fname in distribution_filter: # partial namespace package + return True return False @@ -71,7 +206,7 @@ def cython_namespace_package_support(): import Cython.Build.Cythonize import Cython.Utils orig_is_package_dir = Cython.Utils.is_package_dir - Cython.Utils.is_package_dir = Cython.Build.Cythonize.is_package_dir = Cython.Build.Dependencies.is_package_dir = is_package_or_sage_namespace_package_dir + Cython.Utils.is_package_dir = Cython.Build.Cythonize.is_package_dir = Cython.Build.Dependencies.is_package_dir = Cython.Utils.cached_function(is_package_or_sage_namespace_package_dir) try: yield finally: diff --git a/src/sage/misc/parser.pyx b/src/sage/misc/parser.pyx index a365c3c79ea..29d7a9cd7a0 100644 --- a/src/sage/misc/parser.pyx +++ b/src/sage/misc/parser.pyx @@ -10,16 +10,15 @@ AUTHOR: - Robert Bradshaw 2008-04 (initial version) """ - -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu> # # 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 libc.string cimport strchr from cpython.bytes cimport PyBytes_FromStringAndSize @@ -27,7 +26,6 @@ from cpython.list cimport PyList_Append import math -from sage.cpython.string cimport str_to_bytes, bytes_to_str def foo(*args, **kwds): """ @@ -1098,4 +1096,3 @@ cdef class LookupNameMaker: if self.fallback is not None: return self.fallback(name) raise NameError("Unknown variable: '{}'".format(name)) - diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index 3ac5f1cc2b0..cbdbeadeb80 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -298,7 +298,7 @@ def _base_dumps(obj, compress=True): global already_pickled gherkin = SagePickler.dumps(obj) - already_pickled = { } + already_pickled = { } if compress: return comp.compress(gherkin) diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py new file mode 100644 index 00000000000..320a5a15c06 --- /dev/null +++ b/src/sage/misc/replace_dot_all.py @@ -0,0 +1,464 @@ +r""" +Implementation of the command ``sage --fiximports``. + +This file provides a tool to fix the modularization antipattern ``namespace_pkg_all_import`` +reported by ``tox -e relint``. Sage library code should not import from ``sage.PAC.KAGE.all`` +when ``sage.PAC.KAGE`` is an implicit namespace package. + +AUTHORS: + +- Alex Chandler +- Matthias Köppe + +INSTRUCTIONS: + +To fix the above issue for all Python and Cython source files (``.py``, ``.pyx``, ``.pxi``) files in the ``src/sage`` directory, +run the following from a terminal in ``SAGE_ROOT`` :: + + ./sage -python src/sage/misc/replace_dot_all.py + +or :: + + ./sage --fiximports + +Running replace_dot_all.py will call the function :func:`walkdir_replace_dot_all` which walks through all Python and Cython source files +and replaces certain ``from sage.PAC.KAGE.all import something`` (those matching the pattern above) +with the correct ``import`` statement by applying the function :func:`~sage.misc.dev_tools.import_statements`. + +The user can also pass subdirectories of ``src/sage`` or specific files to fix. For example :: + + ./sage -python src/sage/misc/replace_dot_all.py src/sage/arith + +will fix all files in ``src/sage/arith`` and :: + + ./sage -python src/sage/misc/replace_dot_all.py src/sage/arith/functions.pyx + +will fix the file ``src/sage/arith/functions.py``. The file extension is necessary in the case of a specific file. The user can also +pass the verbose flag ``-v`` to print out the files being fixed. For example :: + + ./sage -python src/sage/misc/replace_dot_all.py -v src/sage/arith + +will fix all files in ``src/sage/arith`` and print out the unusual examples of ``import`` statements it finds. + +In some rare cases, such as ``import`` statements appearing in doctests, the program will not be able to fix the ``import`` statement. The program will +print out the location of the file and the line number of the exceptional ``import`` statement. The user can then manually fix the ``import`` statement. +The program will also (usually) print out the suggested replacement for the ``import`` statement. The user can then copy and paste this replacement +into the file. In the cases a suggested replacement is not printed out, the user should use the function :func:`~sage.misc.dev_tools.import_statements` +to find the correct ``import`` statement. +""" + +# **************************************************************************** +# Copyright (C) 2022-2023 Alex Chandler +# 2023 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +# Importing packages + +from sage.misc.dev_tools import import_statements +import os +import re +import argparse + +# We import this using __import__ so that "tox -e relint" does not complain about this source file. +__import__("sage.all", globals(), locals(), ["*"]) + + +# Keep in sync with SAGE_ROOT/src/.relint.yml (namespace_pkg_all_import) + +default_package_regex = (r"sage(" + r"|[.](arith|categories|combinat|ext|graphs(|[.]decompositions)|interfaces|libs|matrix|misc|numerical(|[.]backends)|rings|sets)" + r")[.]all") + + +# Global variables + +examples = list('ABCDEFGHIJ') # controls how we print out interesting examples to the console +interesting_examples = dict(zip(examples, [0]*len(examples))) +log_messages = '' +number_examples_to_print = 100 # controls how many examples we print out to the console (100 effectively allows all unusual examples to be printed) +numberFiles, numberFilesMatchingRegex, numberFilesChanged, numberStatementsReplaced = 0, 0, 0, 0 # to print report on number of files changed + + +# Functions + +def find_replacements(location, package_regex=None, verbose=False): + r""" + Locate the lines in the file at ``location`` which contain an ``import`` statement. + + INPUT: + + - ``location`` -- a file path + - ``package_regex`` -- (default: :obj:`default_package_regex`) a regular expression matching + the ``sage.PAC.KAGE.all`` package names from which we do not want to import. + - ``verbose`` -- a parameter which if used will issue print statements when interesting examples are found + + OUTPUT: + + an array [row_index,import_index,replaced_commands,lines_spanned (optional)] with entries + + - ``row_index`` -- the row index (zero indexed) in the file which needs replacing + - ``import_index`` -- the index of row row_index which marks the beginning of the word ``import`` in the line + - ``replaced_commands`` -- the string which will replace the current line + - ``lines_spanned`` -- the number of lines the original statement spans + + This output can be processed by the function :func:`process_line`. + + EXAMPLES:: + + sage: from sage.misc.replace_dot_all import * + sage: location = os.path.join(sage.env.SAGE_SRC, 'sage/plot/arc.py') + sage: find_replacements(location, package_regex='sage[.]plot[.]all', verbose=True) + [[..., ..., 'from sage.plot.graphics import Graphics']] + """ + if package_regex is None: + package_regex = default_package_regex + regex = r"from\s+" + package_regex + r"\s+import" + pattern = re.compile(regex) + replacements = [] + global log_messages, interesting_examples + with open(location, "r") as fp: + skip_line = False + lines = fp.readlines() # read all lines using readline() + row_index = 0 + for row in lines: # iterate over all lines of python file + if pattern.search(row): # (match the regex also do not want to mess with documentation) + prefix = '' + if '*' in row or 'SAGE_ROOT' in row: + if verbose and interesting_examples['J'] < number_examples_to_print: + interesting_examples['J'] += 1 + log_messages += (f'J. Match but no changes made (import statement uses *) at {location}:{row_index + 1}. ' + f'Not applying any changes here.\n') + continue + elif not (row.lstrip()[0:4] == 'from'): + skip_line = True + if '"' not in row and "'" not in row: + print(f'\n' + f'NEED TO CHANGE MANUALLY \n' + f' Issue: line with import statement does not start with "from" \n' + f' Location: at {location} \n' + f' Line number: {row_index + 1}. \n' + f' Giving correct import statements:\n') + leading_space = 0 + while len(row) > 0 and row[leading_space] == ' ' and leading_space < len(row)-1: + leading_space += 1 + prefix_space = leading_space + while row[prefix_space:prefix_space+4] != 'from': + prefix_space += 1 + prefix = row[leading_space:prefix_space] + row = row[prefix_space:] + else: + print(f'\n' + f'NEED TO CHANGE MANUALLY \n' + f' Issue: import statement does not start with "from" and contains quotation marks \n' + f' Location: at {location}:{row_index + 1}. \n' + f' Not able to suggest correct import statements. User must use the function import_statements().') + continue + # find() method returns -1 if the value is not found, or if found it returns index of the first occurrence of the substring + import_index = row.find('import ') + modules = '' + to_exec = row.strip() # the import statement itself which we will clean and call to import modules e.g. "import (aa as a, bb, cc as c)" + to_eval = row[import_index + 7:-1].strip() # tuple of modules in import statement but e.g. "(aa as a, bb, cc as c)" is saved as "(a,bb,c)" + to_eval_raw = row[import_index + 7:-1].strip() # same as to_eval but we don't get rid of " as " parts e.g. "(aa as a, bb, cc as c)" + span = 0 # keeps track of how many lines the import statement spans + + if '(' in row: # for examples where we import a tuple of modules and the statement spans several lines + while ')' not in lines[row_index + span]: # finding the line which closes the import statement + span += 1 + to_exec += lines[row_index + span].strip() + to_eval += lines[row_index + span].strip() + to_eval_raw += lines[row_index + span].strip() + if span and verbose: # useful to see these multiline examples for debugging + if " as " in to_eval_raw and interesting_examples['D'] < number_examples_to_print: + log_messages += f'D. Interesting example (spans multiple lines and has " as ") at {location}:{row_index + 1}\n' + interesting_examples['D'] += 1 + elif interesting_examples['B'] < number_examples_to_print: + log_messages += f'B. Interesting example (spans multiple lines) at {location}:{row_index + 1}\n' + interesting_examples['B'] += 1 + + # if there is an "as" statement inside to_eval, we want to keep only the new name for the module e.g. "(aa as a, bb, cc as c)" becomes "(a,bb,c)" + while " as " in to_eval: + as_ind = to_eval.find(" as ") + j = as_ind - 1 + while to_eval[j] not in [',', ' '] and j >= 0: + j -= 1 + to_eval = to_eval[0:j+1] + to_eval[as_ind+4:] + + try: # trying to execute the import statement so we can eval the modules and feed them to the function import_statements + to_exec = to_exec.replace("'", '').replace('"', '') + if (to_exec[-1] == ','): + to_exec = to_exec[:-1] + exec(to_exec) + except ModuleNotFoundError as err: + print(f'ModuleNotFoundError: {err} found when trying to execute {to_exec}') + except ImportError as err: + print(f'ImportError: {err} found when trying to execute {to_exec}') + + try: # try to evaluate the list of module names to get a list of the modules themselves which we can call import_statements on + modules = eval(to_eval) + except NameError as err: + print(f'NameError: {err} found when trying to evaluate {to_eval} at {location}:{row_index + 1}') + except SyntaxError as err: + print(f'SyntaxError: {err} found when trying to evaluate {to_eval} at {location}:{row_index + 1}') + + # Need module to be a list of modules we are importing. If a single module was given, we make it a 1-element list. + if not isinstance(modules, tuple): + modules = [modules] + + to_eval_list = to_eval.replace('(', '').replace(')', '').split(',') # convert comma separated string to_eval to a list + to_eval_list_raw = to_eval_raw.replace('(', '').replace(')', '').split(',') # convert comma separated string to_eval_raw to a list + to_eval_list_index = 0 + + change_to = '' + # constructs the appropriate replacement for the import statement and stores it (along with location data) in list replacements + for mod in modules: + postfix = '' + as_index = -1 + # saves the callable name of module in variable postfix (e.g. for module "b" called by "bb as b" we set postfix = " as b") + if " as " in to_eval_list_raw[to_eval_list_index]: + if verbose and interesting_examples['C'] < number_examples_to_print: + log_messages += f'C. Interesting example (" as " in tuple import) at {location}:{row_index + 1}\n' + interesting_examples['C'] += 1 + as_index = to_eval_list_raw[to_eval_list_index].index(" as ") + postfix = to_eval_list_raw[to_eval_list_index][as_index:] + new_import_statement = import_statements(mod, answer_as_str=True, verbose=False) # import statement for the current mod in the list module + import_index = new_import_statement.find('import') + new_mod_as_string = new_import_statement[import_index + 7:].strip() # the name for the module given by the function import_statements + if as_index >= 0: + # the name for the module as originally called in the document (when there is an " as " statement) + original_mod_string = to_eval_list_raw[to_eval_list_index].strip()[:as_index] + else: + original_mod_string = to_eval_list[to_eval_list_index].strip() # the name for the module as originally called in the document + if original_mod_string != new_mod_as_string: # if the names differ, we use the original name as it was called in the document + if verbose and interesting_examples['A'] < number_examples_to_print: + log_messages += (f'A. Interesting example (module has multiple names) at {location}:{row_index + 1}. ' + f'Names: {original_mod_string}, {new_mod_as_string}. ' + f'Replacing new {new_mod_as_string} by original {original_mod_string}.\n') + interesting_examples['A'] += 1 + new_import_statement = new_import_statement.replace(' ' + new_mod_as_string, ' ' + new_mod_as_string + ' as ' + original_mod_string) + if " as " in postfix and interesting_examples['G'] < number_examples_to_print: + log_messages += (f'G. Interesting example (module has multiple names) at {location}:{row_index + 1}. ' + f'Names: {original_mod_string}, {new_mod_as_string}. ' + f'Replacing new {new_mod_as_string} by original {original_mod_string}.\n') + interesting_examples['G'] += 1 + if len(postfix.strip()) > 0: # if module was called with " as " statement, we put that back in by adding the string "postfix" + # if " as " in new_import_statement locate the index of " as ", remove the end after this, and add the postfix there + if " as " in new_import_statement: + new_import_statement = new_import_statement[:new_import_statement.index(" as ")] + ' ' + postfix.strip() + else: + new_import_statement += (' ' + postfix.strip()) + change_to += (prefix + new_import_statement + '\n') + to_eval_list_index += 1 + # [:-1] on change_to gets rid of the last '\n' we added which adds an unnecessary new line + replacement = [row_index, import_index, change_to[:-1]].copy() + if span: + # if original statement spanned multiple lines, we store that information to signal that we need to skip lines + # as we read the document in the function make_replacements_in_file + replacement.append(span) + if not skip_line: + replacements.append(replacement) + row_index += 1 + skip_line = False + # keeping track of the numbers of files changed and statements replaced + global numberStatementsReplaced, numberFilesChanged + numberStatementsReplaced += len(replacements) + if replacements: + numberFilesChanged += 1 + return replacements + + +def process_line(location, line, replacements, row_index, verbose=False): + r""" + Modify a single source code ``line`` according to the given ``replacements``. + + INPUTS: + + - ``location`` -- a file path; only used for logging + - ``line`` -- a source code line + - ``replacements`` -- the array output from :func:`find_replacements` + - ``row_index`` -- the line number where ``import`` appears + - ``verbose`` -- if True, issue print statements when interesting examples are found + + OUTPUT: + + an array ``[new_line, replacements]`` with entries + + - ``new_line`` -- the modified import statement (possibly now on several lines) + - ``replacements`` -- just returns the original replacements with its index 0 element removed if ``replacements`` is nonempty + + EXAMPLES: + + Replacing the first line which needs a replacement in the source file with filepath ``src/sage/plot/arc.py``:: + + sage: from sage.misc.replace_dot_all import * + sage: location = os.path.join(sage.env.SAGE_SRC, 'sage/plot/arc.py') + sage: replacements = find_replacements(location, package_regex='sage[.]plot[.]all', verbose=True); replacements + [[471, 24, 'from sage.plot.graphics import Graphics']] + sage: with open(location, "r") as file: + ....: lines = file.readlines() + sage: row_index, col_number, *_ = replacements[0] + sage: line = lines[row_index] + sage: print(line.rstrip()) + from sage.plot.all import Graphics + sage: new_line, replacements = process_line(location, line, replacements, row_index) + sage: print(new_line) + from sage.plot.graphics import Graphics + sage: replacements + [] + """ + line = line.rstrip() # stripping line break + new_line = '' + global log_messages, interesting_examples + if len(replacements) == 0: + return line, replacements + if row_index == replacements[0][0]: # if line marked as containing .all + replacement = replacements.pop(0) + leading_space = 0 + while line and line[leading_space] == ' ' and leading_space < len(line)-1: + leading_space += 1 + new_line = ' '*leading_space + replacement[2] # adds leading space to first line (which may or may not start with 'from') + new_line = new_line.replace('\n', '\n'+' '*leading_space) # adds correct amount of indentation to the replacement at each line + # new_line = replacement[2].replace('from ',' '*leading_space + 'from ') # adds correct amount of indentation to the replacement at each line + if verbose and leading_space > 0: + if len(replacement) == 4 and interesting_examples['F'] < number_examples_to_print: + log_messages += f'F. Interesting example (has leading space and multiline) at {location}:{replacement[0] + 1}\n' + interesting_examples['F'] += 1 + elif interesting_examples['E'] < number_examples_to_print: + log_messages += f'E. Interesting example (has leading space) at {location}:{replacement[0] + 1}\n' + interesting_examples['E'] += 1 + + else: # if line does not contain .all + new_line = line + return new_line, replacements + + +def make_replacements_in_file(location, package_regex=None, verbose=False, output=None): + r""" + Replace ``import`` statements in the file with filepath "location". + + INPUT: + + - ``location`` -- a file path + - ``package_regex`` -- (default: :obj:`default_package_regex`) a regular expression matching + the ``sage.PAC.KAGE.all`` package names from which we do not want to import. + - ``verbose`` -- if True, issue print statements when interesting examples are found + - ``output`` -- a file path; if ``None``, overwrite the file given by ``location`` + + EXAMPLES:: + + sage: from sage.misc.replace_dot_all import * + sage: import tempfile + sage: with tempfile.TemporaryDirectory() as d: + ....: location = os.path.join(d, "input.py") + ....: with open(location, "w") as input: + ....: _ = input.write("from sage.plot.all import point2d\n") + ....: _ = input.write("from sage.plot.line import line\n") + ....: make_replacements_in_file(location, 'sage[.]plot[.]all', True) + ....: with open(location, "r") as output: + ....: for line in output: + ....: print(line.strip()) + from sage.plot.point import point2d + from sage.plot.line import line + """ + replacements = find_replacements(location, package_regex, verbose) + with open(location, "r") as file: + lines = file.readlines() + replaced_content = "" + row_index = 0 # keeps track of the line number + while row_index < len(lines): # looping through the file + line = lines[row_index] + span = 0 # keeps track of number of lines import statement spans + if replacements and row_index == replacements[0][0] and len(replacements[0]) == 4: + span = replacements[0][3] # if import statement spans span lines + # returns the line if no replacements are needed and returns the processed line otherwise + new_line, replacements = process_line(location, line, replacements, row_index, verbose=verbose) + replaced_content += new_line + "\n" # concatenate the new string and add an end-line break + row_index += 1 + span + if output is None: + output = location + with open(output, "w") as write_file: # Open file in write mode + write_file.write(replaced_content) # overwriting the old file contents with the new/replaced content + + +def walkdir_replace_dot_all(dir, file_regex=r'.*[.](py|pyx|pxi)$', package_regex=None, verbose=False): + r""" + Replace ``import`` statements in the files in directory ``dir`` matching the regex pattern ``file_regex``. + + INPUTS: + + - ``dir`` -- a directory path + - ``file_regex`` -- a regular expression matching the file names to process + - ``package_regex`` -- (default: :obj:`default_package_regex`) a regular expression matching + the ``sage.PAC.KAGE.all`` package names from which we do not want to import. + - ``verbose`` -- if True, print statements when interesting examples are found + + EXAMPLES:: + + sage: from sage.misc.replace_dot_all import * + sage: walkdir_replace_dot_all(os.path.join(sage.env.SAGE_SRC, 'sage')) # not tested + """ + global numberFiles, numberFilesMatchingRegex + file_regex = re.compile(file_regex) + for root, dirs, files in os.walk(dir, topdown=False): + for name in files: + numberFiles += 1 + if file_regex.search(name): + numberFilesMatchingRegex += 1 + location = os.path.join(root, name) + if location.find('replace_dot_all') == -1: # to avoid changing anything in this file itself + make_replacements_in_file(location, package_regex, verbose) + + +# ******************************************************** EXECUTES MAIN FUNCTION ********************************************************************** +# this executes the main function in this file which writes over all import statements matching regex in files in specified location matching fileRegex: +if __name__ == "__main__": + # Create argument parser + parser = argparse.ArgumentParser() + # Optional arguments + parser.add_argument( + "location", + metavar='files or directories', + nargs='*', + help=("Names of source directories or source files. " + "If none given, walks through all files in src/sage."), + type=str) + parser.add_argument( + "-v", "--verbose", + help="Increase output verbosity. Shows locations of any unusual cases of import statements and the corresponding changes.", + action="store_true") # Parse arguments + args = parser.parse_args() + verbosity = args.verbose + # Declare regular expressions + file_regex = r'.*[.](py|pyx|pxi)$' + package_regex = None + # Execute the main function based on the specified location and verbosity + if not args.location: + args.location = [os.path.join(sage.env.SAGE_SRC, 'sage')] + try: + for location in args.location: + if not (location.endswith('.py') or location.endswith('.pxi')): + # Assume directory + walkdir_replace_dot_all(location, file_regex, package_regex, verbose=verbosity) + else: + # make replacements in file specified by location argument + make_replacements_in_file(location, package_regex, verbose=verbosity) + 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): + # add index to each line + print(f'{i}. {message.rstrip()}') + report = 'REPORT:\n' + report += f'Number of files checked: {numberFiles}\n' + report += f'Number of files matching regex: {numberFilesMatchingRegex}\n' + report += f'Number of files changed: {numberFilesChanged}\n' + report += f'Number of import statements replaced: {numberStatementsReplaced}' + print('*'*100 + '\n' + report + '\n' + '*'*100) + +# ****************************************************************************************************************************************************** diff --git a/src/sage/misc/sage_eval.py b/src/sage/misc/sage_eval.py index ebd9ad6f3d1..4e2a9968f24 100644 --- a/src/sage/misc/sage_eval.py +++ b/src/sage/misc/sage_eval.py @@ -22,10 +22,10 @@ def sage_eval(source, locals=None, cmds='', preparse=True): INPUT: - - ``source`` - a string or object with a _sage_ + - ``source`` - a string or object with a ``_sage_`` method - - ``locals`` - evaluate in namespace of sage.all plus + - ``locals`` - evaluate in namespace of :mod:`sage.all` plus the locals dictionary - ``cmds`` - string; sequence of commands to be run @@ -59,10 +59,10 @@ def sage_eval(source, locals=None, cmds='', preparse=True): x^2 + 1 This example illustrates that evaluation occurs in the context of - ``from sage.all import *``. Even though bernoulli has + ``from sage.all import *``. Even though ``bernoulli`` has been redefined in the local scope, when calling - ``sage_eval`` the default value meaning of bernoulli - is used. Likewise for QQ below. + :func:`sage_eval` the default value meaning of :func:`bernoulli` + is used. Likewise for ``QQ`` below. :: @@ -125,7 +125,7 @@ def sage_eval(source, locals=None, cmds='', preparse=True): sage: vars['rt2'] 1.41421356237310 - This example illustrates how ``sage_eval`` can be + This example illustrates how :mod:`sage_eval` can be useful when evaluating the output of other computer algebra systems. @@ -144,8 +144,8 @@ def sage_eval(source, locals=None, cmds='', preparse=True): RuntimeError: Use ** for exponentiation, not '^', which means xor in Python, and has the wrong precedence. - Here you can see eval simply will not work but - ``sage_eval`` will. + Here you can see that :func:`eval` simply will not work but + :func:`sage_eval` will. TESTS: @@ -201,14 +201,14 @@ def sage_eval(source, locals=None, cmds='', preparse=True): def sageobj(x, vars=None): """ - Return a native Sage object associated to x, if possible and + Return a native Sage object associated to ``x``, if possible and implemented. - If the object has an _sage_ method it is called and the value is - returned. Otherwise str is called on the object, and all preparsing + If the object has a ``_sage_`` method it is called and the value is + returned. Otherwise, :func:`str` is called on the object, and all preparsing is applied and the resulting expression is evaluated in the context of ``from sage.all import *``. To evaluate the - expression with certain variables set, use the vars argument, which + expression with certain variables set, use the ``vars`` argument, which should be a dictionary. EXAMPLES:: diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index 24586170e4e..a50bf15d708 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -107,7 +107,7 @@ The ``int`` method on :class:`SageInputBuilder` returns a SIE for an integer that is always represented in the simple way, without coercions. (So, depending on the preparser mode, it might read in as an -:class:`~sage.rings.integer.Integer`, an ``int``, or a ``long``.):: +:class:`~sage.rings.integer.Integer` or an ``int``.):: sage: test_qq_formatter(qq_sage_input_v2) [-ZZ(5)/7, -ZZ(5)/7, -5/7, -5/7, ZZ(3)/1, ZZ(3)/1, 3/1, 3/1] @@ -161,7 +161,7 @@ - Vincent Delecroix (2015-02): documentation formatting """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Carl Witty <Carl.Witty@gmail.com> # 2015 Vincent Delecroix <20100.delecroix@gmail.com> # @@ -169,8 +169,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/ +# **************************************************************************** def sage_input(x, preparse=True, verify=False, allow_locals=False): @@ -240,7 +240,7 @@ def sage_input(x, preparse=True, verify=False, allow_locals=False): sage: sage_input((3, lambda x: x)) Traceback (most recent call last): ... - ValueError: Can't convert <function <lambda> at 0x...> to sage_input form + ValueError: cannot convert <function <lambda> at 0x...> to sage_input form But we can have :func:`sage_input` continue anyway, and return an input form for the rest of the expression, with ``allow_locals=True``.:: @@ -272,6 +272,7 @@ def sage_input(x, preparse=True, verify=False, allow_locals=False): return final_answer + class SageInputBuilder: r""" An instance of this class is passed to ``_sage_input_`` methods. @@ -427,7 +428,7 @@ def __call__(self, x, coerced=False): sage: sage_input(lambda x: x) Traceback (most recent call last): ... - ValueError: Can't convert <function <lambda> at 0x...> to sage_input form + ValueError: cannot convert <function <lambda> at 0x...> to sage_input form sage: sage_input(lambda x: x, allow_locals=True, verify=True) LOCALS: _sil1: <function <lambda> at 0x...> @@ -462,8 +463,6 @@ def __call__(self, x, coerced=False): return SIE_literal_stringrep(self, str(x)) if isinstance(x, int): - # For longs that don't fit in an int, we just use the int - # code; it will get extended to long automatically. if self._preparse is True: if x < 0: return -SIE_literal_stringrep(self, str(-x) + 'r') @@ -519,7 +518,7 @@ def __call__(self, x, coerced=False): self._locals[loc_name] = x return SIE_literal_stringrep(self, loc_name) else: - raise ValueError("Can't convert {} to sage_input form".format(x)) + raise ValueError("cannot convert {} to sage_input form".format(x)) def preparse(self): r""" @@ -547,12 +546,12 @@ def int(self, n): r""" Return a raw SIE from the integer ``n`` - As it is raw, it may read back as a Sage Integer, a Python int or a - Python long, depending on its size and whether the preparser is enabled. + As it is raw, it may read back as a Sage Integer or a Python int, + depending on its size and whether the preparser is enabled. INPUT: - - ``n`` - a Sage Integer, a Python int or a Python long + - ``n`` -- a Sage Integer or a Python int EXAMPLES:: @@ -815,7 +814,7 @@ def dict(self, entries): """ if isinstance(entries, dict): entries = list(entries.items()) - entries = [(self(key),self(val)) for (key,val) in entries] + entries = [(self(key), self(val)) for (key, val) in entries] return SIE_dict(self, entries) def getattr(self, sie, attr): @@ -1077,7 +1076,7 @@ def prod(self, factors, simplify=False): neg = False break if isinstance(factor, SIE_literal_stringrep) and factor._sie_value == '1': - factors[i:i+1] = [] + factors[i:i + 1] = [] else: i += 1 if len(factors) == 0: @@ -1123,7 +1122,7 @@ def sum(self, terms, simplify=False): while i < len(terms): term = terms[i] if isinstance(term, SIE_literal_stringrep) and term._sie_value == '0': - terms[i:i+1] = [] + terms[i:i + 1] = [] else: i += 1 if len(terms) == 0: @@ -1174,6 +1173,7 @@ def result(self, e): else: return SageInputAnswer(sif._commands, sif.format(e, 0)) + # Python's precedence levels. Hand-transcribed from section 5.14 of # the Python 2 reference manual. In the Python 3 reference manual # this is section 6.16. @@ -1200,6 +1200,7 @@ def result(self, e): _prec_funcall = 40 _prec_atomic = 42 + class SageInputExpression(): r""" Subclasses of this class represent expressions for :func:`sage_input`. @@ -1687,6 +1688,7 @@ def _sie_format_statement(self, sif): result, prec = self._sie_format(sif) return result + class SIE_literal(SageInputExpression): r""" An abstract base class for ``literals`` (basically, values which @@ -1730,6 +1732,7 @@ def _sie_is_simple(self): # times in an expression, it might be better to do the replacement. return not self._sie_share + class SIE_literal_stringrep(SIE_literal): r""" Values in this class are leaves in a :func:`sage_input` expression @@ -1813,6 +1816,7 @@ def _sie_format(self, sif): """ return self._sie_value, _prec_atomic + class SIE_call(SageInputExpression): r""" This class represents a function-call node in a :func:`sage_input` @@ -2024,6 +2028,7 @@ def _sie_format(self, sif): key = sif.format(self._sie_key, 0) return '%s[%s]' % (coll, key), _prec_subscript + class SIE_getattr(SageInputExpression): r""" This class represents a getattr node in a :func:`sage_input` @@ -2222,6 +2227,7 @@ def _sie_format(self, sif): else: return '(%s)' % ', '.join(values), _prec_atomic + class SIE_dict(SageInputExpression): r""" This class represents a dict node in a :func:`sage_input` diff --git a/src/sage/misc/sagedoc_conf.py b/src/sage/misc/sagedoc_conf.py index 83df405a3a1..6a29adb599d 100644 --- a/src/sage/misc/sagedoc_conf.py +++ b/src/sage/misc/sagedoc_conf.py @@ -146,9 +146,6 @@ def apply(self): node.rawsource = source node[:] = [nodes.Text(source)] -from sage.misc.sageinspect import sage_getargspec -autodoc_builtin_argspec = sage_getargspec - # This is only used by sage.misc.sphinxify def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index be8f2527bde..2c172acdfcf 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -109,7 +109,7 @@ sage: print(sage_getsource(foo)) # optional - sage.misc.cython def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return sage: sage_getargspec(foo) # optional - sage.misc.cython - ArgSpec(args=['x', 'a', 'b'], varargs='args', keywords='kwds', defaults=(1, ')"', {False: 'bar'})) + FullArgSpec(args=['x', 'a', 'b'], varargs='args', varkw='kwds', defaults=(1, ')"', {False: 'bar'}), kwonlyargs=[], kwonlydefaults=None, annotations={}) """ @@ -343,7 +343,7 @@ def _extract_embedded_signature(docstring, name): File: sage/misc/nested_class.pyx (starting at line ...) ... sage: _extract_embedded_signature(MainClass.NestedClass.NestedSubClass.dummy.__doc__, 'dummy')[1] - ArgSpec(args=['self', 'x', 'r'], varargs='args', keywords='kwds', defaults=((1, 2, 3.4),)) + FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: _extract_embedded_signature(range.__call__.__doc__, '__call__') ('Call self as a function.', None) """ @@ -359,7 +359,7 @@ def _extract_embedded_signature(docstring, name): docstring = L[1] if len(L) > 1 else '' # Remove first line, keep the rest def_string = "def " + name + signature + ": pass" try: - return docstring, inspect.ArgSpec(*_sage_getargspec_cython(def_string)) + return docstring, inspect.FullArgSpec(*_sage_getargspec_cython(def_string)) except SyntaxError: docstring = os.linesep.join(L) return docstring, None @@ -1018,14 +1018,14 @@ def split_string(s, quot): if s[i] == '\\': escaped = not escaped continue - if not escaped and s[i:i+l] == quot: - return s[:i], s[i+l:] + if not escaped and s[i:i + l] == quot: + return s[:i], s[i + l:] escaped = False raise SyntaxError("EOF while scanning string literal") # 1. s is a triple-quoted string if s.startswith('"""'): a, b = split_string(s[3:], '"""') - return '"""'+a+'"""', b.strip() + return '"""' + a + '"""', b.strip() if s.startswith('r"""'): a, b = split_string(s[4:], '"""') return 'r"""'+a+'"""', b.strip() @@ -1107,22 +1107,18 @@ def _sage_getargspec_from_ast(source): EXAMPLES:: - sage: import warnings - sage: warnings.filterwarnings('ignore', - ....: r'inspect.getargspec\(\) is deprecated', - ....: DeprecationWarning) sage: import inspect, sage.misc.sageinspect as sms sage: from_ast = sms._sage_getargspec_from_ast sage: s = "def f(a, b=2, c={'a': [4, 5.5, False]}, d=(None, True)):\n return" sage: from_ast(s) - ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True))) + FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True)), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: context = {} sage: exec(compile(s, '<string>', 'single'), context) - sage: inspect.getargspec(context['f']) - ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True))) - sage: from_ast(s) == inspect.getargspec(context['f']) + sage: inspect.getfullargspec(context['f']) + FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True)), kwonlyargs=[], kwonlydefaults=None, annotations={}) + sage: from_ast(s) == inspect.getfullargspec(context['f']) True - sage: set(from_ast(sms.sage_getsource(x)) == inspect.getargspec(x) for x in [factor, identity_matrix, Graph.__init__]) + sage: set(from_ast(sms.sage_getsource(x)) == inspect.getfullargspec(x) for x in [factor, identity_matrix, Graph.__init__]) {True} """ ast_args = ast.parse(source.lstrip()).body[0].args @@ -1135,8 +1131,9 @@ def _sage_getargspec_from_ast(source): vararg = getattr(ast_args.vararg, 'arg', None) kwarg = getattr(ast_args.kwarg, 'arg', None) - return inspect.ArgSpec(args, vararg, kwarg, - tuple(defaults) if defaults else None) + return inspect.FullArgSpec(args, vararg, kwarg, + tuple(defaults) if defaults else None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) def _sage_getargspec_cython(source): @@ -1152,29 +1149,29 @@ def _sage_getargspec_cython(source): OUTPUT: - - an instance of :obj:`inspect.ArgSpec`, i.e., a named tuple + - an instance of :class:`inspect.FullArgSpec`, i.e., a named tuple EXAMPLES:: sage: from sage.misc.sageinspect import _sage_getargspec_cython as sgc sage: sgc("cpdef double abc(self, Element x=None, Parent base=0):") - ArgSpec(args=['self', 'x', 'base'], varargs=None, keywords=None, defaults=(None, 0)) + FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc("def __init__(self, x=None, unsigned int base=0):") - ArgSpec(args=['self', 'x', 'base'], varargs=None, keywords=None, defaults=(None, 0)) + FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def o(p, r={}, *q, **s) except? -1:') - ArgSpec(args=['p', 'r'], varargs='q', keywords='s', defaults=({},)) + FullArgSpec(args=['p', 'r'], varargs='q', varkw='s', defaults=({},), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('cpdef how(r=(None, "u:doing?")):') - ArgSpec(args=['r'], varargs=None, keywords=None, defaults=((None, 'u:doing?'),)) + FullArgSpec(args=['r'], varargs=None, varkw=None, defaults=((None, 'u:doing?'),), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def _(x="):"):') - ArgSpec(args=['x'], varargs=None, keywords=None, defaults=('):',)) + FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=('):',), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def f(z = {(1, 2, 3): True}):\n return z') - ArgSpec(args=['z'], varargs=None, keywords=None, defaults=({(1, 2, 3): True},)) + FullArgSpec(args=['z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def f(double x, z = {(1, 2, 3): True}):\n return z') - ArgSpec(args=['x', 'z'], varargs=None, keywords=None, defaults=({(1, 2, 3): True},)) + FullArgSpec(args=['x', 'z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def f(*args): pass') - ArgSpec(args=[], varargs='args', keywords=None, defaults=None) + FullArgSpec(args=[], varargs='args', varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sgc('def f(**args): pass') - ArgSpec(args=[], varargs=None, keywords='args', defaults=None) + FullArgSpec(args=[], varargs=None, varkw='args', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) Some malformed input is detected:: @@ -1206,17 +1203,17 @@ def _sage_getargspec_cython(source): sage: def dummy_python(self, *args, x=1): pass sage: sgc("def dummy_python(self, *args, x=1): pass") - ArgSpec(args=['self', 'x'], varargs='args', keywords=None, defaults=(1,)) + FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: cython("def dummy_cython(self, *args, x=1): pass") sage: sgc("def dummy_cython(self, *args, x=1): pass") - ArgSpec(args=['self', 'x'], varargs='args', keywords=None, defaults=(1,)) + FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) In some examples above, a syntax error was raised when a type definition contains a pointer. An exception is made for ``char*``, since C strings are acceptable input in public Cython functions:: sage: sgc('def f(char *x = "a string", z = {(1,2,3): True}): pass') - ArgSpec(args=['x', 'z'], varargs=None, keywords=None, defaults=('a string', {(1, 2, 3): True})) + FullArgSpec(args=['x', 'z'], varargs=None, varkw=None, defaults=('a string', {(1, 2, 3): True}), kwonlyargs=[], kwonlydefaults=None, annotations={}) AUTHORS: @@ -1313,7 +1310,7 @@ def _sage_getargspec_cython(source): name = None nb_stars = 0 else: - raise SyntaxError("varargs can't be defined twice") + raise SyntaxError("varargs cannot be defined twice") elif nb_stars == 2: if keywords is None: keywords = name @@ -1323,7 +1320,7 @@ def _sage_getargspec_cython(source): name = None nb_stars = 0 else: - raise SyntaxError("varargs can't be defined twice") + raise SyntaxError("varargs cannot be defined twice") else: raise SyntaxError("variable declaration comprises at most two '*'") else: @@ -1502,40 +1499,40 @@ def sage_getargspec(obj): sage: def f(x, y, z=1, t=2, *args, **keywords): ....: pass sage: sage_getargspec(f) - ArgSpec(args=['x', 'y', 'z', 't'], varargs='args', keywords='keywords', defaults=(1, 2)) + FullArgSpec(args=['x', 'y', 'z', 't'], varargs='args', varkw='keywords', defaults=(1, 2), kwonlyargs=[], kwonlydefaults=None, annotations={}) We now run sage_getargspec on some functions from the Sage library:: sage: sage_getargspec(identity_matrix) - ArgSpec(args=['ring', 'n', 'sparse'], varargs=None, keywords=None, defaults=(0, False)) + FullArgSpec(args=['ring', 'n', 'sparse'], varargs=None, varkw=None, defaults=(0, False), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(factor) - ArgSpec(args=['n', 'proof', 'int_', 'algorithm', 'verbose'], varargs=None, keywords='kwds', defaults=(None, False, 'pari', 0)) + FullArgSpec(args=['n', 'proof', 'int_', 'algorithm', 'verbose'], varargs=None, varkw='kwds', defaults=(None, False, 'pari', 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) In the case of a class or a class instance, the ``ArgSpec`` of the ``__new__``, ``__init__`` or ``__call__`` method is returned:: sage: P.<x,y> = QQ[] sage: sage_getargspec(P) - ArgSpec(args=['base_ring', 'n', 'names', 'order'], varargs=None, keywords=None, defaults=('degrevlex',)) + FullArgSpec(args=['base_ring', 'n', 'names', 'order'], varargs=None, varkw=None, defaults=('degrevlex',), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(P.__class__) - ArgSpec(args=['self', 'x'], varargs='args', keywords='kwds', defaults=(0,)) + FullArgSpec(args=['self', 'x'], varargs='args', varkw='kwds', defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={}) The following tests against various bugs that were fixed in :trac:`9976`:: sage: from sage.rings.polynomial.real_roots import bernstein_polynomial_factory_ratlist sage: sage_getargspec(bernstein_polynomial_factory_ratlist.coeffs_bitsize) - ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: sage_getargspec(BooleanMonomialMonoid.gen) - ArgSpec(args=['self', 'i'], varargs=None, keywords=None, defaults=(0,)) + FullArgSpec(args=['self', 'i'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: I = P*[x,y] sage: sage_getargspec(I.groebner_basis) - ArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'], - varargs='args', keywords='kwds', defaults=('', None, None, False)) + FullArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'], + varargs='args', varkw='kwds', defaults=('', None, None, False), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: cython("cpdef int foo(x,y) except -1: return 1") sage: sage_getargspec(foo) - ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['x', 'y'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) If a ``functools.partial`` instance is involved, we see no other meaningful solution than to return the argspec of the underlying function:: @@ -1545,7 +1542,7 @@ def sage_getargspec(obj): sage: import functools sage: f1 = functools.partial(f, 1,c=2) sage: sage_getargspec(f1) - ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=(1,)) + FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) TESTS: @@ -1571,14 +1568,14 @@ def sage_getargspec(obj): sage: print(sage.misc.sageinspect.sage_getsource(O)) def foo(x, a=')"', b={(2+1):'bar', not 1:3, 3<<4:5}): return sage: spec = sage.misc.sageinspect.sage_getargspec(O) - sage: spec.args, spec.varargs, spec.keywords + sage: spec.args, spec.varargs, spec.varkw (['x', 'a', 'b'], None, None) sage: spec.defaults[0] ')"' sage: sorted(spec.defaults[1].items(), key=lambda x: str(x)) [(3, 'bar'), (48, 5), (False, 3)] sage: sage.misc.sageinspect.sage_getargspec(O.__call__) - ArgSpec(args=['self', 'm', 'n'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['self', 'm', 'n'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) :: @@ -1587,13 +1584,13 @@ def foo(x, a=')"', b={(2+1):'bar', not 1:3, 3<<4:5}): return def foo(x, a='\')"', b={not (2+1==3):'bar'}): return <BLANKLINE> sage: sage.misc.sageinspect.sage_getargspec(foo) - ArgSpec(args=['x', 'a', 'b'], varargs=None, keywords=None, defaults=('\')"', {False: 'bar'})) + FullArgSpec(args=['x', 'a', 'b'], varargs=None, varkw=None, defaults=('\')"', {False: 'bar'}), kwonlyargs=[], kwonlydefaults=None, annotations={}) The following produced a syntax error before the patch at :trac:`11913`, see also :trac:`26906`:: sage: sage.misc.sageinspect.sage_getargspec(r.lm) # optional - rpy2 - ArgSpec(args=['self'], varargs='args', keywords='kwds', defaults=None) + FullArgSpec(args=['self'], varargs='args', varkw='kwds', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) The following was fixed in :trac:`16309`:: @@ -1607,23 +1604,23 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return ....: cpdef meet(categories, bint as_list = False, tuple ignore_axioms=(), tuple axioms=()): pass ....: ''') sage: sage_getargspec(Foo.join) - ArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, keywords=None, defaults=(False, (), ())) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.join) - ArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, keywords=None, defaults=(False, (), ())) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.meet) - ArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, keywords=None, defaults=(False, (), ())) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) Test that :trac:`17009` is fixed:: sage: sage_getargspec(gap) - ArgSpec(args=['self', 'x', 'name'], varargs=None, keywords=None, defaults=(None,)) + FullArgSpec(args=['self', 'x', 'name'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={}) By :trac:`17814`, the following gives the correct answer (previously, the defaults would have been found ``None``):: sage: from sage.misc.nested_class import MainClass sage: sage_getargspec(MainClass.NestedClass.NestedSubClass.dummy) - ArgSpec(args=['self', 'x', 'r'], varargs='args', keywords='kwds', defaults=((1, 2, 3.4),)) + FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) In :trac:`18249` was decided to return a generic signature for Python builtin functions, rather than to raise an error (which is what Python's @@ -1631,7 +1628,7 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return sage: import inspect sage: sage_getargspec(range) - ArgSpec(args=[], varargs='args', keywords='kwds', defaults=None) + FullArgSpec(args=[], varargs='args', varkw='kwds', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) Test that :trac:`28524` is fixed:: @@ -1662,11 +1659,11 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return return sage_getargspec(obj.__call__) if isinstance(obj, (lazy_attribute, AbstractMethod)): source = sage_getsource(obj) - return inspect.ArgSpec(*_sage_getargspec_cython(source)) + return inspect.FullArgSpec(*_sage_getargspec_cython(source)) if not callable(obj): raise TypeError("obj is not a code object") try: - return inspect.ArgSpec(*obj._sage_argspec_()) + return inspect.FullArgSpec(*obj._sage_argspec_()) except (AttributeError, TypeError): pass # If we are lucky, the function signature is embedded in the docstring. @@ -1682,7 +1679,8 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return # Note that this may give a wrong result for the constants! try: args, varargs, varkw = inspect.getargs(obj.__code__) - return inspect.ArgSpec(args, varargs, varkw, obj.__defaults__) + return inspect.FullArgSpec(args, varargs, varkw, obj.__defaults__, + kwonlyargs=[], kwonlydefaults=None, annotations={}) except (TypeError, AttributeError): pass if isclassinstance(obj): @@ -1717,7 +1715,7 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return except TypeError: # happens for Python builtins source = '' if source: - return inspect.ArgSpec(*_sage_getargspec_cython(source)) + return inspect.FullArgSpec(*_sage_getargspec_cython(source)) else: func_obj = obj @@ -1730,7 +1728,7 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return except TypeError: # arg is not a code object # The above "hopefully" was wishful thinking: try: - return inspect.ArgSpec(*_sage_getargspec_cython(sage_getsource(obj))) + return inspect.FullArgSpec(*_sage_getargspec_cython(sage_getsource(obj))) except TypeError: # This happens for Python builtins # The best we can do is to return a generic argspec args = [] @@ -1740,7 +1738,8 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return defaults = func_obj.__defaults__ except AttributeError: defaults = None - return inspect.ArgSpec(args, varargs, varkw, defaults) + return inspect.FullArgSpec(args, varargs, varkw, defaults, + kwonlyargs=[], kwonlydefaults=None, annotations={}) def formatannotation(annotation, base_module=None): @@ -1786,7 +1785,7 @@ def formatannotation(annotation, base_module=None): def sage_formatargspec(args, varargs=None, varkw=None, defaults=None, - kwonlyargs=(), kwonlydefaults={}, annotations={}, + kwonlyargs=(), kwonlydefaults=None, annotations={}, formatarg=str, formatvarargs=lambda name: '*' + name, formatvarkw=lambda name: '**' + name, @@ -1811,19 +1810,15 @@ def sage_formatargspec(args, varargs=None, varkw=None, defaults=None, :func:`sage_getargspec`. Since :func:`sage_getargspec` works for Cython functions while Python's inspect module does not, it makes sense to keep this function for formatting instances of - ``inspect.ArgSpec``. + ``inspect.FullArgSpec``. EXAMPLES:: sage: from sage.misc.sageinspect import sage_formatargspec - sage: from inspect import formatargspec # deprecated in Python 3 sage: args = ['a', 'b', 'c'] sage: defaults = [3] sage: sage_formatargspec(args, defaults=defaults) '(a, b, c=3)' - sage: import warnings; warnings.simplefilter('ignore') # ignore DeprecationWarning - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) - True """ def formatargandannotation(arg): result = formatarg(arg) @@ -2144,11 +2139,11 @@ def sage_getsource(obj): - William Stein - extensions by Nick Alexander """ - #First we should check if the object has a _sage_src_ - #method. If it does, we just return the output from - #that. This is useful for getting pexpect interface - #elements to behave similar to regular Python objects - #with respect to introspection. + # First we should check if the object has a _sage_src_ + # method. If it does, we just return the output from + # that. This is useful for getting pexpect interface + # elements to behave similar to regular Python objects + # with respect to introspection. try: return obj._sage_src_() except (AttributeError, TypeError): @@ -2174,7 +2169,7 @@ def _sage_getsourcelines_name_with_dot(obj): sage: print(sage_getsource(C.parent_class)) #indirect doctest class ParentMethods: ... - Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. + Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`. ... TESTS: @@ -2646,11 +2641,11 @@ def test3(b, # 12 Test _sage_getargspec_cython with multiple default arguments and a type:: sage: _sage_getargspec_cython("def init(self, x=None, base=0):") - ArgSpec(args=['self', 'x', 'base'], varargs=None, keywords=None, defaults=(None, 0)) + FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: _sage_getargspec_cython("def __init__(self, x=None, base=0):") - ArgSpec(args=['self', 'x', 'base'], varargs=None, keywords=None, defaults=(None, 0)) + FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: _sage_getargspec_cython("def __init__(self, x=None, unsigned int base=0, **keys):") - ArgSpec(args=['self', 'x', 'base'], varargs=None, keywords='keys', defaults=(None, 0)) + FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw='keys', defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={}) Test _extract_embedded_position: diff --git a/src/sage/misc/table.py b/src/sage/misc/table.py index 8610f06df0d..cbd8b083a6b 100644 --- a/src/sage/misc/table.py +++ b/src/sage/misc/table.py @@ -207,7 +207,7 @@ class table(SageObject): sage: table(rows=[[1,2,3], [4,5,6]], columns=[[0,0,0], [0,0,1024]]) Traceback (most recent call last): ... - ValueError: Don't set both 'rows' and 'columns' when defining a table. + ValueError: do not set both 'rows' and 'columns' when defining a table sage: table(columns=[[0,0,0], [0,0,1024]]) 0 0 @@ -251,7 +251,7 @@ def __init__(self, rows=None, columns=None, header_row=False, """ # If both rows and columns are set, raise an error. if rows and columns: - raise ValueError("Don't set both 'rows' and 'columns' when defining a table.") + raise ValueError("do not set both 'rows' and 'columns' when defining a table") # If columns is set, use its transpose for rows. if columns: rows = list(zip(*columns)) @@ -268,7 +268,7 @@ def __init__(self, rows=None, columns=None, header_row=False, self._options['header_column'] = True elif header_column: self._options['header_column'] = True - rows = [(a,) + tuple(x) for (a,x) in zip(header_column, rows)] + rows = [(a,) + tuple(x) for (a, x) in zip(header_column, rows)] else: self._options['header_column'] = False @@ -442,7 +442,7 @@ def _repr_(self): if len(rows) == 0 or nc == 0: return "" - frame_line = "+" + "+".join("-" * (x+2) for x in self._widths()) + "+\n" + frame_line = "+" + "+".join("-" * (x + 2) for x in self._widths()) + "+\n" if self._options['header_column'] and self._options['frame']: frame_line = "+" + frame_line[1:].replace('+', '++', 1) @@ -503,7 +503,7 @@ def _str_table_row(self, row, header_row=False): """ frame = self._options['frame'] widths = self._widths() - frame_line = "+" + "+".join("-" * (x+2) for x in widths) + "+\n" + frame_line = "+" + "+".join("-" * (x + 2) for x in widths) + "+\n" align = self._options['align'] if align == 'right': @@ -607,16 +607,16 @@ def _latex_(self): # table header s = "\\begin{tabular}{" s += frame_char + align_char + frame_char + head_col_char - s += frame_char.join([align_char] * (nc-1)) + s += frame_char.join([align_char] * (nc - 1)) s += frame_char + "}" + frame_str + "\n" # first row s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr)) - else '$' + latex(x).strip() + '$' for x in rows[0]) + else '$' + latex(x).strip() + '$' for x in rows[0]) s += " \\\\" + frame_str + head_row_str + "\n" # other rows for row in rows[1:]: s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr)) - else '$' + latex(x).strip() + '$' for x in row) + else '$' + latex(x).strip() + '$' for x in row) s += " \\\\" + frame_str + "\n" s += "\\end{tabular}" return s @@ -724,7 +724,7 @@ def _html_(self): if rows: s.writelines([ # If the table has < 100 rows, don't truncate the output in the notebook - '<div class="notruncate">\n' if len(rows) <= 100 else '<div class="truncate">' , + '<div class="notruncate">\n' if len(rows) <= 100 else '<div class="truncate">', '<table {} class="table_form">\n'.format(frame), '<tbody>\n', ]) @@ -813,7 +813,7 @@ def _html_table_row(self, file, row, header=False): # first entry of row entry = row[0] if isinstance(entry, Graphics): - file.write(first_column_tag % entry.show(linkmode = True)) + file.write(first_column_tag % entry.show(linkmode=True)) elif isinstance(entry, str): file.write(first_column_tag % math_parse(entry)) else: @@ -822,7 +822,7 @@ def _html_table_row(self, file, row, header=False): # other entries for column in range(1, len(row)): if isinstance(row[column], Graphics): - file.write(column_tag % row[column].show(linkmode = True)) + file.write(column_tag % row[column].show(linkmode=True)) elif isinstance(row[column], str): file.write(column_tag % math_parse(row[column])) else: diff --git a/src/sage/misc/trace.py b/src/sage/misc/trace.py index 241342a452b..2547426bc0a 100644 --- a/src/sage/misc/trace.py +++ b/src/sage/misc/trace.py @@ -28,7 +28,7 @@ def trace(code, preparse=True): sage: trace("factor(100)") # not tested - then at the (Pdb) prompt type ``s`` (or ``step``), then press return + then at the (Pdb) prompt type ``s`` (or ``step``), then press :kbd:`Return` over and over to step through every line of Python that is called in the course of the above computation. Type ``?`` at any time for help on how to use the debugger (e.g., ``l`` lists 11 lines around diff --git a/src/sage/misc/unknown.py b/src/sage/misc/unknown.py index 39f7fed8f41..b87a3e67506 100644 --- a/src/sage/misc/unknown.py +++ b/src/sage/misc/unknown.py @@ -143,8 +143,6 @@ def __bool__(self): """ raise UnknownError('Unknown does not evaluate in boolean context') - - def __and__(self, other): """ The ``&`` logical operation. diff --git a/src/sage/misc/weak_dict.pyx b/src/sage/misc/weak_dict.pyx index ac7bf3bbbd2..d52bedacfa4 100644 --- a/src/sage/misc/weak_dict.pyx +++ b/src/sage/misc/weak_dict.pyx @@ -129,6 +129,8 @@ from cpython.object cimport PyObject_Hash from cpython.ref cimport Py_INCREF, Py_XINCREF, Py_XDECREF from sage.cpython.dict_del_by_value cimport * +from sage.misc.superseded import deprecation + cdef extern from "Python.h": PyObject* Py_None # we need this redefinition because we want to be able to call @@ -369,7 +371,7 @@ cdef class WeakValueDictionary(dict): True """ - return WeakValueDictionary(self.iteritems()) + return WeakValueDictionary(self.items()) def __deepcopy__(self, memo): """ @@ -403,7 +405,7 @@ cdef class WeakValueDictionary(dict): """ out = WeakValueDictionary() - for k,v in self.iteritems(): + for k,v in self.items(): out[deepcopy(k, memo)] = v return out @@ -526,7 +528,7 @@ cdef class WeakValueDictionary(dict): sage: _ = gc.collect() sage: len(D) 1 - sage: D.items() + sage: list(D.items()) [(2, Integer Ring)] Check that :trac:`15956` has been fixed, i.e., a ``TypeError`` is @@ -620,7 +622,7 @@ cdef class WeakValueDictionary(dict): KeyError: 'popitem(): weak value dictionary is empty' """ - for k,v in self.iteritems(): + for k,v in self.items(): del self[k] return k, v raise KeyError('popitem(): weak value dictionary is empty') @@ -811,6 +813,24 @@ cdef class WeakValueDictionary(dict): return list(iter(self)) def itervalues(self): + """ + Deprecated. + + EXAMPLES:: + + sage: import sage.misc.weak_dict + sage: class Vals(): pass + sage: L = [Vals() for _ in range(10)] + sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L)) + sage: T = list(D.itervalues()) + doctest:warning...: + DeprecationWarning: use values instead + See https://trac.sagemath.org/34488 for details. + """ + deprecation(34488, "use values instead") + return self.values() + + def values(self): """ Iterate over the values of this dictionary. @@ -842,7 +862,7 @@ cdef class WeakValueDictionary(dict): sage: del D[2] sage: del L[5] - sage: for v in sorted(D.itervalues()): + sage: for v in sorted(D.values()): ....: print(v) <0> <1> @@ -866,7 +886,7 @@ cdef class WeakValueDictionary(dict): finally: self._exit_iter() - def values(self): + def values_list(self): """ Return the list of values. @@ -893,13 +913,28 @@ cdef class WeakValueDictionary(dict): sage: del D[2] sage: del L[5] - sage: sorted(D.values()) + sage: sorted(D.values_list()) [<0>, <1>, <3>, <4>, <6>, <7>, <8>, <9>] - """ - return list(self.itervalues()) + return list(self.values()) def iteritems(self): + """ + EXAMPLES:: + + sage: import sage.misc.weak_dict + sage: class Vals(): pass + sage: L = [Vals() for _ in range(10)] + sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L)) + sage: T = list(D.iteritems()) + doctest:warning...: + DeprecationWarning: use items instead + See https://trac.sagemath.org/34488 for details. + """ + deprecation(34488, "use items instead") + return self.items() + + def items(self): """ Iterate over the items of this dictionary. @@ -946,7 +981,7 @@ cdef class WeakValueDictionary(dict): sage: del D[Keys(2)] sage: del L[5] - sage: for k,v in sorted(D.iteritems()): + sage: for k,v in sorted(D.items()): ....: print("{} {}".format(k, v)) [0] <0> [1] <1> @@ -970,7 +1005,7 @@ cdef class WeakValueDictionary(dict): finally: self._exit_iter() - def items(self): + def items_list(self): """ The key-value pairs of this dictionary. @@ -1022,7 +1057,7 @@ cdef class WeakValueDictionary(dict): ([8], <8>), ([9], <9>)] """ - return list(self.iteritems()) + return list(self.items()) cdef int _enter_iter(self) except -1: """ diff --git a/src/sage/modular/abvar/abvar_ambient_jacobian.py b/src/sage/modular/abvar/abvar_ambient_jacobian.py index d6338c53262..32a006b3729 100644 --- a/src/sage/modular/abvar/abvar_ambient_jacobian.py +++ b/src/sage/modular/abvar/abvar_ambient_jacobian.py @@ -1,5 +1,5 @@ """ -Ambient Jacobian Abelian Variety +Ambient Jacobian abelian variety TESTS:: diff --git a/src/sage/modular/abvar/cuspidal_subgroup.py b/src/sage/modular/abvar/cuspidal_subgroup.py index a2769d7eff7..136f6cf23e4 100644 --- a/src/sage/modular/abvar/cuspidal_subgroup.py +++ b/src/sage/modular/abvar/cuspidal_subgroup.py @@ -241,10 +241,11 @@ def lattice(self): try: return self.__lattice except AttributeError: - lattice = self._compute_lattice(rational_only = False) + lattice = self._compute_lattice(rational_only=False) self.__lattice = lattice return lattice + class RationalCuspSubgroup(CuspidalSubgroup_generic): """ EXAMPLES:: @@ -292,10 +293,11 @@ def lattice(self): try: return self.__lattice except AttributeError: - lattice = self._compute_lattice(rational_only = True) + lattice = self._compute_lattice(rational_only=True) self.__lattice = lattice return lattice + class RationalCuspidalSubgroup(CuspidalSubgroup_generic): """ EXAMPLES:: @@ -342,7 +344,7 @@ def lattice(self): try: return self.__lattice except AttributeError: - lattice = self._compute_lattice(rational_subgroup = True) + lattice = self._compute_lattice(rational_subgroup=True) self.__lattice = lattice return lattice diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index d2af5e29e00..3f4f0ffbd8f 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -461,7 +461,7 @@ def __mul__(self, right): """ lattice = self.lattice().scale(right) return FiniteSubgroup_lattice(self.abelian_variety(), lattice, - field_of_definition = self.field_of_definition()) + field_of_definition=self.field_of_definition()) def __rmul__(self, left): """ diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 4698e509f86..adf9889bebe 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -350,7 +350,7 @@ def __call__(self, M, **kwds): elif M.domain() == self.domain() and M.codomain() == self.codomain(): M = M.matrix() else: - raise ValueError("cannot convert %s into %s" % (M, self)) + raise ValueError("cannot convert %s into %s" % (M, self)) elif is_Matrix(M): if M.base_ring() != ZZ: M = M.change_ring(ZZ) diff --git a/src/sage/modular/abvar/morphism.py b/src/sage/modular/abvar/morphism.py index d8676e7db00..a57f4d0b66c 100644 --- a/src/sage/modular/abvar/morphism.py +++ b/src/sage/modular/abvar/morphism.py @@ -1,5 +1,5 @@ r""" -Morphisms between modular abelian varieties, including Hecke operators acting on modular abelian varieties +Hecke operators and morphisms between modular abelian varieties Sage can compute with Hecke operators on modular abelian varieties. A Hecke operator is defined by given a modular abelian variety and @@ -546,7 +546,7 @@ def _image_of_finite_subgroup(self, G): B = G._relative_basis_matrix() * self.restrict_domain(G.abelian_variety()).matrix() * self.codomain().lattice().basis_matrix() lattice = B.row_module(ZZ) return self.codomain().finite_subgroup(lattice, - field_of_definition = G.field_of_definition()) + field_of_definition=G.field_of_definition()) def _image_of_abvar(self, A): """ diff --git a/src/sage/modular/arithgroup/arithgroup_element.pyx b/src/sage/modular/arithgroup/arithgroup_element.pyx index 86ef0e933aa..579cbc654cc 100644 --- a/src/sage/modular/arithgroup/arithgroup_element.pyx +++ b/src/sage/modular/arithgroup/arithgroup_element.pyx @@ -1,5 +1,5 @@ """ -Elements of Arithmetic Subgroups +Elements of arithmetic subgroups """ ################################################################################ diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index 7e07c33e551..cbad28e64c6 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -1,5 +1,5 @@ r""" -Arithmetic subgroups (finite index subgroups of `\SL_2(\ZZ)`) +Arithmetic subgroups, finite index subgroups of `\SL_2(\ZZ)` """ ################################################################################ # @@ -744,7 +744,7 @@ def _find_cusps(self): so this should usually be overridden in subclasses; but it doesn't have to be. """ - i = Cusp([1,0]) + i = Cusp([1, 0]) L = [i] for a in self.coset_reps(): ai = i.apply([a.a(), a.b(), a.c(), a.d()]) @@ -757,11 +757,12 @@ def _find_cusps(self): L.append(ai) return L - def are_equivalent(self, x, y, trans = False): + def are_equivalent(self, x, y, trans=False): r""" - Test whether or not cusps x and y are equivalent modulo self. If self - has a reduce_cusp() method, use that; otherwise do a slow explicit - test. + Test whether or not cusps x and y are equivalent modulo self. + + If self has a reduce_cusp() method, use that; otherwise do a + slow explicit test. If trans = False, returns True or False. If trans = True, then return either False or an element of self mapping x onto y. diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index 0265f5b780a..34a87cca043 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -147,9 +147,9 @@ def sl2z_word_problem(A): True """ A = SL2Z(A) - output=[] + output = [] - ## If A00 is zero + # If A00 is zero if A[0,0]==0: c=A[1,1] if c != 1: @@ -167,20 +167,20 @@ def sl2z_word_problem(A): 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 + # At this point A00>0 and A01>=0 while not (A[0,0]==0 or A[0,1]==0): if A[0,0]>A[0,1]: n=(A[0,0]/A[0,1]).floor() A=A*SL2Z([1,0,-n,1]) output.append((1, n)) - else: #A[0,0]<=A[0,1] + else: # A[0,0]<=A[0,1] n=(A[0,1]/A[0,0]).floor() A=A*SL2Z([1,-n,0,1]) output.append((0, n)) if A==SL2Z(1): - pass #done, so don't add R^0 + pass # done, so don't add R^0 elif A[0,0]==0: c=A[1,1] if c != 1: diff --git a/src/sage/modular/arithgroup/congroup.pyx b/src/sage/modular/arithgroup/congroup.pyx index 9054a80dbe1..86109c9b1eb 100644 --- a/src/sage/modular/arithgroup/congroup.pyx +++ b/src/sage/modular/arithgroup/congroup.pyx @@ -1,5 +1,5 @@ r""" -Cython helper functions for congruence subgroups +Helper functions for congruence subgroups This file contains optimized Cython implementations of a few functions related to the standard congruence subgroups `\Gamma_0, \Gamma_1, \Gamma_H`. These diff --git a/src/sage/modular/arithgroup/congroup_gamma.py b/src/sage/modular/arithgroup/congroup_gamma.py index d6fbedbc16e..bc1cbb6afd6 100644 --- a/src/sage/modular/arithgroup/congroup_gamma.py +++ b/src/sage/modular/arithgroup/congroup_gamma.py @@ -1,5 +1,5 @@ r""" -Congruence Subgroup `\Gamma(N)` +Congruence subgroup `\Gamma(N)` """ #***************************************************************************** diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index 1997f7f8cf6..5a4b31bce86 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -1,5 +1,5 @@ r""" -Congruence Subgroup `\Gamma_0(N)` +Congruence subgroup `\Gamma_0(N)` """ # **************************************************************************** diff --git a/src/sage/modular/arithgroup/congroup_gamma1.py b/src/sage/modular/arithgroup/congroup_gamma1.py index 84c19cff70d..ed84fd670b1 100644 --- a/src/sage/modular/arithgroup/congroup_gamma1.py +++ b/src/sage/modular/arithgroup/congroup_gamma1.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Congruence Subgroup `\Gamma_1(N)` +Congruence subgroup `\Gamma_1(N)` """ #***************************************************************************** diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index af0c27474a3..cfe2da14117 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Congruence Subgroup `\Gamma_H(N)` +Congruence subgroup `\Gamma_H(N)` AUTHORS: @@ -16,7 +16,7 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ################################################################################ @@ -34,6 +34,8 @@ _gammaH_cache = {} + + def GammaH_constructor(level, H): r""" Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of @@ -108,6 +110,7 @@ def is_GammaH(x): """ return isinstance(x, GammaH_class) + def _normalize_H(H, level): """ Normalize representatives for a given subgroup H of the units @@ -398,7 +401,7 @@ def _repr_(self): sage: GammaH(123, [55])._repr_() 'Congruence Subgroup Gamma_H(123) with H generated by [55]' """ - return "Congruence Subgroup Gamma_H(%s) with H generated by %s"%(self.level(), self.__H) + return "Congruence Subgroup Gamma_H(%s) with H generated by %s" % (self.level(), self.__H) def _latex_(self): r""" @@ -481,9 +484,9 @@ def generators(self, algorithm="farey"): [ 7 2], [ 49 -13], [ 7 -3], [14 -3], [-21 8], [35 16], [-42 -13] ] """ - if algorithm=="farey": + if algorithm == "farey": return self.farey_symbol().generators() - elif algorithm=="todd-coxeter": + elif algorithm == "todd-coxeter": from sage.modular.modsym.ghlist import GHlist from .congroup import generators_helper level = self.level() @@ -530,7 +533,7 @@ def _coset_reduction_data_first_coord(self): reduct_data = [0] * N # We can fill in 0 and all elements of H immediately - reduct_data[0] = (0,N,0) + reduct_data[0] = (0, N, 0) for u in H: reduct_data[u] = (1, 1, inverse_mod(u, N)) @@ -543,14 +546,14 @@ def _coset_reduction_data_first_coord(self): if N == d: repr_H_mod_N_over_d[d] = [1] break - N_over_d = N//d + N_over_d = N // d # For each element of H, we look at its image mod # N_over_d. If we haven't yet seen it, add it on to # the end of z. w = [0] * N_over_d z = [1] for x in H: - val = x%N_over_d + val = x % N_over_d if not w[val]: w[val] = 1 z.append(x) @@ -567,7 +570,7 @@ def _coset_reduction_data_first_coord(self): break d = gcd(u, N) for x in repr_H_mod_N_over_d[d]: - reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N)) + reduct_data[(u * x) % N] = (u, d, inverse_mod(x, N)) return reduct_data @@ -620,7 +623,7 @@ def _coset_reduction_data_second_coord(self): """ H = self._list_of_elements_in_H() N = self.level() - v = { 1: [1] , N: H } + v = {1: [1], N: H} for d in divisors(N): if 1 < d < N: N_over_d = N // d @@ -705,7 +708,7 @@ def _reduce_coset(self, uu, vv): first, second = self._coset_reduction_data() if gcd(first[u][1], first[v][1]) != 1: - return (0,0) + return (0, 0) if not u: return (0, first[v][0]) if not v: @@ -716,7 +719,7 @@ def _reduce_coset(self, uu, vv): new_v = (first[u][2] * v) % N H_ls = second[d] if len(H_ls) > 1: - new_v = min([ (new_v * h)%N for h in H_ls ]) + new_v = min((new_v * h) % N for h in H_ls) return (new_u, new_v) @@ -810,20 +813,20 @@ def _reduce_cusp(self, c): if not v: u = c.numerator() % N if u in H: - return Cusps((1,0)), 1 - if (N-u) in H: - return Cusps((1,0)), -1 - ls = [ (u*h)%N for h in H ] + return Cusps((1, 0)), 1 + if (N - u) in H: + return Cusps((1, 0)), -1 + ls = [(u * h) % N for h in H] m1 = min(ls) - m2 = N-max(ls) + m2 = N - max(ls) if m1 < m2: - return Cusps((m1,N)), 1 + return Cusps((m1, N)), 1 else: - return Cusps((m2,N)), -1 + return Cusps((m2, N)), -1 u = int(c.numerator() % v) gcd = get_gcd(N) - d = gcd(v,N) + d = gcd(v, N) # If (N,v) == 1, let v_0 be the minimal element # in \{ v * h | h \in \pm H \}. Then we either return @@ -831,16 +834,16 @@ def _reduce_cusp(self, c): # respectively. if d == 1: if v in H: - return Cusps((0,1)), 1 - if (N-v) in H: - return Cusps((0,1)), -1 - ls = [ (v*h)%N for h in H ] + return Cusps((0, 1)), 1 + if (N - v) in H: + return Cusps((0, 1)), -1 + ls = [(v * h) % N for h in H] m1 = min(ls) - m2 = N-max(ls) + m2 = N - max(ls) if m1 < m2: - return Cusps((1,m1)), 1 + return Cusps((1, m1)), 1 else: - return Cusps((1,m2)), -1 + return Cusps((1, m2)), -1 val_min = v inv_mod = get_inverse_mod(N) @@ -849,21 +852,21 @@ def _reduce_cusp(self, c): # steps: first, compute v_0 as above. While computing this # minimum, keep track of *all* pairs of (h,s) which give this # value of v_0. - hs_ls = [(1,1)] + hs_ls = [(1, 1)] for h in H: - tmp = (v*h)%N + tmp = (v * h) % N if tmp < val_min: val_min = tmp - hs_ls = [(inv_mod(h,N), 1)] + hs_ls = [(inv_mod(h, N), 1)] elif tmp == val_min: - hs_ls.append((inv_mod(h,N), 1)) + hs_ls.append((inv_mod(h, N), 1)) - if (N-tmp) < val_min: + if (N - tmp) < val_min: val_min = N - tmp - hs_ls = [(inv_mod(h,N), -1)] - elif (N-tmp) == val_min: - hs_ls.append((inv_mod(h,N), -1)) + hs_ls = [(inv_mod(h, N), -1)] + elif (N - tmp) == val_min: + hs_ls.append((inv_mod(h, N), -1)) # Finally, we find our minimal numerator. Let u_1 be the # minimum of s*h^-1*u mod d as (h,s) ranges over the elements @@ -872,8 +875,8 @@ def _reduce_cusp(self, c): # v_0. Then u_0/v_0 is our minimal representative. u_min = val_min sign = None - for h_inv,s in hs_ls: - tmp = (h_inv * s * u)%d + for h_inv, s in hs_ls: + tmp = (h_inv * s * u) % d while gcd(tmp, val_min) > 1 and tmp < u_min: tmp += d if tmp < u_min: @@ -921,22 +924,22 @@ def _find_cusps(self): hashes = [] N = self.level() - for d in range(1, 1+N): + for d in range(1, 1 + N): w = N.gcd(d) M = int(w) if w > 1 else 2 - for a in range(1,M): + for a in range(1, M): if gcd(a, w) != 1: continue while gcd(a, d) != 1: a += w - c = self.reduce_cusp(Cusp(a,d)) + c = self.reduce_cusp(Cusp(a, d)) h = hash(c) if h not in hashes: hashes.append(h) s.append(c) return sorted(s) - def _contains_sl2(self, a,b,c,d): + def _contains_sl2(self, a, b, c, d): r""" Test whether [a,b,c,d] is an element of this subgroup. @@ -956,7 +959,7 @@ def _contains_sl2(self, a,b,c,d): [20 3] is not an element of Congruence Subgroup Gamma_H(10) with H generated by [9] """ N = self.level() - return ( (c%N == 0) and (d%N in self._list_of_elements_in_H())) + return (c % N == 0) and (d % N in self._list_of_elements_in_H()) def gamma0_coset_reps(self): r""" @@ -1000,8 +1003,7 @@ def coset_reps(self): for r in reps1: reps2 = self.gamma0_coset_reps() for t in reps2: - yield SL2Z(t)*r - + yield SL2Z(t) * r def is_subgroup(self, other): r""" @@ -1034,7 +1036,7 @@ def is_subgroup(self, other): # easy cases if is_Gamma0(other): - return True # recall self is a GammaH, so it's contained in Gamma0 + return True # recall self is a GammaH, so it's contained in Gamma0 if is_Gamma1(other) and len(self._generators_for_H()) > 0: return False @@ -1047,10 +1049,9 @@ def is_subgroup(self, other): return False return True - def index(self): r""" - Return the index of self in SL2Z. + Return the index of ``self`` in SL2Z. EXAMPLES:: @@ -1077,7 +1078,6 @@ def nu2(self): AUTHORS: - Jordi Quer - """ N = self.level() H = self._list_of_elements_in_H() @@ -1086,7 +1086,7 @@ def nu2(self): for p, r in N.factor(): if p % 4 == 3: return ZZ(0) - return (euler_phi(N) // len(H))*len([x for x in H if (x**2 + 1) % N == 0]) + return (euler_phi(N) // len(H)) * len([x for x in H if (x**2 + 1) % N == 0]) def nu3(self): r""" @@ -1117,7 +1117,7 @@ def nu3(self): lenHpm = len(H) if N - ZZ(1) not in H: lenHpm *= 2 - return (euler_phi(N)//lenHpm)*len([x for x in H if (x**2+x+1) % N == 0]) + return (euler_phi(N) // lenHpm) * len([1 for x in H if not (x**2 + x + 1) % N]) def ncusps(self): r""" @@ -1138,17 +1138,17 @@ def ncusps(self): N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) - for d in [d for d in N.divisors() if d**2 <= N]: - Nd = lcm(d,N//d) + for d in (d for d in N.divisors() if d**2 <= N): + Nd = lcm(d, N // d) Hd = set([x % Nd for x in H]) lenHd = len(Hd) - if Nd-1 not in Hd: + if Nd - 1 not in Hd: lenHd *= 2 - summand = euler_phi(d)*euler_phi(N//d)//lenHd + summand = euler_phi(d) * euler_phi(N // d) // lenHd if d**2 == N: c = c + summand else: - c = c + 2*summand + c = c + 2 * summand return c def nregcusps(self): @@ -1180,15 +1180,15 @@ def nregcusps(self): H = self._list_of_elements_in_H() c = ZZ(0) - for d in [d for d in divisors(N) if d**2 <= N]: - Nd = lcm(d,N//d) - Hd = set([x%Nd for x in H]) + for d in (d for d in divisors(N) if d**2 <= N): + Nd = lcm(d, N // d) + Hd = set([x % Nd for x in H]) if Nd - 1 not in Hd: - summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd)) - if d**2==N: + summand = euler_phi(d) * euler_phi(N // d) // (2 * len(Hd)) + if d**2 == N: c = c + summand else: - c = c + 2*summand + c = c + 2 * summand return c def nirregcusps(self): @@ -1257,15 +1257,13 @@ def dimension_new_cusp_forms(self, k=2, p=0): 19 sage: Gamma1(33).dimension_new_cusp_forms(2,p=11) 21 - """ N = self.level() - if p==0 or N % p != 0: - return sum([H.dimension_cusp_forms(k) * mumu(N // H.level()) \ - for H in self.divisor_subgroups()]) - else: - return self.dimension_cusp_forms(k) - \ - 2*self.restrict(N//p).dimension_new_cusp_forms(k) + if p == 0 or N % p: + return sum(H.dimension_cusp_forms(k) * mumu(N // H.level()) + for H in self.divisor_subgroups()) + return self.dimension_cusp_forms(k) - \ + 2 * self.restrict(N // p).dimension_new_cusp_forms(k) def image_mod_n(self): r""" @@ -1288,9 +1286,9 @@ def image_mod_n(self): """ N = self.level() if N == 1: - raise NotImplementedError("Matrix groups over ring of integers modulo 1 not implemented") - gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()] - gens += [matrix(Zmod(N),2,[1,1,0,1])] + raise NotImplementedError("matrix groups over ring of integers modulo 1 not implemented") + gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N).one() / x]) for x in self._generators_for_H()] + gens += [matrix(Zmod(N), 2, 2, [1, 1, 0, 1])] return MatrixGroup(gens) def atkin_lehner_matrix(self, Q): @@ -1334,9 +1332,9 @@ def atkin_lehner_matrix(self, Q): raise ValueError("Q must divide the level") Q = N // N.prime_to_m_part(Q) - _, z, w = xgcd(-N//Q, Q) + _, z, w = xgcd(-N // Q, Q) # so w * Q - z*(N/Q) = 1 - return matrix(ZZ, 2, 2, [Q, 1, N*z, Q*w]) + return matrix(ZZ, 2, 2, [Q, 1, N * z, Q * w]) @cached_method def characters_mod_H(self, sign=None, galois_orbits=False): @@ -1385,6 +1383,7 @@ def characters_mod_H(self, sign=None, galois_orbits=False): A += U return A + def _list_subgroup(N, gens): r""" Given an integer ``N`` and a list of integers ``gens``, return a list of @@ -1400,15 +1399,15 @@ def _list_subgroup(N, gens): N = int(N) for g in gens: if gcd(g, N) != 1: - raise ValueError("gen (=%s) is not in (Z/%sZ)^*"%(g,N)) + raise ValueError("gen (=%s) is not in (Z/%sZ)^*" % (g, N)) gk = int(g) % N sbgrp = [gk] while not (gk in H): - gk = (gk * g)%N + gk = (gk * g) % N sbgrp.append(gk) - H = set([(x*h)%N for x in sbgrp for h in H]) - H = sorted(H) - return H + H = set([(x * h) % N for x in sbgrp for h in H]) + return sorted(H) + def _GammaH_coset_helper(N, H): r""" @@ -1429,8 +1428,8 @@ def _GammaH_coset_helper(N, H): if gcd(i, N) != 1: continue if i not in W: - t.append(t[0]*i) - W += [i*h for h in HH] + t.append(t[0] * i) + W += [i * h for h in HH] if len(W) == k: break return t @@ -1446,8 +1445,7 @@ def mumu(N): INPUT: - - ``N`` - an integer at least 1 - + - ``N`` -- an integer at least 1 OUTPUT: Integer diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp index a98ade699d0..34f5e5727db 100644 --- a/src/sage/modular/arithgroup/farey.cpp +++ b/src/sage/modular/arithgroup/farey.cpp @@ -256,7 +256,7 @@ bool is_element_general::is_member(const SL2Z& m) const { PyObject* arg = convert_to_SL2Z(m); PyObject* tuple = PyTuple_New(1); PyTuple_SetItem(tuple, 0, arg); - PyObject *result = PyEval_CallObject(method, tuple); + PyObject *result = PyObject_CallObject(method, tuple); Py_DECREF(tuple); if( not PyBool_Check(result) ) { cerr << "__contains__ does not return bool." << endl; diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index 01b3db0b019..07d5c839dad 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -1,7 +1,7 @@ # distutils: sources = sage/modular/arithgroup/sl2z.cpp sage/modular/arithgroup/farey.cpp r""" -Farey Symbol for arithmetic subgroups of `\PSL_2(\ZZ)` +Farey symbol for arithmetic subgroups of `\PSL_2(\ZZ)` AUTHORS: diff --git a/src/sage/modular/arithgroup/tests.py b/src/sage/modular/arithgroup/tests.py index 009f3d569e2..ef9818b39aa 100644 --- a/src/sage/modular/arithgroup/tests.py +++ b/src/sage/modular/arithgroup/tests.py @@ -1,5 +1,5 @@ r""" -Testing Arithmetic subgroup +Testing arithmetic subgroup """ ################################################################################ # diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 890cdc3cd4c..6ba6bb78bbe 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -1667,8 +1667,6 @@ def __bool__(self): """ return any(not o.is_zero() for o in self._value) - - def __getitem__(self, e1): r""" Evaluate a `p`-adic automorphic form on a matrix in `GL_2(\QQ_p)`. diff --git a/src/sage/modular/buzzard.py b/src/sage/modular/buzzard.py index 194988cf207..99ee6673d2a 100644 --- a/src/sage/modular/buzzard.py +++ b/src/sage/modular/buzzard.py @@ -23,6 +23,7 @@ _gp = None + def gp(): r""" Return a copy of the GP interpreter with the appropriate files loaded. @@ -41,32 +42,32 @@ def gp(): _gp.read("Tpprog.g") return _gp -## def buzzard_dimension_cusp_forms(eps, k): -## r""" -## eps is [N, i x 3 matrix], where eps[2][,1] is the primes dividing -## N, eps[2][,2] is the powers of these primes that divide N, and eps[2][,3] -## is the following: for p odd, p^n||N, it's t such that znprimroot(p^n) -## gets sent to exp(2*pi*i/phi(p^n))^t. And for p=2, it's -## 0 for 2^1, it's 0 (trivial) or -1 (non-trivial) for 2^2, and for p^n>=8 -## it's either t>=0 for the even char sending 5 to exp(2*pi*i/p^(n-2))^t, -## or t<=-1 for the odd char sending 5 to exp(2*pi*i/p^(n-2))^(-1-t). -## (so either 0<=t<2^(n-2) or -1>=t>-1-2^(n-2) ) - -## EXAMPLES:: - -## sage: buzzard_dimension_cusp_forms('TrivialCharacter(100)', 4) - -## Next we compute a dimension for the character of level 45 which is -## the product of the character of level 9 sending znprimroot(9)=2 to -## $e^{2 \pi i/6}^1$ and the character of level 5 sending -## \code{znprimroot(5)=2} to $e^{2 \pi i/4}^2=-1$. - -## sage: buzzard_dimension_cusp_forms('DirichletCharacter(45,[1,2])', 4) -## <boom!> which is why this is commented out! -## """ -## s = gp().eval('DimensionCuspForms(%s, %s)'%(eps,k)) -## print(s) -## return Integer(s) +# def buzzard_dimension_cusp_forms(eps, k): +# r""" +# eps is [N, i x 3 matrix], where eps[2][,1] is the primes dividing +# N, eps[2][,2] is the powers of these primes that divide N, and eps[2][,3] +# is the following: for p odd, p^n||N, it's t such that znprimroot(p^n) +# gets sent to exp(2*pi*i/phi(p^n))^t. And for p=2, it's +# 0 for 2^1, it's 0 (trivial) or -1 (non-trivial) for 2^2, and for p^n>=8 +# it's either t>=0 for the even char sending 5 to exp(2*pi*i/p^(n-2))^t, +# or t<=-1 for the odd char sending 5 to exp(2*pi*i/p^(n-2))^(-1-t). +# (so either 0<=t<2^(n-2) or -1>=t>-1-2^(n-2) ) + +# EXAMPLES:: + +# sage: buzzard_dimension_cusp_forms('TrivialCharacter(100)', 4) + +# Next we compute a dimension for the character of level 45 which is +# the product of the character of level 9 sending znprimroot(9)=2 to +# $e^{2 \pi i/6}^1$ and the character of level 5 sending +# \code{znprimroot(5)=2} to $e^{2 \pi i/4}^2=-1$. + +# sage: buzzard_dimension_cusp_forms('DirichletCharacter(45,[1,2])', 4) +# <boom!> which is why this is commented out! +# """ +# s = gp().eval('DimensionCuspForms(%s, %s)'%(eps,k)) +# print(s) +# return Integer(s) def buzzard_tpslopes(p, N, kmax): diff --git a/src/sage/modular/cusps_nf.py b/src/sage/modular/cusps_nf.py index 25d93cac929..157ebabe291 100644 --- a/src/sage/modular/cusps_nf.py +++ b/src/sage/modular/cusps_nf.py @@ -1220,7 +1220,7 @@ def units_mod_ideal(I): sage: I = k.ideal(5, a + 1) sage: units_mod_ideal(I) [1, - 2*a^2 + 4*a - 1, + -2*a^2 - 4*a + 1, ...] :: diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 09cb8557434..b17a99068a8 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -989,7 +989,7 @@ def eta_poly_relations(eta_elements, degree, labels=['x1', 'x2'], if verbose: print("Trying to find a relation of degree %s" % degree) inf = CuspFamily(eta1.level(), 1) - loterm = -(min([0, eta1.order_at_cusp(inf)]) + min([0, eta2.order_at_cusp(inf)])) * degree + loterm = -(min(0, eta1.order_at_cusp(inf)) + min(0, eta2.order_at_cusp(inf))) * degree if verbose: print("Lowest order of a term at infinity = %s" % -loterm) @@ -1036,7 +1036,7 @@ def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose): indices = [(i, j) for j in range(degree) for i in range(degree)] inf = CuspFamily(eta1.level(), 1) - pole_at_infinity = -(min([0, eta1.order_at_cusp(inf)]) + min([0, eta2.order_at_cusp(inf)])) * degree + pole_at_infinity = -(min(0, eta1.order_at_cusp(inf)) + min(0, eta2.order_at_cusp(inf))) * degree if verbose: print("Trying all coefficients from q^%s to q^%s inclusive" % (-pole_at_infinity, -pole_at_infinity + qexp_terms - 1)) diff --git a/src/sage/modular/hecke/ambient_module.py b/src/sage/modular/hecke/ambient_module.py index 03f1f27da08..3e4ae52f00f 100644 --- a/src/sage/modular/hecke/ambient_module.py +++ b/src/sage/modular/hecke/ambient_module.py @@ -1,8 +1,7 @@ """ Ambient Hecke modules """ - -#***************************************************************************** +# **************************************************************************** # Sage: Open Source Mathematical Software # # Copyright (C) 2005 William Stein <wstein@gmail.com> @@ -16,9 +15,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from . import degenmap from . import module from . import submodule @@ -30,13 +28,14 @@ import sage.arith.all as arith import sage.matrix.matrix_space as matrix_space -from sage.matrix.constructor import matrix +from sage.matrix.constructor import matrix -from sage.modular.arithgroup.all import Gamma0 # for Sturm bound +from sage.modular.arithgroup.all import Gamma0 # for Sturm bound -def is_AmbientHeckeModule(x): + +def is_AmbientHeckeModule(x) -> bool: r""" - Return True if x is of type AmbientHeckeModule. + Return ``True`` if ``x`` is of type ``AmbientHeckeModule``. EXAMPLES:: @@ -52,6 +51,7 @@ def is_AmbientHeckeModule(x): """ return isinstance(x, AmbientHeckeModule) + class AmbientHeckeModule(module.HeckeModule_free_module): """ An ambient Hecke module, i.e. a Hecke module that is isomorphic as a module @@ -72,7 +72,7 @@ def __init__(self, base_ring, rank, level, weight, category=None): """ rank = sage.rings.all.Integer(rank) if rank < 0: - raise ValueError("rank (=%s) must be nonnegative"%rank) + raise ValueError("rank (=%s) must be nonnegative" % rank) self.__rank = rank module.HeckeModule_free_module.__init__(self, base_ring, level, weight, category=category) @@ -83,7 +83,7 @@ def rank(self): OUTPUT: - Integer + Integer EXAMPLES:: @@ -96,8 +96,11 @@ def rank(self): def __add__(self, other): r""" - Sum of self and other. As self is an ambient space, this will only make - sense if other is a subspace of self, in which case the answer is self. + Return the sum of ``self`` and ``other``. + + As ``self`` is an ambient space, this will only make sense if + ``other`` is a subspace of ``self``, in which case the answer + is ``self``. EXAMPLES:: @@ -107,30 +110,31 @@ def __add__(self, other): sage: M + 3 Traceback (most recent call last): ... - TypeError: other (=3) must be a Hecke module. + TypeError: other (=3) must be a Hecke module """ if not isinstance(other, module.HeckeModule_free_module): - raise TypeError("other (=%s) must be a Hecke module."%other) + raise TypeError("other (=%s) must be a Hecke module" % other) if other.ambient_hecke_module() == self: return self - raise ArithmeticError("Sum only defined for subspaces of a common ambient Hecke module.") + raise ArithmeticError("sum only defined for subspaces of a common ambient Hecke module") def _repr_(self): r""" - String representation of self. Should be overridden by derived classes. + Return the string representation of ``self``. + + This should be overridden by derived classes. EXAMPLES:: sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4)._repr_() 'Generic ambient Hecke module of rank 3, level 2 and weight 4 over Rational Field' """ - return "Generic ambient Hecke module of rank %s, level %s and weight %s over %s"%(self.rank(), self.level(), self.weight(), self.base_ring()) - + return "Generic ambient Hecke module of rank %s, level %s and weight %s over %s" % (self.rank(), self.level(), self.weight(), self.base_ring()) def _degeneracy_raising_matrix(self, codomain): """ - Matrix of the degeneracy map (with t = 1) from self to codomain, whose - level should be a multiple of the level of self. + Matrix of the degeneracy map (with t = 1) from ``self`` to ``codomain``, + whose level should be a multiple of the level of ``self``. EXAMPLES:: @@ -143,7 +147,8 @@ def _degeneracy_raising_matrix(self, codomain): def _degeneracy_lowering_matrix(self, codomain, t): """ - Matrix of the degeneracy map of index t from self to codomain, whose level should be a divisor of the level of self. + Matrix of the degeneracy map of index t from ``self`` to ``codomain``, + whose level should be a divisor of the level of ``self``. EXAMPLES:: @@ -170,8 +175,9 @@ def _hecke_image_of_ith_basis_element(self, n, i): def _set_dual_free_module(self, V): r""" - Store the embedded dual module of this module. Since this module is an - ambient module, this is not necessary. + Store the embedded dual module of this module. + + Since this module is an ambient module, this is not necessary. EXAMPLES:: @@ -179,11 +185,11 @@ def _set_dual_free_module(self, V): """ pass # setting dual free module of ambient space is not necessary - def ambient_hecke_module(self): r""" - Return the ambient space that contains this ambient space. This is, - of course, just this space again. + Return the ambient space that contains this ambient space. + + This is, of course, just this space again. EXAMPLES:: @@ -211,8 +217,8 @@ def complement(self): def decomposition_matrix(self): r""" - Returns the matrix whose columns form a basis for the canonical - sorted decomposition of self coming from the Hecke operators. + Return the matrix whose columns form a basis for the canonical + sorted decomposition of ``self`` coming from the Hecke operators. If the simple factors are `D_0, \ldots, D_n`, then the first few columns are an echelonized basis for `D_0`, the @@ -236,14 +242,16 @@ def decomposition_matrix(self): for A in self.decomposition(): for x in A.basis(): rows.append(x.list()) - A = matrix_space.MatrixSpace(self.base_ring(),self.rank())(rows) + A = matrix_space.MatrixSpace(self.base_ring(), self.rank())(rows) self.__decomposition_matrix_cache = A return self.__decomposition_matrix_cache def decomposition_matrix_inverse(self): """ - Returns the inverse of the matrix returned by - decomposition_matrix(). + Return the inverse of the decomposition matrix. + + This is the inverse of the matrix returned by + :meth:`decomposition_matrix`. EXAMPLES:: @@ -265,9 +273,10 @@ def decomposition_matrix_inverse(self): def degeneracy_map(self, codomain, t=1): """ - The `t`-th degeneracy map from self to the module ``codomain``. The - level of the codomain must be a divisor or multiple of level, and t - must be a divisor of the quotient. + The `t`-th degeneracy map from ``self`` to the module ``codomain``. + + The level of the codomain must be a divisor or multiple of + level, and `t` must be a divisor of the quotient. INPUT: @@ -278,8 +287,7 @@ def degeneracy_map(self, codomain, t=1): - ``t`` - int, the parameter of the degeneracy map, i.e., the map is related to `f(q)` - `f(q^t)`. - - OUTPUT: A morphism from self to codomain. + OUTPUT: A morphism from ``self`` to ``codomain``. EXAMPLES:: @@ -357,7 +365,7 @@ def degeneracy_map(self, codomain, t=1): sage: ModularSymbols(Gamma0(7), 4).degeneracy_map(ModularSymbols(chi, 4)) Traceback (most recent call last): ... - ValueError: The characters of the domain and codomain must match + ValueError: the characters of the domain and codomain must match """ if is_AmbientHeckeModule(codomain): M = codomain @@ -380,22 +388,20 @@ def degeneracy_map(self, codomain, t=1): else: err = True if err: - raise ValueError(("The level of self (=%s) must be a divisor or multiple of " + \ - "level (=%s), and t (=%s) must be a divisor of the quotient.")%\ - (self.level(), level, t)) + raise ValueError(("the level of self (=%s) must be a divisor or multiple of " + "level (=%s) and t (=%s) must be a divisor of the quotient") % (self.level(), level, t)) eps = self.character() if not (eps is None) and level % eps.conductor() != 0: - raise ArithmeticError("The conductor of the character of this space " + \ - "(=%s) must be divisible by the level (=%s)."%\ - (eps.conductor(), level)) + raise ArithmeticError("the conductor of the character of this space " + "(=%s) must be divisible by the level (=%s)" % (eps.conductor(), level)) if M is None: M = self.hecke_module_of_level(level) if eps is not None and M.character() is not None: if eps.primitive_character() != M.character().primitive_character(): - raise ValueError("The characters of the domain and codomain must match") + raise ValueError("the characters of the domain and codomain must match") key = (M.group(), t) # bad idea to use (M, t) as the key, because using complicated objects @@ -410,15 +416,12 @@ def degeneracy_map(self, codomain, t=1): return self._degeneracy_maps[key] if M.rank() == 0: - - A = matrix_space.MatrixSpace(self.base_ring(), self.rank(),0)(0) + A = matrix_space.MatrixSpace(self.base_ring(), self.rank(), 0)(0) elif self.level() % level == 0: # lower the level - A = self._degeneracy_lowering_matrix(M, t) elif level % self.level() == 0: # raise the level - A = self._degeneracy_raising_matrix(M, t) d = degenmap.DegeneracyMap(A, self, M, t) @@ -442,7 +445,7 @@ def dual_free_module(self): def fcp(self, n, var='x'): """ - Returns the factorization of the characteristic polynomial of + Return the factorization of the characteristic polynomial of the Hecke operator `T_n` of index `n` acting on this space. INPUT: @@ -472,7 +475,7 @@ def fcp(self, n, var='x'): """ n = int(n) if n <= 0: - raise ArithmeticError("n (=%s) must be positive"%n) + raise ArithmeticError("n (=%s) must be positive" % n) return self.hecke_operator(n).fcp(var) def free_module(self): @@ -521,14 +524,15 @@ def hecke_bound(self): except AttributeError: pass verbose("WARNING: ambient.py -- hecke_bound; returning unproven guess.") - return Gamma0(self.level()).sturm_bound(self.weight()) + 2*Gamma0(self.level()).dimension_eis(self.weight()) + 5 + return Gamma0(self.level()).sturm_bound(self.weight()) + 2 * Gamma0(self.level()).dimension_eis(self.weight()) + 5 def hecke_module_of_level(self, level): r""" Return the Hecke module corresponding to self at the given level, which - should be either a divisor or a multiple of the level of self. This - raises NotImplementedError, and should be overridden in derived - classes. + should be either a divisor or a multiple of the level of self. + + This raises NotImplementedError, and should be overridden in + derived classes. EXAMPLES:: @@ -546,18 +550,14 @@ def hecke_images(self, i, v): INPUT: - - ``i`` - nonnegative integer - ``v`` - a list of positive integer - OUTPUT: - - ``matrix`` - whose rows are the Hecke images - EXAMPLES:: sage: M = ModularSymbols(DirichletGroup(13).0, 3) @@ -578,7 +578,7 @@ def hecke_images(self, i, v): def intersection(self, other): """ - Returns the intersection of self and other, which must both lie in + Return the intersection of self and other, which must both lie in a common ambient space of modular symbols. EXAMPLES:: @@ -592,14 +592,14 @@ def intersection(self, other): 1 """ if not isinstance(other, module.HeckeModule_free_module): - raise TypeError("other (=%s) must be a Hecke module."%other) + raise TypeError("other (=%s) must be a Hecke module" % other) if self.ambient_hecke_module() != other.ambient_hecke_module(): - raise ArithmeticError("Intersection only defined for subspaces of a common ambient Hecke module.") + raise ArithmeticError("intersection only defined for subspaces of a common ambient Hecke module") return other # since self is ambient, so the intersection must equal other. - def is_ambient(self): + def is_ambient(self) -> bool: r""" - Returns True if and only if self is an ambient Hecke module. + Return ``True`` if and only if ``self`` is an ambient Hecke module. .. warning:: @@ -626,11 +626,11 @@ def is_ambient(self): """ return True - def is_full_hecke_module(self, compute=True): + def is_full_hecke_module(self, compute=True) -> bool: """ - Returns True if this space is invariant under the action of + Return ``True`` if this space is invariant under the action of all Hecke operators, even those that divide the level. This is - always true for ambient Hecke modules, so return True. + always true for ambient Hecke modules, so return ``True``. EXAMPLES:: @@ -639,9 +639,9 @@ def is_full_hecke_module(self, compute=True): """ return True - def is_new(self, p=None): + def is_new(self, p=None) -> bool: r""" - Return True if this module is entirely new. + Return ``True`` if this module is entirely new. EXAMPLES:: @@ -655,12 +655,12 @@ def is_new(self, p=None): return self.__is_new[p] except AttributeError: pass - AmbientHeckeModule.new_submodule(self,p) + AmbientHeckeModule.new_submodule(self, p) return self.__is_new[p] def is_old(self, p=None): r""" - Return True if this module is entirely old. + Return ``True`` if this module is entirely old. EXAMPLES:: @@ -679,8 +679,10 @@ def is_old(self, p=None): def is_submodule(self, V): """ - Returns True if and only if self is a submodule of V. Since this is an - ambient space, this returns True if and only if V is equal to self. + Return ``True`` if and only if ``self`` is a submodule of ``V``. + + Since this is an ambient space, this returns ``True`` if and + only if ``V`` is equal to ``self``. EXAMPLES:: @@ -711,7 +713,7 @@ def linear_combination_of_basis(self, v): def new_submodule(self, p=None): """ - Returns the new or p-new submodule of self. + Return the new or p-new submodule of self. INPUT: @@ -768,30 +770,29 @@ def new_submodule(self, p=None): D = arith.prime_divisors(N) else: if N % p != 0: - raise ValueError("p must divide the level.") + raise ValueError("p must divide the level") D = [p] for q in D: # Here we are only using degeneracy *lowering* maps, so it is fine # to be careless and pass an integer for the level. One needs to be # a bit more careful with degeneracy *raising* maps for the Gamma1 # and GammaH cases. - if ((N//q) % f) == 0: - NN = N//q - d1 = self.degeneracy_map(NN,1).matrix() + if ((N // q) % f) == 0: + NN = N // q + d1 = self.degeneracy_map(NN, 1).matrix() if d is None: d = d1 else: d = d.augment(d1) - d = d.augment(self.degeneracy_map(NN,q).matrix()) - #end if - #end for + d = d.augment(self.degeneracy_map(NN, q).matrix()) + if d is None or d == 0: self.__is_new[p] = True return self else: self.__is_new[p] = False ns = self.submodule(d.kernel(), check=False) - ns.__is_new = {p:True} + ns.__is_new = {p: True} ns._is_full_hecke_module = True self.__new_submodule[p] = ns return ns @@ -799,8 +800,10 @@ def new_submodule(self, p=None): def nonembedded_free_module(self): r""" Return the free module corresponding to self as an abstract free module - (rather than as a submodule of an ambient free module). As this module - is ambient anyway, this just returns ``self.free_module()``. + (rather than as a submodule of an ambient free module). + + As this module is ambient anyway, this just returns + ``self.free_module()``. EXAMPLES:: @@ -812,7 +815,7 @@ def nonembedded_free_module(self): def old_submodule(self, p=None): """ - Returns the old or p-old submodule of self, i.e. the sum of the images + Return the old or p-old submodule of self, i.e. the sum of the images of the degeneracy maps from level `N/p` (for the given prime `p`, or for all primes `p` dividing `N` if `p` is not given). @@ -880,11 +883,11 @@ def old_submodule(self, p=None): D = arith.prime_divisors(N) else: if N % p != 0: - raise ValueError("p must divide the level.") + raise ValueError("p must divide the level") D = [p] for q in D: - NN = N//q + NN = N // q if NN % f == 0: M = self.hecke_module_of_level(NN) @@ -899,8 +902,7 @@ def old_submodule(self, p=None): else: d = d.stack(d1) d = d.stack(M.degeneracy_map(self, q).matrix()) - #end if - #end for + if d is None: os = self.zero_submodule() else: @@ -908,7 +910,7 @@ def old_submodule(self, p=None): self.__is_old[p] = (os == self) - os.__is_old = {p:True} + os.__is_old = {p: True} os._is_full_hecke_module = True self.__old_submodule[p] = os return os @@ -927,12 +929,12 @@ def submodule(self, M, Mdual=None, check=True): if check: if not sage.modules.free_module.is_FreeModule(M): V = self.free_module() - if isinstance(M, (list,tuple)): + if isinstance(M, (list, tuple)): M = V.span([V(x.element()) for x in M]) else: M = V.span(M) if not M.is_submodule(self.free_module()): - raise TypeError("M must be a submodule of the free module associated to this module.") + raise TypeError("M must be a submodule of the free module associated to this module") if M == self.free_module(): return self return self._submodule_class()(self, M, Mdual, check=check) @@ -978,9 +980,10 @@ def submodule_from_nonembedded_module(self, V, Vdual=None, check=True): def submodule_generated_by_images(self, M): """ Return the submodule of this ambient modular symbols space - generated by the images under all degeneracy maps of M. The space M - must have the same weight, sign, and group or character as this - ambient space. + generated by the images under all degeneracy maps of M. + + The space M must have the same weight, sign, and group or + character as this ambient space. EXAMPLES:: diff --git a/src/sage/modular/hecke/degenmap.py b/src/sage/modular/hecke/degenmap.py index ac0021f03c3..7475244b569 100644 --- a/src/sage/modular/hecke/degenmap.py +++ b/src/sage/modular/hecke/degenmap.py @@ -56,7 +56,7 @@ class DegeneracyMap(morphism.HeckeModuleMorphism_matrix): sage: d = M.degeneracy_map(11,2) Traceback (most recent call last): ... - ValueError: The level of self (=33) must be a divisor or multiple of level (=11), and t (=2) must be a divisor of the quotient. + ValueError: the level of self (=33) must be a divisor or multiple of level (=11) and t (=2) must be a divisor of the quotient Degeneracy maps can also go from lower level to higher level:: diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 572a8f73029..efcd8c9c848 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -27,7 +27,6 @@ from . import module - def is_HeckeSubmodule(x): r""" Return True if x is of type HeckeSubmodule. @@ -112,8 +111,8 @@ def _repr_(self): sage: S._repr_() 'Rank 3 submodule of a Hecke module of level 4' """ - return "Rank %s submodule of a Hecke module of level %s"%( - self.rank(), self.level()) + return "Rank %s submodule of a Hecke module of level %s" % ( + self.rank(), self.level()) def __add__(self, other): r""" @@ -129,9 +128,9 @@ def __add__(self, other): Modular Forms subspace of dimension 6 of Modular Forms space of dimension 6 for Congruence Subgroup Gamma0(4) of weight 10 over Rational Field """ if not isinstance(other, module.HeckeModule_free_module): - raise TypeError("other (=%s) must be a Hecke module."%other) + raise TypeError("other (=%s) must be a Hecke module." % other) if self.ambient() != other.ambient(): - raise ArithmeticError("Sum only defined for submodules of a common ambient space.") + raise ArithmeticError("sum only defined for submodules of a common ambient space") if other.is_ambient(): return other # Neither is ambient @@ -211,7 +210,7 @@ def _compute_dual_hecke_matrix(self, n): [35568 0 72] """ A = self.ambient_hecke_module().dual_hecke_matrix(n) - check = arith.gcd(self.level(), n) != 1 + check = arith.gcd(self.level(), n) != 1 return A.restrict(self.dual_free_module(), check=check) def _compute_hecke_matrix(self, n): @@ -293,7 +292,6 @@ def _set_dual_free_module(self, V): raise ArithmeticError("The rank of V must equal the rank of self.") self.dual_free_module.set_cache(V) - ################################ # Public functions ################################ @@ -365,11 +363,11 @@ def complement(self, bound=None): if anemic: while N % p == 0: p = arith.next_prime(p) - verbose("using T_%s"%p) + verbose("using T_%s" % p) f = self.hecke_polynomial(p) T = A.hecke_matrix(p) g = T.charpoly('x') - V = T.kernel_on(V, poly=g//f, check=False) + V = T.kernel_on(V, poly=g // f, check=False) if V.rank() + self.rank() <= A.rank(): break p = arith.next_prime(p) @@ -394,8 +392,8 @@ def complement(self, bound=None): return C # failed miserably - raise RuntimeError("Computation of complementary space failed (cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), A.rank()-self.rank())) - + raise RuntimeError("Computation of complementary space failed (cut down to rank %s, but should have cut down to rank %s)." % ( + V.rank(), A.rank() - self.rank())) def degeneracy_map(self, level, t=1): """ @@ -445,7 +443,6 @@ def degeneracy_map(self, level, t=1): d = self.ambient_hecke_module().degeneracy_map(level, t) return d.restrict_domain(self) - @cached_method def dual_free_module(self, bound=None, anemic=True, use_star=True): r""" @@ -553,8 +550,8 @@ def dual_free_module(self, bound=None, anemic=True, use_star=True): # then we compute the dual on each eigenspace, then put them # together. if len(self.star_eigenvalues()) == 2: - V = self.plus_submodule(compute_dual = False).dual_free_module() + \ - self.minus_submodule(compute_dual = False).dual_free_module() + V = self.plus_submodule(compute_dual=False).dual_free_module() + \ + self.minus_submodule(compute_dual=False).dual_free_module() return V # At this point, we know that self is an eigenspace for star. @@ -570,7 +567,7 @@ def dual_free_module(self, bound=None, anemic=True, use_star=True): if anemic: while N % p == 0: p = arith.next_prime(p) - verbose("using T_%s"%p) + verbose("using T_%s" % p) f = self.hecke_polynomial(p) T = A.dual_hecke_matrix(p) V = T.kernel_on(V, poly=f, check=False) @@ -582,16 +579,15 @@ def dual_free_module(self, bound=None, anemic=True, use_star=True): if V.rank() == self.rank(): return V - else: - # Failed to reduce V to the appropriate dimension - W = self.complement() - V2 = W.basis_matrix().right_kernel() - if V2.rank() == self.rank(): - return V2 - else: - raise RuntimeError("Computation of embedded dual vector space failed " + \ - "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank())) + # Failed to reduce V to the appropriate dimension + W = self.complement() + V2 = W.basis_matrix().right_kernel() + if V2.rank() == self.rank(): + return V2 + + raise RuntimeError("Computation of embedded dual vector space failed " + "(cut down to rank %s, but should have cut down to rank %s)." % (V.rank(), self.rank())) def free_module(self): """ @@ -647,8 +643,8 @@ def intersection(self, other): 1 """ if self.ambient_hecke_module() != other.ambient_hecke_module(): - raise ArithmeticError("Intersection only defined for subspaces of"\ - + " a common ambient modular symbols space.") + raise ArithmeticError("intersection only defined for subspaces of" + " a common ambient modular symbols space") if other.is_ambient(): return self if self.is_ambient(): @@ -656,10 +652,10 @@ def intersection(self, other): # Neither is ambient V = self.free_module().intersection(other.free_module()) - M = self.ambient_hecke_module().submodule(V,check=False) + M = self.ambient_hecke_module().submodule(V, check=False) - ## if sign is nonzero, the intersection will be, too - ## this only makes sense for modular symbols spaces (and hence shouldn't really be in this file) + # if sign is nonzero, the intersection will be, too + # this only makes sense for modular symbols spaces (and hence shouldn't really be in this file) try: if self.sign(): M._set_sign(self.sign()) @@ -751,7 +747,7 @@ def is_submodule(self, V): if not isinstance(V, module.HeckeModule_free_module): return False return self.ambient_hecke_module() == V.ambient_hecke_module() and \ - self.free_module().is_subspace(V.free_module()) + self.free_module().is_subspace(V.free_module()) def linear_combination_of_basis(self, v): """ @@ -822,7 +818,7 @@ def new_submodule(self, p=None): ns = S.intersection(self) if ns.rank() == self.rank(): self.__is_new[p] = True - ns.__is_new = {p:True} + ns.__is_new = {p: True} self.__new_submodule[p] = ns return ns @@ -881,7 +877,7 @@ def old_submodule(self, p=None): os = S.intersection(self) if os.rank() == self.rank(): self.__is_old[p] = True - os.__is_old = {p:True} + os.__is_old = {p: True} self.__old_submodule[p] = os return os @@ -914,14 +910,14 @@ def submodule(self, M, Mdual=None, check=True): """ if not sage.modules.free_module.is_FreeModule(M): V = self.ambient_module().free_module() - if isinstance(M, (list,tuple)): + if isinstance(M, (list, tuple)): M = V.span([V(x.element()) for x in M]) else: M = V.span(M) if check: if not M.is_submodule(self.free_module()): - raise TypeError("M (=%s) must be a submodule of the free module (=%s) associated to this module."%(M, self.free_module())) + raise TypeError("M (=%s) must be a submodule of the free module (=%s) associated to this module." % (M, self.free_module())) return self.ambient().submodule(M, Mdual, check=check) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 066869a1c57..ad34fb3c92b 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1094,7 +1094,7 @@ def primitive_data(self): d = gcd(g) return HypergeometricData(gamma_list=[x / d for x in g]) -### L-functions + # ----- L-functions ----- def gauss_table(self, p, f, prec): """ diff --git a/src/sage/modular/local_comp/type_space.py b/src/sage/modular/local_comp/type_space.py index 87b3996dc04..2549e5519b0 100644 --- a/src/sage/modular/local_comp/type_space.py +++ b/src/sage/modular/local_comp/type_space.py @@ -33,7 +33,7 @@ @cached_function -def example_type_space(example_no = 0): +def example_type_space(example_no=0): r""" Quickly return an example of a type space. Used mainly to speed up doctesting. diff --git a/src/sage/modular/modform/ambient.py b/src/sage/modular/modform/ambient.py index df2bf8b66ab..b7a963f354d 100644 --- a/src/sage/modular/modform/ambient.py +++ b/src/sage/modular/modform/ambient.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Ambient Spaces of Modular Forms +Ambient spaces of modular forms EXAMPLES: diff --git a/src/sage/modular/modform/ambient_R.py b/src/sage/modular/modform/ambient_R.py index 29fe271b62e..91e3f97b603 100644 --- a/src/sage/modular/modform/ambient_R.py +++ b/src/sage/modular/modform/ambient_R.py @@ -1,5 +1,5 @@ """ -Modular Forms over a Non-minimal Base Ring +Modular forms over a non-minimal base ring """ ######################################################################### diff --git a/src/sage/modular/modform/ambient_eps.py b/src/sage/modular/modform/ambient_eps.py index 093f62daa0d..b5c3c543d67 100644 --- a/src/sage/modular/modform/ambient_eps.py +++ b/src/sage/modular/modform/ambient_eps.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Modular Forms with Character +Modular forms with character EXAMPLES:: @@ -198,9 +198,9 @@ def change_ring(self, base_ring): """ if self.base_ring() == base_ring: return self - return ambient_R.ModularFormsAmbient_R(self, base_ring = base_ring) + return ambient_R.ModularFormsAmbient_R(self, base_ring=base_ring) - @cached_method(key=lambda self,sign: rings.Integer(sign)) # convert sign to an Integer before looking this up in the cache + @cached_method(key=lambda self, sign: rings.Integer(sign)) # convert sign to an Integer before looking this up in the cache def modular_symbols(self, sign=0): """ Return corresponding space of modular symbols with given sign. @@ -222,9 +222,9 @@ def modular_symbols(self, sign=0): """ sign = rings.Integer(sign) return modsym.ModularSymbols(self.character(), - weight = self.weight(), - sign = sign, - base_ring = self.base_ring()) + weight=self.weight(), + sign=sign, + base_ring=self.base_ring()) @cached_method def eisenstein_submodule(self): diff --git a/src/sage/modular/modform/ambient_g0.py b/src/sage/modular/modform/ambient_g0.py index 9b617eb228b..a409a062984 100644 --- a/src/sage/modular/modform/ambient_g0.py +++ b/src/sage/modular/modform/ambient_g0.py @@ -1,5 +1,5 @@ r""" -Modular Forms for `\Gamma_0(N)` over `\QQ` +Modular forms for `\Gamma_0(N)` over `\QQ` TESTS:: diff --git a/src/sage/modular/modform/ambient_g1.py b/src/sage/modular/modform/ambient_g1.py index b357fb5dc60..75f0de39e15 100644 --- a/src/sage/modular/modform/ambient_g1.py +++ b/src/sage/modular/modform/ambient_g1.py @@ -1,5 +1,5 @@ r""" -Modular Forms for `\Gamma_1(N)` and `\Gamma_H(N)` over `\QQ` +Modular forms for `\Gamma_1(N)` and `\Gamma_H(N)` over `\QQ` EXAMPLES:: diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index e16b2e4f1df..6e9acf74e36 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Creating Spaces of Modular Forms +Creating spaces of modular forms EXAMPLES:: @@ -153,12 +153,13 @@ def ModularForms_clear_cache(): global _cache _cache = {} -def ModularForms(group = 1, - weight = 2, - base_ring = None, + +def ModularForms(group=1, + weight=2, + base_ring=None, eis_only=False, - use_cache = True, - prec = defaults.DEFAULT_PRECISION): + use_cache=True, + prec=defaults.DEFAULT_PRECISION): r""" Create an ambient space of modular forms. @@ -346,8 +347,8 @@ def ModularForms(group = 1, eps = eps.minimize_base_ring() if eps.is_trivial(): return ModularForms(eps.modulus(), weight, base_ring, - use_cache = use_cache, - prec = prec) + use_cache=use_cache, + prec=prec) M = ModularFormsAmbient_eps(eps, weight, eis_only=eis_only) if base_ring != eps.base_ring(): M = M.base_extend(base_ring) # ambient_R.ModularFormsAmbient_R(M, base_ring) @@ -360,11 +361,11 @@ def ModularForms(group = 1, return M -def CuspForms(group = 1, - weight = 2, - base_ring = None, - use_cache = True, - prec = defaults.DEFAULT_PRECISION): +def CuspForms(group=1, + weight=2, + base_ring=None, + use_cache=True, + prec=defaults.DEFAULT_PRECISION): """ Create a space of cuspidal modular forms. @@ -380,13 +381,13 @@ def CuspForms(group = 1, use_cache=use_cache, prec=prec).cuspidal_submodule() -def EisensteinForms(group = 1, - weight = 2, - base_ring = None, - use_cache = True, - prec = defaults.DEFAULT_PRECISION): +def EisensteinForms(group=1, + weight=2, + base_ring=None, + use_cache=True, + prec=defaults.DEFAULT_PRECISION): """ - Create a space of eisenstein modular forms. + Create a space of Eisenstein modular forms. See the documentation for the ModularForms command for a description of the input parameters. @@ -396,7 +397,7 @@ def EisensteinForms(group = 1, sage: EisensteinForms(11,2) Eisenstein subspace of dimension 1 of Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field """ - if weight==1: + if weight == 1: return ModularForms(group, weight, base_ring, use_cache=use_cache, eis_only=True, prec=prec).eisenstein_submodule() else: @@ -404,7 +405,6 @@ def EisensteinForms(group = 1, use_cache=use_cache, prec=prec).eisenstein_submodule() - def Newforms(group, weight=2, base_ring=None, names=None): r""" Returns a list of the newforms of the given weight and level (or weight, diff --git a/src/sage/modular/modform/cuspidal_submodule.py b/src/sage/modular/modform/cuspidal_submodule.py index d40af39c52e..4b123ec8dc9 100644 --- a/src/sage/modular/modform/cuspidal_submodule.py +++ b/src/sage/modular/modform/cuspidal_submodule.py @@ -1,5 +1,5 @@ """ -The Cuspidal Subspace +The cuspidal subspace EXAMPLES:: @@ -236,7 +236,7 @@ def _compute_q_expansion_basis(self, prec=None): prec = Integer(prec) if self.dimension() == 0: return [] - M = self.modular_symbols(sign = 1) + M = self.modular_symbols(sign=1) return M.q_expansion_basis(prec) def _compute_hecke_matrix_prime(self, p): diff --git a/src/sage/modular/modform/eis_series.py b/src/sage/modular/modform/eis_series.py index 65d43a39fda..351577db16e 100644 --- a/src/sage/modular/modform/eis_series.py +++ b/src/sage/modular/modform/eis_series.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Eisenstein Series +Eisenstein series """ # **************************************************************************** # Copyright (C) 2004-2006 William Stein <wstein@gmail.com> @@ -26,7 +26,7 @@ from .eis_series_cython import eisenstein_series_poly, Ek_ZZ -def eisenstein_series_qexp(k, prec = 10, K=QQ, var='q', normalization='linear'): +def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'): r""" Return the `q`-expansion of the normalized weight `k` Eisenstein series on `\SL_2(\ZZ)` to precision prec in the ring `K`. Three normalizations @@ -125,8 +125,8 @@ def eisenstein_series_qexp(k, prec = 10, K=QQ, var='q', normalization='linear'): - David Loeffler (2012-03-15): add options for alternative normalizations (motivated by :trac:`12043`) """ - ## we use this to prevent computation if it would fail anyway. - if k <= 0 or k % 2 == 1 : + # we use this to prevent computation if it would fail anyway. + if k <= 0 or k % 2 == 1: raise ValueError("k must be positive and even") a0 = - bernoulli(k) / (2*k) @@ -420,28 +420,29 @@ def eisenstein_series_lseries(weight, prec=53, sage: L(2) -5.0235535164599797471968418348135050804419155747868718371029 """ - f = eisenstein_series_qexp(weight,prec) + f = eisenstein_series_qexp(weight, prec) from sage.lfunctions.all import Dokchitser j = weight - L = Dokchitser(conductor = 1, - gammaV = [0,1], - weight = j, - eps = (-1)**Integer(j/2), - poles = [j], + L = Dokchitser(conductor=1, + gammaV=[0, 1], + weight=j, + eps=(-1)**Integer(j // 2), + poles=[j], # Using a string for residues is a hack but it works well # since this will make PARI/GP compute sqrt(pi) with the # right precision. - residues = '[sqrt(Pi)*(%s)]'%((-1)**Integer(j/2)*bernoulli(j)/j), - prec = prec) + residues='[sqrt(Pi)*(%s)]'%((-1)**Integer(j/2)*bernoulli(j)/j), + prec=prec) s = 'coeff = %s;'%f.list() - L.init_coeffs('coeff[k+1]',pari_precode = s, + L.init_coeffs('coeff[k+1]',pari_precode=s, max_imaginary_part=max_imaginary_part, max_asymp_coeffs=max_asymp_coeffs) L.check_functional_equation() L.rename('L-series associated to the weight %s Eisenstein series %s on SL_2(Z)'%(j,f)) return L + def compute_eisenstein_params(character, k): r""" Compute and return a list of all parameters `(\chi,\psi,t)` that diff --git a/src/sage/modular/modform/eis_series_cython.pyx b/src/sage/modular/modform/eis_series_cython.pyx index 013ae32e62c..c22f2296eed 100644 --- a/src/sage/modular/modform/eis_series_cython.pyx +++ b/src/sage/modular/modform/eis_series_cython.pyx @@ -1,5 +1,5 @@ """ -Eisenstein Series (optimized compiled functions) +Eisenstein series, optimized """ from cysignals.memory cimport check_allocarray, sig_free diff --git a/src/sage/modular/modform/eisenstein_submodule.py b/src/sage/modular/modform/eisenstein_submodule.py index 17a5c506a81..aced6a69aca 100644 --- a/src/sage/modular/modform/eisenstein_submodule.py +++ b/src/sage/modular/modform/eisenstein_submodule.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -The Eisenstein Subspace +The Eisenstein subspace """ from sage.arith.functions import lcm diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 855a46ad1ef..a2ac0b1d1fa 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -3234,6 +3234,14 @@ def __init__(self, parent, forms_datum): Traceback (most recent call last): ... ValueError: the group and/or the base ring of at least one modular form (q - 6*q^2 + 9*q^3 + 4*q^4 + 6*q^5 + O(q^6)) is not consistant with the base space + sage: M = ModularFormsRing(Gamma0(2)) + sage: E4 = ModularForms(1, 4).0 + sage: M(E4)[4].parent() + Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(2) of weight 4 over Rational Field + sage: M = ModularFormsRing(Gamma1(3), base_ring=GF(7)) + sage: E6 = ModularForms(1, 6, base_ring=GF(7)).0 + sage: M(E6)[6].parent() + Modular Forms space of dimension 3 for Congruence Subgroup Gamma1(3) of weight 6 over Finite Field of size 7 """ forms_dictionary = {} if isinstance(forms_datum, dict): @@ -3245,7 +3253,8 @@ def __init__(self, parent, forms_datum): elif is_ModularFormElement(f): if f.weight() == k: if parent.group().is_subgroup(f.group()) and parent.base_ring().has_coerce_map_from(f.base_ring()): - forms_dictionary[k] = f + M = parent.modular_forms_of_weight(f.weight()).change_ring(parent.base_ring()) + forms_dictionary[k] = M(f) else: raise ValueError('the group and/or the base ring of at least one modular form (%s) is not consistant with the base space' % (f)) else: @@ -3261,7 +3270,8 @@ def __init__(self, parent, forms_datum): if (chi is not None) and (not chi.is_trivial()): raise NotImplementedError("graded modular forms for non-trivial characters is not yet implemented") if parent.group().is_subgroup(f.group()) and parent.base_ring().has_coerce_map_from(f.base_ring()): - forms_dictionary[f.weight()] = forms_dictionary.get(f.weight(), 0) + f + M = parent.modular_forms_of_weight(f.weight()).change_ring(parent.base_ring()) + forms_dictionary[f.weight()] = M(forms_dictionary.get(f.weight(), 0) + f) else: raise ValueError('the group and/or the base ring of at least one modular form (%s) is not consistant with the base space' % (f)) else: @@ -3385,6 +3395,18 @@ def _repr_(self): """ return str(self.q_expansion()) + def _latex_(self): + r""" + Return a latex representation of ``self``. + + TESTS:: + + sage: M = ModularFormsRing(1) + sage: latex(M.0) + 1 + 240 q + 2160 q^{2} + 6720 q^{3} + 17520 q^{4} + 30240 q^{5} + O(q^{6}) + """ + return self.q_expansion()._latex_() + def __getitem__(self, weight): r""" Return the homogeneous component of the given graded modular form. diff --git a/src/sage/modular/modform/hecke_operator_on_qexp.py b/src/sage/modular/modform/hecke_operator_on_qexp.py index 6a61cbb46f2..8601c9fd51e 100644 --- a/src/sage/modular/modform/hecke_operator_on_qexp.py +++ b/src/sage/modular/modform/hecke_operator_on_qexp.py @@ -1,16 +1,15 @@ """ -Hecke Operators on `q`-expansions +Hecke operators on `q`-expansions """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004-2006 William Stein <wstein@gmail.com> # # 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.matrix.constructor import matrix @@ -24,7 +23,7 @@ from sage.modular.dirichlet import DirichletGroup, is_DirichletCharacter from .element import is_ModularFormElement -def hecke_operator_on_qexp(f, n, k, eps = None, +def hecke_operator_on_qexp(f, n, k, eps=None, prec=None, check=True, _return_list=False): r""" Given the `q`-expansion `f` of a modular form with character @@ -110,10 +109,10 @@ def hecke_operator_on_qexp(f, n, k, eps = None, # formula v = [f[m*n] for m in range(prec)] else: - l = k-1 + l = k - 1 for m in range(prec): - am = sum([eps(d) * d**l * f[m*n//(d*d)] for \ - d in divisors(gcd(n, m)) if (m*n) % (d*d) == 0]) + am = sum([eps(d) * d**l * f[m*n//(d*d)] + for d in divisors(gcd(n, m)) if (m*n) % (d*d) == 0]) v.append(am) if _return_list: return v @@ -123,6 +122,7 @@ def hecke_operator_on_qexp(f, n, k, eps = None, R = f.parent() return R(v, prec) + def _hecke_operator_on_basis(B, V, n, k, eps): """ Does the work for hecke_operator_on_basis once the input @@ -149,8 +149,8 @@ def _hecke_operator_on_basis(B, V, n, k, eps): TB = [V.coordinate_vector(w) for w in TB] return matrix(V.base_ring(), len(B), len(B), TB, sparse=False) -def hecke_operator_on_basis(B, n, k, eps=None, - already_echelonized = False): + +def hecke_operator_on_basis(B, n, k, eps=None, already_echelonized=False): r""" Given a basis `B` of `q`-expansions for a space of modular forms with character `\varepsilon` to precision at least `\#B\cdot n+1`, @@ -232,8 +232,8 @@ def hecke_operator_on_basis(B, n, k, eps=None, raise TypeError("each element of B must be a power series") n = Integer(n) k = Integer(k) - prec = (f.prec()-1)//n + prec = (f.prec() - 1) // n A = R**prec V = A.span_of_basis([g.padded_list(prec) for g in B], - already_echelonized = already_echelonized) + already_echelonized=already_echelonized) return _hecke_operator_on_basis(B, V, n, k, eps) diff --git a/src/sage/modular/modform/j_invariant.py b/src/sage/modular/modform/j_invariant.py index 3cb5085fa02..9d53d44d909 100644 --- a/src/sage/modular/modform/j_invariant.py +++ b/src/sage/modular/modform/j_invariant.py @@ -1,5 +1,5 @@ r""" -q-expansion of j-invariant +`q`-expansion of `j`-invariant """ from .eis_series import eisenstein_series_qexp from .vm_basis import delta_qexp diff --git a/src/sage/modular/modform/notes.py b/src/sage/modular/modform/notes.py index 3bdc08a3bc1..833bb017bfd 100644 --- a/src/sage/modular/modform/notes.py +++ b/src/sage/modular/modform/notes.py @@ -1,26 +1,26 @@ -""" -Design Notes +r""" +Design notes -The implementation depends the fact that we have dimension formulas -(see dims.py) for spaces of modular forms with character, and new -subspaces, so that we don't have to compute q-expansions for the whole -space in order to compute q-expansions / elements / and dimensions of +The implementation depends on the fact that we have dimension formulas +(see ``dims.py``) for spaces of modular forms with character, and new +subspaces, so that we don't have to compute `q`-expansions for the whole +space in order to compute `q`-expansions / elements / and dimensions of certain subspaces. Also, the following design is much simpler than -the one I used in MAGMA because submodulesq don't have lots of +the one I used in MAGMA because submodules don't have lots of complicated special labels. A modular forms module can consist of the span of any elements; they need not be Hecke equivariant or anything else. -The internal basis of q-expansions of modular forms for the ambient -space is defined as follows: +The internal basis of `q`-expansions of modular forms for the ambient +space is defined as follows:: First Block: Cuspidal Subspace Second Block: Eisenstein Subspace - Cuspidal Subspace: Block for each level M dividing N, from highest - level to lowest. The block for level M - contains the images at level N of the - newsubspace of level M (basis, then + Cuspidal Subspace: Block for each level `M` dividing `N`, from highest + level to lowest. The block for level `M` + contains the images at level `N` of the + newsubspace of level `M` (basis, then basis(q**d), then basis(q**e), etc.) Eisenstein Subspace: characters, etc. @@ -32,7 +32,7 @@ can also create completely arbitrary subspaces as well. The base ring is the ring generated by the character values (or -bigger). In MAGMA the base was always ZZ, which is confusing. +bigger). In MAGMA the base was always `\ZZ`, which is confusing. """ ######################################################################### diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 4c8865fd745..3a56864da97 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -397,7 +397,7 @@ def _generators_variables_dictionnary(self, poly_parent, gens): 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())) - return {poly_parent.gen(i) : self(gens[i]) for i in range(0, nb_var)} + return {poly_parent.gen(i): self(gens[i]) for i in range(0, nb_var)} def from_polynomial(self, polynomial, gens=None): r""" @@ -518,7 +518,7 @@ def _element_constructor_(self, forms_datum): forms_dictionary = forms_datum._forms_dictionary elif is_ModularFormElement(forms_datum): if self.group().is_subgroup(forms_datum.group()) and self.base_ring().has_coerce_map_from(forms_datum.base_ring()): - forms_dictionary = {forms_datum.weight():forms_datum} + forms_dictionary = {forms_datum.weight(): 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(): diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 5dfd3f568d8..82b20da8167 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -54,7 +54,7 @@ # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ######################################################################### from sage.arith.misc import gcd @@ -81,7 +81,8 @@ from . import defaults from . import hecke_operator_on_qexp -WARN=False +WARN = False + def is_ModularFormsSpace(x): r""" @@ -528,8 +529,8 @@ def echelon_basis(self): W = self._q_expansion_module() pr = W.degree() B = self.q_echelon_basis(pr) - E = [self(F.linear_combination_of_basis(W.coordinates(f.padded_list(pr)))) \ - for f in B] + E = [self(F.linear_combination_of_basis(W.coordinates(f.padded_list(pr)))) + for f in B] return Sequence(E, cr=True, immutable=True) @cached_method @@ -537,10 +538,10 @@ def integral_basis(self): """ Return an integral basis for this space of modular forms. - EXAMPLES: In this example the integral and echelon bases are - different. + EXAMPLES: - :: + In this example the integral and echelon bases are + different. :: sage: m = ModularForms(97,2,prec=10) sage: s = m.cuspidal_subspace() @@ -908,8 +909,8 @@ def __add__(self, right): """ from sage.modular.modform.submodule import ModularFormsSubmodule if self.ambient_module() != right.ambient_module(): - raise ArithmeticError(("Sum of %s and %s not defined because " + \ - "they do not lie in a common ambient space.")%\ + raise ArithmeticError(("Sum of %s and %s not defined because " + + "they do not lie in a common ambient space.") % (self, right)) if self.is_ambient(): return self @@ -1309,7 +1310,7 @@ def _compute_hecke_matrix_prime(self, p, prec=None): self.weight(), eps, already_echelonized=False) except ValueError: # Double the precision. - return self._compute_hecke_matrix_prime(p, prec = 2*prec+1) + return self._compute_hecke_matrix_prime(p, prec=2 * prec + 1) def _compute_hecke_matrix(self, n): """ @@ -1381,8 +1382,9 @@ def basis(self): 1 + 12/5*q + 36/5*q^2 + 48/5*q^3 + 84/5*q^4 + 72/5*q^5 + O(q^6) ] """ - return Sequence([self.element_class(self, x) for \ - x in self.free_module().basis()], immutable=True, cr=True) + return Sequence([self.element_class(self, x) + for x in self.free_module().basis()], + immutable=True, cr=True) def gen(self, n): """ @@ -1800,13 +1802,13 @@ def embedded_submodule(self): # raise ArithmeticError, "Intersection of %s and %s not defined."%\ # (self, right) # V = self.embedded_submodule().intersection(right.embedded_submodule()) -## return ModularForms(self.ambient_module(),V) +# return ModularForms(self.ambient_module(),V) # return self.span([ self(b) for b in V.basis() ]) -## def _key(self): -## if self.is_ambient(): -## return self.__key -## return self.__ambient +# def _key(self): +# if self.is_ambient(): +# return self.__key +# return self.__ambient def level(self): """ diff --git a/src/sage/modular/modform/submodule.py b/src/sage/modular/modform/submodule.py index b1da8a92105..49b6c12b493 100644 --- a/src/sage/modular/modform/submodule.py +++ b/src/sage/modular/modform/submodule.py @@ -104,7 +104,8 @@ def _compute_q_expansion_basis(self, prec): O(q^5)] """ A = self.ambient_module() - return [A._q_expansion(element = f.element(), prec=prec) for f in self.basis()] + return [A._q_expansion(element=f.element(), prec=prec) + for f in self.basis()] # TODO diff --git a/src/sage/modular/modform/theta.py b/src/sage/modular/modform/theta.py index 9ebad5cfabc..ea0a88c81ba 100644 --- a/src/sage/modular/modform/theta.py +++ b/src/sage/modular/modform/theta.py @@ -1,9 +1,9 @@ """ -q-expansions of Theta Series +`q`-expansions of theta series AUTHOR: -William Stein +- William Stein """ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -13,8 +13,7 @@ def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" - Return the `q`-expansion of the series - ` \theta_2 = \sum_{n odd} q^{n^2}. ` + Return the `q`-expansion of the series `\theta_2 = \sum_{n \text{ odd}} q^{n^2}`. INPUT: @@ -64,7 +63,7 @@ def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): def theta_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" Return the `q`-expansion of the standard `\theta` series - ` \theta = 1 + 2\sum_{n=1}{^\infty} q^{n^2}. ` + `\theta = 1 + 2\sum_{n=1}^{\infty} q^{n^2}`. INPUT: diff --git a/src/sage/modular/modform/vm_basis.py b/src/sage/modular/modform/vm_basis.py index be690b7b503..4ac4f8c36a0 100644 --- a/src/sage/modular/modform/vm_basis.py +++ b/src/sage/modular/modform/vm_basis.py @@ -1,5 +1,5 @@ r""" -The Victor Miller Basis +The Victor Miller basis This module contains functions for quick calculation of a basis of `q`-expansions for the space of modular forms of level 1 and any weight. The @@ -248,8 +248,8 @@ def _delta_poly(prec=10): stop = int((-1+math.sqrt(1+8*prec))/2.0) # make list of index/value pairs for the sparse poly - values = [(n*(n+1)//2, ((-2*n-1) if (n & 1) else (2*n+1))) \ - for n in range(stop+1)] + values = [(n*(n+1)//2, ((-2*n-1) if (n & 1) else (2*n+1))) + for n in range(stop + 1)] for (i1, v1) in values: for (i2, v2) in values: @@ -260,15 +260,14 @@ def _delta_poly(prec=10): f = Fmpz_poly(v) t = verbose('made series') - f = f*f + f = f * f f._unsafe_mutate_truncate(prec) t = verbose('squared (2 of 3)', t) - f = f*f + f = f * f f._unsafe_mutate_truncate(prec - 1) t = verbose('squared (3 of 3)', t) f = f.left_shift(1) t = verbose('shifted', t) - return f diff --git a/src/sage/modular/modform_hecketriangle/abstract_ring.py b/src/sage/modular/modform_hecketriangle/abstract_ring.py index 5a9b0e86258..0ff086c0a60 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_ring.py +++ b/src/sage/modular/modform_hecketriangle/abstract_ring.py @@ -222,23 +222,21 @@ def _coerce_map_from_(self, S): sage: MR4.has_coerce_map_from(MF2) True """ - from .space import FormsSpace_abstract from .functors import _common_subgroup - if ( isinstance(S, FormsRing_abstract)\ - and self._group == _common_subgroup(self._group, S._group)\ - and self._analytic_type >= S._analytic_type\ - and self.base_ring().has_coerce_map_from(S.base_ring()) ): + if (isinstance(S, FormsRing_abstract) + and self._group == _common_subgroup(self._group, S._group) + and self._analytic_type >= S._analytic_type + and self.base_ring().has_coerce_map_from(S.base_ring())): return True - elif isinstance(S, FormsRing_abstract): + if isinstance(S, FormsRing_abstract): return False - elif isinstance(S, FormsSpace_abstract): - raise RuntimeError( "This case should not occur." ) + if isinstance(S, FormsSpace_abstract): + raise RuntimeError("this case should not occur") # return self._coerce_map_from_(S.graded_ring()) - elif (self.AT("holo") <= self._analytic_type) and (self.coeff_ring().has_coerce_map_from(S)): + if (self.AT("holo") <= self._analytic_type) and (self.coeff_ring().has_coerce_map_from(S)): return True - else: - return False + return False def _an_element_(self): r""" diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index e7c59012429..efd12339000 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -6,16 +6,14 @@ - Jonas Jermann (2013): initial version """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2014 Jonas Jermann <jjermann2@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.modules.free_module_element import is_FreeModuleElement @@ -323,19 +321,19 @@ def _coerce_map_from_(self, S): from .space import ZeroForm from .subspace import SubSpaceForms - if ( isinstance(S, ZeroForm)): + if isinstance(S, ZeroForm): return True - elif ( isinstance(S, SubSpaceForms)\ - and isinstance(self, SubSpaceForms) ): + if (isinstance(S, SubSpaceForms) + and isinstance(self, SubSpaceForms)): if (self.ambient_space().has_coerce_map_from(S.ambient_space())): S2 = S.change_ambient_space(self.ambient_space()) return self.module().has_coerce_map_from(S2.module()) else: return False - elif ( isinstance(S, FormsSpace_abstract)\ - and self.graded_ring().has_coerce_map_from(S.graded_ring())\ - and S.weight() == self._weight\ - and S.ep() == self._ep\ + elif ( isinstance(S, FormsSpace_abstract) + and self.graded_ring().has_coerce_map_from(S.graded_ring()) + and S.weight() == self._weight + and S.ep() == self._ep and not isinstance(self, SubSpaceForms)): return True else: @@ -695,9 +693,9 @@ def weight_parameters(self): if (n == infinity): # TODO: Figure out what to do in this case # (l1 and l2 are no longer defined in an analog/unique way) - #l2 = num % ZZ(2) - #l1 = ((num-l2)/ZZ(2)).numerator() - ## TODO: The correct generalization seems (l1,l2) = (0,num) + # l2 = num % ZZ(2) + # l1 = ((num-l2)/ZZ(2)).numerator() + # TODO: The correct generalization seems (l1,l2) = (0,num) l2 = ZZ(0) l1 = num else: @@ -1743,10 +1741,8 @@ def construct_form(self, laurent_series, order_1=ZZ(0), check=True, rationalize= if (len(coefficients) == 0): return self(0) - rat = sum([\ - coefficients[j] * self.F_basis_pol(exponents[j], order_1=order_1)\ - for j in range(ZZ(len(coefficients))) - ]) + rat = sum([coefficients[j] * self.F_basis_pol(exponents[j], order_1=order_1) + for j in range(ZZ(len(coefficients)))]) el = self(rat) diff --git a/src/sage/modular/modform_hecketriangle/all.py b/src/sage/modular/modform_hecketriangle/all.py index 728c7f729a9..cd236efc334 100644 --- a/src/sage/modular/modform_hecketriangle/all.py +++ b/src/sage/modular/modform_hecketriangle/all.py @@ -2,28 +2,28 @@ AUTHORS: - Jonas Jermann (2013): initial version - """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2014 Jonas Jermann <jjermann2@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from .hecke_triangle_groups import HeckeTriangleGroup -from .series_constructor import MFSeriesConstructor +from .series_constructor import MFSeriesConstructor -from .graded_ring import ( QuasiMeromorphicModularFormsRing, QuasiWeakModularFormsRing, QuasiModularFormsRing,\ - QuasiCuspFormsRing, MeromorphicModularFormsRing, WeakModularFormsRing,\ - ModularFormsRing, CuspFormsRing ) +from .graded_ring import (QuasiMeromorphicModularFormsRing, + QuasiWeakModularFormsRing, QuasiModularFormsRing, + QuasiCuspFormsRing, MeromorphicModularFormsRing, + WeakModularFormsRing, + ModularFormsRing, CuspFormsRing) -from .space import ( QuasiMeromorphicModularForms, QuasiWeakModularForms, QuasiModularForms, QuasiCuspForms,\ - MeromorphicModularForms, WeakModularForms, ModularForms, CuspForms,\ - ZeroForm ) +from .space import (QuasiMeromorphicModularForms, QuasiWeakModularForms, + QuasiModularForms, QuasiCuspForms, + MeromorphicModularForms, WeakModularForms, ModularForms, + CuspForms, ZeroForm) -from .subspace import ModularFormsSubSpace +from .subspace import ModularFormsSubSpace diff --git a/src/sage/modular/modform_hecketriangle/analytic_type.py b/src/sage/modular/modform_hecketriangle/analytic_type.py index 7e8d1ba947d..3b3b691e181 100644 --- a/src/sage/modular/modform_hecketriangle/analytic_type.py +++ b/src/sage/modular/modform_hecketriangle/analytic_type.py @@ -1,5 +1,5 @@ r""" -Analytic types of modular forms. +Analytic types of modular forms Properties of modular forms and their generalizations are assembled into one partially ordered set. See :class:`AnalyticType` for a diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index d7b3a5094b6..b6d01bf0c37 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -2167,12 +2167,10 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): pass # The general case - num_prec = max(\ - ZZ(getattr(tau,'prec',lambda: num_prec)()),\ - num_prec\ - ) + num_prec = max(ZZ(getattr(tau, 'prec', lambda: num_prec)()), num_prec) + tau = tau.n(num_prec) - (x,y,z,d) = self.parent().rat_field().gens() + (x, y, z, d) = self.parent().rat_field().gens() if (self.is_homogeneous() and self.is_modular()): q_exp = self.q_expansion_fixed_d(prec=prec, d_num_prec=num_prec) 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 a8813365963..090d1bfaf0f 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -43,7 +43,7 @@ def coerce_AA(p): sage: AA(p)._exact_field() Number Field in a with defining polynomial y^8 ... with a in ... sage: coerce_AA(p)._exact_field() - Number Field in a with defining polynomial y^4 - 1910*y^2 - 3924*y + 681058 with a in 39.710518724...? + Number Field in a with defining polynomial y^4 - 1910*y^2 - 3924*y + 681058 with a in ...? """ el = AA(p) el.simplify() diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py index c1fc60f724c..fdd7e38b552 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py @@ -834,7 +834,7 @@ def emb_key(emb): return L if len(L) > 1: - L.sort(key = emb_key) + L.sort(key=emb_key) return L[-1] def _elliptic_conj_reps(self): diff --git a/src/sage/modular/modform_hecketriangle/series_constructor.py b/src/sage/modular/modform_hecketriangle/series_constructor.py index 3b698833b2b..881270c5bfa 100644 --- a/src/sage/modular/modform_hecketriangle/series_constructor.py +++ b/src/sage/modular/modform_hecketriangle/series_constructor.py @@ -33,8 +33,7 @@ from .hecke_triangle_groups import HeckeTriangleGroup - -class MFSeriesConstructor(SageObject,UniqueRepresentation): +class MFSeriesConstructor(SageObject, UniqueRepresentation): r""" Constructor for the Fourier expansion of some (specific, basic) modular forms. @@ -44,7 +43,7 @@ class MFSeriesConstructor(SageObject,UniqueRepresentation): """ @staticmethod - def __classcall__(cls, group = HeckeTriangleGroup(3), prec=ZZ(10)): + def __classcall__(cls, group=HeckeTriangleGroup(3), prec=ZZ(10)): r""" Return a (cached) instance with canonical parameters. diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index 6408405d520..627f1c567e7 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -2651,9 +2651,9 @@ def _repr_(self): sage: M # indirect doctest Modular Symbols space of dimension 32 for Gamma_0(37) of weight 6 with sign 0 over Rational Field """ - return ("Modular Symbols space of dimension %s for Gamma_0(%s) of weight %s with sign %s " + \ - "over %s")%(self.dimension(), self.level(),self.weight(), self.sign(), - self.base_ring()) + return ("Modular Symbols space of dimension %s for Gamma_0(%s) of weight %s with sign %s " + + "over %s") % (self.dimension(), self.level(),self.weight(), self.sign(), + self.base_ring()) def _cuspidal_submodule_dimension_formula(self): r""" @@ -3014,7 +3014,7 @@ def _compute_hecke_matrix_prime(self, p, rows=None): P1 = self.p1list() mod2term = self._mod2term R = self.manin_gens_to_basis() - W = R.new_matrix(nrows=len(B), ncols = R.nrows()) # the 0 with given number of rows and cols. + W = R.new_matrix(nrows=len(B), ncols=R.nrows()) # the 0 with given number of rows and cols. j = 0 tm = verbose("Matrix non-reduced", tm) for i in B: @@ -3588,11 +3588,11 @@ def __init__(self, eps, weight, sign, base_ring, custom_init=None, category=None """ level = eps.modulus() ModularSymbolsAmbient.__init__(self, - weight = weight, - group = arithgroup.Gamma1(level), - sign = sign, - base_ring = base_ring, - character = eps.change_ring(base_ring), + weight=weight, + group=arithgroup.Gamma1(level), + sign=sign, + base_ring=base_ring, + character=eps.change_ring(base_ring), custom_init=custom_init, category=category) @@ -3607,10 +3607,10 @@ def _repr_(self): sage: M # indirect doctest Modular Symbols space of dimension 2 and level 5, weight 3, character [zeta4], sign 0, over Cyclotomic Field of order 4 and degree 2 """ - return ("Modular Symbols space of dimension %s and level %s, weight %s, character %s, sign %s, " + \ - "over %s")%(self.dimension(), self.level(), self.weight(), - self.character()._repr_short_(), self.sign(), self.base_ring()) - + return ("Modular Symbols space of dimension %s and level %s, weight %s, character %s, sign %s, " + + "over %s") % (self.dimension(), self.level(), self.weight(), + self.character()._repr_short_(), self.sign(), + self.base_ring()) def _cuspidal_submodule_dimension_formula(self): r""" diff --git a/src/sage/modular/modsym/boundary.py b/src/sage/modular/modsym/boundary.py index f8550d0f05f..ec41f8c36dd 100644 --- a/src/sage/modular/modsym/boundary.py +++ b/src/sage/modular/modsym/boundary.py @@ -274,17 +274,17 @@ def __neg__(self): sage: -x + x # indirect doctest 0 """ - return self*(-1) + return self * (-1) @richcmp_method class BoundarySpace(hecke.HeckeModule_generic): def __init__(self, - group = arithgroup.Gamma0(1), - weight = 2, - sign = 0, - base_ring = rings.QQ, - character = None): + group=arithgroup.Gamma0(1), + weight=2, + sign=0, + base_ring=rings.QQ, + character=None): """ Space of boundary symbols for a congruence subgroup of SL_2(Z). @@ -569,7 +569,7 @@ def __call__(self, x): y = {i: xi for i, xi in enumerate(x)} return BoundarySpaceElement(self, y) - raise TypeError("Coercion of %s (of type %s) into %s not (yet) defined."%(x, type(x), self)) + raise TypeError("Coercion of %s (of type %s) into %s not (yet) defined." % (x, type(x), self)) def _repr_(self): """ @@ -580,10 +580,10 @@ def _repr_(self): sage: sage.modular.modsym.boundary.BoundarySpace(Gamma0(3), 2)._repr_() 'Space of Boundary Modular Symbols of weight 2 for Congruence Subgroup Gamma0(3) with sign 0 and character [1] over Rational Field' """ - return ("Space of Boundary Modular Symbols of weight %s for" + \ + return ("Space of Boundary Modular Symbols of weight %s for" + " %s with sign %s and character %s over %s") % ( - self.weight(), self.group(), self.sign(), - self.character()._repr_short_(), self.base_ring()) + self.weight(), self.group(), self.sign(), + self.character()._repr_short_(), self.base_ring()) def _cusp_index(self, cusp): """ @@ -806,8 +806,8 @@ def _repr_(self): sage: ModularSymbols(Gamma1(5), 3, sign=1).boundary_space()._repr_() 'Boundary Modular Symbols space for Gamma_1(5) of weight 3 over Rational Field' """ - return ("Boundary Modular Symbols space for Gamma_1(%s) of weight %s " + \ - "over %s") % (self.level(),self.weight(), self.base_ring()) + return ("Boundary Modular Symbols space for Gamma_1(%s) of weight %s " + + "over %s") % (self.level(), self.weight(), self.base_ring()) def _is_equiv(self, c1, c2): """ @@ -946,19 +946,19 @@ def _coerce_cusp(self, c): # if sign: if (c.is_infinity() and sign != (-1)**self.weight()) or \ - (c.is_zero() and sign== -1): + (c.is_zero() and sign == -1): self._zero_cusps.append(c) del self._known_gens[-1] return self(0) elif (not c.is_infinity() and not c.is_zero()): t, eps = self._is_equiv(c, -c) - if t and ((eps == 1 and sign == -1) or \ + if t and ((eps == 1 and sign == -1) or (eps == -1 and sign != (-1)**self.weight())): self._zero_cusps.append(c) del self._known_gens[-1] return self(0) - return BoundarySpaceElement(self, {(len(g)-1):1}) + return BoundarySpaceElement(self, {(len(g)-1): 1}) class BoundarySpace_wtk_gamma_h(BoundarySpace): @@ -1016,8 +1016,8 @@ def _repr_(self): sage: ModularSymbols(GammaH(7,[2]), 4).boundary_space()._repr_() 'Boundary Modular Symbols space for Congruence Subgroup Gamma_H(7) with H generated by [2] of weight 4 over Rational Field' """ - return ("Boundary Modular Symbols space for %s of weight %s " + \ - "over %s") % (self.group(),self.weight(), self.base_ring()) + return ("Boundary Modular Symbols space for %s of weight %s " + + "over %s") % (self.group(), self.weight(), self.base_ring()) def _is_equiv(self, c1, c2): """ @@ -1205,13 +1205,13 @@ def _coerce_cusp(self, c): return self(0) elif (not c.is_infinity() and not c.is_zero()): t, eps = self._is_equiv(c, -c) - if t and ((eps == 1 and sign == -1) or \ + if t and ((eps == 1 and sign == -1) or (eps == -1 and sign != (-1)**self.weight())): self._zero_cusps.append(c) del self._known_gens[-1] return self(0) - return BoundarySpaceElement(self, {(len(g)-1):1}) + return BoundarySpaceElement(self, {(len(g)-1): 1}) class BoundarySpace_wtk_eps(BoundarySpace): @@ -1263,9 +1263,11 @@ def _repr_(self): sage: ModularSymbols(DirichletGroup(6).0, 4).boundary_space()._repr_() 'Boundary Modular Symbols space of level 6, weight 4, character [-1] and dimension 0 over Rational Field' """ - return ("Boundary Modular Symbols space of level %s, weight %s, character %s " + \ + return ("Boundary Modular Symbols space of level %s, weight %s, character %s " + "and dimension %s over %s") % (self.level(), self.weight(), - self.character()._repr_short_(), self.rank(), self.base_ring()) + self.character()._repr_short_(), + self.rank(), + self.base_ring()) def _is_equiv(self, c1, c2): """ diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index 0735a58308e..cc81faad50d 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -291,8 +291,8 @@ def manin_symbol_rep(self): v = self.element() manin_symbols = A.ambient_hecke_module().manin_symbols_basis() F = formal_sum.FormalSums(A.base_ring()) - ms = F([(v[i], manin_symbols[i]) for i in \ - range(v.degree()) if v[i] != 0], check=False, reduce=False) + ms = F([(v[i], manin_symbols[i]) for i in range(v.degree()) + if v[i] != 0], check=False, reduce=False) self.__manin_symbols = ms return self.__manin_symbols diff --git a/src/sage/modular/modsym/manin_symbol_list.py b/src/sage/modular/modsym/manin_symbol_list.py index 187db53bd16..182a35ffe32 100644 --- a/src/sage/modular/modsym/manin_symbol_list.py +++ b/src/sage/modular/modsym/manin_symbol_list.py @@ -16,7 +16,7 @@ - :class:`ManinSymbolList_character` """ -#***************************************************************************** +# **************************************************************************** # Sage: Open Source Mathematical Software # # Copyright (C) 2005 William Stein <wstein@gmail.com> @@ -30,8 +30,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sage.modular.modsym.p1list as p1list import sage.modular.modsym.g1list as g1list @@ -949,8 +949,8 @@ def __init__(self, character, weight): # The list returned from P1List is guaranteed to be sorted. # Thus each list constructed below is also sorted. This is # important since the index function assumes the list is sorted. - L = [(i, u, v) for i in range(weight-2+1) \ - for u, v in self.__P1.list()] + L = [(i, u, v) for i in range(weight - 2 + 1) + for u, v in self.__P1.list()] self.__list = L ManinSymbolList.__init__(self, weight, L) diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index a30f53a8a9e..0a87eece1d7 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -180,11 +180,11 @@ def ModularSymbols_clear_cache(): _cache = {} -def ModularSymbols(group = 1, - weight = 2, - sign = 0, - base_ring = None, - use_cache = True, +def ModularSymbols(group=1, + weight=2, + sign=0, + base_ring=None, + use_cache=True, custom_init=None): r""" Create an ambient space of modular symbols. diff --git a/src/sage/modular/modsym/modular_symbols.py b/src/sage/modular/modsym/modular_symbols.py index c832cb8be05..504af3a4096 100644 --- a/src/sage/modular/modsym/modular_symbols.py +++ b/src/sage/modular/modsym/modular_symbols.py @@ -1,5 +1,5 @@ r""" -Modular symbols {alpha, beta} +Modular symbols `\{\alpha`, `\beta\}` The ModularSymbol class represents a single modular symbol `X^i Y^{k-2-i} \{\alpha, \beta\}`. @@ -14,8 +14,7 @@ sage: loads(dumps(s)) == s True """ - -#***************************************************************************** +# **************************************************************************** # Sage: Open Source Mathematical Software # # Copyright (C) 2005, 2009 William Stein <wstein@gmail.com> @@ -29,8 +28,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sage.modular.cusps as cusps from sage.modular.modsym.apply import apply_to_monomial from sage.modular.modsym.manin_symbol import ManinSymbol @@ -316,11 +315,11 @@ def apply(self, g): space = self.__space i = self.__i k = space.weight() - a,b,c,d = tuple(g) - coeffs = apply_to_monomial(i, k-2, d, -b, -c, a) + a, b, c, d = tuple(g) + coeffs = apply_to_monomial(i, k - 2, d, -b, -c, a) g_alpha = self.__alpha.apply(g) g_beta = self.__beta.apply(g) - return formal_sum.FormalSum([(coeffs[j], ModularSymbol(space, j, g_alpha, g_beta)) \ + return formal_sum.FormalSum([(coeffs[j], ModularSymbol(space, j, g_alpha, g_beta)) for j in reversed(range(k-1)) if coeffs[j] != 0]) def __manin_symbol_rep(self, alpha): diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 880462748cf..f6d442d703b 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -1,8 +1,8 @@ -# distutils: libraries = gmp zn_poly +# distutils: libraries = gmp # distutils: extra_compile_args = -D_XPG6 r""" -Lists of Manin symbols (elements of `\mathbb{P}^1(\ZZ/N\ZZ)`) over `\QQ` +Lists of Manin symbols over `\QQ`, elements of `\mathbb{P}^1(\ZZ/N\ZZ)` """ from cysignals.memory cimport check_allocarray, sig_free diff --git a/src/sage/modular/modsym/p1list_nf.py b/src/sage/modular/modsym/p1list_nf.py index 222caacca80..118d8d5e4fa 100644 --- a/src/sage/modular/modsym/p1list_nf.py +++ b/src/sage/modular/modsym/p1list_nf.py @@ -1,5 +1,5 @@ r""" -Lists of Manin symbols (elements of `\mathbb{P}^1(R/N)`) over number fields +Lists of Manin symbols over number fields, elements of `\mathbb{P}^1(R/N)` Lists of elements of `\mathbb{P}^1(R/N)` where `R` is the ring of integers of a number field `K` and `N` is an integral ideal. @@ -58,7 +58,7 @@ sage: alpha = MSymbol(N, a + 2, 3*a^2) sage: alpha.lift_to_sl2_Ok() - [-3*a^2 + a + 12, 25*a^2 - 50*a + 100, a + 2, a^2 - 3*a + 3] + [-1, 4*a^2 - 13*a + 23, a + 2, 5*a^2 + 3*a - 3] sage: Ok = k.ring_of_integers() sage: M = Matrix(Ok, 2, alpha.lift_to_sl2_Ok()) sage: det(M) @@ -945,11 +945,11 @@ def apply_J_epsilon(self, i, e1, e2=1): sage: N = k.ideal(5, a + 1) sage: P = P1NFList(N) sage: u = k.unit_group().gens_values(); u - [-1, 2*a^2 + 4*a - 1] + [-1, -2*a^2 - 4*a + 1] sage: P.apply_J_epsilon(4, -1) 2 sage: P.apply_J_epsilon(4, u[0], u[1]) - 1 + 5 :: @@ -1199,6 +1199,6 @@ def psi(N): raise ValueError("psi only defined for integral ideals") from sage.misc.misc_c import prod - return prod([(np+1)*np**(e-1) \ - for np,e in [(p.absolute_norm(),e) \ - for p,e in N.factor()]]) + return prod([(np + 1) * np**(e - 1) + for np, e in [(p.absolute_norm(), e) + for p, e in N.factor()]]) diff --git a/src/sage/modular/modsym/relation_matrix_pyx.pyx b/src/sage/modular/modsym/relation_matrix_pyx.pyx index f87c796efa8..f5a18c464eb 100644 --- a/src/sage/modular/modsym/relation_matrix_pyx.pyx +++ b/src/sage/modular/modsym/relation_matrix_pyx.pyx @@ -1,5 +1,5 @@ """ -Optimized Cython code for computing relation matrices in certain cases +Optimized computing of relation matrices in certain cases """ ############################################################################# diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 555a9be6aed..273ba7eed8d 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Space of modular symbols (base class) +Base class of the space of modular symbols All the spaces of modular symbols derive from this class. This class is an abstract base class. diff --git a/src/sage/modular/modsym/subspace.py b/src/sage/modular/modsym/subspace.py index e27467a3816..c1f70471c0f 100644 --- a/src/sage/modular/modsym/subspace.py +++ b/src/sage/modular/modsym/subspace.py @@ -72,9 +72,10 @@ def __init__(self, ambient_hecke_module, submodule, """ self.__ambient_hecke_module = ambient_hecke_module A = ambient_hecke_module - sage.modular.modsym.space.ModularSymbolsSpace.__init__(self, A.group(), A.weight(), \ - A.character(), A.sign(), A.base_ring()) - hecke.HeckeSubmodule.__init__(self, A, submodule, dual_free_module = dual_free_module, check=check) + sage.modular.modsym.space.ModularSymbolsSpace.__init__(self, A.group(), + A.weight(), + A.character(), A.sign(), A.base_ring()) + hecke.HeckeSubmodule.__init__(self, A, submodule, dual_free_module=dual_free_module, check=check) def _repr_(self): """ diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py index dcd3d681eac..6fd43f34560 100644 --- a/src/sage/modular/multiple_zeta.py +++ b/src/sage/modular/multiple_zeta.py @@ -170,8 +170,6 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.richcmp import op_EQ, op_NE from sage.structure.element import parent -from sage.algebras.free_zinbiel_algebra import FreeZinbielAlgebra -from sage.arith.misc import bernoulli from sage.categories.cartesian_product import cartesian_product from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis from sage.categories.rings import Rings @@ -189,12 +187,11 @@ from sage.misc.cachefunc import cached_function, cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod -from sage.modules.free_module_element import vector +from sage.modular.multiple_zeta_F_algebra import F_algebra from sage.modules.free_module import VectorSpace from sage.rings.integer_ring import ZZ -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ -from sage.rings.semirings.non_negative_integer_semiring import NN +from sage.sets.positive_integers import PositiveIntegers # multiplicative generators for weight <= 17 # using the following convention @@ -439,7 +436,7 @@ def __repr__(self): sage: MultizetaValues() Cached multiple zeta values at precision 1024 up to weight 8 """ - return "Cached multiple zeta values at precision %d up to weight %d" % (self.prec, self.max_weight) + return f"Cached multiple zeta values at precision {self.prec} up to weight {self.max_weight}" def reset(self, max_weight=8, prec=1024): r""" @@ -497,8 +494,7 @@ def pari_eval(self, index): if weight <= self.max_weight: index = pari.zetamultconvert(index, 2) return self._data[index - 1] - else: - return pari.zetamult(index, precision=self.prec) + return pari.zetamult(index, precision=self.prec) def __call__(self, index, prec=None, reverse=True): r""" @@ -543,97 +539,12 @@ def __call__(self, index, prec=None, reverse=True): index = pari.zetamultconvert(index, 2) value = self._data[index - 1] return value.sage().n(prec=prec) - else: - return pari.zetamult(index, precision=prec).sage().n(prec=prec) + return pari.zetamult(index, precision=prec).sage().n(prec=prec) Values = MultizetaValues() -def basis_f_odd_iterator(n): - """ - Return an iterator over compositions of ``n`` with parts in ``(3,5,7,...)`` - - INPUT: - - - ``n`` -- an integer - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import basis_f_odd_iterator - sage: [list(basis_f_odd_iterator(i)) for i in range(2,9)] - [[], [(3,)], [], [(5,)], [(3, 3)], [(7,)], [(5, 3), (3, 5)]] - sage: list(basis_f_odd_iterator(14)) - [(11, 3), - (5, 3, 3, 3), - (3, 5, 3, 3), - (3, 3, 5, 3), - (9, 5), - (3, 3, 3, 5), - (7, 7), - (5, 9), - (3, 11)] - """ - if n == 0: - yield tuple() - return - if n == 1: - return - if n % 2: - yield (n,) - for k in range(3, n, 2): - for start in basis_f_odd_iterator(n - k): - yield start + (k, ) - - -def basis_f_iterator(n): - """ - Return an iterator over decompositions of ``n`` using ``2,3,5,7,9,...``. - - The means that each term is made of a power of 2 and a composition - of the remaining integer with parts in ``(3,5,7,...)`` - - INPUT: - - - ``n`` -- an integer - - Each term is returned as a pair (integer, word) where - the integer is the exponent of 2. - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import basis_f_iterator - sage: [list(basis_f_iterator(i)) for i in range(2,9)] - [[(1, word: )], - [(0, word: f3)], - [(2, word: )], - [(0, word: f5), (1, word: f3)], - [(0, word: f3,f3), (3, word: )], - [(0, word: f7), (1, word: f5), (2, word: f3)], - [(0, word: f5,f3), (0, word: f3,f5), (1, word: f3,f3), (4, word: )]] - sage: list(basis_f_iterator(11)) - [(0, word: f11), - (0, word: f5,f3,f3), - (0, word: f3,f5,f3), - (0, word: f3,f3,f5), - (1, word: f9), - (1, word: f3,f3,f3), - (2, word: f7), - (3, word: f5), - (4, word: f3)] - - TESTS:: - - sage: list(basis_f_iterator(0)) - [(0, word: )] - """ - if n and n < 2: - return - for k in range(n // 2 + 1): - for start in basis_f_odd_iterator(n - 2 * k): - yield (k, Word(['f{}'.format(d) for d in start])) - - def extend_multiplicative_basis(B, n): """ Extend a multiplicative basis into a basis. @@ -749,9 +660,8 @@ def __init__(self, R): cat = GradedAlgebrasWithBasis(R).Commutative() if R in Domains(): cat = cat & Domains() - CombinatorialFreeModule.__init__(self, R, Words(NN, infinite=False), - prefix="Z", - category=cat) + W = Words(PositiveIntegers(), infinite=False) + CombinatorialFreeModule.__init__(self, R, W, prefix="Z", category=cat) def _repr_(self): r""" @@ -959,7 +869,7 @@ def phi(self): r""" Return the morphism ``phi``. - This sends multiple zeta values to the algebra :func:`F_ring`, + This sends multiple zeta values to the auxiliary F-algebra, which is a shuffle algebra in odd generators `f_3,f_5,f_7,\dots` over the polynomial ring in one variable `f_2`. @@ -975,12 +885,12 @@ def phi(self): sage: m = Multizeta(2,2) + 2*Multizeta(1,3); m 2*ζ(1,3) + ζ(2,2) sage: M.phi(m) - 1/2*f2^2*Z[] + 1/2*f2^2 sage: Z = Multizeta sage: B5 = [3*Z(1,4) + 2*Z(2,3) + Z(3,2), 3*Z(1,4) + Z(2,3)] sage: [M.phi(b) for b in B5] - [f2*Z[f3] - 1/2*Z[f5], 1/2*Z[f5]] + [-1/2*f5 + f2*f3, 1/2*f5] """ M_it = Multizetas_iterated(self.base_ring()) return M_it.phi * self.iterated @@ -1024,7 +934,7 @@ def _element_constructor_(self, x): if isinstance(x, list): x = tuple(x) return self._monomial(W(x, check=False)) - elif isinstance(parent(x), Multizetas_iterated): + if isinstance(parent(x), Multizetas_iterated): return x.composition() raise TypeError('invalid input for building a multizeta value') @@ -1129,7 +1039,7 @@ def basis_filtration(self, d, reverse=False): raise ValueError('d must be a non-negative integer') if d == 0: return [self([])] - elif d == 1: + if d == 1: return [] Values.reset(max_weight=d) @@ -1151,9 +1061,8 @@ def basis_filtration(self, d, reverse=False): v = self(c).phi_as_vector() if v in U: continue - else: - U = V.subspace(U.basis() + [v]) - basis.append(c) + U = V.subspace(U.basis() + [v]) + basis.append(c) k += 1 return [self(c) for c in basis] @@ -1201,10 +1110,10 @@ def single_valued(self): """ phi_im = self.phi() zin = phi_im.parent() - BR2 = zin.base_ring() - sv = zin.sum_of_terms((w, BR2(cf(0))) - for (a, b), cf in phi_im.coproduct() - for w in shuffle(a, b.reversal(), False)) + phi_no_f2 = phi_im.without_f2() + sv = zin.sum_of_terms(((0, w), cf) + for (a, b), cf in phi_no_f2.coproduct() + for w in shuffle(a[1], b[1].reversal(), False)) return rho_inverse(sv) def simplify(self): @@ -1331,7 +1240,7 @@ def _richcmp_(self, other, op): sage: (0*M()) == 0 True """ - if op != op_EQ and op != op_NE: + if op not in [op_EQ, op_NE]: raise TypeError('invalid comparison for multizetas') return self.iterated()._richcmp_(other.iterated(), op) @@ -1351,13 +1260,13 @@ def phi(self): """ Return the image of ``self`` by the morphism ``phi``. - This sends multiple zeta values to the algebra :func:`F_ring`. + This sends multiple zeta values to the auxiliary F-algebra. EXAMPLES:: sage: M = Multizetas(QQ) sage: M((1,2)).phi() - Z[f3] + f3 TESTS:: @@ -1366,7 +1275,7 @@ def phi(self): sage: M = Multizetas(A) sage: tst = u*M((1,2))+M((3,)) sage: tst.phi() - (u+1)*Z[f3] + (u+1)*f3 """ return self.parent().phi(self) @@ -1397,7 +1306,7 @@ def phi_as_vector(self): """ if not self.is_homogeneous(): raise ValueError('only defined for homogeneous elements') - return f_to_vector(self.parent().phi(self)) + return self.parent().phi(self).homogeneous_to_vector() def _numerical_approx_pari(self): r""" @@ -1463,8 +1372,7 @@ def numerical_approx(self, prec=None, digits=None, algorithm=None): if prec < Values.prec: s = sum(cf * Values(tuple(w)) for w, cf in self.monomial_coefficients().items()) return s.n(prec=prec) - else: - return sum(cf * Values(tuple(w), prec=prec) for w, cf in self.monomial_coefficients().items()) + return sum(cf * Values(tuple(w), prec=prec) for w, cf in self.monomial_coefficients().items()) class Multizetas_iterated(CombinatorialFreeModule): @@ -1514,9 +1422,10 @@ def _repr_(self): sage: from sage.modular.multiple_zeta import Multizetas_iterated sage: M = Multizetas_iterated(QQ); M - Algebra of motivic multiple zeta values as convergent iterated integrals over Rational Field + Algebra of motivic multiple zeta values + as convergent iterated integrals over Rational Field """ - return "Algebra of motivic multiple zeta values as convergent iterated integrals over {}".format(self.base_ring()) + return f"Algebra of motivic multiple zeta values as convergent iterated integrals over {self.base_ring()}" def _repr_term(self, m): """ @@ -1645,12 +1554,11 @@ def split_word(indices): w = Word(seq[indices[i]:indices[i + 1] + 1]) if len(w) == 2: # this factor is one continue - elif len(w) <= 4 or len(w) == 6 or w[0] == w[-1]: + if len(w) <= 4 or len(w) == 6 or w[0] == w[-1]: # vanishing factors return self.zero() - else: - value = M_all(w) - L *= value.regularise().simplify() + value = M_all(w) + L *= value.regularise().simplify() return L resu = self.tensor_square().zero() @@ -1837,7 +1745,7 @@ def phi_extended(self, w): OUTPUT: - an element in the algebra :func:`F_ring` + an element in the auxiliary F-algebra The coefficients are in the base ring. @@ -1846,52 +1754,50 @@ def phi_extended(self, w): sage: from sage.modular.multiple_zeta import Multizetas_iterated sage: M = Multizetas_iterated(QQ) sage: M.phi_extended((1,0)) - -f2*Z[] + -f2 sage: M.phi_extended((1,0,0)) - -Z[f3] + -f3 sage: M.phi_extended((1,1,0)) - Z[f3] + f3 sage: M.phi_extended((1,0,1,0,0)) - 3*f2*Z[f3] - 11/2*Z[f5] + -11/2*f5 + 3*f2*f3 More complicated examples:: sage: from sage.modular.multiple_zeta import composition_to_iterated sage: M.phi_extended(composition_to_iterated((4,3))) - 2/5*f2^2*Z[f3] + 10*f2*Z[f5] - 18*Z[f7] + -18*f7 + 10*f2*f5 + 2/5*f2^2*f3 sage: M.phi_extended(composition_to_iterated((3,4))) - -10*f2*Z[f5] + 17*Z[f7] + 17*f7 - 10*f2*f5 sage: M.phi_extended(composition_to_iterated((4,2))) - 10/21*f2^3*Z[] - 2*Z[f3,f3] + -2*f3f3 + 10/21*f2^3 sage: M.phi_extended(composition_to_iterated((3,5))) - -5*Z[f5,f3] + -5*f5f3 sage: M.phi_extended(composition_to_iterated((3,7))) - -6*Z[f5,f5] - 14*Z[f7,f3] + -6*f5f5 - 14*f7f3 sage: M.phi_extended(composition_to_iterated((3,3,2))) - -793/875*f2^4*Z[] - 4*f2*Z[f3,f3] + 9*Z[f3,f5] - 9/2*Z[f5,f3] + 9*f3f5 - 9/2*f5f3 - 4*f2*f3f3 - 793/875*f2^4 TESTS:: sage: M.phi_extended(tuple()) - Z[] + 1 """ # this is now hardcoded # prec = 1024 - f = F_ring_generator + F = F_algebra(self.base_ring()) + f = F.gen if not w: - F = F_ring(self.base_ring()) - empty = F.indices()([]) - return F.monomial(empty) + return F.one() N = len(w) compo = tuple(iterated_to_composition(w)) - BRf2 = PolynomialRing(self.base_ring(), 'f2') if compo in B_data[N]: # do not forget the sign result_QQ = (-1)**len(compo) * phi_on_multiplicative_basis(compo) - return result_QQ.base_extend(BRf2) + return result_QQ u = compute_u_on_basis(w) rho_inverse_u = rho_inverse(u) xi = self.composition_on_basis(w, QQ) @@ -1899,14 +1805,14 @@ def phi_extended(self, w): c_xi /= Multizeta(N)._numerical_approx_pari() c_xi = c_xi.bestappr().sage() # in QQ result_QQ = u + c_xi * f(N) - return result_QQ.base_extend(BRf2) + return result_QQ @lazy_attribute def phi(self): """ Return the morphism ``phi``. - This sends multiple zeta values to the algebra :func:`F_ring`. + This sends multiple zeta values to the auxiliary F-algebra. EXAMPLES:: @@ -1915,19 +1821,19 @@ def phi(self): sage: m = Multizeta(1,0,1,0) + 2*Multizeta(1,1,0,0); m 2*I(1100) + I(1010) sage: M.phi(m) - 1/2*f2^2*Z[] + 1/2*f2^2 sage: Z = Multizeta sage: B5 = [3*Z(1,4) + 2*Z(2,3) + Z(3,2), 3*Z(1,4) + Z(2,3)] sage: [M.phi(b.iterated()) for b in B5] - [f2*Z[f3] - 1/2*Z[f5], 1/2*Z[f5]] + [-1/2*f5 + f2*f3, 1/2*f5] sage: B6 = [6*Z(1,5) + 3*Z(2,4) + Z(3,3), ....: 6*Z(1,1,4) + 4*Z(1,2,3) + 2*Z(1,3,2) + 2*Z(2,1,3) + Z(2,2,2)] sage: [M.phi(b.iterated()) for b in B6] - [Z[f3,f3], 1/6*f2^3*Z[]] + [f3f3, 1/6*f2^3] """ - cod = F_ring(self.base_ring()) + cod = F_algebra(self.base_ring()) return self.module_morphism(self.phi_extended, codomain=cod) def _element_constructor_(self, x): @@ -1974,8 +1880,7 @@ def _element_constructor_(self, x): x = R(x) if x == 0: return self.element_class(self, {}) - else: - return self.from_base_ring_from_one_basis(x) + return self.from_base_ring_from_one_basis(x) class Element(CombinatorialFreeModule.Element): def simplify(self): @@ -2051,14 +1956,14 @@ def phi(self): """ Return the image of ``self`` by the morphism ``phi``. - This sends multiple zeta values to the algebra :func:`F_ring`. + This sends multiple zeta values to the auxiliary F-algebra. EXAMPLES:: sage: from sage.modular.multiple_zeta import Multizetas_iterated sage: M = Multizetas_iterated(QQ) sage: M((1,1,0)).phi() - Z[f3] + f3 """ return self.parent().phi(self) @@ -2121,7 +2026,7 @@ def _richcmp_(self, other, op): sage: a.iterated() == b.iterated() # not tested, long time 20s True """ - if op != op_EQ and op != op_NE: + if op not in [op_EQ, op_NE]: raise TypeError('invalid comparison for multizetas') return (self - other).is_zero() == (op == op_EQ) @@ -2452,100 +2357,6 @@ def regularise(self): # **************** procedures after F. Brown ************ - -def F_ring(basering, N=18): - r""" - Return the free Zinbiel algebra on many generators `f_3,f_5,\dots` - over the polynomial ring with generator `f_2`. - - For the moment, only with a finite number of variables. - - INPUT: - - - ``N`` -- an integer (default 18), upper bound for indices of generators - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import F_ring - sage: F_ring(QQ) - Free Zinbiel algebra on generators (Z[f3], Z[f5], Z[f7], Z[f9], ...) - over Univariate Polynomial Ring in f2 over Rational Field - """ - ring = PolynomialRing(basering, ['f2']) - return FreeZinbielAlgebra(ring, ['f{}'.format(k) - for k in range(3, N, 2)]) - - -def F_prod(a, b): - """ - Return the associative and commutative product of ``a`` and ``b``. - - INPUT: - - - ``a``, ``b`` -- two elements of the F ring - - OUTPUT: - - an element of the F ring - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import F_ring_generator, F_prod - sage: f2 = F_ring_generator(2) - sage: f3 = F_ring_generator(3) - sage: F_prod(f2,f2) - f2^2*Z[] - sage: F_prod(f2,f3) - f2*Z[f3] - sage: F_prod(f3,f3) - 2*Z[f3,f3] - sage: F_prod(3*f2+5*f3,6*f2+f3) - 18*f2^2*Z[] + 33*f2*Z[f3] + 10*Z[f3,f3] - """ - F = a.parent() - empty = F.indices()([]) - one = F.monomial(empty) - ct_a = a.coefficient(empty) - ct_b = b.coefficient(empty) - rem_a = a - ct_a * one - rem_b = b - ct_b * one - resu = ct_a * ct_b * one + ct_a * rem_b + ct_b * rem_a - return resu + rem_a * rem_b + rem_b * rem_a - - -def F_ring_generator(i): - r""" - Return the generator of the F ring over `\QQ`. - - INPUT: - - - ``i`` -- a nonnegative integer - - If ``i`` is odd, this returns a single generator `f_i` of the free - shuffle algebra. - - Otherwise, it returns an appropriate multiple of a power of `f_2`. - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import F_ring_generator - sage: [F_ring_generator(i) for i in range(2,8)] - [f2*Z[], Z[f3], 2/5*f2^2*Z[], Z[f5], 8/35*f2^3*Z[], Z[f7]] - """ - F = F_ring(QQ) - one = F.monomial(Word([])) - f2 = F.base_ring().gen() - if i == 2: - return f2 * one - # now i odd >= 3 - if i % 2: - return F.monomial(Word(['f{}'.format(i)])) - i = i // 2 - B = bernoulli(2 * i) * (-1)**(i - 1) - B *= ZZ(2)**(3 * i - 1) * ZZ(3)**i / ZZ(2 * i).factorial() - return B * f2**i * one - - def coeff_phi(w): """ Return the coefficient of `f_k` in the image by ``phi``. @@ -2577,8 +2388,8 @@ def coeff_phi(w): M = Multizetas_iterated(QQ) z = M.phi_extended(w) W = z.parent().basis().keys() - w = W(['f{}'.format(k)], check=False) - return z.coefficient(w).lc() # in QQ + w = W((0, [k])) + return z.coefficient(w) # in QQ def phi_on_multiplicative_basis(compo): @@ -2597,16 +2408,14 @@ def phi_on_multiplicative_basis(compo): sage: from sage.modular.multiple_zeta import phi_on_multiplicative_basis sage: phi_on_multiplicative_basis((2,)) - f2*Z[] + f2 sage: phi_on_multiplicative_basis((3,)) - Z[f3] + f3 """ - f = F_ring_generator - F = F_ring(QQ) - one = F.monomial(Word([])) + f = F_algebra(QQ).gen if tuple(compo) == (2,): - return f(2) * one + return f(2) if len(compo) == 1: n, = compo @@ -2633,18 +2442,14 @@ def phi_on_basis(L): sage: from sage.modular.multiple_zeta import phi_on_basis sage: phi_on_basis([(3,),(3,)]) - 2*Z[f3,f3] + 2*f3f3 sage: phi_on_basis([(2,),(2,)]) - f2^2*Z[] + f2^2 sage: phi_on_basis([(2,),(3,),(3,)]) - 2*f2*Z[f3,f3] + 2*f2*f3f3 """ - # beware that the default * is the half-shuffle ! - F = F_ring(QQ) - resu = F.monomial(Word([])) - for compo in L: - resu = F_prod(resu, phi_on_multiplicative_basis(compo)) - return resu + F = F_algebra(QQ) + return F.prod(phi_on_multiplicative_basis(compo) for compo in L) def D_on_compo(k, compo): @@ -2707,11 +2512,11 @@ def compute_u_on_compo(compo): sage: from sage.modular.multiple_zeta import compute_u_on_compo sage: compute_u_on_compo((2,4)) - 2*Z[f3,f3] + 2*f3f3 sage: compute_u_on_compo((2,3,2)) - -11/2*f2*Z[f5] + -11/2*f2*f5 sage: compute_u_on_compo((3,2,3,2)) - 11*f2*Z[f3,f5] - 75/4*Z[f3,f7] - 9*f2*Z[f5,f3] + 81/4*Z[f5,f5] + 75/8*Z[f7,f3] + -75/4*f3f7 + 81/4*f5f5 + 75/8*f7f3 + 11*f2*f3f5 - 9*f2*f5f3 """ it = composition_to_iterated(compo) return (-1)**len(compo) * compute_u_on_basis(it) @@ -2733,103 +2538,29 @@ def compute_u_on_basis(w): sage: from sage.modular.multiple_zeta import compute_u_on_basis sage: compute_u_on_basis((1,0,0,0,1,0)) - -2*Z[f3,f3] + -2*f3f3 sage: compute_u_on_basis((1,1,1,0,0)) - f2*Z[f3] + f2*f3 sage: compute_u_on_basis((1,0,0,1,0,0,0,0)) - -5*Z[f5,f3] + -5*f5f3 sage: compute_u_on_basis((1,0,1,0,0,1,0)) - 11/2*f2*Z[f5] + 11/2*f2*f5 sage: compute_u_on_basis((1,0,0,1,0,1,0,0,1,0)) - 11*f2*Z[f3,f5] - 75/4*Z[f3,f7] - 9*f2*Z[f5,f3] + 81/4*Z[f5,f5] - + 75/8*Z[f7,f3] + -75/4*f3f7 + 81/4*f5f5 + 75/8*f7f3 + 11*f2*f3f5 - 9*f2*f5f3 """ M = Multizetas_iterated(QQ) - F = F_ring(QQ) - f = F_ring_generator + F = F_algebra(QQ) N = len(w) xi_dict = {} for k in range(3, N, 2): xi_dict[k] = F.sum(cf * coeff_phi(ww[0]) * M.phi_extended(tuple(ww[1])) for ww, cf in M.D_on_basis(k, w)) - return F.sum(f(k) * xi_dict[k] for k in range(3, N, 2)) - - -def f_to_vector(elt): - """ - Convert an element of F ring to a vector. - - INPUT: - - an homogeneous element of :func:`F_ring` over some base ring - - OUTPUT: - - a vector with coefficients in the base ring - - .. SEEALSO:: :func:`vector_to_f` - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import F_ring, vector_to_f, f_to_vector - sage: F = F_ring(QQ) - sage: f2 = F.base_ring().gen() - sage: x = f2**4*F.monomial(Word([]))+f2*F.monomial(Word(['f3','f3'])) - sage: f_to_vector(x) - (0, 0, 1, 1) - sage: vector_to_f(_,8) - f2^4*Z[] + f2*Z[f3,f3] - - sage: x = F.monomial(Word(['f11'])); x - Z[f11] - sage: f_to_vector(x) - (1, 0, 0, 0, 0, 0, 0, 0, 0) - """ - F = elt.parent() - BR = F.base_ring().base_ring() - if not elt: - return vector(BR, []) - a, b = next(iter(elt)) - N = sum(int(x[1:]) for x in a) + 2 * b.degree() - W = F.basis().keys() - return vector(BR, [elt.coefficient(W(b, check=False)).lc() - for _, b in basis_f_iterator(N)]) - - -def vector_to_f(vec, N): - """ - Convert back a vector to an element of the F ring. - - INPUT: - - a vector with coefficients in some base ring - - OUTPUT: - - an homogeneous element of :func:`F_ring` over this base ring - - .. SEEALSO:: :func:`f_to_vector` - - EXAMPLES:: - - sage: from sage.modular.multiple_zeta import vector_to_f, f_to_vector - sage: vector_to_f((4,5),6) - 5*f2^3*Z[] + 4*Z[f3,f3] - sage: f_to_vector(_) - (4, 5) - """ - if isinstance(vec, (list, tuple)): - vec = vector(vec) - BR = vec.base_ring() - F = F_ring(BR) - f2 = F.base_ring().gen() - basis_F = (f2**k * F.monomial(b) - for k, b in basis_f_iterator(N)) - return sum(cf * bi for cf, bi in zip(vec, basis_F)) + return F.sum(F.half_product(F.gen(k), xi_dict[k]) + for k in range(3, N, 2)) @cached_function @@ -2859,7 +2590,7 @@ def rho_matrix_inverse(n): resu = [] for b in base: phi_b = phi_on_basis(b) - resu.append(f_to_vector(phi_b)) + resu.append(phi_b.homogeneous_to_vector()) dN = len(resu) return ~matrix(QQ, dN, dN, resu) @@ -2878,13 +2609,15 @@ def rho_inverse(elt): EXAMPLES:: - sage: from sage.modular.multiple_zeta import F_ring_generator, rho_inverse - sage: f = F_ring_generator + sage: from sage.modular.multiple_zeta import rho_inverse + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: f = A.gen sage: rho_inverse(f(3)) ζ(3) sage: rho_inverse(f(9)) ζ(9) - sage: rho_inverse(f(5)*f(3)) + sage: rho_inverse(A("53")) -1/5*ζ(3,5) """ pa = elt.parent() @@ -2893,9 +2626,10 @@ def rho_inverse(elt): if elt == pa.zero(): return M_BR.zero() - a, b = next(iter(elt)) - N = sum(int(x[1:]) for x in a) + 2 * b.degree() + pw, _ = next(iter(elt)) + p, w = pw + N = 2 * p + sum(int(c) for c in w) - v = f_to_vector(elt) + v = elt.homogeneous_to_vector() w = v * rho_matrix_inverse(N) return sum(cf * b for cf, b in zip(w, M_BR.basis_data(BR, N))) diff --git a/src/sage/modular/multiple_zeta_F_algebra.py b/src/sage/modular/multiple_zeta_F_algebra.py new file mode 100644 index 00000000000..39e7dcad4e7 --- /dev/null +++ b/src/sage/modular/multiple_zeta_F_algebra.py @@ -0,0 +1,723 @@ +# -*- coding: utf-8 -*- +r""" +F-algebra for motivic multiple zeta values. + +This is a commutative algebra, defined as the tensor product of the +polynomial algebra over one generator `f_2` by the shuffle algebra in +infinitely many generators indexed by odd integers and denoted by +`f_3`, `f_5`, ... It serves as an auxiliary algebra in the study of +the ring of motivic multiple zeta values. + +Here we provide a basic direct implementation, endowed with the +motivic coproduct. + +AUTHORS: + +- Frédéric Chapoton (2022-09): Initial version + +""" +# **************************************************************************** +# Copyright (C) 2022 Frédéric Chapoton <chapoton-unistra-fr> +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** +from __future__ import annotations +from typing import Iterator + +from sage.arith.misc import bernoulli +from sage.categories.rings import Rings +from sage.categories.bialgebras_with_basis import BialgebrasWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.words.words import Words +from sage.combinat.words.finite_word import FiniteWord_class +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.sets.integer_range import IntegerRange +from sage.rings.integer_ring import ZZ +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.rings.infinity import Infinity +from sage.modules.free_module_element import vector + + +# the indexing set: (integer power of f_2, word in 3, 5, 7,...) +W_Odds = Words(IntegerRange(3, Infinity, 2), infinite=False) + + +def str_to_index(x: str) -> tuple: + """ + Convert a string to an index. + + Every letter "2" contributes to the power of `f_2`. Other letters + define a word in `f_3`, `f_5`, ... + + Usually the letters "2" form a prefix of the input. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import str_to_index + sage: str_to_index("22357") + (2, [3, 5, 7]) + """ + p = x.count("2") + w = [int(i) for i in x if i != '2'] + return (p, w) + + +def basis_f_odd_iterator(n) -> Iterator[tuple]: + """ + Return an iterator over compositions of ``n`` with parts in ``(3,5,7,...)`` + + This is used to index a basis. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import basis_f_odd_iterator + sage: [list(basis_f_odd_iterator(i)) for i in range(2,9)] + [[], [(3,)], [], [(5,)], [(3, 3)], [(7,)], [(5, 3), (3, 5)]] + sage: list(basis_f_odd_iterator(14)) + [(11, 3), + (5, 3, 3, 3), + (3, 5, 3, 3), + (3, 3, 5, 3), + (9, 5), + (3, 3, 3, 5), + (7, 7), + (5, 9), + (3, 11)] + """ + if n == 0: + yield tuple() + return + if n == 1: + return + if n % 2: + yield (n,) + for k in range(3, n, 2): + for start in basis_f_odd_iterator(n - k): + yield start + (k, ) + + +def basis_f_iterator(n) -> Iterator[tuple]: + """ + Return an iterator over decompositions of ``n`` using ``2,3,5,7,9,...``. + + The means that each term is made of a power of 2 and a composition + of the remaining integer with parts in ``(3,5,7,...)`` + + This set is indexing a basis of the homogeneous component of weight ``n``. + + INPUT: + + - ``n`` -- an integer + + Each term is returned as a pair (integer, word) where + the integer is the exponent of 2. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import basis_f_iterator + sage: [list(basis_f_iterator(i)) for i in range(2,9)] + [[(1, word: )], + [(0, word: 3)], + [(2, word: )], + [(0, word: 5), (1, word: 3)], + [(0, word: 33), (3, word: )], + [(0, word: 7), (1, word: 5), (2, word: 3)], + [(0, word: 53), (0, word: 35), (1, word: 33), (4, word: )]] + sage: list(basis_f_iterator(11)) + [(0, word: 11), + (0, word: 533), + (0, word: 353), + (0, word: 335), + (1, word: 9), + (1, word: 333), + (2, word: 7), + (3, word: 5), + (4, word: 3)] + + TESTS:: + + sage: list(basis_f_iterator(0)) + [(0, word: )] + """ + if n and n < 2: + return + for k in range(n // 2 + 1): + for start in basis_f_odd_iterator(n - 2 * k): + yield (k, W_Odds(start, check=False)) + + +def morphism_constructor(data: dict): + """ + Build a morphism from the F-algebra to some codomain. + + INPUT: + + a dictionary containing the images of `f_2`, `f_3`, `f_5`, `f_7`, ... + + OUTPUT: + + the unique morphism defined by the dictionary ``data`` + + The codomain must be a zinbiel algebra, namely have both a + commutative associative product ``*`` and a zinbiel product + available as ``half_product``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra, morphism_constructor + sage: F = F_algebra(QQ) + sage: Z = Multizeta + sage: D = {2: Z(2), 3: Z(3)} + sage: rho = morphism_constructor(D) + sage: rho(F("2")) + ζ(2) + sage: rho(F("3")) + ζ(3) + sage: rho(F("33")) + 6*ζ(1,5) + 3*ζ(2,4) + ζ(3,3) + sage: rho(F("23")) + 6*ζ(1,4) + 3*ζ(2,3) + ζ(3,2) + """ + im_f2 = data[2] + codomain = im_f2.parent() + domain = F_algebra(codomain.base_ring()) + + def morphism_on_basis(pw): + p, w = pw + if not w: + return im_f2**p + v = im_f2**p * data[w[-1]] + for letter in w[-2::-1]: + v = codomain.half_product(data[letter], v) + return v + + morphism = domain._module_morphism(morphism_on_basis, codomain=codomain) + + return morphism + + +class F_algebra(CombinatorialFreeModule): + r""" + Auxiliary algebra for the study of motivic multiple zeta values. + + INPUT: + + - ``R`` -- ring + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ); F + F-ring over Rational Field + sage: F.base_ring() + Rational Field + sage: F.is_commutative() + True + sage: TestSuite(F).run() + + sage: f2 = F("2") + sage: f3 = F("3") + sage: f5 = F("5") + + sage: s = f2*f3+f5; s + f5 + f2*f3 + """ + def __init__(self, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ); F + F-ring over Rational Field + + TESTS:: + + sage: F_algebra(24) + Traceback (most recent call last): + ... + TypeError: argument R must be a ring + """ + if R not in Rings(): + raise TypeError("argument R must be a ring") + Indices = NonNegativeIntegers().cartesian_product(W_Odds) + cat = BialgebrasWithBasis(R).Commutative().Graded() + CombinatorialFreeModule.__init__(self, R, Indices, + latex_prefix="", prefix='f', + category=cat) + + def _repr_term(self, pw) -> str: + """ + Return the custom representation of terms. + + Each monomial is written as a power of `f_2` times a word + in `f_3`, `f_5`, ... + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: f2 = F.gen(2) + sage: f3 = F.gen(3) + sage: f5 = F.gen(5) + sage: f2*f3+f5+f2**2 + f5 + f2*f3 + f2^2 + """ + p, w = pw + if not p: + if not w: + return "1" + resu = "" + elif p == 1: + resu = "f2" + else: + resu = f"f2^{p}" + if p and w: + resu += "*" + return resu + "".join(f"f{i}" for i in w) + + def _repr_(self) -> str: + r""" + Text representation of this algebra. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(ZZ) + sage: F # indirect doctest + F-ring over Integer Ring + """ + return f"F-ring over {self.base_ring()}" + + @cached_method + def one_basis(self): + r""" + Return the pair (0, empty word), which index of `1` of this algebra. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: A.one_basis() + (0, word: ) + """ + return self.basis().keys()((0, [])) + + def product_on_basis(self, pw1, pw2): + r""" + Return the product of basis elements ``pw1`` and ``pw2``. + + INPUT: + + - ``pw1``, ``pw2`` -- basis elements + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: W = A.basis().keys() + sage: A.product(A("23"), A("25")) + f2^2*f3f5 + f2^2*f5f3 + """ + p1, w1 = pw1 + p2, w2 = pw2 + p = p1 + p2 + return self.sum_of_monomials((p, u) for u in w1.shuffle(w2)) + + def half_product_on_basis(self, pw1, pw2): + r""" + Return the half product of basis elements ``pw1`` and ``pw2``. + + This is an extension of the zinbiel product of the shuffle algebra. + + INPUT: + + - ``pw1``, ``pw2`` -- Basis elements + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: W = A.basis().keys() + sage: t = A.half_product(A("23"), A("25")); t + f2^2*f3f5 + + TESTS:: + + sage: [list(pw[1]) for pw, cf in t] + [[3, 5]] + """ + p1, w1 = pw1 + p2, w2 = pw2 + p = p1 + p2 + if not w1: + return self.basis()[(p, w2)] + letter = w1[:1] + return self.sum_of_monomials((p, letter + u) + for u in w1[1:].shuffle(w2)) + + @lazy_attribute + def half_product(self): + r""" + Return the `<` product. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: W = A.basis().keys() + sage: A.half_product(A("235"), A("227")) + f2^3*f3f5f7 + f2^3*f3f7f5 + """ + half = self.half_product_on_basis + return self._module_morphism(self._module_morphism(half, position=0, + codomain=self), + position=1) + + def gen(self, i): + r""" + Return the generator of the F ring over `\QQ`. + + INPUT: + + - ``i`` -- a nonnegative integer (at least 2) + + If ``i`` is odd, this returns a single generator `f_i` of the free + shuffle algebra. + + Otherwise, it returns an appropriate multiple of a power of `f_2`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: [A.gen(i) for i in range(2,8)] + [f2, f3, 2/5*f2^2, f5, 8/35*f2^3, f7] + """ + f2 = self.monomial(self._indices((1, []))) + if i == 2: + return f2 + # now i odd >= 3 + if i % 2: + return self.monomial(self._indices((0, [i]))) + # now powers of f2 + i = i // 2 + B = bernoulli(2 * i) * (-1)**(i - 1) + B *= ZZ(2)**(3 * i - 1) * ZZ(3)**i / ZZ(2 * i).factorial() + return B * f2**i + + def an_element(self): + """ + Return a typical element. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(ZZ) + sage: F.an_element() + 3*f2*f3f5 + f2*f5f3 + """ + return self("253") + 3 * self("235") + + def some_elements(self): + """ + Return some typical elements. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(ZZ) + sage: F.some_elements() + [0, 1, f2, f3 + f5] + """ + return [self.zero(), self.one(), self.gen(2), + self.gen(3) + self.gen(5)] + + def coproduct_on_basis(self, pw): + r""" + Return the coproduct of the basis element indexed by the pair ``pw``. + + The coproduct is given by deconcatenation on the shuffle part, + and extended by the value + + .. MATH:: + + \Delta(f_2) = 1 \otimes f_2. + + INPUT: + + - ``pw`` -- an index + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: W = F.basis().keys() + sage: F.coproduct_on_basis(W((1,[]))) + 1 # f2 + sage: F.coproduct_on_basis(W((0,[3]))) + 1 # f3 + f3 # 1 + sage: F.coproduct_on_basis(W((1,[3]))) + 1 # f2*f3 + f3 # f2 + sage: F.coproduct_on_basis(W((0,[3,5]))) + 1 # f3f5 + f3 # f5 + f3f5 # 1 + sage: F.coproduct_on_basis(W((0,[]))) + 1 # 1 + + TESTS:: + + sage: F = F_algebra(QQ) + sage: S = F.an_element(); S + 3*f2*f3f5 + f2*f5f3 + sage: F.coproduct(S) + 3*1 # f2*f3f5 + 1 # f2*f5f3 + 3*f3 # f2*f5 + 3*f3f5 # f2 + + f5 # f2*f3 + f5f3 # f2 + """ + p, w = pw + TS = self.tensor_square() + return TS.sum_of_monomials(((0, w[:i]), (p, w[i:])) + for i in range(len(w) + 1)) + + def degree_on_basis(self, pw): + """ + Return the degree of the element ``w``. + + This is the sum of the power of `f_2` and the indices in the word. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: A = F_algebra(QQ) + sage: [A.degree_on_basis(x.leading_support()) for x in A.some_elements() if x != 0] + [0, 1, 5] + """ + p, w = pw + return ZZ(p + sum(w)) + + def homogeneous_from_vector(self, vec, N): + """ + Convert back a vector to an element of the F-algebra. + + INPUT: + + - ``vec`` -- a vector with coefficients in some base ring + + - ``N`` -- integer, the homogeneous weight + + OUTPUT: + + an homogeneous element of :func:`F_ring` over this base ring + + .. SEEALSO:: :meth:`F_algebra.homogeneous_to_vector` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: F.homogeneous_from_vector((4,5),6) + 4*f3f3 + 5*f2^3 + sage: _.homogeneous_to_vector() + (4, 5) + """ + if isinstance(vec, (list, tuple)): + vec = vector(vec) + return self.sum(cf * self.monomial(bi) + for cf, bi in zip(vec, basis_f_iterator(N))) + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: R = F_algebra(QQ) + sage: R("3") + f3 + sage: R("2") + f2 + sage: R("2235") + f2^2*f3f5 + """ + if isinstance(x, (str, FiniteWord_class)): + return self.monomial(self._indices(str_to_index(x))) + + P = x.parent() + if isinstance(P, F_algebra): + if P is self: + return x + if P is not self.base_ring(): + return self.element_class(self, x.monomial_coefficients()) + + R = self.base_ring() + # coercion via base ring + x = R(x) + if x == 0: + return self.element_class(self, {}) + return self.from_base_ring_from_one_basis(x) + + def _coerce_map_from_(self, R): + r""" + Return ``True`` if there is a coercion from ``R`` into ``self`` + and ``False`` otherwise. + + The things that coerce into ``self`` are + + - an ``F_algebra`` over a base with a coercion + map into ``self.base_ring()``. + + - Anything with a coercion into ``self.base_ring()``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(GF(7)); F + F-ring over Finite Field of size 7 + + Elements of the algebra itself canonically coerce in:: + + sage: F.coerce(F("2")*F("3")) # indirect doctest + f2*f3 + + Elements of the integers coerce in, since there is a coerce map + from `\ZZ` to GF(7):: + + sage: F.coerce(1) # indirect doctest + 1 + + There is no coerce map from `\QQ` to `\GF{7}`:: + + sage: F.coerce(2/3) # indirect doctest + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Rational Field + to F-ring over Finite Field of size 7 + + Elements of the base ring coerce in:: + + sage: F.coerce(GF(7)(5)) + 5 + + The algebra over `\ZZ` coerces in, since + `\ZZ` coerces to `\GF{7}`:: + + sage: G = F_algebra(ZZ) + sage: Gx,Gy = G.gen(2), G.gen(3) + sage: z = F.coerce(Gx**2 * Gy);z + f2^2*f3 + sage: z.parent() is F + True + + However, `\GF{7}` does not coerce to `\ZZ`, so the + algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + + sage: G.coerce(F("2")) + Traceback (most recent call last): + ... + TypeError: no canonical coercion from F-ring over Finite Field + of size 7 to F-ring over Integer Ring + + TESTS:: + + sage: F = F_algebra(ZZ) + sage: G = F_algebra(QQ) + + sage: F._coerce_map_from_(G) + False + sage: G._coerce_map_from_(F) + True + + sage: F._coerce_map_from_(QQ) + False + sage: G._coerce_map_from_(QQ) + True + + sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) + False + """ + if isinstance(R, F_algebra): + return self.base_ring().has_coerce_map_from(R.base_ring()) + + return self.base_ring().has_coerce_map_from(R) + + class Element(CombinatorialFreeModule.Element): + def coefficient(self, w): + """ + Return the coefficient of the given index. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: S = F.an_element(); S + 3*f2*f3f5 + f2*f5f3 + sage: S.coefficient("235") + 3 + sage: S.coefficient((1,[5,3])) + 1 + """ + if isinstance(w, str): + w = str_to_index(w) + w = self.parent()._indices(w) + return super().coefficient(w) + + def homogeneous_to_vector(self): + """ + Convert an homogeneous element to a vector. + + This is using a fixed enumeration of the basis. + + OUTPUT: + + a vector with coefficients in the base ring + + .. SEEALSO:: :meth:`F_algebra.homogeneous_from_vector` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: f2 = F("2") + sage: x = f2**4 + 34 * F("233") + sage: x.homogeneous_to_vector() + (0, 0, 34, 1) + sage: x.coefficients() + [34, 1] + + TESTS:: + + sage: x = F.monomial(F._indices((0,[11]))); x + f11 + sage: x.homogeneous_to_vector() + (1, 0, 0, 0, 0, 0, 0, 0, 0) + """ + F = self.parent() + BR = F.base_ring() + if not self: + return vector(BR, []) + a, b = next(iter(self))[0] + N = 2 * a + sum(int(x) for x in b) + return vector(BR, [self.coefficient(b) + for b in basis_f_iterator(N)]) + + def without_f2(self): + """ + Remove all terms containing a power of `f_2`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta_F_algebra import F_algebra + sage: F = F_algebra(QQ) + sage: t = 4*F("35")+F("27") + sage: t.without_f2() + 4*f3f5 + """ + F = self.parent() + return F._from_dict({(0, w): cf for (p, w), cf in self if not p}) diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 0e30f40d23b..972ef7eed45 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -186,7 +186,6 @@ from sage.modular.modform.j_invariant import j_invariant_qexp from sage.modules.free_module_element import vector from sage.modules.module import Module -from sage.plot.plot import plot from sage.rings.big_oh import O from sage.rings.infinity import Infinity from sage.rings.integer_ring import ZZ @@ -202,11 +201,12 @@ __ocmfdict = {} + #################### # Factory function # #################### -def OverconvergentModularForms(prime, weight, radius, base_ring=QQ, prec = 20, char = None): +def OverconvergentModularForms(prime, weight, radius, base_ring=QQ, prec=20, char=None): r""" Create a space of overconvergent `p`-adic modular forms of level `\Gamma_0(p)`, over the given base ring. The base ring need not be a @@ -316,7 +316,7 @@ def __init__(self, prime, weight, radius, base_ring, prec, char): if isinstance(weight, WeightCharacter): self._wtchar = weight else: - self._wtchar = WeightSpace(prime, base_ring = char.base_ring())(weight, char, algebraic=True) + self._wtchar = WeightSpace(prime, base_ring=char.base_ring())(weight, char, algebraic=True) if not self._wtchar.is_even(): raise ValueError("Weight-character must be even") @@ -951,7 +951,7 @@ def _convert_to_basis(self, qexp): x = x - self._basis_cache[i] * answer[i] return answer + O(g**n) - def hecke_matrix(self, m, n, use_recurrence = False, exact_arith = False): + def hecke_matrix(self, m, n, use_recurrence=False, exact_arith=False): r""" Calculate the matrix of the `T_m` operator in the basis of this space, truncated to an `n \times n` matrix. Conventions are that operators act @@ -1052,12 +1052,14 @@ def slopes(self, n, use_recurrence=False): print("slopes are only defined for base field QQ or a p-adic field") return [-i for i in slopelist] - def eigenfunctions(self, n, F = None, exact_arith=True): + def eigenfunctions(self, n, F=None, exact_arith=True): """ - Calculate approximations to eigenfunctions of self. These are the - eigenfunctions of self.hecke_matrix(p, n), which are approximations to - the true eigenfunctions. Returns a list of - OverconvergentModularFormElement objects, in increasing order of slope. + Calculate approximations to eigenfunctions of self. + + These are the eigenfunctions of self.hecke_matrix(p, n), which + are approximations to the true eigenfunctions. Returns a list + of OverconvergentModularFormElement objects, in increasing + order of slope. INPUT: @@ -1201,7 +1203,7 @@ def recurrence_matrix(self, use_smithline=True): return self._cached_recurrence_matrix MM = OverconvergentModularForms(self.prime(), 0, 0, base_ring=QQ) - m = MM._discover_recurrence_matrix(use_smithline = True).base_extend(self.base_ring()) + m = MM._discover_recurrence_matrix(use_smithline=True).base_extend(self.base_ring()) r = diagonal_matrix([self._const**i for i in range(self.prime())]) self._cached_recurrence_matrix = (r**(-1)) * m * r @@ -1339,7 +1341,7 @@ def _add_(self, other): sage: f + f # indirect doctest 2-adic overconvergent modular form of weight-character 12 with q-expansion 2 - 131040/1414477*q ... """ - return OverconvergentModularFormElement(self.parent(), gexp = self.gexp() + other.gexp()) + return OverconvergentModularFormElement(self.parent(), gexp=self.gexp() + other.gexp()) def _lmul_(self, x): r""" @@ -1353,7 +1355,7 @@ def _lmul_(self, x): 2-adic overconvergent modular form of weight-character 12 with q-expansion 2 - 131040/1414477*q ... """ - return OverconvergentModularFormElement(self.parent(), gexp = x * self.gexp()) + return OverconvergentModularFormElement(self.parent(), gexp=x * self.gexp()) def _rmul_(self, x): r""" @@ -1367,7 +1369,7 @@ def _rmul_(self, x): 2-adic overconvergent modular form of weight-character 12 with q-expansion 3 - 196560/1414477*q ... """ - return OverconvergentModularFormElement(self.parent(), gexp = x * self.gexp()) + return OverconvergentModularFormElement(self.parent(), gexp=x * self.gexp()) def prec(self): r""" @@ -1636,7 +1638,7 @@ def governing_term(self, r): return i raise RuntimeError("Can't get here") - def valuation_plot(self, rmax = None): + def valuation_plot(self, rmax=None): r""" Draw a graph depicting the growth of the norm of this overconvergent modular form as it approaches the boundary of the overconvergent @@ -1649,9 +1651,11 @@ def valuation_plot(self, rmax = None): sage: f.valuation_plot() Graphics object consisting of 1 graphics primitive """ + from sage.plot.plot import plot + if rmax is None: - rmax = ZZ(self.prime())/ZZ(1 + self.prime()) - return plot(self.r_ord, (0, rmax) ) + rmax = ZZ(self.prime()) / ZZ(1 + self.prime()) + return plot(self.r_ord, (0, rmax)) def weight(self): r""" diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index c52b9db5f0a..3261d09d2c2 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# distutils: libraries = gmp zn_poly +# distutils: libraries = gmp # distutils: extra_compile_args = -D_XPG6 """ `p`-adic distributions spaces diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index 7eea04c7291..1bb73ceacc9 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Spaces of Distributions for Pollack-Stevens modular symbols +Spaces of distributions for Pollack-Stevens modular symbols The Pollack-Stevens version of modular symbols take values on a `\Sigma_0(N)`-module which can be either a symmetric power of the standard @@ -567,7 +567,7 @@ def random_element(self, M=None, **args): M = self.precision_cap() R = self.base_ring() return self((R ** M).random_element(**args)) -## return self(self.approx_module(M).random_element()) + # return self(self.approx_module(M).random_element()) def clear_cache(self): """ diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index b165511c53a..3e25c950b30 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Manin Relations for overconvergent modular symbols +Manin relations for overconvergent modular symbols Code to create the Manin Relations class, which solves the "Manin relations". That is, a description of `Div^0(P^1(\QQ))` as a `\ZZ[\Gamma_0(N)]`-module in @@ -599,29 +599,29 @@ def __init__(self, N): self._N = N SN = Sigma0(N) - ## Creates and stores the Sage representation of P^1(Z/NZ) + # Creates and stores the Sage representation of P^1(Z/NZ) P = P1List(N) self._P = P IdN = SN([1, 0, 0, 1]) - ## Creates a fundamental domain for Gamma_0(N) whose boundary - ## is a union of unimodular paths (except in the case of - ## 3-torsion). We will call the intersection of this domain - ## with the real axis the collection of cusps (even if some - ## are Gamma_0(N) equivalent to one another). + # Creates a fundamental domain for Gamma_0(N) whose boundary + # is a union of unimodular paths (except in the case of + # 3-torsion). We will call the intersection of this domain + # with the real axis the collection of cusps (even if some + # are Gamma_0(N) equivalent to one another). cusps = self.form_list_of_cusps() - ## Takes the boundary of this fundamental domain and finds - ## SL_2(Z) matrices whose associated unimodular path gives - ## this boundary. These matrices form the beginning of our - ## collection of coset reps for Gamma_0(N) / SL_2(Z). + # Takes the boundary of this fundamental domain and finds + # SL_2(Z) matrices whose associated unimodular path gives + # this boundary. These matrices form the beginning of our + # collection of coset reps for Gamma_0(N) / SL_2(Z). coset_reps = self.fd_boundary(cusps) - ## Takes the bottom row of each of our current coset reps, - ## thinking of them as distinct elements of P^1(Z/NZ) + # Takes the bottom row of each of our current coset reps, + # thinking of them as distinct elements of P^1(Z/NZ) p1s = [(coset_reps[j])[1] for j in range(len(coset_reps))] - ## Initializes relevant Manin data + # Initializes relevant Manin data gens_index = [] twotor_index = [] twotorrels = [] @@ -630,219 +630,219 @@ def __init__(self, N): rels = [0] * len(coset_reps) gammas = {} - ## the list rels (above) will give Z[Gamma_0(N)] relations between - ## the associated divisor of each coset representatives in terms - ## of our chosen set of generators. - ## entries of rels will be lists of elements of the form (c,A,r) - ## with c a constant, A a Gamma_0(N) matrix, and r the index of a - ## generator. The meaning is that the divisor associated to the - ## j-th coset rep will equal the sum of: + # the list rels (above) will give Z[Gamma_0(N)] relations between + # the associated divisor of each coset representatives in terms + # of our chosen set of generators. + # entries of rels will be lists of elements of the form (c,A,r) + # with c a constant, A a Gamma_0(N) matrix, and r the index of a + # generator. The meaning is that the divisor associated to the + # j-th coset rep will equal the sum of: ## - ## c * A^(-1) * (divisor associated to r-th coset rep) + # c * A^(-1) * (divisor associated to r-th coset rep) ## - ## as one varies over all (c,A,r) in rels[j]. - ## (Here r must be in self.generator_indices().) + # as one varies over all (c,A,r) in rels[j]. + # (Here r must be in self.generator_indices().) ## - ## This will be used for modular symbols as then the value of a - ## modular symbol phi on the (associated divisor) of the j-th - ## element of coset_reps will be the sum of c * phi (r-th generator) | A - ## as one varies over the tuples in rels[j] + # This will be used for modular symbols as then the value of a + # modular symbol phi on the (associated divisor) of the j-th + # element of coset_reps will be the sum of c * phi (r-th generator) | A + # as one varies over the tuples in rels[j] boundary_checked = [False] * len(coset_reps) - ## The list boundary_checked keeps track of which boundary pieces of the - ## fundamental domain have been already used as we are picking - ## our generators + # The list boundary_checked keeps track of which boundary pieces of the + # fundamental domain have been already used as we are picking + # our generators - ## The following loop will choose our generators by picking one edge - ## out of each pair of edges that are glued to each other and picking - ## each edge glued to itself (arising from two-torsion) - ## ------------------------------------------------------------------ + # The following loop will choose our generators by picking one edge + # out of each pair of edges that are glued to each other and picking + # each edge glued to itself (arising from two-torsion) + # ------------------------------------------------------------------ for r in range(len(coset_reps)): if not boundary_checked[r]: - ## We now check if this boundary edge is glued to itself by - ## Gamma_0(N) + # We now check if this boundary edge is glued to itself by + # Gamma_0(N) if P.normalize(p1s[r][0], p1s[r][1]) == P.normalize(-p1s[r][1], p1s[r][0]): - ## This edge is glued to itself and so coset_reps[r] - ## needs to be added to our generator list. + # This edge is glued to itself and so coset_reps[r] + # needs to be added to our generator list. - ## this relation expresses the fact that - ## coset_reps[r] is one of our basic generators + # this relation expresses the fact that + # coset_reps[r] is one of our basic generators rels[r] = [(1, IdN, r)] - ## the index r is adding to our list - ## of indexes of generators + # the index r is adding to our list + # of indexes of generators gens_index.append(r) - ## the index r is adding to our list of indexes of - ## generators which satisfy a 2-torsion relation + # the index r is adding to our list of indexes of + # generators which satisfy a 2-torsion relation twotor_index.append(r) # we use the adjugate instead of the inverse for speed gam = SN(coset_reps[r] * sig * coset_reps[r].adjugate()) - ## gam is 2-torsion matrix and in Gamma_0(N). - ## if D is the divisor associated to coset_reps[r] - ## then gam * D = - D and so (1+gam)D=0. + # gam is 2-torsion matrix and in Gamma_0(N). + # if D is the divisor associated to coset_reps[r] + # then gam * D = - D and so (1+gam)D=0. - ## This gives a restriction to the possible values of - ## modular symbols on D + # This gives a restriction to the possible values of + # modular symbols on D - ## The 2-torsion matrix gam is recorded in our list of - ## 2-torsion relations. + # The 2-torsion matrix gam is recorded in our list of + # 2-torsion relations. twotorrels.append(gam) - ## We have now finished with this edge. + # We have now finished with this edge. boundary_checked[r] = True else: c = coset_reps[r][t10] d = coset_reps[r][t11] - ## In the following case the ideal triangle below - ## the unimodular path described by coset_reps[r] - ## contains a point fixed by a 3-torsion element. + # In the following case the ideal triangle below + # the unimodular path described by coset_reps[r] + # contains a point fixed by a 3-torsion element. if (c ** 2 + d ** 2 + c * d) % N == 0: - ## the index r is adding to our list of indexes - ## of generators + # the index r is adding to our list of indexes + # of generators gens_index.append(r) - ## this relation expresses the fact that coset_reps[r] - ## is one of our basic generators + # this relation expresses the fact that coset_reps[r] + # is one of our basic generators rels[r] = [(1, IdN, r)] - ## the index r is adding to our list of indexes of - ## generators which satisfy a 3-torsion relation + # the index r is adding to our list of indexes of + # generators which satisfy a 3-torsion relation threetor_index.append(r) # Use the adjugate instead of the inverse for speed. gam = SN(coset_reps[r] * tau * coset_reps[r].adjugate()) - ## gam is 3-torsion matrix and in Gamma_0(N). - ## if D is the divisor associated to coset_reps[r] - ## then (1+gam+gam^2)D=0. - ## This gives a restriction to the possible values of - ## modular symbols on D - - ## The 3-torsion matrix gam is recorded in our list of - ## 3-torsion relations. + # gam is 3-torsion matrix and in Gamma_0(N). + # if D is the divisor associated to coset_reps[r] + # then (1+gam+gam^2)D=0. + # This gives a restriction to the possible values of + # modular symbols on D + + # The 3-torsion matrix gam is recorded in our list of + # 3-torsion relations. threetorrels.append(gam) - ## The reverse of the unimodular path associated to - ## coset_reps[r] is not Gamma_0(N) equivalent to it, so - ## we need to include it in our list of coset - ## representatives and record the relevant relations. + # The reverse of the unimodular path associated to + # coset_reps[r] is not Gamma_0(N) equivalent to it, so + # we need to include it in our list of coset + # representatives and record the relevant relations. a = coset_reps[r][t00] b = coset_reps[r][t01] A = M2Z([-b, a, -d, c]) coset_reps.append(A) - ## A (representing the reversed edge) is included in - ## our list of coset reps + # A (representing the reversed edge) is included in + # our list of coset reps rels.append([(-1, IdN, r)]) - ## This relation means that phi on the reversed edge - ## equals -phi on original edge + # This relation means that phi on the reversed edge + # equals -phi on original edge boundary_checked[r] = True - ## We have now finished with this edge. + # We have now finished with this edge. else: - ## This is the generic case where neither 2 or - ## 3-torsion intervenes. - ## The below loop searches through the remaining edges - ## and finds which one is equivalent to the reverse of - ## coset_reps[r] - ## --------------------------------------------------- + # This is the generic case where neither 2 or + # 3-torsion intervenes. + # The below loop searches through the remaining edges + # and finds which one is equivalent to the reverse of + # coset_reps[r] + # --------------------------------------------------- for s in range(r + 1, len(coset_reps)): if boundary_checked[s]: continue if P.normalize(p1s[s][0], p1s[s][1]) == P.normalize(-p1s[r][1], p1s[r][0]): - ## the reverse of coset_reps[r] is - ## Gamma_0(N)-equivalent to coset_reps[s] - ## coset_reps[r] will now be made a generator - ## and we need to express phi(coset_reps[s]) - ## in terms of phi(coset_reps[r]) + # the reverse of coset_reps[r] is + # Gamma_0(N)-equivalent to coset_reps[s] + # coset_reps[r] will now be made a generator + # and we need to express phi(coset_reps[s]) + # in terms of phi(coset_reps[r]) gens_index.append(r) - ## the index r is adding to our list of - ## indexes of generators + # the index r is adding to our list of + # indexes of generators rels[r] = [(1, IdN, r)] - ## this relation expresses the fact that - ## coset_reps[r] is one of our basic generators + # this relation expresses the fact that + # coset_reps[r] is one of our basic generators A = coset_reps[s] * sig - ## A corresponds to reversing the orientation - ## of the edge corr. to coset_reps[r] + # A corresponds to reversing the orientation + # of the edge corr. to coset_reps[r] # Use adjugate instead of inverse for speed gam = SN(coset_reps[r] * A.adjugate()) - ## gam is in Gamma_0(N) (by assumption of - ## ending up here in this if statement) + # gam is in Gamma_0(N) (by assumption of + # ending up here in this if statement) rels[s] = [(-1, gam, r)] - ## this relation means that phi evaluated on - ## coset_reps[s] equals -phi(coset_reps[r])|gam - ## To see this, let D_r be the divisor - ## associated to coset_reps[r] and D_s to - ## coset_reps[s]. Then gam D_s = -D_r and so - ## phi(gam D_s) = - phi(D_r) and thus - ## phi(D_s) = -phi(D_r)|gam - ## since gam is in Gamma_0(N) + # this relation means that phi evaluated on + # coset_reps[s] equals -phi(coset_reps[r])|gam + # To see this, let D_r be the divisor + # associated to coset_reps[r] and D_s to + # coset_reps[s]. Then gam D_s = -D_r and so + # phi(gam D_s) = - phi(D_r) and thus + # phi(D_s) = -phi(D_r)|gam + # since gam is in Gamma_0(N) gammas[coset_reps[r]] = gam - ## this is a dictionary whose keys are the - ## non-torsion generators and whose values - ## are the corresponding gamma_i. It is - ## eventually stored as self.gammas. + # this is a dictionary whose keys are the + # non-torsion generators and whose values + # are the corresponding gamma_i. It is + # eventually stored as self.gammas. boundary_checked[r] = True boundary_checked[s] = True break - ## We now need to complete our list of coset representatives by - ## finding all unimodular paths in the interior of the fundamental - ## domain, as well as express these paths in terms of our chosen set - ## of generators. - ## ------------------------------------------------------------------- + # We now need to complete our list of coset representatives by + # finding all unimodular paths in the interior of the fundamental + # domain, as well as express these paths in terms of our chosen set + # of generators. + # ------------------------------------------------------------------- for r in range(len(cusps) - 2): - ## r is the index of the cusp on the left of the path. We only run - ## thru to the number of cusps - 2 since you cannot start an - ## interior path on either of the last two cusps + # r is the index of the cusp on the left of the path. We only run + # thru to the number of cusps - 2 since you cannot start an + # interior path on either of the last two cusps for s in range(r + 2, len(cusps)): - ## s is in the index of the cusp on the right of the path + # s is in the index of the cusp on the right of the path cusp1 = cusps[r] cusp2 = cusps[s] if self.is_unimodular_path(cusp1, cusp2): A, B = self.unimod_to_matrices(cusp1, cusp2) - ## A and B are the matrices whose associated paths - ## connect cusp1 to cusp2 and cusp2 to cusp1 (respectively) + # A and B are the matrices whose associated paths + # connect cusp1 to cusp2 and cusp2 to cusp1 (respectively) coset_reps.extend([A, B]) - ## A and B are added to our coset reps + # A and B are added to our coset reps vA = [] vB = [] - ## This loop now encodes the relation between the - ## unimodular path A and our generators. This is done - ## simply by accounting for all of the edges that lie - ## below the path attached to A (as they form a triangle) - ## Similarly, this is also done for B. + # This loop now encodes the relation between the + # unimodular path A and our generators. This is done + # simply by accounting for all of the edges that lie + # below the path attached to A (as they form a triangle) + # Similarly, this is also done for B. - ## Running between the cusps between cusp1 and cusp2 + # Running between the cusps between cusp1 and cusp2 for rel in rels[r + 2: s + 2]: - ## Add edge relation + # Add edge relation vA.append(rel[0]) - ## Add negative of edge relation + # Add negative of edge relation vB.append((-rel[0][0], rel[0][1], rel[0][2])) - ## Add relations for A and B to relations list + # Add relations for A and B to relations list rels.extend([vA, vB]) - ## Make the translation table between the Sage and Geometric - ## descriptions of P^1 + # Make the translation table between the Sage and Geometric + # descriptions of P^1 equiv_ind = {} for i, rep in enumerate(coset_reps): ky = P.normalize(rep[t10], rep[t11]) @@ -852,28 +852,28 @@ def __init__(self, N): PollackStevensModularDomain.__init__(self, N, coset_reps, gens_index, rels, equiv_ind) - ## A list of indices of the (geometric) coset representatives whose - ## paths are identified by some 2-torsion element (which switches the - ## path orientation) + # A list of indices of the (geometric) coset representatives whose + # paths are identified by some 2-torsion element (which switches the + # path orientation) self._indices_with_two_torsion = twotor_index self._reps_with_two_torsion = [coset_reps[i] for i in twotor_index] - ## A dictionary of (2-torsion in PSL_2(Z)) matrices in - ## Gamma_0(N) that give the orientation identification in the - ## paths listed in twotor_index above! + # A dictionary of (2-torsion in PSL_2(Z)) matrices in + # Gamma_0(N) that give the orientation identification in the + # paths listed in twotor_index above! self._two_torsion = {} for j, tor_elt in zip(twotor_index, twotorrels): self._two_torsion[coset_reps[j]] = tor_elt - ## A list of indices of the (geometric) coset representatives that - ## form one side of an ideal triangle with an interior fixed point of - ## a 3-torsion element of Gamma_0(N) + # A list of indices of the (geometric) coset representatives that + # form one side of an ideal triangle with an interior fixed point of + # a 3-torsion element of Gamma_0(N) self._indices_with_three_torsion = threetor_index self._reps_with_three_torsion = [coset_reps[i] for i in threetor_index] - ## A dictionary of (3-torsion in PSL_2(Z)) matrices in - ## Gamma_0(N) that give the interior fixed point described in - ## threetor_index above! + # A dictionary of (3-torsion in PSL_2(Z)) matrices in + # Gamma_0(N) that give the interior fixed point described in + # threetor_index above! self._three_torsion = {} for j, tor_elt in zip(threetor_index, threetorrels): self._three_torsion[coset_reps[j]] = tor_elt @@ -1144,118 +1144,118 @@ def form_list_of_cusps(self): -4/9, -3/7, -5/12, -7/17, -2/5, -3/8, -4/11, -1/3, -2/7, -3/11, -1/4, -2/9, -1/5, -1/6, 0] """ - ## Get the level + # Get the level N = self.level() - ## Some convenient shortcuts + # Some convenient shortcuts P = self.P1() sP = len(P.list()) # Size of P^1(Z/NZ) - ## Initialize some lists + # Initialize some lists C = [QQ(-1), "?", QQ(0)] - ## Initialize the list of cusps at the bottom of the fund. domain. - ## The ? denotes that it has not yet been checked if more cusps need - ## to be added between the surrounding cusps. + # Initialize the list of cusps at the bottom of the fund. domain. + # The ? denotes that it has not yet been checked if more cusps need + # to be added between the surrounding cusps. full_domain = False # Says that we are not done yet! v = [False for r in range(sP)] - ## This initializes a list indexed by P^1(Z/NZ) which keeps track of - ## which right coset representatives we've found for Gamma_0(N)/SL_2(Z) - ## thru the construction of a fundamental domain + # This initializes a list indexed by P^1(Z/NZ) which keeps track of + # which right coset representatives we've found for Gamma_0(N)/SL_2(Z) + # thru the construction of a fundamental domain - ## Includeds the coset repns formed by the original ideal triangle - ## (with corners at -1, 0, infty) + # Includeds the coset repns formed by the original ideal triangle + # (with corners at -1, 0, infty) v[P.index(0, 1)] = True v[P.index(1, -1)] = True v[P.index(-1, 0)] = True - ## Main Loop -- Ideal Triangle Flipping - ## ==================================== + # Main Loop -- Ideal Triangle Flipping + # ==================================== while (not full_domain): full_domain = True - ## This loop runs through the current set of cusps - ## and checks to see if more cusps should be added - ## ----------------------------------------------- + # This loop runs through the current set of cusps + # and checks to see if more cusps should be added + # ----------------------------------------------- for s in range(1, len(C), 2): # range over odd indices in the # final list C if C[s] == "?": - ## Single out our two cusps (path from cusp2 to cusp1) + # Single out our two cusps (path from cusp2 to cusp1) cusp1 = C[s - 1] cusp2 = C[s + 1] - ## Makes the unimodular transform for the path from cusp2 - ## to cusp1 + # Makes the unimodular transform for the path from cusp2 + # to cusp1 b1 = cusp1.denominator() b2 = cusp2.denominator() - ## This is the point where it is determined whether - ## or not the adjacent triangle should be added - ## ------------------------------------------------ + # This is the point where it is determined whether + # or not the adjacent triangle should be added + # ------------------------------------------------ pos = P.index(b2, b1) # The Sage index of the bottom - ## row of our unimodular - ## transformation gam + # row of our unimodular + # transformation gam - ## Check if we need to flip (since this P1 element has not - ## yet been accounted for!) + # Check if we need to flip (since this P1 element has not + # yet been accounted for!) if not v[pos]: v[pos] = True # Say this P1 element now occurs v[P.index(b1, -(b1 + b2))] = True - ## Say that the other two ideal triangle edges - ## also occur! + # Say that the other two ideal triangle edges + # also occur! v[P.index(-(b1 + b2), b2)] = True - ## Check to see if this triangle contains a fixed - ## point by an element of Gamma_0(N). If such an - ## element is present, the fundamental domain can be - ## extended no further. + # Check to see if this triangle contains a fixed + # point by an element of Gamma_0(N). If such an + # element is present, the fundamental domain can be + # extended no further. if (b1 ** 2 + b2 ** 2 + b1 * b2) % N != 0: - ## this congruence is exactly equivalent to - ## gam * [0 -1; 1 -1] * gam^(-1) is in Gamma_0(N) - ## where gam is the matrix corresponding to the - ## unimodular path connecting cusp1 to cusp2 + # this congruence is exactly equivalent to + # gam * [0 -1; 1 -1] * gam^(-1) is in Gamma_0(N) + # where gam is the matrix corresponding to the + # unimodular path connecting cusp1 to cusp2 C[s] = "i" # The '?' is changed to an 'i' - ## indicating that a new cusp needs to - ## be inserted here + # indicating that a new cusp needs to + # be inserted here full_domain = False else: C[s] = "x" # The '?' is changed to an 'x' and no # more checking below is needed! =) else: C[s] = "x" # The '?' is changed to an 'x' and no more - ## checking below is needed! =) + # checking below is needed! =) - ## Now insert the missing cusps (where there is an 'i' in - ## the final list) - ## This will keep the fundamental domain as flat as possible! - ## --------------------------------------------------------------- + # Now insert the missing cusps (where there is an 'i' in + # the final list) + # This will keep the fundamental domain as flat as possible! + # --------------------------------------------------------------- s = 1 while s < len(C): # range over odd indices in the final list C if C[s] == "i": C[s] = "?" - ## Single out our two cusps (path from cusp2 to cusp1) + # Single out our two cusps (path from cusp2 to cusp1) cusp1 = C[s - 1] cusp2 = C[s + 1] - ## Makes the unimodular transform for the path - ## from cusp2 to cusp1 + # Makes the unimodular transform for the path + # from cusp2 to cusp1 a1 = cusp1.numerator() b1 = cusp1.denominator() a2 = cusp2.numerator() b2 = cusp2.denominator() - ## Inserts the Farey center of these two cusps! + # Inserts the Farey center of these two cusps! a = a1 + a2 b = b1 + b2 C.insert(s + 1, a / b) @@ -1263,8 +1263,8 @@ def form_list_of_cusps(self): s += 2 s += 2 - ## Remove the (now superfluous) extra string characters that appear - ## in the odd list entries + # Remove the (now superfluous) extra string characters that appear + # in the odd list entries C = [QQ(C[ss]) for ss in range(0, len(C), 2)] return C @@ -1401,12 +1401,12 @@ def fd_boundary(self, C): """ C.reverse() # Reverse here to get clockwise orientation of boundary - ## These matrices correspond to the paths from infty to 0 and - ## -1 to infty + # These matrices correspond to the paths from infty to 0 and + # -1 to infty mats = [Id, minone_inf_path] - ## Now find SL_2(Z) matrices whose associated unimodular paths - ## connect the cusps listed in C. + # Now find SL_2(Z) matrices whose associated unimodular paths + # connect the cusps listed in C. for j in range(len(C) - 1): a = C[j].numerator() b = C[j + 1].numerator() diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 74de1b039f3..4acbecf038b 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -762,7 +762,7 @@ def specialize(self, *args): return self.__class__(self._codomain.specialize(*args), self._manin, D, check=False) - def hecke(self, ell, algorithm = 'prep'): + def hecke(self, ell, algorithm='prep'): r""" Return the image of this Manin map under the Hecke operator `T_{\ell}`. @@ -796,7 +796,7 @@ def hecke(self, ell, algorithm = 'prep'): M = self._manin if algorithm == 'prep': - ## psi will denote self | T_ell + # psi will denote self | T_ell psi = {} for g in M.gens(): psi_g = sum((self[h] * A for h, A in M.prep_hecke_on_gen_list(ell, g)), self._codomain(0)) diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index 96bdcfc3e07..0d0f064445c 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Element class for Pollack-Stevens' Modular Symbols +Element class for Pollack-Stevens' modular symbols This is the class of elements in the spaces of Pollack-Steven's modular symbols as described in [PS2011]_. @@ -93,18 +93,18 @@ def _iterate_Up(Phi, p, M, ap, q, aq, check): if ap.valuation(p) > 0: raise ValueError("Lifting non-ordinary eigensymbols not implemented (issue #20)") - ## Act by Hecke to ensure values are in D and not D^dag after solving difference equation + # Act by Hecke to ensure values are in D and not D^dag after solving difference equation verbose("Applying Hecke", level = 2) apinv = ~ap Phi = apinv * Phi.hecke(p) - ## Killing eisenstein part + # Killing eisenstein part verbose("Killing eisenstein part with q = %s" % q, level = 2) k = Phi.parent().weight() Phi = ((q ** (k + 1) + 1) * Phi - Phi.hecke(q)) - ## Iterating U_p + # Iterating U_p verbose("Iterating U_p", level = 2) Psi = apinv * Phi.hecke(p) @@ -814,20 +814,20 @@ def _consistency_check(self): """ f = self._map MR = self._map._manin - ## Test two torsion relations + # Test two torsion relations for g in MR.reps_with_two_torsion(): gamg = MR.two_torsion_matrix(g) if not (f[g] * gamg + f[g]).is_zero(): raise ValueError("Two torsion relation failed with", g) - ## Test three torsion relations + # Test three torsion relations for g in MR.reps_with_three_torsion(): gamg = MR.three_torsion_matrix(g) if not (f[g] * (gamg ** 2) + f[g] * gamg + f[g]).is_zero(): raise ValueError("Three torsion relation failed with", g) - ## Test that the symbol adds to 0 around the boundary of the - ## fundamental domain + # Test that the symbol adds to 0 around the boundary of the + # fundamental domain t = self.parent().coefficient_module().zero() for g in MR.gens()[1:]: if not(g in MR.reps_with_two_torsion() @@ -1321,8 +1321,8 @@ def _lift_to_OMS(self, p, M, new_base_ring, algorithm = 'greenberg'): D[g] = self._map[g].lift(p, M, new_base_ring) t = self.parent().coefficient_module().lift(p, M, new_base_ring).zero() - ## This loops adds up around the boundary of fundamental - ## domain except the two vertical lines + # This loops adds up around the boundary of fundamental + # domain except the two vertical lines for g in manin.gens()[1:]: twotor = g in manin.reps_with_two_torsion() threetor = g in manin.reps_with_three_torsion() @@ -1330,11 +1330,11 @@ def _lift_to_OMS(self, p, M, new_base_ring, algorithm = 'greenberg'): t = t - D[g] else: t += D[g] * manin.gammas[g] - D[g] - ## t now should be sum Phi(D_i) | (gamma_i - 1) - sum - ## Phi(D'_i) - sum Phi(D''_i) + # t now should be sum Phi(D_i) | (gamma_i - 1) - sum + # Phi(D'_i) - sum Phi(D''_i) - ## (Here I'm using the opposite sign convention of [PS2011] - ## regarding D'_i and D''_i) + # (Here I'm using the opposite sign convention of [PS2011] + # regarding D'_i and D''_i) D[manin.gen(0)] = -t.solve_difference_equation() # Check this! else: diff --git a/src/sage/modular/pollack_stevens/space.py b/src/sage/modular/pollack_stevens/space.py index 8bb41867770..43e396b03c8 100644 --- a/src/sage/modular/pollack_stevens/space.py +++ b/src/sage/modular/pollack_stevens/space.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Pollack-Stevens' Modular Symbols Spaces +Pollack-Stevens' modular symbols spaces This module contains a class for spaces of modular symbols that use Glenn Stevens' conventions, as explained in [PS2011]_. @@ -482,8 +482,8 @@ def precision_cap(self): sage: M.precision_cap() 10 """ - ### WARNING -- IF YOU ARE WORKING IN SYM^K(Q^2) THIS WILL JUST - ### RETURN K-1. NOT GOOD + # WARNING -- IF YOU ARE WORKING IN SYM^K(Q^2) THIS WILL JUST + # RETURN K-1. NOT GOOD return self.coefficient_module()._prec_cap def weight(self): @@ -720,19 +720,19 @@ def random_element(self, M=None): # p = self.prime() manin = self.source() -# ## There must be a problem here with that +1 -- should be -# ## variable depending on a c of some matrix We'll need to -# ## divide by some power of p and so we add extra accuracy -# ## here. +# # There must be a problem here with that +1 -- should be +# # variable depending on a c of some matrix We'll need to +# # divide by some power of p and so we add extra accuracy +# # here. # if k != 0: # MM = M + valuation(k,p) + 1 + M.exact_log(p) # else: # MM = M + M.exact_log(p) + 1 - ## this loop runs thru all of the generators (except - ## (0)-(infty)) and randomly chooses a distribution to assign - ## to this generator (in the 2,3-torsion cases care is taken - ## to satisfy the relevant relation) + # this loop runs thru all of the generators (except + # (0)-(infty)) and randomly chooses a distribution to assign + # to this generator (in the 2,3-torsion cases care is taken + # to satisfy the relevant relation) D = {} for g in manin.gens(): D[g] = self.coefficient_module().random_element(M) @@ -747,7 +747,7 @@ def random_element(self, M=None): D[g] = 2 * D[g] - D[g] * gamg - D[g] * gamg ** 2 # print("post:",D[g]) - ## now we compute nu_infty of Prop 5.1 of [PS1] + # now we compute nu_infty of Prop 5.1 of [PS1] t = self.coefficient_module().zero() for g in manin.gens()[1:]: if (g not in manin.reps_with_two_torsion()) and (g not in manin.reps_with_three_torsion()): @@ -759,11 +759,11 @@ def random_element(self, M=None): else: t -= D[g] - ## If k = 0, then t has total measure zero. However, this is not true when k != 0 - ## (unlike Prop 5.1 of [PS1] this is not a lift of classical symbol). - ## So instead we simply add (const)*mu_1 to some (non-torsion) v[j] to fix this - ## here since (mu_1 |_k ([a,b,c,d]-1))(trivial char) = chi(a) k a^{k-1} c , - ## we take the constant to be minus the total measure of t divided by (chi(a) k a^{k-1} c) + # If k = 0, then t has total measure zero. However, this is not true when k != 0 + # (unlike Prop 5.1 of [PS1] this is not a lift of classical symbol). + # So instead we simply add (const)*mu_1 to some (non-torsion) v[j] to fix this + # here since (mu_1 |_k ([a,b,c,d]-1))(trivial char) = chi(a) k a^{k-1} c , + # we take the constant to be minus the total measure of t divided by (chi(a) k a^{k-1} c) if k != 0: j = 1 @@ -851,7 +851,7 @@ def cusps_from_mat(g): return ac, bd -def ps_modsym_from_elliptic_curve(E, sign = 0, implementation='eclib'): +def ps_modsym_from_elliptic_curve(E, sign=0, implementation='eclib'): r""" Return the overconvergent modular symbol associated to an elliptic curve defined over the rationals. diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 340d8b0320e..6821f3e97bf 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -16,6 +16,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.modular.arithgroup.congroup_sl2z import is_SL2Z +from sage.modular.modform.constructor import EisensteinForms from sage.modular.modform.eis_series import eisenstein_series_qexp from sage.modular.modform.element import GradedModularFormElement @@ -62,6 +64,26 @@ class QuasiModularFormsElement(ModuleElement): sage: M = QM.modular_forms_subring() sage: QM(M.0 * E2 + M.1 * E2^2) 2 - 336*q + 4320*q^2 + 398400*q^3 - 3772992*q^4 - 89283168*q^5 + O(q^6) + + One may convert a quasimodular form into a multivariate polynomial in the + generators of the ring by calling + :meth:`~sage.modular.quasimodform.element.QuasiModularFormsElement.polynomial`:: + + sage: QM = QuasiModularForms(1) + sage: F = QM.0^2 + QM.1^2 + QM.0*QM.1*QM.2 + sage: F.polynomial() + E2*E4*E6 + E4^2 + E2^2 + + If the group is not the full modular group, the default names of the + generators are given by ``Ek_i`` and ``Sk_i`` to denote the `i`-th basis + element of the weight `k` Eisenstein subspace and cuspidal subspace + respectively (for more details, see the documentation of + :meth:`~sage.modular.quasimodform.ring.QuasiModularFormsRing.polynomial_ring`) :: + + sage: QM = QuasiModularForms(Gamma1(4)) + sage: F = (QM.0^4)*(QM.1^3) + QM.3 + sage: F.polynomial() + -512*E2^4*E2_1^3 + E2^4*E3_0^2 + 48*E2^4*E3_1^2 + E3_0 """ def __init__(self, parent, polynomial): r""" @@ -135,6 +157,18 @@ def _repr_(self): """ return str(self.q_expansion()) + def _latex_(self): + r""" + Return a latex representation of ``self``. + + TESTS:: + + sage: QM = QuasiModularForms(1) + sage: latex(QM.0) + 1 - 24 q - 72 q^{2} - 96 q^{3} - 168 q^{4} - 144 q^{5} + O(q^{6}) + """ + return self.q_expansion()._latex_() + def _richcmp_(self, other, op): r""" Compare self with other. @@ -273,6 +307,9 @@ def is_zero(self): False sage: (QM.0).is_zero() False + sage: QM = QuasiModularForms(Gamma0(2)) + sage: QM(0).is_zero() + True """ return not self @@ -290,6 +327,9 @@ def is_one(self): True sage: (QM.0).is_one() False + sage: QM = QuasiModularForms(Gamma0(2)) + sage: QM(1).is_one() + True """ return self._polynomial.is_one() @@ -317,6 +357,13 @@ def is_graded_modular_form(self): True sage: QM.zero().is_graded_modular_form() True + sage: QM = QuasiModularForms(Gamma0(6)) + sage: (QM.0).is_graded_modular_form() + False + sage: (QM.0 + QM.1*QM.2 + QM.3).is_graded_modular_form() + False + sage: (QM.1*QM.2 + QM.3).is_graded_modular_form() + True .. NOTE:: @@ -341,21 +388,29 @@ def is_modular_form(self): False sage: QM.zero().is_modular_form() True + sage: QM = QuasiModularForms(Gamma0(4)) + sage: (QM.0).is_modular_form() + False + sage: (QM.1).is_modular_form() + True """ return self._polynomial.degree() <= 0 and self._polynomial[0].is_modular_form() - def polynomial(self, names='E2, E4, E6'): + def polynomial(self, names=None): r""" - Return a multivariate polynomial `P(E_2, E_4, E_6)` corresponding to the - given form where `E_2`, `E_4` and `E_6` are the generators of the - quasimodular form ring given by the following method: + Return a multivariate polynomial such that every variable corresponds to + a generator of the ring, ordered by the method: :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. + An alias of this method is ``to_polynomial``. + INPUT: - - ``names`` (str, default: ``'E2, E4, E6'``) -- a list or tuple of names - (strings), or a comma separated string. Correspond to the names of the - variables; + - ``names`` (str, default: ``None``) -- a list or tuple of names + (strings), or a comma separated string. Defines the names for the + generators of the multivariate polynomial ring. The default names are + of the form ``ABCk`` where ``k`` is a number corresponding to the + weight of the form ``ABC``. OUTPUT: A multivariate polynomial in the variables ``names`` @@ -366,11 +421,25 @@ def polynomial(self, names='E2, E4, E6'): E4 + E2 sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial() E2*E6 + 2*E4^2 + E2 + 1/2 + + Check that :trac:`34569` is fixed:: + + sage: QM = QuasiModularForms(Gamma1(3)) + sage: QM.ngens() + 5 + sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).polynomial() + E3_1*E4_0 + E2_0*E3_0 + E2 + E2_0 + """ P = self.parent().polynomial_ring(names) - g0, g1 = self.parent().modular_forms_subring().polynomial_ring(names='x').gens() - E2, E4, E6 = P.gens() - return sum(f.to_polynomial().subs({g0:E4, g1:E6}) * E2 ** exp for exp, f in enumerate(self._polynomial.coefficients(sparse=False))) + poly_gens = P.gens() + E2 = poly_gens[0] + poly_gens = poly_gens[1:] + modform_poly_gens = self.parent().modular_forms_subring().polynomial_ring(names='x').gens() + subs_dictionnary = {} + for idx, g in enumerate(modform_poly_gens): + subs_dictionnary[g] = poly_gens[idx] + return sum(f.to_polynomial().subs(subs_dictionnary) * E2 ** exp for exp, f in enumerate(self._polynomial.coefficients(sparse=False))) to_polynomial = polynomial # alias @@ -390,6 +459,9 @@ def weights_list(self): [6] sage: QM(1/2).weights_list() [0] + sage: QM = QuasiModularForms(Gamma1(3)) + sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).weights_list() + [2, 5, 7] """ return sorted(list(self.to_polynomial().homogeneous_components())) @@ -412,6 +484,16 @@ def is_homogeneous(self): True sage: (1 + QM.0).is_homogeneous() False + sage: QM = QuasiModularForms(Gamma0(4)) + sage: (QM.0).is_homogeneous() + True + sage: (QM.0 + QM.1).is_homogeneous() + True + sage: (QM.0 + QM.1 + QM.2).is_homogeneous() + True + sage: (QM.0 + QM.1^3).is_homogeneous() + False + """ return len(self.weights_list()) == 1 @@ -452,10 +534,16 @@ def homogeneous_components(self): {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)} sage: (QM.0 + QM.1 + QM.2).homogeneous_components() {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 4: 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 6: 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)} + 4: 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 6: 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)} sage: (1 + QM.0).homogeneous_components() {0: 1, 2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)} + sage: QM = QuasiModularForms(Gamma0(5)) + sage: F = QM.0 + QM.1 * QM.0 + QM.3^2*QM.0 + sage: F.homogeneous_components() + {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + 4: 1 - 18*q - 198*q^2 - 936*q^3 - 2574*q^4 - 5610*q^5 + O(q^6), + 10: q^2 - 24*q^3 - 52*q^4 - 520*q^5 + O(q^6)} """ QM = self.parent() poly_self = self.to_polynomial() @@ -493,12 +581,23 @@ def serre_derivative(self): 6 sage: F.serre_derivative().weight() 8 + + Check that :trac:`34569` is fixed:: + + sage: QM = QuasiModularForms(Gamma1(3)) + sage: E2 = QM.weight_2_eisenstein_series() + sage: E2.serre_derivative() + -1/6 - 16*q - 216*q^2 - 832*q^3 - 2248*q^4 - 4320*q^5 + O(q^6) + sage: F = QM.0 + QM.1*QM.2 """ # initial variables: QM = self.parent() R = QM.base_ring() E2 = QM.gen(0) - E4 = QM.gen(1) + if is_SL2Z(QM.group()): + E4 = QM.gen(1) + else: + E4 = QM(EisensteinForms(group=1, weight=4, base_ring=R).gen(0)) # compute the derivative of E2: q*dE2/dq E2deriv = R(12).inverse_of_unit() * (E2 ** 2 - E4) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 8f0d8baed62..5579aa621d6 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -44,8 +44,8 @@ Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: QM.gens() [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] sage: E2 = QM.0; E4 = QM.1; E6 = QM.2 sage: E2 * E4 + E6 2 - 288*q - 20304*q^2 - 185472*q^3 - 855216*q^4 - 2697408*q^5 + O(q^6) @@ -74,6 +74,71 @@ sage: F == Delta + E4 * E2 + (Delta + E4) * E2^2 True +One may also create rings of quasimodular forms for certain congruence subgroups:: + + sage: QM = QuasiModularForms(Gamma0(5)); QM + Ring of Quasimodular Forms for Congruence Subgroup Gamma0(5) over Rational Field + sage: QM.ngens() + 4 + +The first generator is the weight 2 Eisenstein series:: + + sage: E2 = QM.0; E2 + 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) + +The other generators correspond to the generators given by the method +:meth:`sage.modular.modform.ring.ModularFormsRing.gens`:: + + sage: QM.gens() + [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + 1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), + 1 + 240*q^5 + O(q^6), + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + sage: QM.modular_forms_subring().gens() + [1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), + 1 + 240*q^5 + O(q^6), + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + +It is possible to convert a graded quasimodular form into a polynomial where +each variable corresponds to a generator of the ring:: + + sage: QM = QuasiModularForms(1) + sage: E2, E4, E6 = QM.gens() + sage: F = E2*E4*E6 + E6^2; F + 2 - 1296*q + 91584*q^2 + 14591808*q^3 + 464670432*q^4 + 6160281120*q^5 + O(q^6) + sage: p = F.polynomial('E2, E4, E6'); p + E2*E4*E6 + E6^2 + sage: P = p.parent(); P + Multivariate Polynomial Ring in E2, E4, E6 over Rational Field + +The generators of the polynomial ring have degree equal to the weight of the +corresponding form:: + + sage: P.inject_variables() + Defining E2, E4, E6 + sage: E2.degree() + 2 + sage: E4.degree() + 4 + sage: E6.degree() + 6 + +This works also for congruence subgroup:: + + sage: QM = QuasiModularForms(Gamma1(4)) + sage: QM.ngens() + 5 + sage: QM.polynomial_ring() + Multivariate Polynomial Ring in E2, E2_0, E2_1, E3_0, E3_1 over Rational Field + sage: (QM.0 + QM.1*QM.0^2 + QM.3 + QM.4^3).polynomial() + E3_1^3 + E2^2*E2_0 + E3_0 + E2 + +One can also convert a multivariate polynomial into a quasimodular form:: + + sage: QM.polynomial_ring().inject_variables() + Defining E2, E2_0, E2_1, E3_0, E3_1 + sage: QM.from_polynomial(E3_1^3 + E2^2*E2_0 + E3_0 + E2) + 3 - 72*q + 396*q^2 + 2081*q^3 + 19752*q^4 + 98712*q^5 + O(q^6) .. NOTE:: @@ -100,6 +165,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from itertools import product, chain + from sage.categories.graded_algebras import GradedAlgebras from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0 @@ -109,7 +176,6 @@ from sage.modular.modform.ring import ModularFormsRing from sage.rings.integer import Integer -from sage.rings.integer_ring import ZZ from sage.rings.polynomial.multi_polynomial import MPolynomial from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -134,8 +200,8 @@ class QuasiModularForms(Parent, UniqueRepresentation): Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: QM.gens() [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] It is possible to access the weight 2 Eisenstein series:: @@ -526,53 +592,142 @@ def polygen(self): """ return self.__polynomial_subring.gen() - def polynomial_ring(self, names='E2, E4, E6'): + def polynomial_ring(self, names=None): r""" - Return a multivariate polynomial ring isomorphic to the given graded - quasimodular forms ring. + Return a multivariate polynomial ring of which the quasimodular forms + ring is a quotient. - In the case of the full modular group, this - ring is `R[E_2, E_4, E_6]` where `E_2`, `E_4` and `E_6` have degrees 2, - 4 and 6 respectively. + In the case of the full modular group, this ring is `R[E_2, E_4, E_6]` + where `E_2`, `E_4` and `E_6` have degrees 2, 4 and 6 respectively. INPUT: - - ``names`` (str, default: ``'E2, E4, E6'``) -- a list or tuple of names - (strings), or a comma separated string. Correspond to the names of the - variables. + - ``names`` (str, default: ``None``) -- a list or tuple of names + (strings), or a comma separated string. Defines the names for the + generators of the multivariate polynomial ring. The default names are + of the following form: + + - ``E2`` denotes the weight 2 Eisenstein series; + + - ``Ek_i`` and ``Sk_i`` denote the `i`-th basis element of the weight + `k` Eisenstein subspace and cuspidal subspace respectively; + + - If the level is one, the default names are ``E2``, ``E4`` and + ``E6``; + + - In any other cases, we use the letters ``Fk``, ``Gk``, ``Hk``, ..., + ``FFk``, ``FGk``, ... to denote any generator of weight `k`. OUTPUT: A multivariate polynomial ring in the variables ``names`` EXAMPLES:: sage: QM = QuasiModularForms(1) - sage: P.<E2, E4, E6> = QM.polynomial_ring(); P + sage: P = QM.polynomial_ring(); P Multivariate Polynomial Ring in E2, E4, E6 over Rational Field + sage: P.inject_variables() + Defining E2, E4, E6 sage: E2.degree() 2 sage: E4.degree() 4 sage: E6.degree() 6 - sage: P.<x, y, z, w> = QQ[] - sage: QM.from_polynomial(x+y+z+w) - Traceback (most recent call last): - ... - ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring + + Example when the level is not one:: + + sage: QM = QuasiModularForms(Gamma0(29)) + sage: P_29 = QM.polynomial_ring() + sage: P_29 + Multivariate Polynomial Ring in E2, F2, S2_0, S2_1, E4_0, F4, G4, H4 over Rational Field + sage: P_29.inject_variables() + Defining E2, F2, S2_0, S2_1, E4_0, F4, G4, H4 + sage: F2.degree() + 2 + sage: E4_0.degree() + 4 + + The name ``Sk_i`` stands for the `i`-th basis element of the cuspidal subspace of weight `k`:: + + sage: F2 = QM.from_polynomial(S2_0) + sage: F2.qexp(10) + q - q^4 - q^5 - q^6 + 2*q^7 - 2*q^8 - 2*q^9 + O(q^10) + sage: CuspForms(Gamma0(29), 2).0.qexp(10) + q - q^4 - q^5 - q^6 + 2*q^7 - 2*q^8 - 2*q^9 + O(q^10) + sage: F2 == CuspForms(Gamma0(29), 2).0 + True + + The name ``Ek_i`` stands for the `i`-th basis element of the Eisenstein subspace of weight `k`:: + + sage: F4 = QM.from_polynomial(E4_0) + sage: F4.qexp(30) + 1 + 240*q^29 + O(q^30) + sage: EisensteinForms(Gamma0(29), 4).0.qexp(30) + 1 + 240*q^29 + O(q^30) + sage: F4 == EisensteinForms(Gamma0(29), 4).0 + True + + One may also choose the name of the variables:: + + sage: QM = QuasiModularForms(1) + sage: QM.polynomial_ring(names="P, Q, R") + Multivariate Polynomial Ring in P, Q, R over Rational Field """ - return PolynomialRing(self.base_ring(), 3, names, order=TermOrder('wdeglex', [ZZ(2), ZZ(4), ZZ(6)])) + gens = self.__modular_forms_subring.gen_forms() + weights = [f.weight() for f in gens] + gens = iter(gens) + if names is None: + if self.group() == Gamma0(1): + names = ["E2", "E4", "E6"] + else: + names = ["E2"] + letters = "FGHIJK" + for unique_weight in set(weights): + same_weights = [k for k in weights if k == unique_weight] + # create all the names of the form: + # F, G, H, I, J, K, FF, FG, FH,..., FFF, FFG,... + # the letters E and S are reserved for basis elements of the + # Eisenstein subspaces and cuspidal subspaces respectively. + iter_names = (product(letters, repeat=r) + for r in range(1, len(same_weights)//len(letters) + 2)) + iter_names = chain(*iter_names) + for k in same_weights: + form = next(gens) + Mk = self.__modular_forms_subring.modular_forms_of_weight(k) + if form.is_eisenstein(): + Ek_basis = Mk.eisenstein_subspace().basis() + # check if form is a basis element of the Eisenstein subspace of weight k + try: + n = Ek_basis.index(form) + name = f"E{str(k)}_{str(n)}" + except ValueError: + name = "".join(next(iter_names)) + str(k) + elif form.is_cuspidal(): + Sk_basis = Mk.cuspidal_subspace().basis() + # check if form is a basis element of the cuspidal subspace of weight k + try: + n = Sk_basis.index(form) + name = f"S{str(k)}_{str(n)}" + except ValueError: + name = "".join(next(iter_names)) + str(k) + else: + name = "".join(next(iter_names)) + str(k) + names.append(name) + weights.insert(0, 2) # add the weight 2 Eisenstein series + return PolynomialRing(self.base_ring(), len(weights), names, + order=TermOrder('wdeglex', weights)) def from_polynomial(self, polynomial): r""" - Convert the given polynomial `P(X, Y, Z)` to the graded quasiform - `P(E_2, E_4, E_6)` where `E_2`, `E_4` and `E_6` are the generators given + Convert the given polynomial `P(x,\ldots, y)` to the graded quasiform + `P(g_0, \ldots, g_n)` where the `g_i` are the generators given by :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. INPUT: - - ``plynomial`` -- A multivariate polynomial + - ``polynomial`` -- A multivariate polynomial - OUTPUT: the graded quasimodular forms `P(E_2, E_4, E_6)` + OUTPUT: the graded quasimodular forms `P(g_0, \ldots, g_n)` EXAMPLES:: @@ -588,6 +743,25 @@ def from_polynomial(self, polynomial): True sage: QM.from_polynomial(x^2 + y + x*z + 1) 4 - 336*q - 2016*q^2 + 322368*q^3 + 3691392*q^4 + 21797280*q^5 + O(q^6) + sage: QM = QuasiModularForms(Gamma0(2)) + sage: P = QM.polynomial_ring() + sage: P.inject_variables() + Defining E2, E2_0, E4_0 + sage: QM.from_polynomial(E2) + 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) + sage: QM.from_polynomial(E2 + E4_0*E2_0) == QM.0 + QM.2*QM.1 + True + + Naturally, the number of variable must not exceed the number of generators:: + + sage: P = PolynomialRing(QQ, 'F', 4) + sage: P.inject_variables() + Defining F0, F1, F2, F3 + sage: QM.from_polynomial(F0 + F1 + F2 + F3) + Traceback (most recent call last): + ... + ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring + TESTS:: diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 996e9287697..9bbe2865b47 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -1,5 +1,5 @@ r""" -Brandt Modules +Brandt modules Introduction ------------ @@ -218,6 +218,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.rational_field import QQ from sage.rings.ring import CommutativeRing from sage.structure.richcmp import richcmp, richcmp_method @@ -490,7 +491,7 @@ def quaternion_order_with_given_level(A, level): level = abs(level) N = A.discriminant() N1 = gcd(level, N) - M1 = level / N1 + M1 = level // N1 O = maximal_order(A) # if N1 != 1: @@ -1034,7 +1035,7 @@ def hecke_matrix(self, n, algorithm='default', sparse=False, B=None): """ n = ZZ(n) if n <= 0: - raise IndexError("n must be positive.") + raise IndexError("n must be positive") if n not in self._hecke_matrices: if algorithm == 'default': try: @@ -1055,7 +1056,7 @@ def hecke_matrix(self, n, algorithm='default', sparse=False, B=None): elif algorithm == 'brandt': T = self._compute_hecke_matrix_brandt(n, sparse=sparse) else: - raise ValueError("unknown algorithm '%s'" % algorithm) + raise ValueError(f"unknown algorithm '{algorithm}'") T.set_immutable() self._hecke_matrices[n] = T return self._hecke_matrices[n] @@ -1517,7 +1518,7 @@ def brandt_series(self, prec, var='q'): [ 1/6 + 2*t^2 + O(t^3) 1/6 + t + O(t^3)] """ A = self._brandt_series_vectors(prec) - R = QQ[[var]] + R = PowerSeriesRing(QQ, var) n = len(A[0]) return matrix(R, n, n, [[R(x.list()[:prec], prec) for x in Y] for Y in A]) @@ -1560,7 +1561,7 @@ def eisenstein_subspace(self): V = A.kernel() return V - def is_cuspidal(self): + def is_cuspidal(self) -> bool: r""" Return whether ``self`` is cuspidal, i.e. has no Eisenstein part. @@ -1584,7 +1585,7 @@ def monodromy_weights(self): fixed choice of basis. The weight of an ideal class `[I]` is half the number of units of the right order `I`. - NOTE: The base ring must be `\QQ` or `\ZZ`. + .. NOTE:: The base ring must be `\QQ` or `\ZZ`. EXAMPLES:: @@ -1619,9 +1620,9 @@ def monodromy_weights(self): return tuple(a[1] / a[0] / 2 for a in thetas) -############################################################################# -# Benchmarking -############################################################################# +# ==================== +# Benchmarking +# ==================== def benchmark_magma(levels, silent=False): """ INPUT: diff --git a/src/sage/modular/ssmod/ssmod.py b/src/sage/modular/ssmod/ssmod.py index 50865f10c80..089089995b8 100644 --- a/src/sage/modular/ssmod/ssmod.py +++ b/src/sage/modular/ssmod/ssmod.py @@ -1,16 +1,8 @@ """ -Module of Supersingular Points +Module of supersingular points The module of divisors on the modular curve `X_0(N)` over `F_p` supported at supersingular points. -AUTHORS: - -- William Stein - -- David Kohel - -- Iftikhar Burhanuddin - EXAMPLES:: sage: x = SupersingularModule(389) @@ -52,6 +44,15 @@ True sage: loads(dumps(d)) == d True + +AUTHORS: + +- William Stein + +- David Kohel + +- Iftikhar Burhanuddin + """ # **************************************************************************** diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index c2ed4590188..faf255b2ded 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -363,7 +363,7 @@ def _vector_(self, base_ring=None): - ``base_ring`` -- the desired base ring of the vector. OUTPUT: - + A vector over the base ring. EXAMPLES:: @@ -448,7 +448,7 @@ def additive_order(self): from sage.rings.infinity import infinity from sage.rings.finite_rings.integer_mod import Mod from sage.rings.integer import Integer - from sage.arith.all import lcm + from sage.arith.functions import lcm n = Integer(1) for i, a in enumerate(I): if a == 0: diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index 5f61c221d93..71b43dcdc1a 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -510,7 +510,7 @@ def __init__(self, X, Y, category=None): from sage.categories.modules_with_basis import ModulesWithBasis category = ModulesWithBasis(X.base_ring()) else: - from sage.categories.all import Modules + from sage.categories.modules import Modules category = Modules(X.base_ring()) Homset.__init__(self, X, Y, category) diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py index aac6625d377..a4a32815197 100644 --- a/src/sage/modules/filtered_vector_space.py +++ b/src/sage/modules/filtered_vector_space.py @@ -99,16 +99,20 @@ in Vector space of dimension 3 over Algebraic Field """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2013 Volker Braun <vbraun.name@gmail.com> # # 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/ -#***************************************************************************** - -from sage.rings.all import QQ, ZZ, RDF, RR, Integer +# https://www.gnu.org/licenses/ +# *************************************************************************** + +from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ +from sage.rings.real_double import RDF +from sage.rings.real_mpfr import RR +from sage.rings.integer import Integer from sage.rings.infinity import InfinityRing, infinity, minus_infinity from sage.categories.fields import Fields from sage.modules.free_module import FreeModule_ambient_field, VectorSpace @@ -275,7 +279,7 @@ def construct_from_dim_degree(dim, max_degree, base_ring, check): dim = ZZ(dim) from sage.matrix.constructor import identity_matrix generators = identity_matrix(base_ring, dim).columns() - filtration = dict() + filtration = {} if max_degree is None: max_degree = infinity filtration[normalize_degree(max_degree)] = range(dim) @@ -314,7 +318,7 @@ def normalize_gen(v): generators = tuple(sorted(generators)) # normalize filtration data - normalized = dict() + normalized = {} for deg, gens_deg in filtration.items(): indices = [generators.index(normalize_gen(v)) for v in gens_deg] normalized[deg] = tuple(indices) @@ -373,13 +377,13 @@ def construct_from_generators_indices(generators, filtration, base_ring, check): v.set_immutable() # normalize filtration data - normalized = dict() + normalized = {} for deg, gens in filtration.items(): deg = normalize_degree(deg) - gens = [ZZ(i) for i in gens] - if any(i < 0 or i >= len(generators) for i in gens): + gens = tuple(sorted(ZZ(i) for i in gens)) + if gens and (gens[0] < 0 or gens[-1] >= len(generators)): raise ValueError('generator index out of bounds') - normalized[deg] = tuple(sorted(gens)) + normalized[deg] = gens try: del normalized[minus_infinity] except KeyError: @@ -389,8 +393,6 @@ def construct_from_generators_indices(generators, filtration, base_ring, check): return FilteredVectorSpace_class(base_ring, dim, generators, filtration, check=check) - - class FilteredVectorSpace_class(FreeModule_ambient_field): def __init__(self, base_ring, dim, generators, filtration, check=True): @@ -734,7 +736,7 @@ def graded(self, d): Basis matrix: [1 1] """ - return self.get_degree(d).quotient(self.get_degree(d+1)) + return self.get_degree(d).quotient(self.get_degree(d + 1)) def presentation(self): """ @@ -760,7 +762,7 @@ def presentation(self): generators.update(V.echelonized_basis()) generators = tuple(sorted(generators)) - filtration = dict() + filtration = {} for d, V in filt: indices = [ZZ(generators.index(v)) for v in V.echelonized_basis()] filtration[d] = tuple(indices) @@ -770,6 +772,8 @@ def _repr_field_name(self): """ Return an abbreviated field name as string + .. NOTE: This should rather be a method of fields and rings. + RAISES: ``NotImplementedError``: The field does not have an @@ -1001,15 +1005,15 @@ def direct_sum(self, other): self_gens, self_filt = self.presentation() other_gens, other_filt = other.presentation() generators = \ - [ list(v) + [base_ring.zero()]*other.dimension() for v in self_gens ] + \ - [ [base_ring.zero()]*self.dimension() + list(v) for v in other_gens ] + [list(v) + [base_ring.zero()] * other.dimension() for v in self_gens] + \ + [[base_ring.zero()] * self.dimension() + list(v) for v in other_gens] # construct the filtration dictionary def join_indices(self_indices, other_indices): self_indices = tuple(self_indices) other_indices = tuple(i + len(self_gens) for i in other_indices) return self_indices + other_indices - filtration = dict() + filtration = {} self_indices = set() other_indices = set() degrees = list(self_filt) + list(other_filt) @@ -1071,7 +1075,7 @@ def tensor_product(self, other): W_coll = VectorCollection(W_generators, base_ring, W.dimension()) T = TensorOperation([V_coll, W_coll], 'product') - filtration = dict() + filtration = {} for V_deg in V.support(): for W_deg in W.support(): deg = V_deg + W_deg @@ -1112,7 +1116,7 @@ def _power_operation(self, n, operation): T = TensorOperation([V] * n, operation) iters = [self.support()] * n - filtration = dict() + filtration = {} from sage.categories.cartesian_product import cartesian_product for degrees in cartesian_product(iters): deg = sum(degrees) @@ -1124,7 +1128,6 @@ def _power_operation(self, n, operation): filtration[deg] = filt_deg return FilteredVectorSpace(T.vectors(), filtration, base_ring=self.base_ring()) - def exterior_power(self, n): """ Return the `n`-th graded exterior power. @@ -1204,7 +1207,7 @@ def dual(self): sage: F.dual().support() (-2, 0) """ - filtration = dict() + filtration = {} prev_deg = minus_infinity for deg, V in self._filt[1:]: filtration[-prev_deg] = V.complement().echelonized_basis() @@ -1226,7 +1229,7 @@ def shift(self, deg): (-5, -3) """ generators, filtration = self.presentation() - shifted = dict() + shifted = {} for d, indices in filtration.items(): shifted[d + deg] = indices return FilteredVectorSpace(generators, shifted, base_ring=self.base_ring()) @@ -1269,7 +1272,7 @@ def random_deformation(self, epsilon=None): R = self.base_ring() if epsilon is None: epsilon = R.one() - filtration = dict() + filtration = {} for deg, filt in self._filt[1:]: generators = [v + epsilon * random_vector(R, self.rank()) for v in filt.echelonized_basis()] diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b849f1e86ee..743fc2fbbcc 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -4075,13 +4075,13 @@ def span_of_basis(self, basis, base_ring=None, check=True, already_echelonized=F try: M = self.change_ring(base_ring) except TypeError: - raise ValueError("Argument base_ring (= %s) is not compatible "%base_ring + \ - "with the base ring (= %s)."%self.base_ring()) + raise ValueError("Argument base_ring (= %s) is not compatible " % base_ring + + "with the base ring (= %s)." % self.base_ring()) try: return M.span_of_basis(basis) except TypeError: - raise ValueError("Argument gens (= %s) is not compatible "%basis + \ - "with base_ring (= %s)."%base_ring) + raise ValueError("Argument gens (= %s) is not compatible "%basis + + "with base_ring (= %s)." % base_ring) def submodule_with_basis(self, basis, check=True, already_echelonized=False): r""" @@ -4611,7 +4611,7 @@ def span_of_basis(self, basis, base_ring=None, check=True, already_echelonized=F M = self.change_ring(base_ring) except TypeError: raise ValueError("Argument base_ring (= %s) is not compatible with the base field (= %s)." % ( - base_ring, self.base_field() )) + base_ring, self.base_field())) try: return M.span_of_basis(basis) except TypeError: @@ -5653,7 +5653,7 @@ def basis(self): """ try: return self.__basis - except AttributeError: + except AttributeError: ZERO = self(0) one = self.coordinate_ring().one() w = [] @@ -5681,7 +5681,9 @@ def echelonized_basis(self): def change_ring(self, R): """ - Return the ambient free module over R of the same rank as self. + Return the ambient free module over ``R`` of the same rank as ``self``. + + This also preserves the sparsity. EXAMPLES:: @@ -5690,20 +5692,27 @@ def change_ring(self, R): sage: A = ZZ^3; A.change_ring(GF(5)) Vector space of dimension 3 over Finite Field of size 5 - For ambient modules any change of rings is defined. - - :: + For ambient modules any change of rings is defined:: sage: A = GF(5)**3; A.change_ring(QQ) Vector space of dimension 3 over Rational Field + + TESTS: + + Check for :trac:`29630`:: + + sage: V = VectorSpace(QQ, 2, sparse=True) + sage: V.change_ring(RR).is_sparse() + True """ if self.base_ring() is R: return self from .free_quadratic_module import is_FreeQuadraticModule if is_FreeQuadraticModule(self): - return FreeModule(R, self.rank(), inner_product_matrix=self.inner_product_matrix()) - else: - return FreeModule(R, self.rank()) + return FreeModule(R, self.rank(), + inner_product_matrix=self.inner_product_matrix(), + sparse=self.is_sparse()) + return FreeModule(R, self.rank(), sparse=self.is_sparse()) def linear_combination_of_basis(self, v): """ @@ -6591,7 +6600,7 @@ def _echelon_matrix_richcmp(self, other, op): lx = self.ambient_vector_space() rx = other.ambient_vector_space() if lx != rx: - return lx._echelon_matrix_richcmp( rx, op) + return lx._echelon_matrix_richcmp(rx, op) lx = self.dimension() rx = other.dimension() @@ -6709,7 +6718,7 @@ def _denominator(self, B): if not B: return 1 d = B[0].denominator() - from sage.arith.all import lcm + from sage.arith.functions import lcm for x in B[1:]: d = lcm(d,x.denominator()) return d @@ -8243,4 +8252,3 @@ def __richcmp__(self, other, op): True """ return self.obj._echelon_matrix_richcmp(other.obj, op) - diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index de627d5939a..6c1c5083682 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -371,7 +371,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): ... TypeError: cannot convert 2-dimensional array to a vector - If any of the arguments to vector have Python type int, long, real, + If any of the arguments to vector have Python type int, real, or complex, they will first be coerced to the appropriate Sage objects. This fixes :trac:`3847`. :: @@ -475,7 +475,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): # We first efficiently handle the important special case of the zero vector # over a ring. See trac 11657. # !! PLEASE DO NOT MOVE THIS CODE LOWER IN THIS FUNCTION !! - arg1_integer = isinstance(arg1, (int, long, Integer)) + arg1_integer = isinstance(arg1, (int, Integer)) if arg2 is None and is_Ring(arg0) and arg1_integer: M = FreeModule(arg0, arg1, bool(sparse)) v = M.zero_vector() @@ -898,14 +898,14 @@ def random_vector(ring, degree=None, *args, **kwds): ... ValueError: degree of a random vector must be non-negative, not -9 """ - if isinstance(ring, (Integer, int, long)): - if not degree is None: + if isinstance(ring, (Integer, int)): + if degree is not None: arglist = list(args) arglist.insert(0, degree) args = tuple(arglist) degree = ring ring = ZZ - if not isinstance(degree,(Integer, int, long)): + if not isinstance(degree, (Integer, int)): raise TypeError("degree of a random vector must be an integer, not %s" % degree) if degree < 0: raise ValueError("degree of a random vector must be non-negative, not %s" % degree) @@ -1536,10 +1536,20 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: v = vector(QQ['x,y'], [1..5]); v.change_ring(GF(3)) (1, 2, 0, 1, 2) + + TESTS: + + Check for :trac:`29630`:: + + sage: v = vector(QQ, 4, {0:1}, sparse=True) + sage: v.change_ring(AA).is_sparse() + True """ if self.base_ring() is R: return self M = self._parent.change_ring(R) + if M.is_sparse(): + return M(self.dict(), coerce=True) return M(self.list(), coerce=True) def coordinate_ring(self): @@ -1592,7 +1602,7 @@ cdef class FreeModuleElement(Vector): # abstract base class if isinstance(ord, AnInfinity): return ord v.append(ord) - from sage.arith.all import lcm + from sage.arith.functions import lcm return lcm(v) def items(self): @@ -3743,7 +3753,7 @@ cdef class FreeModuleElement(Vector): # abstract base class from sage.misc.latex import latex vector_delimiters = latex.vector_delimiters() s = '\\left' + vector_delimiters[0] - s += ',\,'.join(latex(a) for a in self.list()) + s += r',\,'.join(latex(a) for a in self.list()) return s + '\\right' + vector_delimiters[1] def dense_vector(self): @@ -4371,7 +4381,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): sage: w.pairwise_product(v) (2*x^2, x^3, 3*x^2 + 9*x) """ - if not right._parent is left._parent: + if right._parent is not left._parent: right = left.parent().ambient_module()(right) cdef list a = left._entries cdef list b = (<FreeModuleElement_generic_dense>right)._entries diff --git a/src/sage/modules/free_module_morphism.py b/src/sage/modules/free_module_morphism.py index 6a112282fa0..f7508ebeb97 100644 --- a/src/sage/modules/free_module_morphism.py +++ b/src/sage/modules/free_module_morphism.py @@ -61,6 +61,7 @@ def is_FreeModuleMorphism(x): """ return isinstance(x, FreeModuleMorphism) + class FreeModuleMorphism(matrix_morphism.MatrixMorphism): def __init__(self, parent, A, side="left"): """ @@ -80,7 +81,7 @@ def __init__(self, parent, A, side="left"): <class 'sage.modules.free_module_morphism.FreeModuleMorphism'> """ if not free_module_homspace.is_FreeModuleHomspace(parent): - raise TypeError("parent (=%s) must be a free module hom space"%parent) + raise TypeError("parent (=%s) must be a free module hom space" % parent) if isinstance(A, matrix_morphism.MatrixMorphism): A = A.matrix() A = parent._matrix_space(side)(A) @@ -162,15 +163,15 @@ def _repr_(self): The representation displays which side of the vectors the matrix is acting:: - sage: V = ZZ^3 - sage: h = V.hom([V.1, V.2, V.0]); h + sage: V = ZZ^3 + sage: h = V.hom([V.1, V.2, V.0]); h Free module morphism defined by the matrix [0 1 0] [0 0 1] [1 0 0] Domain: Ambient free module of rank 3 over the principal ideal domain Integer Ring Codomain: Ambient free module of rank 3 over the principal ideal domain Integer Ring - sage: h2 = V.hom([V.1, V.2, V.0], side="right"); h2 + sage: h2 = V.hom([V.1, V.2, V.0], side="right"); h2 Free module morphism defined as left-multiplication by the matrix [0 0 1] [1 0 0] @@ -551,18 +552,18 @@ def eigenvectors(self,extend=True): ], 1), (2, [ (0, 1, 0, 17/7) ], 2)] - + :: - sage: V = QQ^2 - sage: m = matrix(2, [1, 1, 0, 1]) - sage: V.hom(m, side="right").eigenvectors() + sage: V = QQ^2 + sage: m = matrix(2, [1, 1, 0, 1]) + sage: V.hom(m, side="right").eigenvectors() [(1, [ (1, 0) ], 2)] - sage: V.hom(m).eigenvectors() + sage: V.hom(m).eigenvectors() [(1, [ (0, 1) @@ -638,16 +639,16 @@ def eigenspaces(self,extend=True): Basis matrix: [0 1 0] [0 0 1])] - + :: - sage: V = QQ^2; m = matrix(2, [1, 1, 0, 1]) - sage: V.hom(m, side="right").eigenspaces() + sage: V = QQ^2; m = matrix(2, [1, 1, 0, 1]) + sage: V.hom(m, side="right").eigenspaces() [(1, Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0])] - sage: V.hom(m).eigenspaces() + sage: V.hom(m).eigenspaces() [(1, Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: @@ -796,7 +797,7 @@ class BaseIsomorphism1D_to_FM(BaseIsomorphism1D): sage: W, from_W, to_W = R.free_module(R, basis=4) Traceback (most recent call last): ... - ValueError: Basis element must be a unit + ValueError: basis element must be a unit """ def __init__(self, parent, basis=None): """ @@ -852,7 +853,7 @@ class BaseIsomorphism1D_from_FM(BaseIsomorphism1D): sage: W, from_W, to_W = R.free_module(R, basis=x) Traceback (most recent call last): ... - ValueError: Basis element must be a unit + ValueError: basis element must be a unit """ def __init__(self, parent, basis=None): """ @@ -878,5 +879,4 @@ def _call_(self, x): """ if self._basis is None: return x[0] - else: - return self.codomain()(x[0] / self._basis) + return self.codomain()(x[0] / self._basis) diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index 8710352f87a..b512db6ffbc 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -8,9 +8,9 @@ can specify and change. Create the free module of rank `n` over an arbitrary commutative ring `R` -using the command ``FreeModule(R,n)`` with a given inner_product_matrix. +using the command ``FreeModule(R,n)`` with a given ``inner_product_matrix``. -The following example illustrates the creation of both a vector spaces +The following example illustrates the creation of both a vector space and a free module over the integers and a submodule of it. Use the functions ``FreeModule``, ``span`` and member functions of free modules to create free modules. ''Do not use the ``FreeModule_xxx`` constructors @@ -56,7 +56,6 @@ - David Kohel (2008-06): First created (based on free_module.py) """ - # **************************************************************************** # Copyright (C) 2008 David Kohel <kohel@iml.univ-mrs.fr> # @@ -66,39 +65,38 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - import weakref import sage.matrix.matrix_space import sage.misc.latex as latex import sage.rings.ring as ring -import sage.rings.integer from sage.categories.principal_ideal_domains import PrincipalIdealDomains from . import free_module -############################################################################### +# ############################################################################# # # Constructor functions # -############################################################################### +# ############################################################################# _cache = {} -def FreeQuadraticModule( - base_ring, rank, inner_product_matrix, sparse=False, inner_product_ring=None): + +def FreeQuadraticModule(base_ring, rank, inner_product_matrix, + sparse=False, inner_product_ring=None): r""" Create the free quadratic module over the given commutative ring of the given rank. INPUT: - - base_ring -- a commutative ring + - ``base_ring`` -- a commutative ring - - rank -- a nonnegative integer + - ``rank`` -- a nonnegative integer - - inner_product_matrix -- the inner product matrix + - ``inner_product_matrix`` -- the inner product matrix - - sparse -- bool; (default False) + - ``sparse`` -- bool; (default ``False``) - - inner_product_ring -- the inner product codomain ring; (default None) + - ``inner_product_ring`` -- the inner product codomain ring; (default ``None``) OUTPUT: @@ -106,9 +104,9 @@ def FreeQuadraticModule( .. NOTE:: - In Sage it is the case that there is only one dense and one - sparse free ambient quadratic module of rank `n` over `R` and given - inner product matrix. + In Sage, it is the case that there is only one dense and one + sparse free ambient quadratic module of rank `n` over `R` and + given inner product matrix. EXAMPLES:: @@ -140,7 +138,7 @@ def FreeQuadraticModule( # In order to use coercion into the inner_product_ring we need to pass # this ring into the vector classes. if inner_product_ring is not None: - raise NotImplementedError("An inner_product_ring cannot currently be defined.") + raise NotImplementedError("an inner_product_ring cannot currently be defined") # We intentionally create a new matrix instead of using the given # inner_product_matrix. This ensures that the matrix has the correct @@ -162,11 +160,11 @@ def FreeQuadraticModule( if not base_ring.is_commutative(): raise TypeError("base_ring must be a commutative ring") - #elif not sparse and isinstance(base_ring,sage.rings.real_double.RealDoubleField_class): - # M = RealDoubleQuadraticSpace_class(rank, inner_product_matrix=inner_product_matrix, sparse=False) + # elif not sparse and isinstance(base_ring,sage.rings.real_double.RealDoubleField_class): + # M = RealDoubleQuadraticSpace_class(rank, inner_product_matrix=inner_product_matrix, sparse=False) - #elif not sparse and isinstance(base_ring,sage.rings.complex_double.ComplexDoubleField_class): - # M = ComplexDoubleQuadraticSpace_class(rank, inner_product_matrix=inner_product_matrix, sparse=False) + # elif not sparse and isinstance(base_ring,sage.rings.complex_double.ComplexDoubleField_class): + # M = ComplexDoubleQuadraticSpace_class(rank, inner_product_matrix=inner_product_matrix, sparse=False) elif base_ring.is_field(): M = FreeQuadraticModule_ambient_field( @@ -186,6 +184,7 @@ def FreeQuadraticModule( _cache[key] = weakref.ref(M) return M + def QuadraticSpace(K, dimension, inner_product_matrix, sparse=False): """ EXAMPLES: @@ -213,25 +212,27 @@ def QuadraticSpace(K, dimension, inner_product_matrix, sparse=False): sage: QuadraticSpace(ZZ,5,identity_matrix(ZZ,2)) Traceback (most recent call last): ... - TypeError: Argument K (= Integer Ring) must be a field. + TypeError: argument K (= Integer Ring) must be a field """ if not K.is_field(): - raise TypeError("Argument K (= %s) must be a field." % K) + raise TypeError(f"argument K (= {K}) must be a field") if sparse not in (True, False): raise TypeError("Argument sparse (= %s) must be a boolean." % sparse) return FreeQuadraticModule(K, rank=dimension, inner_product_matrix=inner_product_matrix, sparse=sparse) + InnerProductSpace = QuadraticSpace -############################################################################### + +# ############################################################################# # # Base class for all free modules # -############################################################################### +# ############################################################################# def is_FreeQuadraticModule(M): """ - Return True if `M` is a free quadratic module. + Return ``True`` if `M` is a free quadratic module. EXAMPLES:: @@ -248,6 +249,7 @@ def is_FreeQuadraticModule(M): """ return isinstance(M, FreeQuadraticModule_generic) + class FreeQuadraticModule_generic(free_module.FreeModule_generic): """ Base class for all free quadratic modules. @@ -302,13 +304,13 @@ class FreeQuadraticModule_generic(free_module.FreeModule_generic): """ def __init__(self, base_ring, rank, degree, inner_product_matrix, sparse=False): """ - Create the free module of given rank over the given base_ring. + Create the free module of given rank over the given ``base_ring``. INPUT: - - base_ring -- a commutative ring + - ``base_ring`` -- a commutative ring - - rank -- a non-negative integer + - ``rank`` -- a non-negative integer EXAMPLES:: @@ -326,9 +328,9 @@ def __init__(self, base_ring, rank, degree, inner_product_matrix, sparse=False): def _dense_module(self): """ - Creates a dense module with the same defining data as self. + Create a dense module with the same defining data as ``self``. - N.B. This function is for internal use only! See dense_module for use. + .. NOTE:: This function is for internal use only! See ``dense_module`` for use. EXAMPLES:: @@ -343,9 +345,9 @@ def _dense_module(self): def _sparse_module(self): """ - Creates a sparse module with the same defining data as self. + Create a sparse module with the same defining data as ``self``. - N.B. This function is for internal use only! See sparse_module for use. + .. NOTE:: This function is for internal use only! See ``sparse_module`` for use. EXAMPLES:: @@ -399,9 +401,10 @@ def determinant(self): def discriminant(self): """ - Return the discriminant of this free module, defined to be (-1)^r - of the determinant, where r = n/2 (n even) or (n-1)/2 (n odd) for - a module of rank n. + Return the discriminant of this free module. + + This is defined to be `(-1)^r` of the determinant, where `r = n/2` + (`n` even) or `(n-1)/2` (`n` odd) for a module of rank `n`. EXAMPLES:: @@ -417,23 +420,23 @@ def discriminant(self): TESTS:: - sage: M=FreeQuadraticModule(ZZ,2,matrix.identity(2)) + sage: M = FreeQuadraticModule(ZZ, 2, matrix.identity(2)) sage: M.discriminant() -1 - sage: M=FreeQuadraticModule(QQ,3,matrix.identity(3)) + sage: M = FreeQuadraticModule(QQ, 3, matrix.identity(3)) sage: M.discriminant() -1 - """ - n = self.rank() - r = n//2 - return (-1)**r*self.gram_matrix().determinant() + r = self.rank() // 2 + return (-1)**r * self.gram_matrix().determinant() def gram_matrix(self): """ - Return the gram matrix associated to this free module, defined to be - G = B*A*B.transpose(), where A is the inner product matrix (induced from - the ambient space), and B the basis matrix. + Return the Gram matrix associated to this free module. + + This is defined to be ``B*A*B.transpose()``, where ``A`` is the + inner product matrix (induced from the ambient space), and ``B`` + the basis matrix. EXAMPLES:: @@ -451,28 +454,30 @@ def gram_matrix(self): [1 1 1] [1 2 1] [1 1 2] - """ if self.is_ambient(): return self.inner_product_matrix() - else: - if self._gram_matrix is None: - A = self.inner_product_matrix() - B = self.basis_matrix() - self._gram_matrix = B*A*B.transpose() - return self._gram_matrix + if self._gram_matrix is None: + A = self.inner_product_matrix() + B = self.basis_matrix() + self._gram_matrix = B * A * B.transpose() + return self._gram_matrix def inner_product_matrix(self): """ - Return the inner product matrix associated to this module. By definition this - is the inner product matrix of the ambient space, hence may be of degree greater - than the rank of the module. + Return the inner product matrix associated to this module. + + By definition, this is the inner product matrix of the ambient + space, hence may be of degree greater than the rank of the + module. + + .. NOTE:: The inner product does not have to be symmetric (see examples). - N.B. The inner product does not have to be symmetric (see examples). + .. TODO:: - TODO: Differentiate the image ring of the inner product from the base ring of - the module and/or ambient space. E.g. On an integral module over ZZ the inner - product pairing could naturally take values in ZZ, QQ, RR, or CC. + Differentiate the image ring of the inner product from the base ring of + the module and/or ambient space. E.g. On an integral module over ZZ the inner + product pairing could naturally take values in ZZ, QQ, RR, or CC. EXAMPLES:: @@ -494,7 +499,7 @@ def inner_product_matrix(self): sage: v.inner_product(u) 2 - The inner product matrix is defined with respect to the ambient space. + The inner product matrix is defined with respect to the ambient space:: sage: V = QQ^3 sage: u = V([1/2,1,1]) @@ -509,15 +514,16 @@ def inner_product_matrix(self): sage: M.gram_matrix() [ 1/2 -3/4] [-3/4 13/4] - """ return self._inner_product_matrix def _inner_product_is_dot_product(self): """ Return whether or not the inner product on this module is induced by - the dot product on the ambient vector space. This is used internally - by the inner_product function for optimization. + the dot product on the ambient vector space. + + This is used internally by the ``inner_product`` function for + optimization. EXAMPLES:: @@ -538,12 +544,16 @@ def _inner_product_is_dot_product(self): def _inner_product_is_diagonal(self): """ Return whether or not the inner product on this module is induced by - the dot product on the ambient vector space. This is used internally - by the inner_product function for optimization. + the dot product on the ambient vector space. + + This is used internally by the ``inner_product`` function for + optimization. + + .. NOTE:: - N.B. The FreeModule classes have the identity inner product matrix, - while FreeQuadraticModules must have an inner_product_matrix, although - it can be diagonal. + The ``FreeModule`` classes have the identity inner product matrix, + while ``FreeQuadraticModules`` must have an ``inner_product_matrix``, although + it can be diagonal. EXAMPLES:: @@ -565,13 +575,12 @@ def _inner_product_is_diagonal(self): .. TODO:: Actually use the diagonal form of the inner product. """ A = self.inner_product_matrix() - D = sage.matrix.constructor.diagonal_matrix([A[i, i] - for i in range(A.nrows())]) + D = sage.matrix.constructor.diagonal_matrix(A.diagonal()) return A == D -class FreeQuadraticModule_generic_pid( - free_module.FreeModule_generic_pid, FreeQuadraticModule_generic): +class FreeQuadraticModule_generic_pid(free_module.FreeModule_generic_pid, + FreeQuadraticModule_generic): """ Class of all free modules over a PID. """ @@ -589,16 +598,17 @@ def __init__(self, base_ring, rank, degree, inner_product_matrix, sparse=False): """ free_module.FreeModule_generic_pid.__init__( self, base_ring=base_ring, rank=rank, degree=degree, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def span(self, gens, check=True, already_echelonized=False): """ - Return the R-span of the given list of gens, where R - is the base ring of self. Note that this span need not - be a submodule of self, nor even of the ambient space. - It must, however, be contained in the ambient vector space, i.e., - the ambient space tensored with the fraction field of R. + Return the `R`-span of the given list of gens, where `R` + is the base ring of ``self``. + + Note that this span need not be a submodule of ``self``, nor even + of the ambient space. It must, however, be contained in the + ambient vector space, i.e., the ambient space tensored with + the fraction field of `R`. EXAMPLES:: @@ -619,11 +629,13 @@ def span(self, gens, check=True, already_echelonized=False): def span_of_basis(self, basis, check=True, already_echelonized=False): r""" - Return the free R-module with the given basis, where R - is the base ring of self. Note that this R-module need not - be a submodule of self, nor even of the ambient space. It - must, however, be contained in the ambient vector space, i.e., - the ambient space tensored with the fraction field of R. + Return the free `R`-module with the given basis, where `R` + is the base ring of ``self``. + + Note that this `R`-module need not be a submodule of ``self``, nor + even of the ambient space. It must, however, be contained in + the ambient vector space, i.e., the ambient space tensored + with the fraction field of `R`. EXAMPLES:: @@ -673,14 +685,15 @@ def zero_submodule(self): return FreeQuadraticModule_submodule_pid( self.ambient_module(), [], self.inner_product_matrix(), check=False) -class FreeQuadraticModule_generic_field( - free_module.FreeModule_generic_field, FreeQuadraticModule_generic_pid): + +class FreeQuadraticModule_generic_field(free_module.FreeModule_generic_field, + FreeQuadraticModule_generic_pid): """ Base class for all free modules over fields. """ def __init__(self, base_field, dimension, degree, inner_product_matrix, sparse=False): """ - Creates a vector space over a field. + Create a vector space over a field. EXAMPLES:: @@ -701,26 +714,27 @@ def __init__(self, base_field, dimension, degree, inner_product_matrix, sparse=F [0 0 0 0 0 0 1] """ if not isinstance(base_field, ring.Field): - raise TypeError("The base_field (=%s) must be a field" % base_field) + raise TypeError("the base_field (=%s) must be a field" % base_field) free_module.FreeModule_generic_field.__init__( self, base_field=base_field, dimension=dimension, degree=degree, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def span(self, gens, check=True, already_echelonized=False): """ - Return the K-span of the given list of gens, where K is the - base field of self. Note that this span is a subspace of the - ambient vector space, but need not be a subspace of self. + Return the `K`-span of the given list of gens, where `K` is the + base field of ``self``. + + Note that this span is a subspace of the ambient vector space, + but need not be a subspace of ``self``. INPUT: - - gens -- list of vectors + - ``gens`` -- list of vectors - - check -- bool (default: True): whether or not to coerce + - ``check`` -- bool (default: ``True``): whether or not to coerce entries of gens into base field - - already_echelonized -- bool (default: False): set this if + - ``already_echelonized`` -- bool (default: ``False``): set this if you know the gens are already in echelon form EXAMPLES:: @@ -738,7 +752,7 @@ def span(self, gens, check=True, already_echelonized=False): if free_module.is_FreeModule(gens): gens = gens.gens() if not isinstance(gens, (list, tuple)): - raise TypeError("gens (=%s) must be a list or tuple"%gens) + raise TypeError("gens (=%s) must be a list or tuple" % gens) return FreeQuadraticModule_submodule_field( self.ambient_module(), gens, @@ -747,19 +761,20 @@ def span(self, gens, check=True, already_echelonized=False): def span_of_basis(self, basis, check=True, already_echelonized=False): r""" - Return the free K-module with the given basis, where K - is the base field of self. Note that this span is - a subspace of the ambient vector space, but need - not be a subspace of self. + Return the free `K`-module with the given basis, where `K` + is the base field of ``self``. + + Note that this span is a subspace of the ambient vector space, + but need not be a subspace of ``self``. INPUT: - - basis -- list of vectors + - ``basis`` -- list of vectors - - check -- bool (default: True): whether or not to coerce + - ``check`` -- bool (default: ``True``): whether or not to coerce entries of gens into base field - - already_echelonized -- bool (default: False): set this if + - ``already_echelonized`` -- bool (default: ``False``): set this if you know the gens are already in echelon form EXAMPLES:: @@ -788,40 +803,39 @@ def span_of_basis(self, basis, check=True, already_echelonized=False): inner_product_matrix=self.inner_product_matrix(), check=check, already_echelonized=already_echelonized) -############################################################################### + +# ############################################################################# # # Generic ambient free modules, i.e., of the form R^n for some commutative ring R. # -############################################################################### +# ############################################################################# -class FreeQuadraticModule_ambient( - free_module.FreeModule_ambient, FreeQuadraticModule_generic): +class FreeQuadraticModule_ambient(free_module.FreeModule_ambient, + FreeQuadraticModule_generic): """ Ambient free module over a commutative ring. """ def __init__(self, base_ring, rank, inner_product_matrix, sparse=False): """ - The free module of given rank over the given base_ring. + The free module of given rank over the given ``base_ring``. INPUT: - - base_ring -- a commutative ring + - ``base_ring`` -- a commutative ring - - rank -- a non-negative integer + - ``rank`` -- a non-negative integer EXAMPLES:: sage: FreeModule(ZZ, 4) Ambient free module of rank 4 over the principal ideal domain Integer Ring - """ free_module.FreeModule_ambient.__init__(self, base_ring=base_ring, rank=rank, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -848,11 +862,10 @@ def _repr_(self): Ambient sparse free module of rank 12 over Ring of integers modulo 12 """ if self.is_sparse(): - return "Ambient sparse free quadratic module of rank %s over %s\n" % ( self.rank(), self.base_ring() ) + \ - "Inner product matrix:\n%s" % self.inner_product_matrix() - else: - return "Ambient free quadratic module of rank %s over %s\n" % ( self.rank(), self.base_ring() ) + \ + return "Ambient sparse free quadratic module of rank %s over %s\n" % (self.rank(), self.base_ring()) + \ "Inner product matrix:\n%s" % self.inner_product_matrix() + return "Ambient free quadratic module of rank %s over %s\n" % (self.rank(), self.base_ring()) + \ + "Inner product matrix:\n%s" % self.inner_product_matrix() def _latex_(self): r""" @@ -871,16 +884,18 @@ def _latex_(self): sage: V = QuadraticSpace(QQ,3,inner_product_matrix=[[2,1,0],[1,4,1],[0,1,8]]) sage: latex(V) - None + Traceback (most recent call last): + ... + NotImplementedError """ # How do we want to represent this object? - NotImplementedError + raise NotImplementedError def _dense_module(self): """ - Creates a dense module with the same defining data as self. + Create a dense module with the same defining data as ``self``. - N.B. This function is for internal use only! See dense_module for use. + .. NOTE:: This function is for internal use only! See dense_module for use. EXAMPLES:: @@ -890,14 +905,15 @@ def _dense_module(self): sage: M is S._dense_module() True """ - return FreeQuadraticModule(base_ring=self.base_ring(), rank = self.rank(), - inner_product_matrix = self.inner_product_matrix(), sparse=False) + return FreeQuadraticModule(base_ring=self.base_ring(), rank=self.rank(), + inner_product_matrix=self.inner_product_matrix(), + sparse=False) def _sparse_module(self): """ - Creates a sparse module with the same defining data as self. + Create a sparse module with the same defining data as ``self``. - N.B. This function is for internal use only! See sparse_module for use. + .. NOTE:: This function is for internal use only! See sparse_module for use. EXAMPLES:: @@ -907,17 +923,19 @@ def _sparse_module(self): sage: M._sparse_module() is S True """ - return FreeQuadraticModule(base_ring = self.base_ring(), rank = self.rank(), - inner_product_matrix = self.inner_product_matrix(), sparse=True) + return FreeQuadraticModule(base_ring=self.base_ring(), rank=self.rank(), + inner_product_matrix=self.inner_product_matrix(), + sparse=True) -############################################################################### + +# ############################################################################# # # Ambient free modules over an integral domain. # -############################################################################### +# ############################################################################# -class FreeQuadraticModule_ambient_domain( - free_module.FreeModule_ambient_domain, FreeQuadraticModule_ambient): +class FreeQuadraticModule_ambient_domain(free_module.FreeModule_ambient_domain, + FreeQuadraticModule_ambient): """ Ambient free quadratic module over an integral domain. """ @@ -930,12 +948,11 @@ def __init__(self, base_ring, rank, inner_product_matrix, sparse=False): Univariate Polynomial Ring in x over Finite Field of size 5 """ free_module.FreeModule_ambient.__init__(self, base_ring=base_ring, rank=rank, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -975,17 +992,16 @@ def _repr_(self): -b^2 + 4*a*c """ if self.is_sparse(): - return "Ambient sparse free quadratic module of rank %s over the integral domain %s\n"%( - self.rank(), self.base_ring() ) + \ - "Inner product matrix:\n%s" % self.inner_product_matrix() - else: - return "Ambient free quadratic module of rank %s over the integral domain %s\n"%( - self.rank(), self.base_ring() ) + \ + return "Ambient sparse free quadratic module of rank %s over the integral domain %s\n" % ( + self.rank(), self.base_ring()) + \ "Inner product matrix:\n%s" % self.inner_product_matrix() + return "Ambient free quadratic module of rank %s over the integral domain %s\n" % ( + self.rank(), self.base_ring()) + \ + "Inner product matrix:\n%s" % self.inner_product_matrix() def ambient_vector_space(self): """ - Returns the ambient vector space, which is this free module tensored + Return the ambient vector space, which is this free module tensored with its fraction field. EXAMPLES:: @@ -1001,31 +1017,33 @@ def ambient_vector_space(self): inner_product_matrix=self.inner_product_matrix(), sparse=self.is_sparse()) return self.__ambient_vector_space -############################################################################### + +# ############################################################################# # # Ambient free modules over a principal ideal domain. # -############################################################################### +# ############################################################################# -class FreeQuadraticModule_ambient_pid( - free_module.FreeModule_ambient_pid, FreeQuadraticModule_generic_pid, FreeQuadraticModule_ambient_domain): +class FreeQuadraticModule_ambient_pid(free_module.FreeModule_ambient_pid, + FreeQuadraticModule_generic_pid, + FreeQuadraticModule_ambient_domain): """ Ambient free quadratic module over a principal ideal domain. """ def __init__(self, base_ring, rank, inner_product_matrix, sparse=False): """ Create the ambient free module of given rank over the given - principal ideal domain + principal ideal domain. INPUT: - - base_ring -- a principal ideal domain + - ``base_ring`` -- a principal ideal domain - - rank -- a non-negative integer + - ``rank`` -- a non-negative integer - - sparse -- bool (default: False) + - ``sparse`` -- bool (default: ``False``) - - inner_product_matrix -- bool (default: None) + - ``inner_product_matrix`` -- bool (default: ``None``) EXAMPLES:: @@ -1039,12 +1057,11 @@ def __init__(self, base_ring, rank, inner_product_matrix, sparse=False): [ 0 -1 2] """ free_module.FreeModule_ambient_pid.__init__(self, base_ring=base_ring, rank=rank, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -1078,26 +1095,25 @@ def _repr_(self): sage: N = FreeModule(ZZ,7,sparse=True) sage: N Ambient sparse free module of rank 7 over the principal ideal domain Integer Ring - """ if self.is_sparse(): - return "Ambient sparse free quadratic module of rank %s over the principal ideal domain %s\n"%( - self.rank(), self.base_ring() ) + \ - "Inner product matrix:\n%s" % self.inner_product_matrix() - else: - return "Ambient free quadratic module of rank %s over the principal ideal domain %s\n"%( + return "Ambient sparse free quadratic module of rank %s over the principal ideal domain %s\n" % ( self.rank(), self.base_ring()) + \ "Inner product matrix:\n%s" % self.inner_product_matrix() + return "Ambient free quadratic module of rank %s over the principal ideal domain %s\n" % ( + self.rank(), self.base_ring()) + \ + "Inner product matrix:\n%s" % self.inner_product_matrix() + -############################################################################### +# ############################################################################# # # Ambient free modules over a field (i.e., a vector space). # -############################################################################### +# ############################################################################# -class FreeQuadraticModule_ambient_field( - free_module.FreeModule_ambient_field, - FreeQuadraticModule_generic_field, FreeQuadraticModule_ambient_pid): +class FreeQuadraticModule_ambient_field(free_module.FreeModule_ambient_field, + FreeQuadraticModule_generic_field, + FreeQuadraticModule_ambient_pid): def __init__(self, base_field, dimension, inner_product_matrix, sparse=False): """ @@ -1105,11 +1121,11 @@ def __init__(self, base_field, dimension, inner_product_matrix, sparse=False): INPUT: - - base_field -- a field + - ``base_field`` -- a field - - dimension -- a non-negative integer + - ``dimension`` -- a non-negative integer - - sparse -- bool (default: False) + - ``sparse`` -- bool (default: ``False``) EXAMPLES:: @@ -1135,12 +1151,11 @@ def __init__(self, base_field, dimension, inner_product_matrix, sparse=False): """ free_module.FreeModule_ambient_field.__init__( self, base_field=base_field, dimension=dimension, sparse=sparse) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -1166,21 +1181,21 @@ def _repr_(self): Sparse vector space of dimension 7 over Rational Field """ if self.is_sparse(): - return "Ambient sparse free quadratic space of dimension %s over %s\n" % ( self.rank(), self.base_ring() ) + \ - "Inner product matrix:\n%s" % self.inner_product_matrix() - else: - return "Ambient quadratic space of dimension %s over %s\n" % ( self.rank(), self.base_ring() ) + \ + return "Ambient sparse free quadratic space of dimension %s over %s\n" % (self.rank(), self.base_ring()) + \ "Inner product matrix:\n%s" % self.inner_product_matrix() + return "Ambient quadratic space of dimension %s over %s\n" % (self.rank(), self.base_ring()) + \ + "Inner product matrix:\n%s" % self.inner_product_matrix() + -############################################################################### +# ############################################################################# # # R-Submodule of K^n where K is the fraction field of a principal ideal domain R. # -############################################################################### +# ############################################################################# -class FreeQuadraticModule_submodule_with_basis_pid( - free_module.FreeModule_submodule_with_basis_pid, FreeQuadraticModule_generic_pid): +class FreeQuadraticModule_submodule_with_basis_pid(free_module.FreeModule_submodule_with_basis_pid, + FreeQuadraticModule_generic_pid): r""" An `R`-submodule of `K^n` with distinguished basis, where `K` is the fraction field of a principal ideal domain `R`. @@ -1218,7 +1233,8 @@ class FreeQuadraticModule_submodule_with_basis_pid( False """ def __init__(self, ambient, basis, inner_product_matrix, - check=True, echelonize=False, echelonized_basis=None, already_echelonized=False): + check=True, echelonize=False, echelonized_basis=None, + already_echelonized=False): """ Create a free module with basis over a PID. @@ -1250,21 +1266,20 @@ def __init__(self, ambient, basis, inner_product_matrix, We test that :trac:`23703` is fixed:: - sage: A=FreeQuadraticModule(ZZ,1,matrix.identity(1)) - sage: B=A.span([[1/2]]) - sage: C=B.span([[1]]) - sage: B.intersection(C)==C.intersection(B) + sage: A = FreeQuadraticModule(ZZ, 1, matrix.identity(1)) + sage: B = A.span([[1/2]]) + sage: C = B.span([[1]]) + sage: B.intersection(C) == C.intersection(B) True """ free_module.FreeModule_submodule_with_basis_pid.__init__( self, ambient=ambient, basis=basis, check=check, echelonize=echelonize, echelonized_basis=echelonized_basis, already_echelonized=already_echelonized) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -1295,12 +1310,12 @@ def _repr_(self): [-1 0 0 0 0 0 0 1] """ if self.is_sparse(): - s = "Sparse free quadratic module of degree %s and rank %s over %s\n"%( + s = "Sparse free quadratic module of degree %s and rank %s over %s\n" % ( self.degree(), self.rank(), self.base_ring()) + \ "Basis matrix:\n%r\n" % self.basis_matrix() + \ "Inner product matrix:\n%r" % self.inner_product_matrix() else: - s = "Free quadratic module of degree %s and rank %s over %s\n"%( + s = "Free quadratic module of degree %s and rank %s over %s\n" % ( self.degree(), self.rank(), self.base_ring()) + \ "Basis matrix:\n%r\n" % self.basis_matrix() + \ "Inner product matrix:\n%r" % self.inner_product_matrix() @@ -1317,14 +1332,16 @@ def _latex_(self): sage: M._latex_() '\\mathrm{RowSpan}_{\\Bold{Z}}\\left(\\begin{array}{rrr}\n1 & 2 & 3 \\\\\n4 & 5 & 6\n\\end{array}\\right)' """ - return "\\mathrm{RowSpan}_{%s}%s"%(latex.latex(self.base_ring()), latex.latex(self.basis_matrix())) + return "\\mathrm{RowSpan}_{%s}%s" % (latex.latex(self.base_ring()), + latex.latex(self.basis_matrix())) def change_ring(self, R): """ - Return the free module over R obtained by coercing each - element of self into a vector over the fraction field of R, - then taking the resulting R-module. Raises a TypeError - if coercion is not possible. + Return the free module over `R` obtained by coercing each + element of ``self`` into a vector over the fraction field of `R`, + then taking the resulting `R`-module. + + This raises a ``TypeError`` if coercion is not possible. INPUT: @@ -1358,15 +1375,15 @@ def change_ring(self, R): K = R.fraction_field() A = self.inner_product_matrix() V = QuadraticSpace(K, self.degree(), inner_product_matrix=A) - B = [ V(b) for b in self.basis() ] + B = [V(b) for b in self.basis()] M = FreeQuadraticModule(R, self.degree(), inner_product_matrix=A) if self.has_user_basis(): return M.span_of_basis(B) - else: - return M.span(B) + return M.span(B) -class FreeQuadraticModule_submodule_pid( - free_module.FreeModule_submodule_pid, FreeQuadraticModule_submodule_with_basis_pid): + +class FreeQuadraticModule_submodule_pid(free_module.FreeModule_submodule_pid, + FreeQuadraticModule_submodule_with_basis_pid): """ An `R`-submodule of `K^n` where `K` is the fraction field of a principal ideal domain `R`. @@ -1404,12 +1421,11 @@ def __init__(self, ambient, gens, inner_product_matrix, check=True, already_eche """ free_module.FreeModule_submodule_pid.__init__( self, ambient=ambient, gens=gens, check=check, already_echelonized=already_echelonized) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -1427,18 +1443,19 @@ def _repr_(self): [ 0 0 0 0 0 0 1 -1] """ if self.is_sparse(): - s = "Sparse free module of degree %s and rank %s over %s\n"%( + s = "Sparse free module of degree %s and rank %s over %s\n" % ( self.degree(), self.rank(), self.base_ring()) + \ - "Echelon basis matrix:\n%s"%self.basis_matrix() + "Echelon basis matrix:\n%s" % self.basis_matrix() else: - s = "Free module of degree %s and rank %s over %s\n"%( + s = "Free module of degree %s and rank %s over %s\n" % ( self.degree(), self.rank(), self.base_ring()) + \ - "Echelon basis matrix:\n%s"%self.basis_matrix() + "Echelon basis matrix:\n%s" % self.basis_matrix() return s -class FreeQuadraticModule_submodule_with_basis_field( - free_module.FreeModule_submodule_with_basis_field, - FreeQuadraticModule_generic_field, FreeQuadraticModule_submodule_with_basis_pid): + +class FreeQuadraticModule_submodule_with_basis_field(free_module.FreeModule_submodule_with_basis_field, + FreeQuadraticModule_generic_field, + FreeQuadraticModule_submodule_with_basis_pid): """ An embedded vector subspace with a distinguished user basis. @@ -1512,12 +1529,11 @@ def __init__(self, ambient, basis, inner_product_matrix, free_module.FreeModule_submodule_with_basis_field.__init__( self, ambient=ambient, basis=basis, check=check, echelonize=echelonize, echelonized_basis=echelonized_basis, already_echelonized=already_echelonized) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The printing representation of self. + The printing representation of ``self``. EXAMPLES:: @@ -1565,25 +1581,26 @@ def _repr_(self): [ 0 0 0 1 -1] """ if self.is_sparse(): - return "Sparse quadratic space of degree %s and dimension %s over %s\n"%( - self.degree(), self.dimension(), self.base_field()) + \ - "Basis matrix:\n%r" % self.basis_matrix() + \ - "Inner product matrix:\n%r" % self.inner_product_matrix() - else: - return "Quadratic space of degree %s and dimension %s over %s\n"%( - self.degree(), self.dimension(), self.base_field()) + \ - "Basis matrix:\n%r\n" % self.basis_matrix() + \ - "Inner product matrix:\n%r" % self.inner_product_matrix() + return "Sparse quadratic space of degree %s and dimension %s over %s\n" % ( + self.degree(), self.dimension(), self.base_field()) + \ + "Basis matrix:\n%r" % self.basis_matrix() + \ + "Inner product matrix:\n%r" % self.inner_product_matrix() + return "Quadratic space of degree %s and dimension %s over %s\n" % ( + self.degree(), self.dimension(), self.base_field()) + \ + "Basis matrix:\n%r\n" % self.basis_matrix() + \ + "Inner product matrix:\n%r" % self.inner_product_matrix() -class FreeQuadraticModule_submodule_field( - free_module.FreeModule_submodule_field, FreeQuadraticModule_submodule_with_basis_field): + +class FreeQuadraticModule_submodule_field(free_module.FreeModule_submodule_field, + FreeQuadraticModule_submodule_with_basis_field): """ An embedded vector subspace with echelonized basis. EXAMPLES: Since this is an embedded vector subspace with echelonized basis, - the echelon_coordinates() and user coordinates() agree:: + the methods :meth:`echelon_coordinates` and :meth:`coordinates` return the same + coordinates:: sage: V = QQ^3 sage: W = V.span([[1,2,3],[4,5,6]]) @@ -1621,12 +1638,11 @@ def __init__(self, ambient, gens, inner_product_matrix, check=True, already_eche """ free_module.FreeModule_submodule_field.__init__( self, ambient=ambient, gens=gens, check=check, already_echelonized=already_echelonized) - #self._FreeQuadraticModule_generic_inner_product_matrix = inner_product_matrix self._inner_product_matrix = inner_product_matrix def _repr_(self): """ - The default printing representation of self. + The default printing representation of ``self``. EXAMPLES:: @@ -1674,29 +1690,11 @@ def _repr_(self): [ 0 0 0 1 -1] """ if self.is_sparse(): - return "Sparse quadratic space of degree %s and dimension %s over %s\n"%( - self.degree(), self.dimension(), self.base_field()) + \ - "Basis matrix:\n%r\n" % self.basis_matrix() + \ - "Inner product matrix:\n%r" % self.inner_product_matrix() - else: - return "Quadratic space of degree %s and dimension %s over %s\n"%( + return "Sparse quadratic space of degree %s and dimension %s over %s\n" % ( self.degree(), self.dimension(), self.base_field()) + \ "Basis matrix:\n%r\n" % self.basis_matrix() + \ "Inner product matrix:\n%r" % self.inner_product_matrix() - -#class RealDoubleQuadraticSpace_class(free_module.RealDoubleVectorSpace_class, FreeQuadraticModule_ambient_field): -# def __init__(self, dimension, inner_product_matrix, sparse=False): -# if sparse: -# raise NotImplementedError, "Sparse matrices over RDF not implemented yet" -# free_module.RealDoubleVectorSpace_class.__init__(self, dimension=dimension, sparse=False) -# self._inner_product_matrix = inner_product_matrix - -#class ComplexDoubleQuadraticSpace_class( -# free_module.ComplexDoubleVectorSpace_class, FreeQuadraticModule_generic): #FreeQuadraticModule_ambient_field): -# def __init__(self, dimension, inner_product_matrix, sparse=False): -# if sparse: -# raise NotImplementedError, "Sparse matrices over CDF not implemented yet" -# free_module.ComplexDoubleVectorSpace_class.__init__(self, dimension=dimension, sparse=False) -# self._inner_product_matrix = inner_product_matrix - -###################################################### + return "Quadratic space of degree %s and dimension %s over %s\n" % ( + self.degree(), self.dimension(), self.base_field()) + \ + "Basis matrix:\n%r\n" % self.basis_matrix() + \ + "Inner product matrix:\n%r" % self.inner_product_matrix() diff --git a/src/sage/modules/free_quadratic_module_integer_symmetric.py b/src/sage/modules/free_quadratic_module_integer_symmetric.py index a206f0c721a..0902a272f39 100644 --- a/src/sage/modules/free_quadratic_module_integer_symmetric.py +++ b/src/sage/modules/free_quadratic_module_integer_symmetric.py @@ -349,7 +349,7 @@ def IntegralLatticeDirectSum(Lattices, return_embeddings=False): basis = [matrix.block(1, 3, [matrix.zero(dims[i], sum_degree[i]), Lattices[i].basis_matrix(), matrix.zero(dims[i], sum_degree[-1] - sum_degree[i+1]) - ]) for i in range(N)] + ]) for i in range(N)] basis_matrix = matrix.block(N, 1, basis) ipm = ambient.inner_product_matrix() direct_sum = FreeQuadraticModule_integer_symmetric(ambient=ambient, @@ -363,6 +363,7 @@ def IntegralLatticeDirectSum(Lattices, return_embeddings=False): for i in range(N)] return [direct_sum, phi] + def IntegralLatticeGluing(Lattices, glue, return_embeddings=False): r""" Return an overlattice of the direct sum as defined by ``glue``. @@ -1092,7 +1093,7 @@ def maximal_overlattice(self, p=None): for t in D: if t != 0 and t.q() == 0: break - if t.q() != 0 : + if t.q() != 0: # no isotropic vector left break L = L.overlattice([t.lift()]) @@ -1168,11 +1169,11 @@ def orthogonal_group(self, gens=None, is_finite=None): sage: A4 = IntegralLattice("A4") sage: Aut = A4.orthogonal_group() sage: Aut - Group of isometries with 5 generators ( - [-1 0 0 0] [0 0 0 1] [-1 -1 -1 0] [ 1 0 0 0] [ 1 0 0 0] - [ 0 -1 0 0] [0 0 1 0] [ 0 0 0 -1] [-1 -1 -1 -1] [ 0 1 0 0] - [ 0 0 -1 0] [0 1 0 0] [ 0 0 1 1] [ 0 0 0 1] [ 0 0 1 1] - [ 0 0 0 -1], [1 0 0 0], [ 0 1 0 0], [ 0 0 1 0], [ 0 0 0 -1] + Group of isometries with 4 generators ( + [0 0 0 1] [-1 -1 -1 0] [ 1 0 0 0] [ 1 0 0 0] + [0 0 1 0] [ 0 0 0 -1] [-1 -1 -1 -1] [ 0 1 0 0] + [0 1 0 0] [ 0 0 1 1] [ 0 0 0 1] [ 0 0 1 1] + [1 0 0 0], [ 0 1 0 0], [ 0 0 1 0], [ 0 0 0 -1] ) The group acts from the right on the lattice and its discriminant group:: @@ -1180,19 +1181,19 @@ def orthogonal_group(self, gens=None, is_finite=None): sage: x = A4.an_element() sage: g = Aut.an_element() sage: g - [ 1 1 1 0] - [ 0 0 -1 0] - [ 0 0 1 1] - [ 0 -1 -1 -1] + [-1 -1 -1 0] + [ 0 0 1 0] + [ 0 0 -1 -1] + [ 0 1 1 1] sage: x*g - (1, 1, 1, 0) + (-1, -1, -1, 0) sage: (x*g).parent()==A4 True sage: (g*x).parent() Vector space of dimension 4 over Rational Field sage: y = A4.discriminant_group().an_element() sage: y*g - (1) + (4) If the group is finite we can compute the usual things:: @@ -1208,10 +1209,10 @@ def orthogonal_group(self, gens=None, is_finite=None): sage: A2 = IntegralLattice(matrix.identity(3),Matrix(ZZ,2,3,[1,-1,0,0,1,-1])) sage: A2.orthogonal_group() - Group of isometries with 3 generators ( - [-1/3 2/3 2/3] [ 2/3 2/3 -1/3] [1 0 0] - [ 2/3 -1/3 2/3] [ 2/3 -1/3 2/3] [0 0 1] - [ 2/3 2/3 -1/3], [-1/3 2/3 2/3], [0 1 0] + Group of isometries with 2 generators ( + [ 2/3 2/3 -1/3] [1 0 0] + [ 2/3 -1/3 2/3] [0 0 1] + [-1/3 2/3 2/3], [0 1 0] ) It can be negative definite as well:: diff --git a/src/sage/modules/matrix_morphism.py b/src/sage/modules/matrix_morphism.py index d9105fe21c4..c725989f2fc 100644 --- a/src/sage/modules/matrix_morphism.py +++ b/src/sage/modules/matrix_morphism.py @@ -817,8 +817,8 @@ def characteristic_polynomial(self, var='x'): T^2 - 3*T + 2 """ if not self.is_endomorphism(): - raise ArithmeticError("charpoly only defined for endomorphisms " +\ - "(i.e., domain = range)") + raise ArithmeticError("charpoly only defined for endomorphisms " + "(i.e., domain = range)") return self.matrix().charpoly(var) charpoly = characteristic_polynomial @@ -1236,7 +1236,7 @@ def is_identity(self): # testing for the identity matrix will only work for # endomorphisms which have the same basis for domain and codomain # so we test equality on a basis, which is sufficient - return all( self(u) == u for u in self.domain().basis() ) + return all(self(u) == u for u in self.domain().basis()) def is_zero(self): r""" @@ -1369,7 +1369,7 @@ def is_equal_function(self, other): if self.codomain() != other.codomain(): return False # check agreement on any basis of the domain - return all( self(u) == other(u) for u in self.domain().basis() ) + return all(self(u) == other(u) for u in self.domain().basis()) def restrict_domain(self, sub): """ diff --git a/src/sage/modules/module.pyx b/src/sage/modules/module.pyx index 3b6875ce0d1..180222b8b18 100644 --- a/src/sage/modules/module.pyx +++ b/src/sage/modules/module.pyx @@ -254,7 +254,7 @@ cdef class Module(Parent): sage: M.endomorphism_ring() Set of Morphisms from <sage.modules.module.Module object at ...> to <sage.modules.module.Module object at ...> in Category of modules over Integer Ring """ - from sage.categories.all import End + from sage.categories.homset import End return End(self) def is_Module(x): @@ -302,5 +302,3 @@ def is_VectorSpace(x): return is_Module(x) and x.base_ring().is_field() except AttributeError: return False - - diff --git a/src/sage/modules/module_functors.py b/src/sage/modules/module_functors.py index 5e84c79008d..c3977183c56 100644 --- a/src/sage/modules/module_functors.py +++ b/src/sage/modules/module_functors.py @@ -7,7 +7,7 @@ :class:`QuotientModuleFunctor` """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2017 Travis Scrimshaw <tcscrims at gmail.com> # # This program is free software: you can redistribute it and/or modify @@ -15,8 +15,8 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** - +# **************************************************************************** + ############################################################## # Construction functor for quotient modules ############################################################## @@ -24,6 +24,7 @@ from sage.categories.pushout import ConstructionFunctor from sage.categories.modules import Modules + class QuotientModuleFunctor(ConstructionFunctor): r""" Construct the quotient of a module by a submodule. @@ -48,9 +49,9 @@ class QuotientModuleFunctor(ConstructionFunctor): QuotientModuleFunctor sage: F(A) == Q True - + The modules are constructed from the cover not the ambient module:: - + sage: F(B.ambient_module()) == Q False @@ -75,7 +76,7 @@ class QuotientModuleFunctor(ConstructionFunctor): sage: Q2 = A2 / B2 sage: q3 = Q1.an_element() + Q2.an_element() """ - rank = 5 # ranking of functor, not rank of module + rank = 5 # ranking of functor, not rank of module def __init__(self, relations): """ @@ -187,4 +188,3 @@ def merge(self, other): """ if isinstance(other, QuotientModuleFunctor): return QuotientModuleFunctor(self._relations + other._relations) - diff --git a/src/sage/modules/multi_filtered_vector_space.py b/src/sage/modules/multi_filtered_vector_space.py index ddc83eb87dd..e3f551b9e4b 100644 --- a/src/sage/modules/multi_filtered_vector_space.py +++ b/src/sage/modules/multi_filtered_vector_space.py @@ -39,7 +39,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.rings.all import QQ, ZZ, Integer +from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ +from sage.rings.integer import Integer from sage.rings.infinity import infinity, minus_infinity from sage.categories.fields import Fields from sage.modules.free_module import FreeModule_ambient_field, VectorSpace diff --git a/src/sage/modules/submodule.py b/src/sage/modules/submodule.py index f8078c4066e..4e0d6994f02 100644 --- a/src/sage/modules/submodule.py +++ b/src/sage/modules/submodule.py @@ -243,4 +243,3 @@ def ambient_module(self): True """ return self._ambient - diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 3414c487a80..45a6699a0cc 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -20,7 +20,10 @@ from sage.modules.fg_pid.fgp_element import FGP_Element from sage.modules.free_quadratic_module import FreeQuadraticModule from sage.arith.misc import gcd -from sage.rings.all import ZZ, Zp, QQ, IntegerModRing +from sage.rings.integer_ring import ZZ +from sage.rings.padics.factory import Zp +from sage.rings.rational_field import QQ +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.groups.additive_abelian.qmodnz import QmodnZ from sage.matrix.constructor import matrix from sage.matrix.special import diagonal_matrix @@ -144,7 +147,7 @@ def _mul_(self, other): 1/4 """ value_module = self.parent().value_module() - return value_module( self.lift().inner_product(other.lift()) ) + return value_module(self.lift().inner_product(other.lift())) inner_product = _mul_ b = _mul_ @@ -296,7 +299,6 @@ def __init__(self, V, W, gens, modulus, modulus_qf): self._modulus = modulus self._modulus_qf = modulus_qf - def _repr_(self): r""" Return a string representation of ``self``. @@ -313,10 +315,10 @@ def _repr_(self): [0 0 0] [0 0 0] """ - return ( "Finite quadratic module over %s with invariants %s\n" - % (self.base_ring(), self.invariants()) + - "Gram matrix of the quadratic form with values in %r:\n%r" - % (self.value_module_qf(), self.gram_matrix_quadratic())) + return ("Finite quadratic module over %s with invariants %s\n" + % (self.base_ring(), self.invariants()) + + "Gram matrix of the quadratic form with values in %r:\n%r" + % (self.value_module_qf(), self.gram_matrix_quadratic())) def _module_constructor(self, V, W, check=False): r""" diff --git a/src/sage/modules/vector_complex_double_dense.pyx b/src/sage/modules/vector_complex_double_dense.pyx index cee4f2f7970..43f3322d627 100644 --- a/src/sage/modules/vector_complex_double_dense.pyx +++ b/src/sage/modules/vector_complex_double_dense.pyx @@ -113,5 +113,3 @@ def unpickle_v1(parent, entries, degree, is_mutable=None): if is_mutable is not None: v._is_immutable = not is_mutable return v - - diff --git a/src/sage/modules/vector_integer_sparse.pyx b/src/sage/modules/vector_integer_sparse.pyx index 01c45059880..de91aab408e 100644 --- a/src/sage/modules/vector_integer_sparse.pyx +++ b/src/sage/modules/vector_integer_sparse.pyx @@ -402,4 +402,3 @@ cdef int mpz_vector_cmp(mpz_vector* v, mpz_vector* w): elif c > 0: return 1 return 0 - diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index 4798810c886..e8886eda079 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -135,7 +135,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): """ self._entries = NULL self._is_immutable = 0 - if not parent is None: + if parent is not None: self._init(parent.degree(), parent) def __init__(self, parent, x, coerce=True, copy=True): @@ -200,7 +200,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): raise TypeError("x must be a list of the right length") for i in range(len(x)): xi = x[i] - if isinstance(xi, (IntegerMod_int, int, long, Integer)): + 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) elif isinstance(xi, Rational): @@ -512,4 +512,3 @@ def unpickle_v0(parent, entries, degree, is_immutable): 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 e987a724213..5a0e27912b9 100644 --- a/src/sage/modules/vector_modn_dense.pyx +++ b/src/sage/modules/vector_modn_dense.pyx @@ -163,7 +163,7 @@ cdef class Vector_modn_dense(free_module_element.FreeModuleElement): def __cinit__(self, parent=None, x=None, coerce=True, copy=True): self._entries = NULL self._is_immutable = 0 - if not parent is None: + if parent is not None: self._init(parent.degree(), parent, parent.base_ring().order()) def __init__(self, parent, x, coerce=True, copy=True): diff --git a/src/sage/modules/vector_rational_sparse.pyx b/src/sage/modules/vector_rational_sparse.pyx index 78002a1a75f..14d0953b3df 100644 --- a/src/sage/modules/vector_rational_sparse.pyx +++ b/src/sage/modules/vector_rational_sparse.pyx @@ -409,4 +409,3 @@ cdef int mpq_vector_cmp(mpq_vector* v, mpq_vector* w): elif c > 0: return 1 return 0 - diff --git a/src/sage/modules/with_basis/cell_module.py b/src/sage/modules/with_basis/cell_module.py index 5c6145d167e..c51d43c32b6 100644 --- a/src/sage/modules/with_basis/cell_module.py +++ b/src/sage/modules/with_basis/cell_module.py @@ -12,7 +12,7 @@ #***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.categories.all import ModulesWithBasis +from sage.categories.modules_with_basis import ModulesWithBasis from sage.structure.element import Element from sage.combinat.free_module import CombinatorialFreeModule from sage.data_structures.blas_dict import linear_combination @@ -357,7 +357,7 @@ def _acted_upon_(self, scalar, self_on_left=False): # Temporary needed by coercion (see Polynomial/FractionField tests). if not P._algebra.has_coerce_map_from(scalar.parent()): return None - scalar = P._algebra( scalar ) + scalar = P._algebra(scalar) if self_on_left: raise NotImplementedError @@ -473,4 +473,3 @@ def _acted_upon_(self, scalar, self_on_left=False): # For backward compatibility _lmul_ = _acted_upon_ _rmul_ = _acted_upon_ - diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index d5ccebde9ef..e16268a4aa8 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -28,7 +28,9 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.superseded import deprecation from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art -from sage.categories.all import Category, Sets, ModulesWithBasis +from sage.categories.category import Category +from sage.categories.sets_cat import Sets +from sage.categories.modules_with_basis import ModulesWithBasis from sage.data_structures.blas_dict cimport add, negate, scal, axpy diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 33af946e8a0..12565a411e2 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -22,6 +22,7 @@ from sage.sets.family import Family from sage.matrix.constructor import Matrix + class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" The invariant submodule under a semigroup action. @@ -65,7 +66,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] - + The we could also have the action be a right-action, instead of the default left-action:: diff --git a/src/sage/modules/with_basis/morphism.py b/src/sage/modules/with_basis/morphism.py index b8fe98111b2..6a43cab1461 100644 --- a/src/sage/modules/with_basis/morphism.py +++ b/src/sage/modules/with_basis/morphism.py @@ -403,11 +403,11 @@ def __call__(self, *args): mc = x.monomial_coefficients(copy=False) if self._is_module_with_basis_over_same_base_ring: return self.codomain().linear_combination( - (self._on_basis(*(before+(index,)+after)), coeff ) - for (index, coeff) in mc.items()) + (self._on_basis(*(before + (index,) + after)), coeff) + for (index, coeff) in mc.items()) else: - return sum((coeff * self._on_basis(*(before+(index,)+after)) - for (index, coeff) in mc.items()), self._zero) + return sum((coeff * self._on_basis(*(before + (index,) + after)) + for (index, coeff) in mc.items()), self._zero) # As per the specs of Map, we should in fact implement _call_. # However we currently need to abuse Map.__call__ (which strict @@ -415,6 +415,7 @@ def __call__(self, *args): # To be cleaned up _call_ = __call__ + class TriangularModuleMorphism(ModuleMorphism): r""" An abstract class for triangular module morphisms @@ -683,10 +684,9 @@ def __init__(self, triangular="upper", unitriangular=False, self._inverse = inverse if inverse_on_support == "compute": - inverse_on_support = { - self._dominant_item(on_basis(i))[0] : i - for i in self.domain().basis().keys() - }.get + inverse_on_support = {self._dominant_item(on_basis(i))[0]: i + for i in self.domain().basis().keys() + }.get self._inverse_on_support = inverse_on_support @@ -883,7 +883,7 @@ def _invert_on_basis(self, i): sage: phi._invert_on_basis(2) B[2] - B[3] """ - return self.preimage( self.codomain().monomial(i) ) + return self.preimage(self.codomain().monomial(i)) def preimage(self, f): r""" @@ -1351,13 +1351,13 @@ def __init__(self, domain, matrix, codomain=None, category=None, side="left"): matrix = matrix.transpose() if matrix.nrows() != len(indices): raise ValueError("The dimension of the matrix (%s) does not match with the dimension of the domain (%s)" - %(matrix.nrows(), len(indices))) + % (matrix.nrows(), len(indices))) if matrix.ncols() != codomain.dimension(): raise ValueError("The dimension of the matrix (%s) does not match with the dimension of the codomain (%s)" - %(matrix.ncols(), codomain.dimension())) + % (matrix.ncols(), codomain.dimension())) self._matrix = matrix - d = { xt: codomain.from_vector(matrix.row(rank_domain(xt))) - for xt in domain.basis().keys() } + d = {xt: codomain.from_vector(matrix.row(rank_domain(xt))) + for xt in domain.basis().keys()} ModuleMorphismByLinearity.__init__(self, on_basis=d.__getitem__, domain=domain, codomain=codomain, @@ -1551,6 +1551,7 @@ def pointwise_inverse_function(f): return f.pointwise_inverse() return PointwiseInverseFunction(f) + from sage.structure.sage_object import SageObject class PointwiseInverseFunction(SageObject): r""" @@ -1630,4 +1631,3 @@ def pointwise_inverse(self): True """ return self._pointwise_inverse - diff --git a/src/sage/modules/with_basis/subquotient.py b/src/sage/modules/with_basis/subquotient.py index 29cc4e963a8..0918c5a8b19 100644 --- a/src/sage/modules/with_basis/subquotient.py +++ b/src/sage/modules/with_basis/subquotient.py @@ -12,7 +12,7 @@ from sage.sets.family import Family from sage.combinat.free_module import CombinatorialFreeModule from sage.misc.lazy_attribute import lazy_attribute -from sage.categories.all import ModulesWithBasis +from sage.categories.modules_with_basis import ModulesWithBasis class QuotientModuleWithBasis(CombinatorialFreeModule): diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index d1a5dce30c1..54771b247e4 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -308,4 +308,3 @@ def cardinality(self): return ZZ.one() from sage.rings.infinity import infinity return infinity - diff --git a/src/sage/monoids/free_abelian_monoid_element.pyx b/src/sage/monoids/free_abelian_monoid_element.pyx index 232deeadac6..87c9f91f445 100644 --- a/src/sage/monoids/free_abelian_monoid_element.pyx +++ b/src/sage/monoids/free_abelian_monoid_element.pyx @@ -388,4 +388,3 @@ cdef class FreeAbelianMonoidElement(MonoidElement): """ cdef Py_ssize_t i return [_Integer_from_mpz(self._element_vector[i]) for i in range(self._n)] - diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index 23a6cc709c7..23d2a8abc66 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -149,7 +149,7 @@ def _latex_(self): \alpha b sage: latex(b*alpha) b \alpha - sage: "%s"%latex(alpha*b) + sage: "%s" % latex(alpha*b) '\\alpha b' """ s = "" @@ -159,10 +159,10 @@ def _latex_(self): g = x[int(v[i][0])] e = v[i][1] if e == 1: - s += "%s "%(g,) + s += "%s " % (g,) else: - s += "%s^{%s}"%(g,e) - s = s.rstrip(" ") # strip the trailing whitespace caused by adding a space after each element name + s += "%s^{%s}" % (g, e) + s = s.rstrip(" ") # strip the trailing whitespace caused by adding a space after each element name if len(s) == 0: s = "1" return s @@ -408,7 +408,6 @@ def to_list(self, indices=False): :meth:`to_word` """ if not indices: - return sum( ([i[0]] * i[1] for i in list(self)), []) + return sum(([i[0]] * i[1] for i in list(self)), []) gens = self.parent().gens() - return sum( ([gens.index(i[0])] * i[1] for i in list(self)), []) - + return sum(([gens.index(i[0])] * i[1] for i in list(self)), []) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index cda2681c5be..58910533a9a 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -1002,7 +1002,6 @@ def gen(self, x): if x not in self._indices: raise IndexError("{} is not in the index set".format(x)) try: - return self.element_class(self, {self._indices(x):1}) - except (TypeError, NotImplementedError): # Backup (e.g., if it is a string) - return self.element_class(self, {x:1}) - + return self.element_class(self, {self._indices(x): 1}) + except (TypeError, NotImplementedError): # Backup (e.g., if it is a string) + return self.element_class(self, {x: 1}) diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index 4ba247e7d99..31e6c219f89 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -70,4 +70,3 @@ def monoid_generators(self): """ from sage.sets.family import Family return Family(self.gens()) - diff --git a/src/sage/monoids/string_monoid.py b/src/sage/monoids/string_monoid.py index 49c2d3c26e9..5f4e3819b5e 100644 --- a/src/sage/monoids/string_monoid.py +++ b/src/sage/monoids/string_monoid.py @@ -521,7 +521,7 @@ def __init__(self): sage: S([ i for i in range(26) ]) ABCDEFGHIJKLMNOPQRSTUVWXYZ """ - from sage.rings.all import RealField + from sage.rings.real_mpfr import RealField RR = RealField() # The characteristic frequency probability distribution of # Robert Edward Lewand. diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index c3032e463f9..185aa901a07 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -22,7 +22,7 @@ # import operator from sage.rings.integer import Integer -from sage.rings.all import RealField +from sage.rings.real_mpfr import RealField from .free_monoid_element import FreeMonoidElement from sage.structure.richcmp import richcmp diff --git a/src/sage/monoids/string_ops.py b/src/sage/monoids/string_ops.py index 9a23ba82bfd..8f822f98cc8 100644 --- a/src/sage/monoids/string_ops.py +++ b/src/sage/monoids/string_ops.py @@ -8,7 +8,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.all import RealField +from sage.rings.real_mpfr import RealField from .string_monoid_element import StringMonoidElement def strip_encoding(S): diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 59938bf175b..10358c3a8f9 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -1002,4 +1002,3 @@ cdef class CVXOPTBackend(GenericBackend): return self.param[name] else: self.param[name] = value - diff --git a/src/sage/numerical/backends/cvxopt_backend_test.py b/src/sage/numerical/backends/cvxopt_backend_test.py index 0e7227940c1..6029221fda2 100644 --- a/src/sage/numerical/backends/cvxopt_backend_test.py +++ b/src/sage/numerical/backends/cvxopt_backend_test.py @@ -1,14 +1,16 @@ import pytest + from sage.structure.sage_object import SageObject from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestCVXOPTBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() + return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() def test_sage_unittest_testsuite(self, sage_object: SageObject): # TODO: Remove this test as soon as all old test methods are migrated diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index ac167aefa8d..64d47f7bc2b 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -512,7 +512,7 @@ cdef class GenericBackend: for i, c in coefficients: coefficients_d.append((i, c[d])) lower_bound_d = None if lower_bound is None else lower_bound[d] - upper_bound_d = None if upper_bound is None else upper_bound[d] + upper_bound_d = None if upper_bound is None else upper_bound[d] self.add_linear_constraint(coefficients_d, lower_bound_d, upper_bound_d, name=name) @classmethod @@ -878,7 +878,7 @@ cdef class GenericBackend: tester = self._tester(**options) p = self tester.assertGreaterEqual(self.ncols(), 0) - + cpdef int nrows(self): """ Return the number of rows/constraints. @@ -1265,7 +1265,7 @@ cdef class GenericBackend: "{}({}) does not match".format(method, i)) for method in ("row_bounds", "row", "row_name"): assert_equal_row_data(method) - + def _test_copy(self, **options): """ Test whether the backend can be copied @@ -1276,16 +1276,19 @@ cdef class GenericBackend: cp = copy(self) self._do_test_problem_data(tester, cp) + def _test_copy_does_not_share_data(self, **options): """ Test whether copy makes an independent copy of the backend. """ tester = self._tester(**options) + cp = copy(self) cpcp = copy(cp) del cp self._do_test_problem_data(tester, cpcp) + # TODO: We should have a more systematic way of generating MIPs for testing. @classmethod def _test_copy_some_mips(cls, tester=None, **options): @@ -1302,6 +1305,7 @@ cdef class GenericBackend: pass # From doctest of GenericBackend.problem_name: p.problem_name("There once was a french fry") + p._test_copy(**options) p._test_copy_does_not_share_data(**options) @@ -1591,7 +1595,7 @@ def default_mip_solver(solver=None): return default_solver else: - for s in ["Cplex", "Gurobi", "Coin", "Glpk"]: + for s in ["Cplex", "Gurobi", "Coin", "Glpk", "SCIP"]: try: default_mip_solver(s) return s @@ -1645,8 +1649,15 @@ def default_mip_solver(solver=None): elif solver == "Interactivelp": default_solver = solver + elif solver == "Scip": + try: + from sage.numerical.backends.scip_backend import SCIPBackend + default_solver = solver + except ImportError: + raise ValueError("SCIP is not available. Please refer to the documentation to install it.") + else: - raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', a callable, or None.") + raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'SCIP', 'InteractiveLP', a callable, or None.") cpdef GenericBackend get_solver(constraint_generation = False, solver = None, base_ring = None): """ @@ -1678,7 +1689,7 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba - When set to ``True``, after solving the ``MixedIntegerLinearProgram``, it is possible to add a constraint, and then solve it again. The effect is that solvers that do not support this feature will not be - used. + used. (Coin and SCIP are such solvers.) - Defaults to ``False``. @@ -1758,7 +1769,7 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba # We do not want to use Coin for constraint_generation. It just does not # work - if solver == "Coin" and constraint_generation: + if solver in ("Coin", "SCIP") and constraint_generation: solver = "Glpk" if callable(solver): @@ -1802,5 +1813,10 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba from sage.numerical.backends.interactivelp_backend import InteractiveLPBackend return InteractiveLPBackend(base_ring=base_ring) + elif solver == "Scip": + from sage.numerical.backends.scip_backend import SCIPBackend + return SCIPBackend() + else: - raise ValueError("'solver' should be set to 'GLPK', 'GLPK/exact', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', None (in which case the default one is used), or a callable.") + raise ValueError("'solver' should be set to 'GLPK', 'GLPK/exact', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'SCIP', 'InteractiveLP', None (in which case the default one is used), or a callable.") + diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 58595389f3c..dcf732916c0 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -1135,8 +1135,8 @@ cdef class GLPKBackend(GenericBackend): if solution_status == GLP_OPT: pass - elif (solution_status == GLP_FEAS) and (solve_status == GLP_ETMLIM or solve_status == GLP_EITLIM \ - or solve_status == GLP_EMIPGAP or solve_status == GLP_EOBJLL or solve_status == GLP_EOBJUL): + elif (solution_status == GLP_FEAS) and (solve_status == GLP_ETMLIM or solve_status == GLP_EITLIM + or solve_status == GLP_EMIPGAP or solve_status == GLP_EOBJLL or solve_status == GLP_EOBJUL): # no exception when time limit or iteration limit or mip gap tolerances or objective limits reached. pass elif solution_status == GLP_UNDEF: diff --git a/src/sage/numerical/backends/glpk_backend_test.py b/src/sage/numerical/backends/glpk_backend_test.py index ec37cd25f36..b41b1ae80c8 100644 --- a/src/sage/numerical/backends/glpk_backend_test.py +++ b/src/sage/numerical/backends/glpk_backend_test.py @@ -1,10 +1,12 @@ import pytest + from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestGLPKBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="GLPK").get_backend() + return MixedIntegerLinearProgram(solver="GLPK").get_backend() diff --git a/src/sage/numerical/backends/ppl_backend_test.py b/src/sage/numerical/backends/ppl_backend_test.py index ac241275ee2..852c3be82c1 100644 --- a/src/sage/numerical/backends/ppl_backend_test.py +++ b/src/sage/numerical/backends/ppl_backend_test.py @@ -1,10 +1,12 @@ import pytest + from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestPPLBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="PPL").get_backend() + return MixedIntegerLinearProgram(solver="PPL").get_backend() diff --git a/src/sage/numerical/backends/scip_backend.pxd b/src/sage/numerical/backends/scip_backend.pxd new file mode 100644 index 00000000000..dc4981a89c3 --- /dev/null +++ b/src/sage/numerical/backends/scip_backend.pxd @@ -0,0 +1,21 @@ +#***************************************************************************** +# Copyright (C) 2017 Matthias Koeppe <mkoeppe@math.ucdavis.edu> +# +# 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 .generic_backend cimport GenericBackend + +cdef class SCIPBackend(GenericBackend): + + cdef model + cdef object variables + cdef object constraints + + cpdef _get_model(self) + cpdef get_row_prim(self, int i) + cpdef write_cip(self, filename) diff --git a/src/sage/numerical/backends/scip_backend.pyx b/src/sage/numerical/backends/scip_backend.pyx new file mode 100644 index 00000000000..b2ecf0f923b --- /dev/null +++ b/src/sage/numerical/backends/scip_backend.pyx @@ -0,0 +1,1204 @@ +# distutils: language = c++ +# sage.doctest: optional - pyscipopt +""" +SCIP Backend + +AUTHORS: + +- Nathann Cohen (2010-10): generic backend +- Matthias Koeppe (2017): stubs +- Moritz Firsching (2018-04): rest +""" + +# ***************************************************************************** +# Copyright (C) 2010 Nathann Cohen <nathann.cohen@gmail.com> +# 2017-2022 Matthias Koeppe +# 2018 Moritz Firsching +# +# 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 os import sys +from os.path import splitext +from sage.ext.memory_allocator cimport MemoryAllocator +from sage.numerical.mip import MIPSolverException +from libc.float cimport DBL_MAX +from libc.limits cimport INT_MAX +from pyscipopt import Model + + +cdef class SCIPBackend(GenericBackend): + + """ + MIP Backend that uses the SCIP solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="SCIP") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + + def __cinit__(self, maximization=True): + """ + Constructor + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram(solver="SCIP") + """ + self.model = Model('') + if maximization: + self.set_sense(1) + else: + self.set_sense(-1) + self.obj_constant_term = 0.0 + self.variables = [] + self.model.hideOutput() + # always set this to None if the list of constraints may change + self.constraints = None + + def get_constraints(self): + """ + Get all constraints of the problem. + """ + if self.constraints is None: + self.constraints = self.model.getConss() + return self.constraints + + cpdef _get_model(self): + """ + Get the model as a pyscipopt Model. + + EXAMPLES:: + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p._get_model() + <pyscipopt.scip.Model object at ... + """ + return self.model + + cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, + binary=False, continuous=False, integer=False, + obj=0.0, name=None) except -1: + """ + Add a variable. + + This amounts to adding a new column to the matrix. By default, + the variable is both positive, real and the coefficient in the + objective function is 0.0. + + INPUT: + + - ``lower_bound`` - the lower bound of the variable (default: 0) + + - ``upper_bound`` - the upper bound of the variable (default: ``None``) + + - ``binary`` - ``True`` if the variable is binary (default: ``False``). + + - ``continuous`` - ``True`` if the variable is binary (default: ``True``). + + - ``integer`` - ``True`` if the variable is binary (default: ``False``). + + - ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0) + + - ``name`` - an optional name for the newly added variable (default: ``None``). + + OUTPUT: The index of the newly created variable + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.ncols() + 1 + sage: p.add_variable(binary=True) + 1 + sage: p.add_variable(lower_bound=-2.0, integer=True) + 2 + sage: p.add_variable(continuous=True, integer=True) + Traceback (most recent call last): + ... + ValueError: ... + sage: p.add_variable(name='x', obj=1.0) + 3 + sage: p.col_name(3) + 'x' + sage: p.objective_coefficient(3) + 1.0 + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer)) + if vtype == 0: + continuous = True + elif vtype != 1: + raise ValueError("Exactly one parameter of 'binary', 'integer'" + "and 'continuous' must be 'True'.") + + if name is None: + vname = '' + else: + assert type(name) in [str, unicode] + vname = name + + if continuous: + vtypestr = 'C' + if binary: + vtypestr = 'B' + if integer: + vtypestr = 'I' + + v = self.model.addVar(name=vname, vtype=vtypestr, ub=upper_bound, + lb=lower_bound, obj=obj, pricedVar=False) + index = v.getIndex() + assert index == self.ncols() + self.variables.append(v) + + return index + + cpdef set_variable_type(self, int variable, int vtype): + """ + Set the type of a variable + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``vtype`` (integer): + + * 1 Integer + * 0 Binary + * -1 Real + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.set_variable_type(0,1) + sage: p.is_variable_integer(0) + True + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + vtypenames = {1: 'I', 0: 'B', -1: 'C'} + self.model.chgVarType(var=self.variables[variable], vtype=vtypenames[vtype]) + + cpdef set_sense(self, int sense): + """ + Set the direction (maximization/minimization). + + INPUT: + + - ``sense`` (integer): + + * +1 => Maximization + * -1 => Minimization + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + if sense == 1: + self.model.setMaximize() + elif sense == -1: + self.model.setMinimize() + else: + raise AssertionError("sense must be either 1 or -1") + + cpdef objective_coefficient(self, int variable, coeff=None): + """ + Set or get the coefficient of a variable in the objective function + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``coeff`` (double) -- its coefficient or ``None`` for + reading (default: ``None``) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variable() + 0 + sage: p.objective_coefficient(0) + 0.0 + sage: p.objective_coefficient(0,2) + sage: p.objective_coefficient(0) + 2.0 + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + if coeff is None: + return self.variables[variable].getObj() + else: + objexpr = self.model.getObjective() + var = self.variables[variable] + linfun = sum([e * c for e, c in objexpr.terms.iteritems() if e != var]) + var * coeff + self.model.setObjective(linfun, sense=self.model.getObjectiveSense()) + + cpdef problem_name(self, name=None): + """ + Return or define the problem's name + + INPUT: + + - ``name`` (``str``) -- the problem's name. When set to + ``None`` (default), the method returns the problem's name. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.problem_name("Nomen est omen") + sage: p.problem_name() + 'Nomen est omen' + """ + if name is None: + return self.model.getProbName() + else: + self.model.setProbName(name) + + cpdef set_objective(self, list coeff, d=0.0): + """ + Set the objective function. + + INPUT: + + - ``coeff`` - a list of real values, whose ith element is the + coefficient of the ith variable in the objective function. + + - ``d`` (double) -- the constant term in the linear function (set to `0` by default) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(5) + 4 + sage: p.set_objective([1, 1, 2, 1, 3]) + sage: [p.objective_coefficient(x) for x in range(5)] + [1.0, 1.0, 2.0, 1.0, 3.0] + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + linfun = sum([c * x for c, x in zip(coeff, self.variables)]) + d + self.model.setObjective(linfun, sense=self.model.getObjectiveSense()) + + cpdef set_verbosity(self, int level): + """ + Set the verbosity level + + INPUT: + + - ``level`` (integer) -- From 0 (no verbosity) to 1. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.set_verbosity(1) + + TODOs:: + + - Currently, the output is written to stdout, even when running + Jupyter: https://github.com/SCIP-Interfaces/PySCIPOpt/issues/116 . + This should be fixed upstream + - get access to more verbosity levels (e.g. via parameter settings) + """ + if level == 0: + self.model.hideOutput() + elif level == 1: + self.model.hideOutput(False) + else: + raise AssertionError('level must be "0" or "1"') + + cpdef remove_constraint(self, int i): + r""" + Remove a constraint from self. + + INPUT: + + - ``i`` -- index of the constraint to remove + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram(solver='SCIP') + sage: x, y = p['x'], p['y'] + sage: p.add_constraint(2*x + 3*y <= 6) + sage: p.add_constraint(3*x + 2*y <= 6) + sage: p.add_constraint(x >= 0) + sage: p.set_objective(x + y + 7) + sage: p.set_integer(x); p.set_integer(y) + sage: p.solve() + 9.0 + sage: p.remove_constraint(0) + sage: p.solve() + 10.0 + + Removing fancy constraints does not make Sage crash:: + + sage: MixedIntegerLinearProgram(solver="SCIP").remove_constraint(-2) + Traceback (most recent call last): + ... + ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints + """ + if i < 0 or i >= self.nrows(): + raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints") + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + self.constraints = None + self.model.delCons(self.get_constraints()[i]) + self.constraints = None + + cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): + """ + Add a linear constraint. + + INPUT: + + - ``coefficients`` an iterable with ``(c, v)`` pairs where ``c`` + is a variable index (integer) and ``v`` is a value (real + value). + + - ``lower_bound`` - a lower bound, either a real value or ``None`` + + - ``upper_bound`` - an upper bound, either a real value or ``None`` + + - ``name`` - an optional name for this row (default: ``None``) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) + sage: p.row_bounds(0) + (2.0, 2.0) + sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') + sage: p.row_name(1) + 'foo' + """ + if self.model.getStatus() != 'unknown': + self.model.freeTransform() + mvars = self.variables + from pyscipopt.scip import quicksum + linfun = quicksum([v * mvars[c] for c, v in coefficients]) + # we introduced patch 0001 for pyscipopt, in order to handle the case + # when linfun is an empty expression. + if name is None: + name = '' + + if lower_bound is None: + lower_bound = -self.model.infinity() + if upper_bound is None: + upper_bound = self.model.infinity() + + cons = lower_bound <= (linfun <= upper_bound) + self.model.addCons(cons, name=name) + self.constraints = None + + cpdef row(self, int index): + r""" + Return a row + + INPUT: + + - ``index`` (integer) -- the constraint's id. + + OUTPUT: + + A pair ``(indices, coeffs)`` where ``indices`` lists the + entries whose coefficient is nonzero, and to which ``coeffs`` + associates their coefficient on the model of the + ``add_linear_constraint`` method. + + EXAMPLES:: + + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) + sage: p.row(0) + ([1, 2, 3, 4], [1.0, 2.0, 3.0, 4.0]) + sage: p.row_bounds(0) + (2.0, 2.0) + """ + namedvars = [var.name for var in self.variables] + valslinear = self.model.getValsLinear(self.get_constraints()[index]) + cdef list indices = [] + cdef list values = [] + for var, coeff in valslinear.iteritems(): + indices.append(namedvars.index(var)) + values.append(coeff) + return (indices, values) + + cpdef row_bounds(self, int index): + """ + Return the bounds of a specific constraint. + + INPUT: + + - ``index`` (integer) -- the constraint's id. + + OUTPUT: + + A pair ``(lower_bound, upper_bound)``. Each of them can be set + to ``None`` if the constraint is not bounded in the + corresponding direction, and is a real value otherwise. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) + sage: p.row_bounds(0) + (2.0, 2.0) + """ + cons = self.get_constraints()[index] + lhs = self.model.getLhs(cons) + rhs = self.model.getRhs(cons) + if lhs == -self.model.infinity(): + lhs = None + if rhs == self.model.infinity(): + rhs = None + return (lhs, rhs) + + cpdef col_bounds(self, int index): + """ + Return the bounds of a specific variable. + + INPUT: + + - ``index`` (integer) -- the variable's id. + + OUTPUT: + + A pair ``(lower_bound, upper_bound)``. Each of them can be set + to ``None`` if the variable is not bounded in the + corresponding direction, and is a real value otherwise. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variable() + 0 + sage: p.col_bounds(0) + (0.0, None) + sage: p.variable_upper_bound(0, 5) + sage: p.col_bounds(0) + (0.0, 5.0) + """ + var = self.variables[index] + lb = var.getLbOriginal() + if lb == -self.model.infinity(): + lb = None + ub = var.getUbOriginal() + if ub == self.model.infinity(): + ub = None + return (lb, ub) + + cpdef add_col(self, indices, coeffs): + """ + Add a column. + + INPUT: + + - ``indices`` (list of integers) -- this list constains the + indices of the constraints in which the variable's + coefficient is nonzero + + - ``coeffs`` (list of real values) -- associates a coefficient + to the variable in each of the constraints in which it + appears. Namely, the ith entry of ``coeffs`` corresponds to + the coefficient of the variable in the constraint + represented by the ith entry in ``indices``. + + .. NOTE:: + + ``indices`` and ``coeffs`` are expected to be of the same + length. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.nrows() + 0 + sage: p.add_linear_constraints(5, 0, None) + sage: p.add_col(range(5), range(5)) + sage: p.nrows() + 5 + """ + mcons = self.get_constraints() + self.constraints = None # is this necessary? + # after update of + index = self.add_variable(lower_bound=-self.model.infinity()) + var = self.variables[index] + + for i, coeff in zip(indices, coeffs): + self.model.addConsCoeff(var=var, cons=mcons[i], coeff=coeff) + + cpdef int solve(self) except -1: + """ + Solve the problem. + + Sage uses SCIP's implementation of the branch-and-cut algorithm + to solve the mixed-integer linear program. + (If all variables are continuous, the algorithm reduces to solving the + linear program by the simplex method.) + + EXAMPLES:: + + sage: lp = MixedIntegerLinearProgram(solver = 'SCIP', maximization = False) + sage: x, y = lp[0], lp[1] + sage: lp.add_constraint(-2*x + y <= 1) + sage: lp.add_constraint(x - y <= 1) + sage: lp.add_constraint(x + y >= 2) + sage: lp.set_objective(x + y) + sage: lp.set_integer(x) + sage: lp.set_integer(y) + sage: lp.solve() + 2.0 + sage: lp.get_values([x, y]) + [1.0, 1.0] + + .. NOTE:: + + This method raises ``MIPSolverException`` exceptions when + the solution can not be computed for any reason (none + exists, or the LP solver was not able to find it, etc...) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_linear_constraints(5, 0, None) + sage: p.add_col(range(5), range(5)) + sage: p.solve() + 0 + sage: p.objective_coefficient(0,1) + sage: p.solve() + Traceback (most recent call last): + ... + MIPSolverException: ... + + sage: lp = MixedIntegerLinearProgram(solver = "SCIP") + sage: v = lp.new_variable(nonnegative=True) + sage: lp.add_constraint(v[1] +v[2] -2.0 *v[3], max=-1.0) + sage: lp.add_constraint(v[0] -4.0/3 *v[1] +1.0/3 *v[2], max=-1.0/3) + sage: lp.add_constraint(v[0] +0.5 *v[1] -0.5 *v[2] +0.25 *v[3], max=-0.25) + sage: lp.solve() + 0.0 + + Solving a LP within the acceptable gap. No exception is raised, even if + the result is not optimal. To do this, we try to compute the maximum + number of disjoint balls (of diameter 1) in a hypercube:: + + sage: g = graphs.CubeGraph(9) + sage: p = MixedIntegerLinearProgram(solver = "SCIP") + + sage: b = p.new_variable(binary=True) + sage: p.set_objective(p.sum(b[v] for v in g)) + sage: for v in g: + ....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1) + sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution + sage: p.solver_parameter("limits/absgap", 100) + sage: p.solve() # rel tol 100 + 1 + """ + if self.model.getStatus() != 'unknown' or self.model.getStage() > 1: + # This should actually be self.model.freeReoptSolve, but it seems to fail. + self.model.freeTransform() + self.model.optimize() + + status = self.model.getStatus() + + if status == 'unbounded': + raise MIPSolverException("SCIP: Solution is unbounded") + elif status == 'infeasible': + raise MIPSolverException("SCIP: There is no feasible solution") + # elif status == 'timelimit': + # raise MIPSolverException("SCIP: Time limit reached") + return 0 + + cpdef get_objective_value(self): + """ + Return the value of the objective function. + + .. NOTE:: + + Behaviour is undefined unless ``solve`` has been called before. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) + sage: p.set_objective([2, 5]) + sage: p.solve() + 0 + sage: p.get_objective_value() + 7.5 + sage: p.get_variable_value(0) # abs tol 1e-15 + 0.0 + sage: p.get_variable_value(1) + 1.5 + """ + return self.model.getObjVal() + + cpdef best_known_objective_bound(self): + r""" + Return the value of the currently best known bound. + + This method returns the current best upper (resp. lower) bound on the + optimal value of the objective function in a maximization + (resp. minimization) problem. It is equal to the output of + :meth:`get_objective_value` if the MILP found an optimal solution, but + it can differ if it was interrupted manually or after a time limit (cf + :meth:`solver_parameter`). + + .. NOTE:: + + Has no meaning unless ``solve`` has been called before. + + EXAMPLES:: + + """ + return self.model.getPrimalbound() + + cpdef get_relative_objective_gap(self): + r""" + Return the relative objective gap of the best known solution. + + For a minimization problem, this value is computed by + `(\texttt{bestinteger} - \texttt{bestobjective}) / (1e-10 + + |\texttt{bestobjective}|)`, where ``bestinteger`` is the value returned + by :meth:`get_objective_value` and ``bestobjective`` is the value + returned by :meth:`best_known_objective_bound`. For a maximization + problem, the value is computed by `(\texttt{bestobjective} - + \texttt{bestinteger}) / (1e-10 + |\texttt{bestobjective}|)`. + + .. NOTE:: + + Has no meaning unless ``solve`` has been called before. + + EXAMPLES:: + + + TESTS: + + Just make sure that the variable *has* been defined, and is not just + undefined:: + + """ + return self.model.getGap() + + cpdef get_variable_value(self, int variable): + """ + Return the value of a variable given by the solver. + + .. NOTE:: + + Behaviour is undefined unless ``solve`` has been called before. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) + sage: p.set_objective([2, 5]) + sage: p.solve() + 0 + sage: p.get_objective_value() + 7.5 + sage: p.get_variable_value(0) # abs tol 1e-15 + 0.0 + sage: p.get_variable_value(1) + 1.5 + """ + return self.model.getVal(self.variables[variable]) + + cpdef get_row_prim(self, int i): + r""" + Return the value of the auxiliary variable associated with i-th row. + + .. NOTE:: + + Behaviour is undefined unless ``solve`` has been called before. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: lp = get_solver(solver = "SCIP") + sage: lp.add_variables(3) + 2 + sage: lp.add_linear_constraint(zip([0, 1, 2], [8, 6, 1]), None, 48) + sage: lp.add_linear_constraint(zip([0, 1, 2], [4, 2, 1.5]), None, 20) + sage: lp.add_linear_constraint(zip([0, 1, 2], [2, 1.5, 0.5]), None, 8) + sage: lp.set_objective([60, 30, 20]) + sage: lp.solver_parameter('presolving/maxrounds',0) + sage: lp.solve() + 0 + sage: lp.get_objective_value() + 280.0 + sage: lp.get_row_prim(0) + 24.0 + sage: lp.get_row_prim(1) + 20.0 + sage: lp.get_row_prim(2) + 8.0 + """ + return self.model.getActivity(self.get_constraints()[i]) + + cpdef int ncols(self): + """ + Return the number of columns/variables. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variables(2) + 1 + sage: p.ncols() + 2 + """ + return len(self.variables) + + cpdef int nrows(self): + """ + Return the number of rows/constraints. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.nrows() + 0 + sage: p.add_linear_constraints(2, 2, None) + sage: p.nrows() + 2 + """ + return len(self.get_constraints()) + + cpdef col_name(self, int index): + """ + Return the ``index``th col name + + INPUT: + + - ``index`` (integer) -- the col's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variable(name='I am a variable') + 0 + sage: p.col_name(0) + 'I am a variable' + """ + return self.variables[index].name + + cpdef row_name(self, int index): + """ + Return the ``index`` th row name + + INPUT: + + - ``index`` (integer) -- the row's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) + sage: p.row_name(0) + 'Empty constraint 1' + """ + return self.get_constraints()[index].name + + cpdef bint is_variable_binary(self, int index): + """ + Test whether the given variable is of binary type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.set_variable_type(0,0) + sage: p.is_variable_binary(0) + True + + """ + return self.variables[index].vtype() == 'BINARY' + + cpdef bint is_variable_integer(self, int index): + """ + Test whether the given variable is of integer type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.set_variable_type(0,1) + sage: p.is_variable_integer(0) + True + """ + return self.variables[index].vtype() == 'INTEGER' + + cpdef bint is_variable_continuous(self, int index): + """ + Test whether the given variable is of continuous/real type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.is_variable_continuous(0) + True + sage: p.set_variable_type(0,1) + sage: p.is_variable_continuous(0) + False + """ + return self.variables[index].vtype() == 'CONTINUOUS' + + cpdef bint is_maximization(self): + """ + Test whether the problem is a maximization + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + return self.model.getObjectiveSense() != 'minimize' + + cpdef variable_upper_bound(self, int index, value=False): + """ + Return or define the upper bound on a variable + + INPUT: + + - ``index`` (integer) -- the variable's id + + - ``value`` -- real value, or ``None`` to mean that the + variable has not upper bound. When set to ``False`` + (default), the method returns the current value. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variable() + 0 + sage: p.col_bounds(0) + (0.0, None) + sage: p.variable_upper_bound(0, 5) + sage: p.col_bounds(0) + (0.0, 5.0) + + TESTS: + + :trac:`14581`:: + + sage: P = MixedIntegerLinearProgram(solver="SCIP") + sage: x = P["x"] + sage: P.set_max(x, 0) + sage: P.get_max(x) + 0.0 + + Check that :trac:`10232` is fixed:: + + sage: p = get_solver(solver="SCIP") + sage: p.variable_upper_bound(2) + Traceback (most recent call last): + ... + ValueError: The variable's id must satisfy 0 <= id < number_of_variables + sage: p.variable_upper_bound(3, 5) + Traceback (most recent call last): + ... + ValueError: The variable's id must satisfy 0 <= id < number_of_variables + + sage: p.add_variable() + 0 + sage: p.variable_upper_bound(0, 'hey!') + Traceback (most recent call last): + ... + TypeError: must be real number, not str + """ + if index < 0 or index >= self.ncols(): + raise ValueError("The variable's id must satisfy 0 <= id < number_of_variables") + var = self.variables[index] + if value is False: + return var.getUbOriginal() + else: + self.model.chgVarUb(var=var, ub=value) + + cpdef variable_lower_bound(self, int index, value=False): + """ + Return or define the lower bound on a variable + + INPUT: + + - ``index`` (integer) -- the variable's id + + - ``value`` -- real value, or ``None`` to mean that the + variable has not lower bound. When set to ``False`` + (default), the method returns the current value. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variable() + 0 + sage: p.col_bounds(0) + (0.0, None) + sage: p.variable_lower_bound(0, 5) + sage: p.col_bounds(0) + (5.0, None) + + TESTS: + + :trac:`14581`:: + + sage: P = MixedIntegerLinearProgram(solver="SCIP") + sage: x = P["x"] + sage: P.set_min(x, 5) + sage: P.set_min(x, 0) + sage: P.get_min(x) + 0.0 + + Check that :trac:`10232` is fixed:: + + sage: p = get_solver(solver="SCIP") + sage: p.variable_lower_bound(2) + Traceback (most recent call last): + ... + ValueError: The variable's id must satisfy 0 <= id < number_of_variables + sage: p.variable_lower_bound(3, 5) + Traceback (most recent call last): + ... + ValueError: The variable's id must satisfy 0 <= id < number_of_variables + + sage: p.add_variable() + 0 + sage: p.variable_lower_bound(0, 'hey!') + Traceback (most recent call last): + ... + TypeError: must be real number, not str + """ + if index < 0 or index >= self.ncols(): + raise ValueError("The variable's id must satisfy 0 <= id < number_of_variables") + var = self.variables[index] + if value is False: + return var.getLbOriginal() + else: + self.model.chgVarLb(var=var, lb=value) + + cpdef write_cip(self, filename): + """ + Write the problem to a .cip file + + INPUT: + + - ``filename`` (string) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) + sage: p.set_objective([2, 5]) + sage: import tempfile + sage: with tempfile.NamedTemporaryFile(suffix=".cip") as f: + ....: p.write_cip(f.name) + wrote problem to file ... + """ + self.model.writeProblem(filename) + + cpdef write_lp(self, filename): + """ + Write the problem to a .lp file + + INPUT: + + - ``filename`` (string) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) + sage: p.set_objective([2, 5]) + sage: import tempfile + sage: with tempfile.NamedTemporaryFile(suffix=".lp") as f: + ....: p.write_lp(f.name) + wrote problem to file ... + """ + filenamestr = filename + fname, fext = splitext(filenamestr) + + if fext.lower() != 'lp': + filenamestr = filename + '.lp' + + self.model.writeProblem(filenamestr) + + cpdef write_mps(self, filename, int modern): + """ + Write the problem to a .mps file + + INPUT: + + - ``filename`` (string) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "SCIP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) + sage: p.set_objective([2, 5]) + sage: import tempfile + sage: with tempfile.NamedTemporaryFile(suffix=".mps") as f: + ....: p.write_mps(f.name, 2) + wrote problem to file ... + """ + filenamestr = filename + fname, fext = splitext(filenamestr) + + if fext.lower() != 'mps': + filenamestr = filename + '.mps' + + self.model.writeProblem(filenamestr) + + cpdef __copy__(self): + """ + Return a copy of self. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "SCIP") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: copy(p).solve() + 6.0 + """ + cdef SCIPBackend cp = type(self)(maximization=self.is_maximization()) + cp.problem_name(self.problem_name()) + for i, v in enumerate(self.variables): + vtype = v.vtype() + cp.add_variable(self.variable_lower_bound(i), + self.variable_upper_bound(i), + binary=vtype == 'BINARY', + continuous=vtype == 'CONTINUOUS', + integer=vtype == 'INTEGER', + obj=self.objective_coefficient(i), + name=self.col_name(i)) + assert self.ncols() == cp.ncols() + + for i in range(self.nrows()): + coefficients = zip(*self.row(i)) + lower_bound, upper_bound = self.row_bounds(i) + name = self.row_name(i) + cp.add_linear_constraint(coefficients, + lower_bound, + upper_bound, + name=name) + assert self.nrows() == cp.nrows() + return cp + + cpdef solver_parameter(self, name, value=None): + """ + Return or define a solver parameter + + INPUT: + + - ``name`` (string) -- the parameter + + - ``value`` -- the parameter's value if it is to be defined, + or ``None`` (default) to obtain its current value. + + """ + if value is not None: + if name.lower() == 'timelimit': + self.model.setRealParam("limits/time", float(value)) + else: + self.model.setParam(name, value) + else: + return self.model.getParam(name) diff --git a/src/sage/numerical/backends/scip_backend_test.py b/src/sage/numerical/backends/scip_backend_test.py new file mode 100644 index 00000000000..43648d0c3ce --- /dev/null +++ b/src/sage/numerical/backends/scip_backend_test.py @@ -0,0 +1,10 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestSCIPBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="SCIP").get_backend() diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 175513687df..0b8dd4361e3 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -216,7 +216,7 @@ def nodes(degree, prec): def estimate_error(results, prec, epsilon): r""" Routine to estimate the error in a list of quadrature approximations. - + The method used is based on Borwein, Bailey, and Girgensohn. As mentioned in mpmath: Although not very conservative, this method seems to be very robust in practice. @@ -226,7 +226,7 @@ def estimate_error(results, prec, epsilon): of the maximum norm of the error in the last approximation. INPUT: - + - ``results`` -- list. List of approximations to estimate the error from. Should be at least length 2. - ``prec`` -- integer. Binary precision at which computations are happening. diff --git a/src/sage/numerical/linear_tensor.py b/src/sage/numerical/linear_tensor.py index bdf9529659a..26d94b12cc6 100644 --- a/src/sage/numerical/linear_tensor.py +++ b/src/sage/numerical/linear_tensor.py @@ -150,7 +150,7 @@ def LinearTensorParent(free_module_parent, linear_functions_parent): for a given base ring. INPUT: - + - ``free_module_parent`` -- module. A free module, like vector or matrix space. @@ -267,7 +267,7 @@ def is_vector_space(self): sage: LF = mip.linear_functions_parent() sage: LF.tensor(RDF^2).is_vector_space() True - sage: LF.tensor(RDF^(2,2)).is_vector_space() + sage: LF.tensor(RDF^(2,2)).is_vector_space() False """ from sage.modules.free_module import is_FreeModule @@ -319,7 +319,7 @@ def linear_functions(self): def _repr_(self): """ Return a string representation - + OUTPUT: String. @@ -364,7 +364,7 @@ def _convert_constant(self, m): return m_vector else: return M(m) - + def _element_constructor_(self, x): """ Construct a :class:`LinearTensor` from ``x``. @@ -393,7 +393,7 @@ def _element_constructor_(self, x): sage: LT(123) # indirect doctest (123.0, 123.0) - + Similar, over ``QQ`` and with matrices instead of vectors:: sage: p_QQ = MixedIntegerLinearProgram(solver='ppl') @@ -478,7 +478,4 @@ def _an_element_(self): (1.0, 0.0) + (5.0, 0.0)*x_2 + (7.0, 0.0)*x_5 """ m = self.free_module().an_element() - return self._element_constructor_({-1:m, 2:5*m, 5:7*m}) - - - + return self._element_constructor_({-1: m, 2: 5 * m, 5: 7 * m}) diff --git a/src/sage/numerical/linear_tensor_constraints.py b/src/sage/numerical/linear_tensor_constraints.py index ce09731cc6e..24f94f3b3d6 100644 --- a/src/sage/numerical/linear_tensor_constraints.py +++ b/src/sage/numerical/linear_tensor_constraints.py @@ -97,7 +97,7 @@ def LinearTensorConstraintsParent(linear_functions_parent): sage: LF = LinearFunctionsParent(QQ) sage: LT = LinearTensorParent(QQ^2, LF) sage: LinearTensorConstraintsParent(LT) - Linear constraints in the tensor product of Vector space of dimension 2 + Linear constraints in the tensor product of Vector space of dimension 2 over Rational Field and Linear functions over Rational Field """ return LinearTensorConstraintsParent_class(linear_functions_parent) @@ -215,7 +215,7 @@ def lhs(self): sage: mip.<x> = MixedIntegerLinearProgram() sage: (x[0] * vector([1,2]) == 0).lhs() - (1.0, 2.0)*x_0 + (1.0, 2.0)*x_0 """ return self._lhs @@ -250,7 +250,7 @@ def _ascii_art_(self): sage: mip.<x> = MixedIntegerLinearProgram() sage: ascii_art(x[0] * vector([1,2]) >= 0) (0.0, 0.0) <= (1.0, 2.0)*x_0 - sage: ascii_art(x[0] * matrix([[1,2],[3,4]]) >= 0) + sage: ascii_art(x[0] * matrix([[1,2],[3,4]]) >= 0) [0 0] <= [x_0 2*x_0] [0 0] [3*x_0 4*x_0] """ @@ -313,12 +313,12 @@ class LinearTensorConstraintsParent_class(Parent): sage: p = MixedIntegerLinearProgram() sage: LT = p.linear_functions_parent().tensor(RDF^2); LT - Tensor product of Vector space of dimension 2 over Real Double + Tensor product of Vector space of dimension 2 over Real Double Field and Linear functions over Real Double Field sage: from sage.numerical.linear_tensor_constraints import LinearTensorConstraintsParent sage: LTC = LinearTensorConstraintsParent(LT); LTC - Linear constraints in the tensor product of Vector space of - dimension 2 over Real Double Field and Linear functions over + Linear constraints in the tensor product of Vector space of + dimension 2 over Real Double Field and Linear functions over Real Double Field sage: type(LTC) <class 'sage.numerical.linear_tensor_constraints.LinearTensorConstraintsParent_class'> @@ -330,10 +330,10 @@ def __init__(self, linear_tensor_parent): The Python constructor INPUT: - + - ``linear_tensor_parent`` -- instance of :class:`LinearTensorParent_class`. - + TESTS:: sage: from sage.numerical.linear_functions import LinearFunctionsParent @@ -342,7 +342,7 @@ def __init__(self, linear_tensor_parent): sage: LT = LinearTensorParent(RDF^2, LF) sage: from sage.numerical.linear_tensor_constraints import LinearTensorConstraintsParent sage: LinearTensorConstraintsParent(LT) - Linear constraints in the tensor product of Vector space of + Linear constraints in the tensor product of Vector space of dimension 2 over Real Double Field and Linear functions over Real Double Field """ @@ -399,7 +399,7 @@ def _repr_(self): sage: ieq = (x[0] * vector([1,2]) >= 0) sage: ieq.parent() # indirect doctests Linear constraints in the tensor product of Vector space of - dimension 2 over Real Double Field and Linear functions over + dimension 2 over Real Double Field and Linear functions over Real Double Field """ return 'Linear constraints in the tensor product of {0} and {1}'.format( @@ -446,7 +446,7 @@ def _element_constructor_(self, left, right, equality): def _an_element_(self): """ - Returns an element + Return an element. EXAMPLES:: @@ -457,4 +457,3 @@ def _an_element_(self): """ LT = self.linear_tensors() return LT.an_element() >= 0 - diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index c2ed1368837..bdbee5d974c 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -64,7 +64,7 @@ A mixed integer linear program can give you an answer: The following example shows all these steps:: - sage: p = MixedIntegerLinearProgram(maximization=False, solver = "GLPK") + sage: p = MixedIntegerLinearProgram(maximization=False, solver="GLPK") sage: w = p.new_variable(integer=True, nonnegative=True) sage: p.add_constraint(w[0] + w[1] + w[2] - 14*w[3] == 0) sage: p.add_constraint(w[1] + 2*w[2] - 8*w[3] == 0) @@ -320,7 +320,7 @@ cdef class MixedIntegerLinearProgram(SageObject): Computation of a maximum stable set in Petersen's graph:: sage: g = graphs.PetersenGraph() - sage: p = MixedIntegerLinearProgram(maximization=True,solver='GLPK') + sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK') sage: b = p.new_variable(binary=True) sage: p.set_objective(sum([b[v] for v in g])) sage: for (u,v) in g.edges(sort=False, labels=None): @@ -335,16 +335,16 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: for type in ["binary", "integer"]: ....: k = 3 ....: items = [1/5, 1/3, 2/3, 3/4, 5/7] - ....: maximum=1 - ....: p=MixedIntegerLinearProgram(solver='GLPK') - ....: box=p.new_variable(nonnegative=True, **{type:True}) + ....: maximum = 1 + ....: p = MixedIntegerLinearProgram(solver='GLPK') + ....: box = p.new_variable(nonnegative=True, **{type: True}) ....: for b in range(k): - ....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum) + ....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum) ....: for i in range(len(items)): ....: p.add_constraint(p.sum([box[i,b] for b in range(k)]) == 1) ....: p.set_objective(None) ....: _ = p.solve() - ....: box=p.get_values(box) + ....: box = p.get_values(box) ....: print(all(v in ZZ for v in box.values())) True True @@ -415,7 +415,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: def just_create_variables(): ....: p = MixedIntegerLinearProgram(solver='GLPK') ....: b = p.new_variable(nonnegative=True) - ....: p.add_constraint(b[3]+b[6] <= 2) + ....: p.add_constraint(b[3] + b[6] <= 2) ....: p.solve() sage: C = sage.numerical.mip.MixedIntegerLinearProgram sage: import gc @@ -560,7 +560,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: v = p.new_variable(nonnegative=True) - sage: p.add_constraint(v[0] + v[1], max = 10) + sage: p.add_constraint(v[0] + v[1], max=10) sage: q = copy(p) sage: q.number_of_constraints() 1 @@ -779,7 +779,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x = p.new_variable(real=True) sage: y = p.new_variable(integer=True) - sage: p.add_constraint(x[0]+x[3] <= 8) + sage: p.add_constraint(x[0] + x[3] <= 8) sage: p.add_constraint(y[0] >= y[1]) sage: p.show() Maximization: @@ -860,7 +860,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLES:: - sage: mip.<a,b> = MixedIntegerLinearProgram(solver='GLPK') + sage: mip.<a,b> = MixedIntegerLinearProgram(solver='GLPK') # indirect doctest sage: a[0] + b[2] x_0 + x_1 sage: mip.show() @@ -880,8 +880,8 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLES:: sage: p = MixedIntegerLinearProgram(solver='GLPK') - sage: p.add_constraint(p[0] - p[2], min = 1, max = 4) - sage: p.add_constraint(p[0] - 2*p[1], min = 1) + sage: p.add_constraint(p[0] - p[2], min=1, max=4) + sage: p.add_constraint(p[0] - 2*p[1], min=1) sage: p.number_of_constraints() 2 """ @@ -901,18 +901,18 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLES:: sage: p = MixedIntegerLinearProgram(solver='GLPK') - sage: p.add_constraint(p[0] - p[2], max = 4) + sage: p.add_constraint(p[0] - p[2], max=4) sage: p.number_of_variables() 2 - sage: p.add_constraint(p[0] - 2*p[1], min = 1) + sage: p.add_constraint(p[0] - 2*p[1], min=1) sage: p.number_of_variables() 3 sage: p = MixedIntegerLinearProgram(solver="glpk") - sage: p.add_constraint(p[0] - p[2], min = 1, max = 4) + sage: p.add_constraint(p[0] - p[2], min=1, max=4) sage: p.number_of_variables() 2 sage: p = MixedIntegerLinearProgram(solver="gurobi") # optional - Gurobi - sage: p.add_constraint(p[0] - p[2], min = 1, max = 4) # optional - Gurobi + sage: p.add_constraint(p[0] - p[2], min=1, max=4) # optional - Gurobi sage: p.number_of_variables() # optional - Gurobi 3 """ @@ -950,8 +950,8 @@ cdef class MixedIntegerLinearProgram(SageObject): First, let us define a small LP:: sage: p = MixedIntegerLinearProgram(solver='GLPK') - sage: p.add_constraint(p[0] - p[2], min = 1, max = 4) - sage: p.add_constraint(p[0] - 2*p[1], min = 1) + sage: p.add_constraint(p[0] - p[2], min=1, max=4) + sage: p.add_constraint(p[0] - 2*p[1], min=1) To obtain the list of all constraints:: @@ -982,9 +982,9 @@ cdef class MixedIntegerLinearProgram(SageObject): Running the examples from above, reordering applied:: - sage: p = MixedIntegerLinearProgram(solver = "GLPK") - sage: p.add_constraint(p[0] - p[2], min = 1, max = 4) - sage: p.add_constraint(p[0] - 2*p[1], min = 1) + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: p.add_constraint(p[0] - p[2], min=1, max=4) + sage: p.add_constraint(p[0] - 2*p[1], min=1) sage: sorted(reorder_constraint(*c) for c in p.constraints()) [(1.0, ([0, 1], [1.0, -1.0]), 4.0), (1.0, ([0, 2], [1.0, -2.0]), None)] sage: reorder_constraint(*p.constraints(0)) @@ -1091,10 +1091,10 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x,y = p['x'], p['y'] - sage: p.add_constraint( x <= 1 ) - sage: p.add_constraint( x >= -1 ) - sage: p.add_constraint( y <= 1 ) - sage: p.add_constraint( y >= -1 ) + sage: p.add_constraint(x <= 1) + sage: p.add_constraint(x >= -1) + sage: p.add_constraint(y <= 1) + sage: p.add_constraint(y >= -1) sage: p.polyhedron() A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices @@ -1102,10 +1102,10 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='PPL') sage: x,y = p['x'], p['y'] - sage: p.add_constraint( x <= 1 ) - sage: p.add_constraint( x >= -1 ) - sage: p.add_constraint( y <= 1 ) - sage: p.add_constraint( y >= -1 ) + sage: p.add_constraint(x <= 1) + sage: p.add_constraint(x >= -1) + sage: p.add_constraint(y <= 1) + sage: p.add_constraint(y >= -1) sage: p.polyhedron() A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices @@ -1117,7 +1117,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: x, y = p['x'], p['y'] sage: p.set_min(x, 0); p.set_min(y, 0) sage: p.set_objective(3.5*x + 2.5*y) - sage: p.add_constraint(x+y <= 10) + sage: p.add_constraint(x + y <= 10) sage: p.add_constraint(18.5*x + 5.1*y <= 110.3) sage: p.polyhedron() A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices @@ -1133,20 +1133,20 @@ cdef class MixedIntegerLinearProgram(SageObject): for lb, (indices, values), ub in self.constraints(): coeffs = dict(zip(indices, values)) # Equalities - if (not lb is None) and lb == ub: + if (lb is not None) and lb == ub: linear_function = [] linear_function = [coeffs.get(i,0) for i in range(nvar)] linear_function.insert(0,-lb) equalities.append(linear_function) continue # Lower Bound - if not lb is None: + if lb is not None: linear_function = [] linear_function = [coeffs.get(i,0) for i in range(nvar)] linear_function.insert(0,-lb) inequalities.append(linear_function) # Upper Bound - if not ub is None: + if ub is not None: linear_function = [] linear_function = [-coeffs.get(i,0) for i in range(nvar)] linear_function.insert(0,ub) @@ -1157,25 +1157,25 @@ cdef class MixedIntegerLinearProgram(SageObject): for 0<= i < nvar: lb, ub = b.col_bounds(i) # Fixed variable - if (not lb is None) and lb == ub: + if (lb is not None) and lb == ub: linear_function = copy(zero) linear_function[i] = 1 linear_function.insert(0,-lb) equalities.append(linear_function) continue # Lower bound - if not lb is None: + if lb is not None: linear_function = copy(zero) linear_function[i] = 1 linear_function.insert(0,-lb) inequalities.append(linear_function) # Upper bound - if not ub is None: + if ub is not None: linear_function = copy(zero) linear_function[i] = -1 linear_function.insert(0,ub) inequalities.append(linear_function) - return Polyhedron(ieqs = inequalities, eqns = equalities, **kwds) + return Polyhedron(ieqs=inequalities, eqns=equalities, **kwds) def show(self): r""" @@ -1335,7 +1335,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + x[2]) - sage: p.add_constraint(-3*x[1] + 2*x[2], max=2,name="OneConstraint") + sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name="OneConstraint") sage: import tempfile sage: with tempfile.NamedTemporaryFile(suffix=".mps") as f: ....: p.write_mps(f.name) @@ -1819,7 +1819,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 5*x[2]) sage: p.add_constraint(x[1] + 2/10*x[2], max=4) - sage: p.add_constraint(1.5*x[1]+3*x[2], max=4) + sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4) sage: round(p.solve(),5) 6.66667 sage: p.set_objective(None) @@ -1860,7 +1860,8 @@ cdef class MixedIntegerLinearProgram(SageObject): values.append(f.get(i,self._backend.zero())) self._backend.set_objective(values, d) - def add_constraint(self, linear_function, max=None, min=None, name=None): + def add_constraint(self, linear_function, max=None, min=None, name=None, + return_indices=False): r""" Adds a constraint to the ``MixedIntegerLinearProgram``. @@ -1899,6 +1900,16 @@ cdef class MixedIntegerLinearProgram(SageObject): - ``name`` -- A name for the constraint. + - ``return_indices`` -- boolean (optional, default False), + whether to return the indices of the added constraints. + + OUTPUT: + + The row indices of the constraints added, if + ``return_indices`` is true and the backend guarantees that + removing them again yields the original MIP, ``None`` + otherwise. + To set a lower and/or upper bound on the variables use the methods ``set_min`` and/or ``set_max`` of ``MixedIntegerLinearProgram``. @@ -1997,9 +2008,9 @@ cdef class MixedIntegerLinearProgram(SageObject): Complex constraints:: - sage: p = MixedIntegerLinearProgram(solver = "GLPK") + sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: b = p.new_variable(nonnegative=True) - sage: p.add_constraint( b[8] - b[15] <= 3*b[8] + 9) + sage: p.add_constraint(b[8] - b[15] <= 3*b[8] + 9) sage: p.show() Maximization: <BLANKLINE> @@ -2009,10 +2020,21 @@ cdef class MixedIntegerLinearProgram(SageObject): x_0 is a continuous variable (min=0.0, max=+oo) x_1 is a continuous variable (min=0.0, max=+oo) - Empty constraint:: + Trivially true empty constraint: sage: p = MixedIntegerLinearProgram(solver='GLPK') - sage: p.add_constraint(sum([]),min=2) + sage: p.add_constraint(sum([]), max=2) + sage: p.solve() + 0.0 + + Infeasible empty constraint:: + + sage: p = MixedIntegerLinearProgram(solver='GLPK') + sage: p.add_constraint(sum([]), min=2) + sage: p.solve() + Traceback (most recent call last): + ... + MIPSolverException: GLPK: Problem has no feasible solution Min/Max are numerical :: @@ -2029,7 +2051,8 @@ cdef class MixedIntegerLinearProgram(SageObject): Do not add redundant elements (notice only one copy of each constraint is added):: sage: lp = MixedIntegerLinearProgram(solver="GLPK", check_redundant=True) - sage: for each in range(10): lp.add_constraint(lp[0]-lp[1],min=1) + sage: for each in range(10): + ....: lp.add_constraint(lp[0]-lp[1], min=1) sage: lp.show() Maximization: <BLANKLINE> @@ -2041,7 +2064,8 @@ cdef class MixedIntegerLinearProgram(SageObject): We check for constant multiples of constraints as well:: - sage: for each in range(10): lp.add_constraint(2*lp[0]-2*lp[1],min=2) + sage: for each in range(10): + ....: lp.add_constraint(2*lp[0] - 2*lp[1], min=2) sage: lp.show() Maximization: <BLANKLINE> @@ -2053,13 +2077,14 @@ cdef class MixedIntegerLinearProgram(SageObject): But if the constant multiple is negative, we should add it anyway (once):: - sage: for each in range(10): lp.add_constraint(-2*lp[0]+2*lp[1],min=-2) + sage: for each in range(10): + ....: lp.add_constraint(-2*lp[0] + 2*lp[1], min=-2) sage: lp.show() Maximization: <BLANKLINE> Constraints: 1.0 <= x_0 - x_1 - -2.0 <= -2.0 x_0 + 2.0 x_1 + -2.0 <= -2.0 x_0 + 2.0 x_1 Variables: x_0 is a continuous variable (min=-oo, max=+oo) x_1 is a continuous variable (min=-oo, max=+oo) @@ -2072,10 +2097,45 @@ cdef class MixedIntegerLinearProgram(SageObject): Traceback (most recent call last): ... ValueError: argument must be a linear function or constraint, got True - """ - if linear_function is 0: - return + Check that adding and removing constraints works:: + + sage: p = MixedIntegerLinearProgram(check_redundant=True) + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(x[0] + x[1] <= 3) + sage: p.set_objective(x[0] + 2*x[1]) + sage: p.solve() + 6.0 + + We add (non-trivially) redundant constraints:: + + sage: c1 = p.add_constraint(0 <= x[0] <= 3, return_indices=True); c1 + [1, 2] + sage: p.solve() + 6.0 + + We add a non-redundant constraint:: + + sage: c2 = p.add_constraint(1 <= x[1] <= 2, return_indices=True); c2 + [3, 4] + sage: p.solve() + 5.0 + + We remove the redundant constraints `1` and `2`, keep in mind + that indices change when removing constraints:: + + sage: p.remove_constraint(1) + sage: p.remove_constraint(1) + sage: p.solve() + 5.0 + + We remove another constraint:: + + sage: p.remove_constraint(2) + sage: p.solve() + 6.0 + + """ from sage.numerical.linear_functions import is_LinearFunction, is_LinearConstraint from sage.numerical.linear_tensor import is_LinearTensor from sage.numerical.linear_tensor_constraints import is_LinearTensorConstraint @@ -2108,30 +2168,58 @@ cdef class MixedIntegerLinearProgram(SageObject): # Send to backend if is_LinearFunction(linear_function): if self._check_redundant and self._is_redundant_constraint(constraint, min, max): + if return_indices: + return [] return + nrows_before = self._backend.nrows() self._backend.add_linear_constraint(constraint.items(), min, max, name) elif is_LinearTensor(linear_function): + nrows_before = self._backend.nrows() self._backend.add_linear_constraint_vector(M.degree(), constraint.items(), min, max, name) else: assert False, 'unreachable' + if return_indices: + return list(range(nrows_before, self._backend.nrows())) + return elif is_LinearConstraint(linear_function): if not(min is None and max is None): raise ValueError('min and max must not be specified for (in)equalities') relation = linear_function + if return_indices: + row_indices = [] + else: + row_indices = None for lhs, rhs in relation.equations(): - self.add_constraint(lhs-rhs, min=0, max=0, name=name) + new_indices = self.add_constraint(lhs-rhs, min=0, max=0, + name=name, + return_indices=return_indices) + if new_indices is not None: + row_indices.extend(new_indices) for lhs, rhs in relation.inequalities(): - self.add_constraint(lhs-rhs, max=0, name=name) + new_indices = self.add_constraint(lhs-rhs, max=0, + name=name, + return_indices=return_indices) + if new_indices is not None: + row_indices.extend(new_indices) + return row_indices elif is_LinearTensorConstraint(linear_function): if not(min is None and max is None): raise ValueError('min and max must not be specified for (in)equalities') relation = linear_function M = relation.parent().linear_tensors().free_module() - self.add_constraint(relation.lhs() - relation.rhs(), - min=M(0) if relation.is_equation() else None, - max=M(0), name=name) + return self.add_constraint(relation.lhs() - relation.rhs(), + min=M(0) if relation.is_equation() else None, + max=M(0), + name=name, return_indices=return_indices) + elif isinstance(linear_function, bool): + raise ValueError('argument must be a linear function or constraint, got ' + str(linear_function)) else: - raise ValueError('argument must be a linear function or constraint, got '+str(linear_function)) + try: + linear_function = self.linear_functions_parent()(linear_function) + except (TypeError, ValueError): + raise ValueError('argument must be a linear function or constraint, got ' + str(linear_function)) + return self.add_constraint(linear_function, max=max, min=min, + name=name, return_indices=return_indices) def _is_redundant_constraint(self, constraint, min_bound, max_bound): """ @@ -2190,9 +2278,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x, y = p[0], p[1] - sage: p.add_constraint(x + y, max = 10) - sage: p.add_constraint(x - y, max = 0) - sage: p.add_constraint(x, max = 4) + sage: p.add_constraint(x + y, max=10) + sage: p.add_constraint(x - y, max=0) + sage: p.add_constraint(x, max=4) sage: p.show() Maximization: <BLANKLINE> @@ -2212,7 +2300,8 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.number_of_constraints() 2 """ - if self._check_redundant: self._constraints.pop(i) + if self._check_redundant: + self._constraints.pop(i) self._backend.remove_constraint(i) def remove_constraints(self, constraints): @@ -2227,9 +2316,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x, y = p[0], p[1] - sage: p.add_constraint(x + y, max = 10) - sage: p.add_constraint(x - y, max = 0) - sage: p.add_constraint(x, max = 4) + sage: p.add_constraint(x + y, max=10) + sage: p.add_constraint(x - y, max=0) + sage: p.add_constraint(x, max=4) sage: p.show() Maximization: <BLANKLINE> @@ -2257,12 +2346,13 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(check_redundant=True, solver='GLPK') sage: x, y = p[0], p[1] - sage: p.add_constraint(x + y, max = 10) - sage: for each in range(10): p.add_constraint(x - y, max = 10) - sage: p.add_constraint(x, max = 4) + sage: p.add_constraint(x + y, max=10) + sage: for each in range(10): + ....: p.add_constraint(x - y, max=10) + sage: p.add_constraint(x, max=4) sage: p.number_of_constraints() 3 - sage: p.remove_constraints(range(1,9)) + sage: p.remove_constraints(range(1, 9)) Traceback (most recent call last): ... IndexError: pop index out of range @@ -2272,14 +2362,24 @@ cdef class MixedIntegerLinearProgram(SageObject): We should now be able to add the old constraint back in:: - sage: for each in range(10): p.add_constraint(x - y, max = 10) + sage: for each in range(10): + ....: p.add_constraint(x - y, max=10) sage: p.number_of_constraints() 3 + + TESTS: + + Removing no constraints does not make Sage crash, see + :trac:`34881`:: + + sage: MixedIntegerLinearProgram().remove_constraints([]) + """ if self._check_redundant: for i in sorted(constraints, reverse=True): self._constraints.pop(i) - self._backend.remove_constraints(constraints) + if constraints: + self._backend.remove_constraints(constraints) def set_binary(self, ee): r""" @@ -2548,14 +2648,15 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x, y = p[0], p[1] - sage: p.add_constraint(2*x + 3*y, max = 6) - sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.add_constraint(2*x + 3*y, max=6) + sage: p.add_constraint(3*x + 2*y, max=6) sage: p.set_objective(x + y + 7) sage: p.set_integer(x); p.set_integer(y) sage: p.solve() 9.0 """ - if log is not None: self._backend.set_verbosity(log) + if log is not None: + self._backend.set_verbosity(log) self._backend.solve() return self._backend.get_objective_value() @@ -2713,11 +2814,11 @@ cdef class MixedIntegerLinearProgram(SageObject): Very common parameters have aliases making them solver-independent. For example, the following:: - sage: p = MixedIntegerLinearProgram(solver = "GLPK") + sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: p.solver_parameter("timelimit", 60) Sets the solver to stop its computations after 60 seconds, and works - with GLPK, CPLEX and Gurobi. + with GLPK, CPLEX , SCIP, and Gurobi. - ``"timelimit"`` -- defines the maximum time spent on a computation. Measured in seconds. @@ -2727,7 +2828,7 @@ cdef class MixedIntegerLinearProgram(SageObject): are not recorded, and we can disable this feature providing an empty filename. This is currently working with CPLEX and Gurobi:: - sage: p = MixedIntegerLinearProgram(solver = "CPLEX") # optional - CPLEX + sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX sage: p.solver_parameter("logfile") # optional - CPLEX '' sage: p.solver_parameter("logfile", "/dev/null") # optional - CPLEX @@ -2751,7 +2852,7 @@ cdef class MixedIntegerLinearProgram(SageObject): The command :: - sage: p = MixedIntegerLinearProgram(solver = "CPLEX") # optional - CPLEX + sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX sage: p.solver_parameter("CPX_PARAM_TILIM", 60) # optional - CPLEX works as intended. @@ -2760,6 +2861,9 @@ cdef class MixedIntegerLinearProgram(SageObject): method. Their list is available on Gurobi's website `<http://www.gurobi.com/documentation/5.5/reference-manual/node798>`_. + SCIP's parameter can be found here: + `<http://scip.zib.de/doc-5.0.1/html/PARAMETERS.php>`_. + INPUT: - ``name`` (string) -- the parameter @@ -2769,7 +2873,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLES:: - sage: p = MixedIntegerLinearProgram(solver = "GLPK") + sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: p.solver_parameter("timelimit", 60) sage: p.solver_parameter("timelimit") 60.0 @@ -2828,8 +2932,8 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: x, y = p[0], p[1] - sage: p.add_constraint(2*x + 3*y, max = 6) - sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.add_constraint(2*x + 3*y, max=6) + sage: p.add_constraint(3*x + 2*y, max=6) sage: p.set_objective(x + y + 7) sage: b = p.get_backend() sage: b.solver_parameter("simplex_or_intopt", "simplex_only") @@ -2857,8 +2961,8 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: x, y = p[0], p[1] - sage: p.add_constraint(2*x + 3*y, max = 6) - sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.add_constraint(2*x + 3*y, max=6) + sage: p.add_constraint(3*x + 2*y, max=6) sage: p.set_objective(x + y + 7) sage: p.solve() # rel tol 1e-5 9.4 @@ -2890,11 +2994,11 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: b = p.new_variable(binary=True) sage: p.set_objective(p.sum(b[v] for v in g)) sage: for v in g: - ....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1) - sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution + ....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1) + sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution sage: p.solve() # rel tol 100 1.0 - sage: p.best_known_objective_bound() # random + sage: p.best_known_objective_bound() # random 48.0 """ return self._backend.best_known_objective_bound() @@ -2924,11 +3028,11 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: b = p.new_variable(binary=True) sage: p.set_objective(p.sum(b[v] for v in g)) sage: for v in g: - ....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1) - sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution + ....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1) + sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution sage: p.solve() # rel tol 100 1.0 - sage: p.get_relative_objective_gap() # random + sage: p.get_relative_objective_gap() # random 46.99999999999999 TESTS: @@ -2966,10 +3070,10 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: x = p.new_variable(nonnegative=True) sage: y = p.new_variable(nonnegative=True, name='n') sage: v = p.new_variable(nonnegative=True) - sage: p.add_constraint( x[0] + x[1] - 7*y[0] + v[0]<= 2, name='K' ) - sage: p.add_constraint( x[1] + 2*y[0] - v[0] <= 3 ) - sage: p.add_constraint( 5*x[0] + y[0] <= 21, name='L' ) - sage: p.set_objective( 2*x[0] + 3*x[1] + 4*y[0] + 5*v[0]) + sage: p.add_constraint(x[0] + x[1] - 7*y[0] + v[0] <= 2, name='K') + sage: p.add_constraint(x[1] + 2*y[0] - v[0] <= 3) + sage: p.add_constraint(5*x[0] + y[0] <= 21, name='L') + sage: p.set_objective(2*x[0] + 3*x[1] + 4*y[0] + 5*v[0]) sage: lp, basis = p.interactive_lp_problem() sage: basis ['K', 'w_1', 'L'] @@ -3101,8 +3205,8 @@ class MIPSolverException(RuntimeError): sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: v = p.new_variable(nonnegative=True) - sage: p.add_constraint(v[0],max=5.5) - sage: p.add_constraint(v[0],min=7.6) + sage: p.add_constraint(v[0], max=5.5) + sage: p.add_constraint(v[0], min=7.6) sage: p.set_objective(v[0]) Tests of GLPK's Exceptions:: @@ -3116,8 +3220,8 @@ class MIPSolverException(RuntimeError): sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: v = p.new_variable(nonnegative=True) - sage: p.add_constraint(v[0],max=5.6) - sage: p.add_constraint(v[0],min=5.2) + sage: p.add_constraint(v[0], max=5.6) + sage: p.add_constraint(v[0], min=5.2) sage: p.set_objective(v[0]) sage: p.set_integer(v) @@ -3170,7 +3274,7 @@ cdef class MIPVariable(SageObject): indexed by arbitrary keys and are created dynamically on access - For more informations, see the method + For more information, see the method ``MixedIntegerLinearProgram.new_variable``. EXAMPLES:: @@ -3178,6 +3282,7 @@ cdef class MIPVariable(SageObject): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: p.new_variable(nonnegative=True) MIPVariable with 0 real components, >= 0 + """ self._dict = {} self._p = mip @@ -3576,7 +3681,7 @@ cdef class MIPVariable(SageObject): """ Implement the action of a matrix multiplying from the left. """ - result = dict() + result = {} for i, col in enumerate(m.columns()): x = self[i] x_index, = x.dict().keys() @@ -3585,4 +3690,3 @@ cdef class MIPVariable(SageObject): V = FreeModule(self._p.base_ring(), m.nrows()) T = self._p.linear_functions_parent().tensor(V) return T(result) - diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index 021c5b33aa9..82e954baa0f 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -420,10 +420,11 @@ def minimize(func, x0, gradient=None, hessian=None, algorithm="default", hessian=lambda p: [[a(*p) for a in row] for row in hess_fast] from scipy import dot hessian_p=lambda p,v: dot(numpy.array(hessian(p)),v) - min = optimize.fmin_ncg(f, [float(_) for _ in x0], fprime=gradient, \ + min = optimize.fmin_ncg(f, [float(_) for _ in x0], fprime=gradient, fhess=hessian, fhess_p=hessian_p, disp=verbose, **args) return vector(RDF, min) + def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args): r""" Minimize a function with constraints. diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index c14518af570..8428b39b4fd 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -23,12 +23,12 @@ def normalize_input(a): INPUT: - - ``a`` -- object + - ``a`` -- object OUTPUT: - - ``args`` -- tuple - - ``kwds`` -- dictionary + - ``args`` -- tuple + - ``kwds`` -- dictionary EXAMPLES:: @@ -41,7 +41,7 @@ def normalize_input(a): sage: sage.parallel.decorate.normalize_input( 5 ) ((5,), {}) """ - if isinstance(a, tuple) and len(a) == 2 and isinstance(a[0],tuple) and isinstance(a[1],dict): + if isinstance(a, tuple) and len(a) == 2 and isinstance(a[0], tuple) and isinstance(a[1], dict): return a elif isinstance(a, tuple): return (a, {}) @@ -97,11 +97,11 @@ def __call__(self, f): INPUT: - - ``f`` -- Python callable object or function + - ``f`` -- Python callable object or function OUTPUT: - - Decorated version of ``f`` + - Decorated version of ``f`` EXAMPLES:: @@ -131,8 +131,8 @@ def __init__(self, parallel, func): """ .. note:: - This is typically accessed indirectly through - :meth:`Parallel.__call__`. + This is typically accessed indirectly through + :meth:`Parallel.__call__`. INPUT: @@ -159,13 +159,11 @@ def __call__(self, *args, **kwds): 4 sage: sorted(pf([2,3])) [(((2,), {}), 4), (((3,), {}), 9)] - """ - if len(args) > 0 and isinstance(args[0], (list, -types.GeneratorType)): + """ + if len(args) > 0 and isinstance(args[0], (list, types.GeneratorType)): return self.parallel.p_iter(self.func, (normalize_input(a) -for a in args[0])) - else: - return self.func(*args, **kwds) + for a in args[0])) + return self.func(*args, **kwds) def __get__(self, instance, owner): """ @@ -174,7 +172,7 @@ def __get__(self, instance, owner): .. note:: - This is the key to fixing :trac:`11461`. + This is the key to fixing :trac:`11461`. EXAMPLES: @@ -217,14 +215,14 @@ def __get__(self, instance, owner): [(((2,), {}), 4), (((3,), {}), 9)] """ try: - #If this ParallelFunction object is accessed as an - #attribute of a class or instance, the underlying function - #should be "accessed" in the same way. + # If this ParallelFunction object is accessed as an + # attribute of a class or instance, the underlying function + # should be "accessed" in the same way. new_func = self.func.__get__(instance, owner) except AttributeError: - #This will happen if a non-function attribute is - #decorated. For example, an expression that's an - #attribute of a class. + # This will happen if a non-function attribute is + # decorated. For example, an expression that's an + # attribute of a class. new_func = self.func return ParallelFunction(self.parallel, new_func) @@ -243,7 +241,8 @@ def _sage_argspec_(self): ....: return x + y sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(p(f)) - ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['x', 'y'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ from sage.misc.sageinspect import sage_getargspec return sage_getargspec(self.func) @@ -413,8 +412,6 @@ def parallel(p_iter='fork', ncpus=None, **kwds): return Parallel(p_iter, ncpus, **kwds) - - ################################################################### # The @fork decorator -- evaluate a function with no side effects # in memory, so the only side effects (if any) are on disk. @@ -437,10 +434,10 @@ def __init__(self, timeout=0, verbose=False): """ INPUT: - - ``timeout`` -- (default: 0) kill the subprocess after it has run this - many seconds (wall time), or if ``timeout`` is zero, do not kill it. - - ``verbose`` -- (default: ``False``) whether to print anything about - what the decorator does (e.g., killing the subprocess) + - ``timeout`` -- (default: 0) kill the subprocess after it has run this + many seconds (wall time), or if ``timeout`` is zero, do not kill it. + - ``verbose`` -- (default: ``False``) whether to print anything about + what the decorator does (e.g., killing the subprocess) EXAMPLES:: @@ -456,11 +453,11 @@ def __call__(self, f): """ INPUT: - - ``f`` -- a function + - ``f`` -- a function OUTPUT: - - A decorated function. + - A decorated function. EXAMPLES:: @@ -481,18 +478,18 @@ def h(*args, **kwds): def fork(f=None, timeout=0, verbose=False): """ - Decorate a function so that when called it runs in a forked - subprocess. This means that it won't have any in-memory - side effects on the parent Sage process. The pexpect interfaces - are all reset. + Decorate a function so that when called it runs in a forked subprocess. + + This means that it will not have any in-memory side effects on the + parent Sage process. The pexpect interfaces are all reset. INPUT: - - ``f`` -- a function - - ``timeout`` -- (default: 0) if positive, kill the subprocess after - this many seconds (wall time) - - ``verbose`` -- (default: ``False``) whether to print anything - about what the decorator does (e.g., killing the subprocess) + - ``f`` -- a function + - ``timeout`` -- (default: 0) if positive, kill the subprocess after + this many seconds (wall time) + - ``verbose`` -- (default: ``False``) whether to print anything + about what the decorator does (e.g., killing the subprocess) .. warning:: diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 8881d7a01ac..7307a68dd44 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -410,7 +410,7 @@ - ``worker._request`` -- a :class:`~multiprocessing.queues.SimpleQueue` storing steal request submitted to ``worker``. - ``worker._read_task``, ``worker._write_task`` -- a - :class:`~multiprocessing.queues.Pipe` used to transfert node during steal. + :class:`~multiprocessing.queues.Pipe` used to transfer node during steal. - ``worker._thief`` -- a :class:`~threading.Thread` which is in charge of stealing from ``worker._todo``. @@ -1196,9 +1196,9 @@ def get_results(self, timeout=None): active_proc = self._nprocess while active_proc > 0: try: - logger.debug('Waiting on results; active_proc: %s, ' - 'timeout: %s, aborted: %s' % - (active_proc, timeout, self._aborted.value)) + logger.debug('Waiting on results; active_proc: {}, ' + 'timeout: {}, aborted: {}'.format( + active_proc, timeout, self._aborted.value)) newres = self._results.get(timeout=timeout) except queue.Empty: logger.debug('Timed out waiting for results; aborting') @@ -1249,13 +1249,13 @@ def finish(self): if not self._aborted.value: logger.debug("Joining worker processes...") for worker in self._workers: - logger.debug("Joining %s" % worker.name) + logger.debug(f"Joining {worker.name}") worker.join() logger.debug("Joining done") else: logger.debug("Killing worker processes...") for worker in self._workers: - logger.debug("Terminating %s" % worker.name) + logger.debug(f"Terminating {worker.name}") worker.terminate() logger.debug("Killing done") @@ -1527,9 +1527,9 @@ def print_communication_statistics(self, blocksize=16): # local function (see e.g: # https://stackoverflow.com/questions/2609518/python-nested-function-scopes). - def pstat(name, start, end, ist): + def pstat(name, start, end, istat): res[0] += ("\n" + name + " ".join( - "%4i" % (self._stats[i][ist]) for i in range(start, end))) + "%4i" % (self._stats[i][istat]) for i in range(start, end))) for start in range(0, self._nprocess, blocksize): end = min(start + blocksize, self._nprocess) res[0] = ("#proc: " + @@ -1613,17 +1613,17 @@ def _thief(self): for ireq in iter(self._request.get, AbortError): reqs += 1 target = self._mapred._workers[ireq] - logger.debug("Got a Steal request from %s" % target.name) + logger.debug(f"Got a Steal request from {target.name}") self._mapred._signal_task_start() try: work = self._todo.popleft() except IndexError: target._write_task.send(None) - logger.debug("Failed Steal %s" % target.name) + logger.debug(f"Failed Steal {target.name}") self._mapred._signal_task_done() else: target._write_task.send(work) - logger.debug("Succesful Steal %s" % target.name) + logger.debug(f"Successful Steal {target.name}") thefts += 1 except AbortError: logger.debug("Thief aborted") @@ -1668,10 +1668,10 @@ def steal(self): while node is None: victim = self._mapred.random_worker() if victim is not self: - logger.debug("Trying to steal from %s" % victim.name) + logger.debug(f"Trying to steal from {victim.name}") victim._request.put(self._iproc) self._stats[0] += 1 - logger.debug("waiting for steal answer from %s" % victim.name) + logger.debug(f"waiting for steal answer from {victim.name}") node = self._read_task.recv() # logger.debug("Request answer: %s" % (node,)) if node is AbortError: @@ -1713,7 +1713,7 @@ def run(self): PROFILER.runcall(self.run_myself) output = profile + str(self._iproc) - logger.warn("Profiling in %s ..." % output) + logger.warn(f"Profiling in {output} ...") PROFILER.dump_stats(output) else: self.run_myself() diff --git a/src/sage/parallel/multiprocessing_sage.py b/src/sage/parallel/multiprocessing_sage.py index 0cd628a6d3b..be9b980aba9 100644 --- a/src/sage/parallel/multiprocessing_sage.py +++ b/src/sage/parallel/multiprocessing_sage.py @@ -8,7 +8,7 @@ # Distributed under the terms of (any version of) the GNU # General Public License (GPL). The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ################################################################################ from multiprocessing import Pool @@ -16,6 +16,7 @@ from sage.misc.fpickle import pickle_function, call_pickled_function from . import ncpus + def pyprocessing(processes=0): """ Return a parallel iterator using a given number of processes diff --git a/src/sage/parallel/ncpus.py b/src/sage/parallel/ncpus.py index 0dcdd16fe17..dfb6c3c6734 100644 --- a/src/sage/parallel/ncpus.py +++ b/src/sage/parallel/ncpus.py @@ -33,6 +33,7 @@ import os import subprocess + def ncpus(): """ Detects the number of effective CPUs in the system. @@ -52,22 +53,25 @@ def ncpus(): else: return int(n) - #for Linux, Unix and MacOS + # for Linux, Unix and MacOS if hasattr(os, "sysconf"): if "SC_NPROCESSORS_ONLN" in os.sysconf_names: - #Linux and Unix + # Linux and Unix ncpus = os.sysconf("SC_NPROCESSORS_ONLN") if isinstance(ncpus, int) and ncpus > 0: return ncpus else: - #MacOS X - #deprecated: return int(os.popen2("sysctl -n hw.ncpu")[1].read()) - process = subprocess.Popen("sysctl -n hw.ncpu", shell=True, stdin=subprocess.PIPE, stdout = subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) + # MacOS X + # deprecated: return int(os.popen2("sysctl -n hw.ncpu")[1].read()) + process = subprocess.Popen("sysctl -n hw.ncpu", shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True) return int(process.stdout.read()) - #for Windows + # for Windows if "NUMBER_OF_PROCESSORS" in os.environ: ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) if ncpus > 0: return ncpus - #return the default value + # return the default value return 1 diff --git a/src/sage/parallel/parallelism.py b/src/sage/parallel/parallelism.py index b974616a784..8aafe232fee 100644 --- a/src/sage/parallel/parallelism.py +++ b/src/sage/parallel/parallelism.py @@ -28,6 +28,7 @@ from sage.parallel.ncpus import ncpus from sage.rings.integer import Integer + class Parallelism(Singleton, SageObject): r""" Singleton class for managing the number of processes used in parallel @@ -106,9 +107,12 @@ def __init__(self): sage: TestSuite(par).run() """ - self._default = ncpus() # default number of proc. used in parallelizations - self._nproc = {'tensor' : 1, 'linbox' : 1} # dict. of number of processes to be used - # (keys: computational field) + self._default = ncpus() + # default number of proc. used in parallelizations + + self._nproc = {'tensor': 1, 'linbox': 1} + # dict. of number of processes to be used + # (keys: computational field) def _repr_(self): r""" @@ -242,7 +246,7 @@ def set(self, field=None, nproc=None): if nproc is None: self._nproc[field] = self._default else: - if not isinstance(nproc, (int,Integer)): + if not isinstance(nproc, (int, Integer)): raise TypeError("nproc must be integer") self._nproc[field] = nproc @@ -281,7 +285,6 @@ def get(self, field): "implemented in Parallelism()") return self._nproc[field] - def get_all(self): r""" Return the number of processes which will be used in parallel @@ -307,7 +310,6 @@ def get_all(self): """ return self._nproc - def set_default(self, nproc=None): r""" Set the default number of processes to be launched in parallel @@ -343,7 +345,7 @@ def set_default(self, nproc=None): if nproc is None: self._default = ncpus() else: - if not isinstance(nproc,(int,Integer)): + if not isinstance(nproc, (int, Integer)): raise TypeError("nproc must be integer") self._default = nproc diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index 758a572a2c2..69f2ede8420 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -2,15 +2,15 @@ Parallel iterator built using the ``fork()`` system call """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 William Stein <wstein@gmail.com> # # 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 shutil import rmtree @@ -41,7 +41,7 @@ class WorkerData(): sage: W.starttime # random 1499330252.463206 """ - def __init__(self, input, starttime=None, failure=""): + def __init__(self, input_value, starttime=None, failure=""): r""" See the class documentation for description of the inputs. @@ -50,7 +50,7 @@ def __init__(self, input, starttime=None, failure=""): sage: from sage.parallel.use_fork import WorkerData sage: W = WorkerData(42) """ - self.input = input + self.input = input_value self.starttime = starttime or walltime() self.failure = failure @@ -197,8 +197,7 @@ def __call__(self, f, inputs): if T - W.starttime > timeout: if self.verbose: print( - "Killing subprocess %s with input %s which took too long" - % (pid, W.input) ) + "Killing subprocess %s with input %s which took too long" % (pid, W.input)) os.kill(pid, signal.SIGKILL) W.failure = " (timed out)" except KeyError: @@ -206,7 +205,7 @@ def __call__(self, f, inputs): pass else: # collect data from process that successfully terminated - sobj = os.path.join(dir, '%s.sobj'%pid) + sobj = os.path.join(dir, '%s.sobj' % pid) try: with open(sobj, "rb") as file: data = file.read() @@ -219,7 +218,7 @@ def __call__(self, f, inputs): except Exception as E: answer = "INVALID DATA {}".format(E) - out = os.path.join(dir, '%s.out'%pid) + out = os.path.join(dir, '%s.out' % pid) try: with open(out) as file: sys.stdout.write(file.read()) diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index bb834763afc..f65973bcbd5 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -273,9 +273,9 @@ def _matplotlib_arc(self): p = patches.Arc((self.x, self.y), 2. * self.r1, 2. * self.r2, - fmod(self.angle, 2 * pi) * (180. / pi), - self.s1 * (180. / pi), - self.s2 * (180. / pi)) + angle=fmod(self.angle, 2 * pi) * (180. / pi), + theta1=self.s1 * (180. / pi), + theta2=self.s2 * (180. / pi)) return p def bezier_path(self): diff --git a/src/sage/plot/bar_chart.py b/src/sage/plot/bar_chart.py index 1f0d7d62cf7..34ed242af4c 100644 --- a/src/sage/plot/bar_chart.py +++ b/src/sage/plot/bar_chart.py @@ -1,5 +1,5 @@ """ -Bar Charts +Bar charts """ # ***************************************************************************** diff --git a/src/sage/plot/bezier_path.py b/src/sage/plot/bezier_path.py index 99290dc9a9d..27c95f10c0c 100644 --- a/src/sage/plot/bezier_path.py +++ b/src/sage/plot/bezier_path.py @@ -1,5 +1,5 @@ r""" -Bezier Paths +Bezier paths """ #***************************************************************************** # Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>, diff --git a/src/sage/plot/complex_plot.pyx b/src/sage/plot/complex_plot.pyx index 47617d9a353..6f0aeab87ae 100644 --- a/src/sage/plot/complex_plot.pyx +++ b/src/sage/plot/complex_plot.pyx @@ -1,5 +1,5 @@ """ -Complex Plots +Complex plots AUTHORS: diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 3cf3d0f932d..c0cab456686 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -1,5 +1,5 @@ """ -Contour Plots +Contour plots """ # **************************************************************************** # Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>, diff --git a/src/sage/plot/density_plot.py b/src/sage/plot/density_plot.py index 741b5d9be00..155ac8d7a8e 100644 --- a/src/sage/plot/density_plot.py +++ b/src/sage/plot/density_plot.py @@ -1,5 +1,5 @@ """ -Density Plots +Density plots """ #***************************************************************************** diff --git a/src/sage/plot/ellipse.py b/src/sage/plot/ellipse.py index a77e6fe640a..c35bed574ef 100644 --- a/src/sage/plot/ellipse.py +++ b/src/sage/plot/ellipse.py @@ -192,7 +192,8 @@ def _render_on_subplot(self, subplot): options = self.options() p = patches.Ellipse( (self.x,self.y), - self.r1*2.,self.r2*2.,self.angle/pi*180.) + self.r1*2.,self.r2*2., + angle=self.angle/pi*180.) p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) a = float(options['alpha']) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index e779e1210e3..019ad79c7a4 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2007,7 +2007,7 @@ def show(self, **kwds): We can also do custom formatting if you need it. See above for full details:: - sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") + sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") # not tested (broken with matplotlib 3.6) Graphics object consisting of 1 graphics primitive This is particularly useful when setting custom ticks in multiples @@ -2341,7 +2341,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), sage: subplot = Figure().add_subplot(111) sage: p._objects[0]._render_on_subplot(subplot) sage: p._matplotlib_tick_formatter(subplot, **d) - (<AxesSubplot:>, + (<AxesSubplot:...>, <matplotlib.ticker.MaxNLocator object at ...>, <matplotlib.ticker.MaxNLocator object at ...>, <matplotlib.ticker.ScalarFormatter object at ...>, @@ -2366,7 +2366,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), elif x_locator == []: x_locator = NullLocator() elif isinstance(x_locator, list): - x_locator = FixedLocator(x_locator) + x_locator = FixedLocator([float(x) for x in x_locator]) else: # x_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(xmax / x_locator) - ceil(xmin / x_locator) > 1: @@ -2387,7 +2387,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), elif y_locator == []: y_locator = NullLocator() elif isinstance(y_locator, list): - y_locator = FixedLocator(y_locator) + y_locator = FixedLocator([float(y) for y in y_locator]) else: # y_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(ymax / y_locator) - ceil(ymin / y_locator) > 1: @@ -2419,7 +2419,11 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), LogFormatterMathtext(base=base[0])(n, pos).replace( "\\mathdefault", "")) else: - x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) + # circumvent the problem of symbolic tick values (trac #34693) + if isinstance(x_locator, FixedLocator): + x_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[0]]) + else: + x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) elif isinstance(x_formatter, (list, tuple)): if (not isinstance(ticks[0], (list, tuple)) or len(ticks[0]) != len(x_formatter)): @@ -2444,7 +2448,11 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), LogFormatterMathtext(base=base[1])(n, pos).replace( "\\mathdefault", "")) else: - y_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) + # circumvent the problem of symbolic tick values (trac #34693) + if isinstance(y_locator, FixedLocator): + y_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[1]]) + else: + y_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) elif isinstance(y_formatter, (list, tuple)): if (not isinstance(ticks[1], (list, tuple)) or len(ticks[1]) != len(y_formatter)): diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py index e4b3b4ddc82..691e3c31307 100644 --- a/src/sage/plot/line.py +++ b/src/sage/plot/line.py @@ -1,5 +1,5 @@ """ -Line Plots +Line plots """ # ***************************************************************************** diff --git a/src/sage/plot/matrix_plot.py b/src/sage/plot/matrix_plot.py index e15b007184e..f62d362ea11 100644 --- a/src/sage/plot/matrix_plot.py +++ b/src/sage/plot/matrix_plot.py @@ -1,5 +1,5 @@ """ -Matrix Plots +Matrix plots """ #***************************************************************************** # Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>, diff --git a/src/sage/plot/misc.py b/src/sage/plot/misc.py index a64457ef661..37a8000f9aa 100644 --- a/src/sage/plot/misc.py +++ b/src/sage/plot/misc.py @@ -1,4 +1,6 @@ -"Plotting utilities" +""" +Plotting utilities +""" # **************************************************************************** # Distributed under the terms of the GNU General Public License (GPL) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index d41b4bad908..48f84060c8c 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1,5 +1,5 @@ r""" -2D Plotting +2D plotting Sage provides extensive 2D plotting functionality. The underlying rendering is done using the matplotlib Python library. @@ -1741,15 +1741,15 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) :: - sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") + sage: plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") + g = plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") sphinx_plot(g) - This is particularly useful when setting custom ticks in multiples of `pi`. + This is particularly useful when setting custom ticks in multiples of `\pi`. :: @@ -2287,8 +2287,8 @@ def golden_rainbow(i,lightness=0.4): elif legend_color_temp is not None: legend_color_entry = legend_color_temp - G += plot(h, xrange, polar=polar, fill=fill_entry, fillcolor=fillcolor_entry, \ - rgbcolor=color_entry, linestyle=linestyle_entry, \ + G += plot(h, xrange, polar=polar, fill=fill_entry, fillcolor=fillcolor_entry, + rgbcolor=color_entry, linestyle=linestyle_entry, legend_label=legend_label_entry, legend_color=legend_color_entry, **options_temp) return G diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index ae1fd739499..77dabadc78e 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -1,5 +1,5 @@ r""" -Base Classes for 3D Graphics Objects and Plotting +Base classes for 3D graphics objects and plotting The most important facts about these classes are that you can simply add graphics objects @@ -9,7 +9,7 @@ choosing a viewer and setting various parameters for displaying the graphics. Most of the other methods of these classes are technical and -for special usage. +for special usage. AUTHORS: @@ -999,7 +999,7 @@ cdef class Graphics3d(SageObject): "camera_position","updir", # "look_at", # omit look_at. viewdir is sufficient for most purposes "viewdir") - + # The tachyon "aspectratio" parameter is outdated for normal users: # From the tachyon documentation: # "By using the aspect ratio parameter, one can produce images which look @@ -1085,7 +1085,7 @@ cdef class Graphics3d(SageObject): updir = _flip_orientation(updir) camera_position = _flip_orientation(camera_position) light_position = _flip_orientation(light_position) - + return """ begin_scene resolution {resolution_x:d} {resolution_y:d} @@ -1167,9 +1167,9 @@ end_scene""".format( def _tostring(s): r""" Converts vector information to a space-separated string. - + EXAMPLES:: - + sage: sage.plot.plot3d.base.Graphics3d._tostring((1.0,1.2,-1.3)) '1.00000000000000 1.20000000000000 -1.30000000000000' """ @@ -1682,7 +1682,7 @@ end_scene""".format( the viewpoint, with respect to the cube $[-1,1]\\times[-1,1]\\times[-1,1]$, into which the bounding box of the scene - is scaled and centered. + is scaled and centered. The default viewing direction is towards the origin. - ``viewdir`` (for tachyon) -- (default: None) three coordinates @@ -1713,7 +1713,7 @@ end_scene""".format( * ``'lowest'``: worst quality rendering, preview (and fastest). Sets tachyon command line flag ``-lowestshade``. - + - ``extra_opts`` (for tachyon) -- string (default: empty string); extra options that will be appended to the tachyon command line. diff --git a/src/sage/plot/plot3d/implicit_plot3d.py b/src/sage/plot/plot3d/implicit_plot3d.py index 2eb18f7d64d..aa05059878d 100644 --- a/src/sage/plot/plot3d/implicit_plot3d.py +++ b/src/sage/plot/plot3d/implicit_plot3d.py @@ -1,5 +1,5 @@ """ -Implicit Plots +Implicit plots """ from .implicit_surface import ImplicitSurface diff --git a/src/sage/plot/plot3d/implicit_surface.pyx b/src/sage/plot/plot3d/implicit_surface.pyx index aaa2db0c3a8..0a2d99cb5a7 100644 --- a/src/sage/plot/plot3d/implicit_surface.pyx +++ b/src/sage/plot/plot3d/implicit_surface.pyx @@ -1,5 +1,5 @@ r""" -Graphics 3D Object for Representing and Triangulating Isosurfaces. +Graphics 3D object for representing and triangulating isosurfaces AUTHORS: diff --git a/src/sage/plot/plot3d/index_face_set.pyx b/src/sage/plot/plot3d/index_face_set.pyx index 867529d59c5..642397e9ecf 100644 --- a/src/sage/plot/plot3d/index_face_set.pyx +++ b/src/sage/plot/plot3d/index_face_set.pyx @@ -1,5 +1,5 @@ """ -Indexed Face Sets +Indexed face sets Graphics3D object that consists of a list of polygons, also used for triangulations of other objects. diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index 417b9a3528a..c8179d036c5 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -1,5 +1,5 @@ """ -List Plots +List plots """ from sage.structure.element import is_Matrix diff --git a/src/sage/plot/plot3d/parametric_plot3d.py b/src/sage/plot/plot3d/parametric_plot3d.py index f0411e199fe..e16df517fb8 100644 --- a/src/sage/plot/plot3d/parametric_plot3d.py +++ b/src/sage/plot/plot3d/parametric_plot3d.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Parametric Plots +Parametric plots """ from .parametric_surface import ParametricSurface diff --git a/src/sage/plot/plot3d/parametric_surface.pyx b/src/sage/plot/plot3d/parametric_surface.pyx index ad6ea410e4d..68b388b587e 100644 --- a/src/sage/plot/plot3d/parametric_surface.pyx +++ b/src/sage/plot/plot3d/parametric_surface.pyx @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Parametric Surface +Parametric surface Graphics 3D object for triangulating surfaces, and a base class for many other objects that can be represented by a 2D parametrization. diff --git a/src/sage/plot/plot3d/platonic.py b/src/sage/plot/plot3d/platonic.py index ef9a22aabdd..eee95d3290f 100644 --- a/src/sage/plot/plot3d/platonic.py +++ b/src/sage/plot/plot3d/platonic.py @@ -1,5 +1,5 @@ r""" -Platonic Solids +Platonic solids EXAMPLES: The five platonic solids in a row: diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 44620a0edcb..174765980f7 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -1,5 +1,5 @@ r""" -Plotting Functions +Plotting functions EXAMPLES:: @@ -329,19 +329,24 @@ def to_cartesian(self, func, params=None): sage: t1,t2,t3=T.to_cartesian(lambda a,b: 2*a+b) sage: from sage.misc.sageinspect import sage_getargspec sage: sage_getargspec(t1) - ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(t2) - ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(t3) - ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: def g(a,b): return 2*a+b sage: t1,t2,t3=T.to_cartesian(g) sage: sage_getargspec(t1) - ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: t1,t2,t3=T.to_cartesian(2*a+b) sage: sage_getargspec(t1) - ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) If we cannot guess the right parameter names, then the parameters are named `u` and `v`:: @@ -352,7 +357,8 @@ def to_cartesian(self, func, params=None): sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y]) sage: t1,t2,t3=T.to_cartesian(operator.add) sage: sage_getargspec(t1) - ArgSpec(args=['u', 'v'], varargs=None, keywords=None, defaults=None) + FullArgSpec(args=['u', 'v'], varargs=None, varkw=None, defaults=None, + kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: [h(1,2) for h in T.to_cartesian(operator.mul)] [3.0, -1.0, 2.0] sage: [h(u=1,v=2) for h in T.to_cartesian(operator.mul)] @@ -1300,7 +1306,7 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: @interact ....: def _(which_plot=[A,B,C,D,E]): ....: show(which_plot) - Interactive function <function _ at ...> with 1 widget + ...Interactive function <function _ at ...> with 1 widget which_plot: Dropdown(description='which_plot', options=(Graphics3d Object, Graphics3d Object, Graphics3d Object, Graphics3d Object, Graphics3d Object), value=Graphics3d Object) Now plot a function:: @@ -1314,7 +1320,7 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: @interact ....: def _(which_plot=[F, G, H, I, J]): ....: show(which_plot) - Interactive function <function _ at ...> with 1 widget + ...Interactive function <function _ at ...> with 1 widget which_plot: Dropdown(description='which_plot', options=(Graphics3d Object, Graphics3d Object, Graphics3d Object, Graphics3d Object, Graphics3d Object), value=Graphics3d Object) TESTS: diff --git a/src/sage/plot/plot3d/plot_field3d.py b/src/sage/plot/plot3d/plot_field3d.py index 4f1a28cc197..bdf39391d3e 100644 --- a/src/sage/plot/plot3d/plot_field3d.py +++ b/src/sage/plot/plot3d/plot_field3d.py @@ -1,5 +1,5 @@ """ -Plotting 3D Fields +Plotting 3D fields """ #***************************************************************************** # Copyright (C) 2009 Jason Grout <jason-sage@creativetrax.com> diff --git a/src/sage/plot/plot3d/texture.py b/src/sage/plot/plot3d/texture.py index deb400773d7..c2aa4f652d1 100644 --- a/src/sage/plot/plot3d/texture.py +++ b/src/sage/plot/plot3d/texture.py @@ -1,5 +1,5 @@ r""" -Texture Support +Texture support This module provides texture/material support for 3D Graphics objects and plotting. This is a very rough common interface for diff --git a/src/sage/plot/plot3d/transform.pyx b/src/sage/plot/plot3d/transform.pyx index 7e91d30823b..299ea0ee25c 100644 --- a/src/sage/plot/plot3d/transform.pyx +++ b/src/sage/plot/plot3d/transform.pyx @@ -1,4 +1,6 @@ -"Transformations" +""" +Transformations +""" #***************************************************************************** # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu> @@ -264,4 +266,3 @@ def rotate_arbitrary(v, double theta): (1 - cos_t)*z*z + cos_t ] return matrix(RDF, 3, 3, entries) - diff --git a/src/sage/plot/scatter_plot.py b/src/sage/plot/scatter_plot.py index 83e1836479b..c78b33a5258 100644 --- a/src/sage/plot/scatter_plot.py +++ b/src/sage/plot/scatter_plot.py @@ -1,5 +1,5 @@ """ -Scatter Plots +Scatter plots """ # ***************************************************************************** diff --git a/src/sage/plot/streamline_plot.py b/src/sage/plot/streamline_plot.py index 8e34bcc297b..bd9156bf000 100644 --- a/src/sage/plot/streamline_plot.py +++ b/src/sage/plot/streamline_plot.py @@ -1,5 +1,5 @@ """ -Streamline Plots +Streamline plots """ # **************************************************************************** # Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>, diff --git a/src/sage/probability/probability_distribution.pyx b/src/sage/probability/probability_distribution.pyx index a76518b7969..e90b20fb7ab 100644 --- a/src/sage/probability/probability_distribution.pyx +++ b/src/sage/probability/probability_distribution.pyx @@ -43,9 +43,9 @@ REFERENCES: import sys from cysignals.memory cimport sig_malloc, sig_free -import sage.plot.plot from sage.libs.gsl.all cimport * import sage.misc.prandom as random +import sage.rings.real_double from sage.modules.free_module_element import vector #TODO: Add more distributions available in gsl @@ -307,11 +307,10 @@ cdef class SphericalDistribution(ProbabilityDistribution): sage: T.get_random_element() # rel tol 4e-16 (0.07961564104639995, -0.05237671627581255, 0.9954486572862178) """ - cdef int i v = [0]*self.dimension gsl_ran_dir_nd(self.r, self.dimension, self.vec) - for i from 0 <= i<self.dimension: + for i in range(self.dimension): v[i] = self.vec[i] return vector(sage.rings.real_double.RDF, v) #This could be made more efficient by directly constructing the vector, TODO. @@ -952,7 +951,7 @@ cdef class RealDistribution(ProbabilityDistribution): def plot(self, *args, **kwds): """ Plot the distribution function for the probability - distribution. Parameters to ``sage.plot.plot.plot.plot`` can be + distribution. Parameters to :func:`sage.plot.plot.plot` can be passed through ``*args`` and ``**kwds``. EXAMPLES:: @@ -960,8 +959,9 @@ cdef class RealDistribution(ProbabilityDistribution): sage: T = RealDistribution('uniform', [0, 2]) sage: P = T.plot() """ + from sage.plot.plot import plot + return plot(self.distribution_function, *args, **kwds) - return sage.plot.plot.plot(self.distribution_function, *args, **kwds) cdef class GeneralDiscreteDistribution(ProbabilityDistribution): """ diff --git a/src/sage/probability/random_variable.py b/src/sage/probability/random_variable.py index 44501d5e2e2..11dc4ca3cf7 100644 --- a/src/sage/probability/random_variable.py +++ b/src/sage/probability/random_variable.py @@ -75,8 +75,11 @@ def __init__(self, X, f, codomain=None, check=False): r""" Create free binary string monoid on `n` generators. - INPUT: x: A probability space f: A dictionary such that X[x] = - value for x in X is the discrete function on X + INPUT: + + - X -- a probability space + - f -- a dictionary such that X[x] = value for x in X + is the discrete function on X """ if not is_DiscreteProbabilitySpace(X): raise TypeError("Argument X (= %s) must be a discrete probability space" % X) diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index cfa3ada73ef..e7cd7086f09 100755 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -1,5 +1,5 @@ """ -Binary Quadratic Forms with Integer Coefficients +Binary quadratic forms with integer coefficients This module provides a specialized class for working with a binary quadratic form `a x^2 + b x y + c y^2`, stored as a triple of integers `(a, b, c)`. @@ -141,7 +141,7 @@ def __init__(self, a, b=None, c=None): and a.degree() == 2 and a.parent().ngens() == 2): x, y = a.parent().gens() a, b, c = [a.monomial_coefficient(mon) for mon in [x**2, x*y, y**2]] - elif isinstance(a, pari_gen) and a.type() in ('t_QFI', 't_QFR'): + elif isinstance(a, pari_gen) and a.type() in ('t_QFI', 't_QFR', 't_QFB'): # a has 3 or 4 components a, b, c = a[0], a[1], a[2] try: @@ -495,6 +495,48 @@ def polynomial(self): self._poly = self(ZZ['x, y'].gens()) return self._poly + @staticmethod + def from_polynomial(poly): + r""" + Construct a :class:`BinaryQF` from a bivariate polynomial + with integer coefficients. Inverse of :meth:`polynomial`. + + EXAMPLES:: + + sage: R.<u,v> = ZZ[] + sage: f = u^2 + 419*v^2 + sage: Q = BinaryQF.from_polynomial(f); Q + x^2 + 419*y^2 + sage: Q.polynomial() + x^2 + 419*y^2 + sage: Q.polynomial()(R.gens()) == f + True + + The method fails if the given polynomial is not a quadratic form:: + + sage: BinaryQF.from_polynomial(u^3 - 5*v) + Traceback (most recent call last): + ... + ValueError: polynomial has monomials of degree != 2 + + ...or if the coefficients aren't integers:: + + sage: BinaryQF.from_polynomial(u^2/7 + v^2) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + """ + R = poly.parent() + from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base + if not isinstance(R, MPolynomialRing_base) or R.ngens() != 2: + raise TypeError(f'not a bivariate polynomial ring: {R}') + if not all(mon.degree() == 2 for mon in poly.monomials()): + raise ValueError(f'polynomial has monomials of degree != 2') + x,y = R.gens() + coeffs = (poly.monomial_coefficient(mon) for mon in (x**2, x*y, y**2)) + a,b,c = map(ZZ, coeffs) + return BinaryQF(a, b, c) + @cached_method def discriminant(self): """ diff --git a/src/sage/quadratic_forms/constructions.py b/src/sage/quadratic_forms/constructions.py index 933a534c49f..dd9f83216d3 100644 --- a/src/sage/quadratic_forms/constructions.py +++ b/src/sage/quadratic_forms/constructions.py @@ -1,5 +1,5 @@ """ -Some Extras +Constructions of quadratic forms """ ## # Some extra routines to make the QuadraticForm class more useful. diff --git a/src/sage/quadratic_forms/count_local_2.pyx b/src/sage/quadratic_forms/count_local_2.pyx index 21c935c41f6..36b6b9d593c 100644 --- a/src/sage/quadratic_forms/count_local_2.pyx +++ b/src/sage/quadratic_forms/count_local_2.pyx @@ -1,5 +1,5 @@ r""" -Optimised Cython code for counting congruence solutions +Optimized counting of congruence solutions """ from sage.arith.all import valuation, kronecker_symbol, is_prime diff --git a/src/sage/quadratic_forms/extras.py b/src/sage/quadratic_forms/extras.py index 04223d76e4f..be7f05b0439 100644 --- a/src/sage/quadratic_forms/extras.py +++ b/src/sage/quadratic_forms/extras.py @@ -1,4 +1,6 @@ -"Quadratic form extras" +""" +Extra functions for quadratic forms +""" from sage.matrix.constructor import matrix from sage.structure.element import is_Matrix diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 8290b6c4fa8..0fc43f33c62 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -3088,8 +3088,8 @@ def representatives(self, backend=None, algorithm=None): sage: G = Genus(matrix(ZZ, 3, [6,3,0, 3,6,0, 0,0,2])) sage: G.representatives() ( - [2 0 0] [ 2 -1 0] - [0 6 3] [-1 2 0] + [2 0 0] [ 2 1 0] + [0 6 3] [ 1 2 0] [0 3 6], [ 0 0 18] ) diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index 4744e3d079e..bebd2478ec4 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -1,5 +1,5 @@ r""" -Normal forms for `p`-adic quadratic and bilinear forms. +Normal forms for `p`-adic quadratic and bilinear forms We represent a quadratic or bilinear form by its `n \times n` Gram matrix `G`. Then two `p`-adic forms `G` and `G'` are integrally equivalent if and only if @@ -120,7 +120,7 @@ def collect_small_blocks(G): L = _get_small_block_indices(D)[1:] D.subdivide(L, L) blocks = [] - for i in range(len(L)+1): + for i in range(len(L) + 1): block = copy(D.subdivision(i, i)) blocks.append(block) return blocks @@ -246,11 +246,11 @@ def p_adic_normal_form(G, p, precision=None, partial=False, debug=False): d = denom.valuation(p) r = G0.rank() if r != G0.ncols(): - U = G0.hermite_form(transformation=True)[1] + U = G0.hermite_form(transformation=True)[1] else: U = G0.parent().identity_matrix() - kernel = U[r:,:] - nondeg = U[:r,:] + kernel = U[r:, :] + nondeg = U[:r, :] # continue with the non-degenerate part G = nondeg * G * nondeg.T * p**d @@ -282,10 +282,10 @@ def p_adic_normal_form(G, p, precision=None, partial=False, debug=False): if debug: assert B.determinant().valuation() == 0 # B is invertible! if p == 2: - assert B*G*B.T == Matrix.block_diagonal(collect_small_blocks(D)) + assert B * G * B.T == Matrix.block_diagonal(collect_small_blocks(D)) else: - assert B*G*B.T == Matrix.diagonal(D.diagonal()) - return D/p**d, B + assert B * G * B.T == Matrix.diagonal(D.diagonal()) + return D / p**d, B def _find_min_p(G, cnt, lower_bound=0): @@ -338,7 +338,7 @@ def _find_min_p(G, cnt, lower_bound=0): minval = v # off diagonal for i in range(cnt, n): - for j in range(i+1, n): + for j in range(i + 1, n): v = G[i, j].valuation() if v == lower_bound: return lower_bound, i, j @@ -375,16 +375,17 @@ def _get_small_block_indices(G): L = [] n = G.ncols() i = 0 - while i < n-1: + while i < n - 1: L.append(i) - if G[i, i+1]!=0: + if G[i, i + 1] != 0: i += 2 else: i += 1 - if i == n-1: + if i == n - 1: L.append(i) return L[:] + def _get_homogeneous_block_indices(G): r""" Return the indices of the homogeneous blocks. @@ -394,7 +395,7 @@ def _get_homogeneous_block_indices(G): INPUT: - - ``G`` - a block diagonal matrix over the p-adics + - ``G`` -- a block diagonal matrix over the p-adics with blocks of size at most `2`. OUTPUT: @@ -418,26 +419,27 @@ def _get_homogeneous_block_indices(G): n = G.ncols() i = 0 val = -5 - while i < n-1: - if G[i,i+1] != 0: - m = G[i,i+1].valuation() + while i < n - 1: + if G[i, i + 1] != 0: + m = G[i, i + 1].valuation() else: - m = G[i,i].valuation() + m = G[i, i].valuation() if m > val: L.append(i) val = m vals.append(val) - if G[i, i+1] != 0: + if G[i, i + 1] != 0: i += 1 i += 1 - if i == n-1: - m = G[i,i].valuation() + if i == n - 1: + m = G[i, i].valuation() if m > val: L.append(i) val = m vals.append(val) return L, vals + def _homogeneous_normal_form(G, w): r""" Return the homogeneous normal form of the homogeneous ``G``. @@ -522,34 +524,35 @@ def _homogeneous_normal_form(G, w): D = copy(G) n = B.ncols() if w == 2: - if n>2 and D[-3,-3]!=0: + if n > 2 and D[-3, -3] != 0: v = 2 else: v = 0 - if v==2: - e1 = D[-2,-2].unit_part() - e2 = D[-1,-1].unit_part() + if v == 2: + e1 = D[-2, -2].unit_part() + e2 = D[-1, -1].unit_part() e = {e1, e2} - E = [{1,3}, {1,7}, {5,7}, {3,5}] + E = [{1, 3}, {1, 7}, {5, 7}, {3, 5}] if e not in E: - B[-4:,:] = _relations(D[-4:,-4:], 5) * B[-4:,:] + B[-4:, :] = _relations(D[-4:, -4:], 5) * B[-4:, :] D = B * G * B.T - e1 = D[-2,-2].unit_part() - e2 = D[-1,-1].unit_part() - e = {e1,e2} - E = [{3,3}, {3,5}, {5,5}, {5,7}] + e1 = D[-2, -2].unit_part() + e2 = D[-1, -1].unit_part() + e = {e1, e2} + E = [{3, 3}, {3, 5}, {5, 5}, {5, 7}] if e in E: - B[-2:,:] = _relations(D[-2:,-2:], 1) * B[-2:,:] + B[-2:, :] = _relations(D[-2:, -2:], 1) * B[-2:, :] D = B * G * B.T # assert that e1 < e2 - e1 = D[-2,-2].unit_part() - e2 = D[-1,-1].unit_part() + e1 = D[-2, -2].unit_part() + e2 = D[-1, -1].unit_part() if ZZ(e1) > ZZ(e2): - B.swap_rows(n-1, n-2) - D.swap_rows(n-1, n-2) - D.swap_columns(n-1, n-2) + B.swap_rows(n - 1, n - 2) + D.swap_rows(n - 1, n - 2) + D.swap_columns(n - 1, n - 2) return D, B + def _jordan_odd_adic(G): r""" Return the Jordan decomposition of a symmetric matrix over an odd prime. @@ -608,8 +611,8 @@ def _jordan_odd_adic(G): D.swap_columns(cnt, piv1) # we are already orthogonal to the part with i < cnt # now make the rest orthogonal too - for i in range(cnt+1,n): - if D[i, cnt]!= 0: + for i in range(cnt + 1, n): + if D[i, cnt] != 0: c = D[i, cnt] // D[cnt, cnt] B[i, :] += - c * B[cnt, :] D[i, :] += - c * D[cnt, :] @@ -680,55 +683,56 @@ def _jordan_2_adic(G): cnt = 0 minval = None while cnt < n: - pivot = _find_min_p(D, cnt) - piv1 = pivot[1] - piv2 = pivot[2] - minval = pivot[0] - # the smallest valuation is on the diagonal - if piv1 == piv2: - # move pivot to position [cnt,cnt] - if piv1 != cnt: - B.swap_rows(cnt, piv1) - D.swap_rows(cnt, piv1) - D.swap_columns(cnt, piv1) - # we are already orthogonal to the part with i < cnt - # now make the rest orthogonal too - for i in range(cnt+1, n): - if D[i, cnt] != 0: - c = D[i, cnt]//D[cnt, cnt] - B[i, :] += -c * B[cnt, :] - D[i, :] += -c * D[cnt, :] - D[:, i] += -c * D[:, cnt] - cnt = cnt + 1 - # the smallest valuation is off the diagonal - else: - # move this 2 x 2 block to the top left (starting from cnt) - if piv1 != cnt: - B.swap_rows(cnt, piv1) - D.swap_rows(cnt, piv1) - D.swap_columns(cnt, piv1) - if piv2 != cnt+1: - B.swap_rows(cnt+1, piv2) - D.swap_rows(cnt+1, piv2) - D.swap_columns(cnt+1, piv2) - # we split off a 2 x 2 block - # if it is the last 2 x 2 block, there is nothing to do. - if cnt != n-2: - content = R(2 ** minval) - eqn_mat = D[cnt:cnt+2, cnt:cnt+2].list() - eqn_mat = Matrix(R, 2, 2, [e // content for e in eqn_mat]) - # calculate the inverse without using division - inv = eqn_mat.adjugate() * eqn_mat.det().inverse_of_unit() - B1 = B[cnt:cnt+2, :] - B2 = D[cnt+2:, cnt:cnt+2] * inv - for i in range(B2.nrows()): - for j in range(B2.ncols()): - B2[i, j]=B2[i, j] // content - B[cnt+2:, :] -= B2 * B1 - D[cnt:, cnt:] = B[cnt:, :] * G * B[cnt:, :].transpose() - cnt += 2 + pivot = _find_min_p(D, cnt) + piv1 = pivot[1] + piv2 = pivot[2] + minval = pivot[0] + # the smallest valuation is on the diagonal + if piv1 == piv2: + # move pivot to position [cnt,cnt] + if piv1 != cnt: + B.swap_rows(cnt, piv1) + D.swap_rows(cnt, piv1) + D.swap_columns(cnt, piv1) + # we are already orthogonal to the part with i < cnt + # now make the rest orthogonal too + for i in range(cnt + 1, n): + if D[i, cnt] != 0: + c = D[i, cnt] // D[cnt, cnt] + B[i, :] += -c * B[cnt, :] + D[i, :] += -c * D[cnt, :] + D[:, i] += -c * D[:, cnt] + cnt = cnt + 1 + # the smallest valuation is off the diagonal + else: + # move this 2 x 2 block to the top left (starting from cnt) + if piv1 != cnt: + B.swap_rows(cnt, piv1) + D.swap_rows(cnt, piv1) + D.swap_columns(cnt, piv1) + if piv2 != cnt + 1: + B.swap_rows(cnt + 1, piv2) + D.swap_rows(cnt + 1, piv2) + D.swap_columns(cnt + 1, piv2) + # we split off a 2 x 2 block + # if it is the last 2 x 2 block, there is nothing to do. + if cnt != n - 2: + content = R(2 ** minval) + eqn_mat = D[cnt:cnt+2, cnt:cnt+2].list() + eqn_mat = Matrix(R, 2, 2, [e // content for e in eqn_mat]) + # calculate the inverse without using division + inv = eqn_mat.adjugate() * eqn_mat.det().inverse_of_unit() + B1 = B[cnt:cnt+2, :] + B2 = D[cnt+2:, cnt:cnt+2] * inv + for i in range(B2.nrows()): + for j in range(B2.ncols()): + B2[i, j] = B2[i, j] // content + B[cnt + 2:, :] -= B2 * B1 + D[cnt:, cnt:] = B[cnt:, :] * G * B[cnt:, :].transpose() + cnt += 2 return D, B + def _min_nonsquare(p): r""" Return the minimal nonsquare modulo the prime `p`. @@ -752,11 +756,11 @@ def _min_nonsquare(p): sage: _min_nonsquare(7) 3 """ - R = GF(p) - for i in R: - if not R(i).is_square(): + for i in GF(p): + if not i.is_square(): return i + def _normalize(G, normal_odd=True): r""" Return the transformation to sums of forms of types `U`, `V` and `W`. @@ -805,14 +809,14 @@ def _normalize(G, normal_odd=True): non_squares = [] val = 0 for i in range(n): - if D[i,i].valuation() > val: + if D[i, i].valuation() > val: # a new block starts - val = D[i,i].valuation() + val = D[i, i].valuation() if normal_odd and len(non_squares) != 0: # move the non-square to # the last entry of the previous block j = non_squares.pop() - B.swap_rows(j, i-1) + B.swap_rows(j, i - 1) d = D[i, i].unit_part() if d.is_square(): D[i, i] = 1 @@ -824,15 +828,15 @@ def _normalize(G, normal_odd=True): # we combine two non-squares to get # the 2 x 2 identity matrix j = non_squares.pop() - trafo = _normalize_odd_2x2(D[[i,j],[i,j]]) - B[[i,j],:] = trafo*B[[i,j],:] - D[i,i] = 1 - D[j,j] = 1 + trafo = _normalize_odd_2x2(D[[i, j], [i, j]]) + B[[i, j], :] = trafo * B[[i, j], :] + D[i, i] = 1 + D[j, j] = 1 else: non_squares.append(i) - if normal_odd and len(non_squares) != 0: - j=non_squares.pop() - B.swap_rows(j,n-1) + if normal_odd and non_squares: + j = non_squares.pop() + B.swap_rows(j, n - 1) else: # squareclasses 1,3,5,7 modulo 8 for i in range(n): @@ -841,14 +845,15 @@ def _normalize(G, normal_odd=True): v = R(mod(d, 8)) B[i, :] *= (v * d.inverse_of_unit()).sqrt() D = B * G * B.T - for i in range(n-1): - if D[i, i+1] != 0: # there is a 2 x 2 block here + for i in range(n - 1): + if D[i, i + 1] != 0: # there is a 2 x 2 block here block = D[i:i+2, i:i+2] trafo = _normalize_2x2(block) B[i:i+2, :] = trafo * B[i:i+2, :] D = B * G * B.T return D, B + def _normalize_2x2(G): r""" Normalize this indecomposable `2` by `2` block. @@ -904,9 +909,9 @@ def _normalize_2x2(G): # The input must be an even block odd1 = (G[0, 0].valuation() < G[1, 0].valuation()) odd2 = (G[1, 1].valuation() < G[1, 0].valuation()) - if odd1 or odd2: - raise ValueError("Not a valid 2 x 2 block.") - scale = 2 ** G[0,1].valuation() + if odd1 or odd2: + raise ValueError("not a valid 2 x 2 block") + scale = 2 ** G[0, 1].valuation() D = Matrix(R, 2, 2, [d // scale for d in G.list()]) # now D is of the form # [2a b ] @@ -932,7 +937,7 @@ def _normalize_2x2(G): # 1 2 # Find a point of norm 2 # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] - pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0]-2) // 2 + pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0] - 2) // 2 # somehow else pari can get a hickup see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] @@ -944,35 +949,35 @@ def _normalize_2x2(G): # solve: v*D*v == 2 with v = (x, -2*x+1) if D[1, 1] != 2: - v = vector([x, -2*x + 1]) - pol = (v*D*v - 2) // 2 + v = vector([x, -2 * x + 1]) + pol = (v * D * v - 2) // 2 # somehow else pari can get a hickup see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] - B[1, :] = sol * B[0,:] + (-2*sol + 1)*B[1, :] + B[1, :] = sol * B[0, :] + (-2 * sol + 1) * B[1, :] D = B * G * B.transpose() # check the result - assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" %D + assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" % D elif mod(D.det(), 8) == 7: # in this case we can transform D to # 0 1 # 1 0 # Find a point representing 0 # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] - pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0])//2 + pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0]) // 2 # somehow else pari can get a hickup, see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] - B[0,:] += sol*B[1, :] + B[0, :] += sol * B[1, :] D = B * G * B.transpose() # make the second basis vector have 0 square as well. - B[1, :] = B[1, :] - D[1, 1]//(2*D[0, 1])*B[0,:] + B[1, :] = B[1, :] - D[1, 1] // (2 * D[0, 1]) * B[0, :] D = B * G * B.transpose() # rescale to get D[0,1] = 1 B[0, :] *= D[1, 0].inverse_of_unit() D = B * G * B.transpose() # check the result - assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" %D + assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" % D return B @@ -1000,19 +1005,20 @@ def _normalize_odd_2x2(G): [1 0] [0 1] """ - assert G[0,0]==G[1,1] - u = G[0,0] + assert G[0, 0] == G[1, 1] + u = G[0, 0] y = G.base_ring().zero() - while not (1/u-y**2).is_square(): - y = y + 1 - x = (1/u-y**2).sqrt() + while not (1 / u - y**2).is_square(): + y += 1 + x = (1 / u - y**2).sqrt() B = copy(G.parent().identity_matrix()) - B[0,0] = x - B[0,1] = y - B[1,0] = y - B[1,1] = -x + B[0, 0] = x + B[0, 1] = y + B[1, 0] = y + B[1, 1] = -x return B + def _partial_normal_form_of_block(G): r""" Return the partial normal form of the homogeneous block ``G``. @@ -1079,16 +1085,16 @@ def _partial_normal_form_of_block(G): V = [] W = [] for i in blocks: - if i+1 in blocks or i==n-1: + if i + 1 in blocks or i == n - 1: W.append(i) else: - if D[i,i] != 0: - V += [i,i+1] + if D[i, i] != 0: + V += [i, i + 1] else: - U += [i,i+1] + U += [i, i + 1] if len(W) == 3: # W W W transforms to W U or W V - B[W,:] = _relations(D[W,W],2) * B[W,:] + B[W, :] = _relations(D[W, W], 2) * B[W, :] D = B * G * B.T if mod(D[W[1:], W[1:]].det().unit_part(), 8) == 3: V += W[1:] @@ -1096,18 +1102,18 @@ def _partial_normal_form_of_block(G): U += W[1:] W = W[:1] if len(V) == 4: - B[V,:] = _relations(D[V,V],3) * B[V,:] + B[V, :] = _relations(D[V, V], 3) * B[V, :] U += V V = [] D = B * G * B.T # put everything into the right order UVW = U + V + W - B = B[UVW,:] + B = B[UVW, :] D = B * G * B.T return D, B, len(W) -def _relations(G,n): +def _relations(G, n): r""" Return relations of `2`-adic quadratic forms. @@ -1315,59 +1321,61 @@ def _relations(G,n): """ R = G.base_ring() if n == 1: - e1 = G[0,0].unit_part() - e2 = G[1,1].unit_part() - B = Matrix(R,2,[1,2,2*e2,-e1]) - if n == 2: - e1 = G[0,0].unit_part() - e2 = G[1,1].unit_part() - e3 = G[2,2].unit_part() - B = Matrix(R,3,[1,1,1,e2,-e1,0,e3,0,-e1]) - if n == 3: - B = Matrix(R,4,[1,1,1,0, 1,1,0,1, 1,0,-1,-1, 0,1,-1,-1]) - if n == 4: + e1 = G[0, 0].unit_part() + e2 = G[1, 1].unit_part() + B = Matrix(R, 2, 2, [1, 2, 2 * e2, -e1]) + elif n == 2: + e1 = G[0, 0].unit_part() + e2 = G[1, 1].unit_part() + e3 = G[2, 2].unit_part() + B = Matrix(R, 3, 3, [1, 1, 1, e2, -e1, 0, e3, 0, -e1]) + elif n == 3: + B = Matrix(R, 4, 4, + [1, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, -1]) + elif n == 4: raise NotImplementedError("relation 4 is not needed") - if n == 5: - e1 = G[2,2].unit_part() - e2 = G[3,3].unit_part() - if mod(e1,4) != mod(e2,4): + elif n == 5: + e1 = G[2, 2].unit_part() + e2 = G[3, 3].unit_part() + if mod(e1, 4) != mod(e2, 4): raise ValueError("W is of the wrong type for relation 5") - B = Matrix(R,4,[ 1, 0, 1, 1, - 0, 1, 1, 1, - -e2, -e2, 0, 3, - -e1, -e1, 2*e2 + 3, -2*e1]) - if n == 6: - if G[0,0].valuation()+1 != G[1,1].valuation(): + B = Matrix(R, 4, [1, 0, 1, 1, + 0, 1, 1, 1, + -e2, -e2, 0, 3, + -e1, -e1, 2 * e2 + 3, -2 * e1]) + elif n == 6: + if G[0, 0].valuation() + 1 != G[1, 1].valuation(): raise ValueError("wrong scales for relation 6") - e1 = G[0,0].unit_part() - e2 = G[1,1].unit_part() - B = Matrix(R,2,[1,1,-2*e2,e1]) - if n == 7: - e = G[0,0].unit_part() - B = Matrix(R,3,[-3, e**2, e**2, 2*e, 1, 0, 2*e, 0, 1]) - if n == 8: - e = G[2,2].unit_part() - if G[0,0]==0: - B = Matrix(R,3,[e, 0, -1, - 0, e, -1, - 2, 2, 1]) + e1 = G[0, 0].unit_part() + e2 = G[1, 1].unit_part() + B = Matrix(R, 2, 2, [1, 1, -2 * e2, e1]) + elif n == 7: + e = G[0, 0].unit_part() + B = Matrix(R, 3, 3, [-3, e**2, e**2, 2 * e, 1, 0, 2 * e, 0, 1]) + elif n == 8: + e = G[2, 2].unit_part() + if G[0, 0] == 0: + B = Matrix(R, 3, 3, [e, 0, -1, + 0, e, -1, + 2, 2, 1]) else: - B = Matrix(R,3,[ 1, 0, 1, - 0, 1, 1, - 2*e, 2*e, - 3]) - if n == 9: - e1 = G[0,0].unit_part() - e2 = G[1,1].unit_part() - e3 = G[2,2].unit_part() - B = Matrix(R,3,[1, 0, 1, - 2*e3, 1, - -e1, -2*e2*e3, 2*e1**2*e3 + 4*e1*e3**2, e1*e2]) - if n == 10: - e1 = G[0,0].unit_part() - e2 = G[1,1].unit_part() - B = Matrix(R,2,[1,1,-4*e2,e1]) - D, B1 = _normalize(B*G*B.T) - return B1*B + B = Matrix(R, 3, 3, [1, 0, 1, + 0, 1, 1, + 2 * e, 2 * e, - 3]) + elif n == 9: + e1 = G[0, 0].unit_part() + e2 = G[1, 1].unit_part() + e3 = G[2, 2].unit_part() + B = Matrix(R, 3, 3, [1, 0, 1, + 2 * e3, 1, -e1, + -2 * e2 * e3, 2 * e1**2 * e3 + 4 * e1 * e3**2, + e1 * e2]) + elif n == 10: + e1 = G[0, 0].unit_part() + e2 = G[1, 1].unit_part() + B = Matrix(R, 2, 2, [1, 1, -4 * e2, e1]) + D, B1 = _normalize(B * G * B.T) + return B1 * B def _two_adic_normal_forms(G, partial=False): @@ -1424,21 +1432,21 @@ def _two_adic_normal_forms(G, partial=False): # UVlist[k] is a list of indices of the block of scale p^k. # It contains the indices of the part of types U or V. # So it may be empty. - UVlist = [[],[]] # empty lists are appended to avoid special cases. + UVlist = [[], []] # empty lists are appended to avoid special cases. # same as UVlist but contains the indices of the part of type W - Wlist = [[],[]] + Wlist = [[], []] # homogeneous normal form for each part - for k in range(scales[-1] - scales[0]+1): - if k+scales[0] in scales: + for k in range(scales[-1] - scales[0] + 1): + if k + scales[0] in scales: i = scales.index(k + scales[0]) - Gk = G[h[i]:h[i+1], h[i]:h[i+1]] + Gk = G[h[i]:h[i + 1], h[i]:h[i + 1]] Dk, Bk, wk = _partial_normal_form_of_block(Gk) - B[h[i]:h[i+1],:] = Bk * B[h[i]:h[i+1], :] + B[h[i]:h[i + 1], :] = Bk * B[h[i]:h[i + 1], :] if not partial: Dk, B1k = _homogeneous_normal_form(Dk, wk) - B[h[i]:h[i+1],:] = B1k * B[h[i]:h[i+1], :] - UVlist.append(list(range(h[i], h[i+1] - wk))) - Wlist.append(list(range(h[i+1]-wk, h[i+1]))) + B[h[i]:h[i + 1], :] = B1k * B[h[i]:h[i + 1], :] + UVlist.append(list(range(h[i], h[i + 1] - wk))) + Wlist.append(list(range(h[i + 1] - wk, h[i + 1]))) else: UVlist.append([]) Wlist.append([]) @@ -1449,69 +1457,70 @@ def _two_adic_normal_forms(G, partial=False): # we never leave partial normal form # but the homogeneous normal form may be destroyed # it is restored at the end. - for k in range(len(UVlist)-1,2,-1): + for k in range(len(UVlist) - 1, 2, -1): # setup notation W = Wlist[k] - Wm = Wlist[k-1] - Wmm = Wlist[k-2] + Wm = Wlist[k - 1] + Wmm = Wlist[k - 2] UV = UVlist[k] - UVm = UVlist[k-1] + UVm = UVlist[k - 1] V = UVlist[k][-2:] - if len(V)!=0 and D[V[0], V[0]]==0: + if V and D[V[0], V[0]] == 0: V = [] # it is U not V # condition b) - if len(Wm) != 0: - if len(V)==2: + if Wm: + if len(V) == 2: R = Wm[:1] + V - B[R,:] = _relations(D[R,R],7) * B[R,:] + B[R, :] = _relations(D[R, R], 7) * B[R, :] V = [] D = B * G * B.T - E = {3,7} + E = {3, 7} for w in W: - if D[w,w].unit_part() in E: + if D[w, w].unit_part() in E: R = Wm[:1] + [w] - B[R,:] = _relations(D[R,R],6) * B[R,:] + B[R, :] = _relations(D[R, R], 6) * B[R, :] D = B * G * B.T # condition c) # We want type a or W = [] # modify D[w,w] to go from type b to type a - x = [len(V)] + [ZZ(mod(w.unit_part(),8)) for w in D[W,W].diagonal()] - if len(x)==3 and x[1]>x[2]: - x[1],x[2] = x[2], x[1] + x = [len(V)] + [ZZ(mod(w.unit_part(), 8)) for w in D[W, W].diagonal()] + if len(x) == 3 and x[1] > x[2]: + x[1], x[2] = x[2], x[1] # the first entry of x is either # 0 if there is no type V component or # 2 if there is a single type V component - # a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [2,1,3], [0,7,7], [0,1,7]] - b = [[0,5], [2,7], [2,1], [0,3], [0,1,5], [2,1,7], [0,3,7], [0,1,3]] + # a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [2,1,3], [0,7,7], [0,1,7]] + b = [[0, 5], [2, 7], [2, 1], [0, 3], + [0, 1, 5], [2, 1, 7], [0, 3, 7], [0, 1, 3]] if x in b: w = W[-1] - if x == [0,3,7]: + if x == [0, 3, 7]: # relation 10 should be applied to 3 to stay in homogeneous normal form w = W[0] - if len(UVm) > 0: + if UVm: R = UVm[-2:] + [w] - B[R,:] = _relations(D[R,R],8) * B[R,:] - elif len(Wmm) > 0: + B[R, :] = _relations(D[R, R], 8) * B[R, :] + elif Wmm: R = Wmm[:1] + [w] - B[R,:] = _relations(D[R,R],10) * B[R,:] + B[R, :] = _relations(D[R, R], 10) * B[R, :] elif len(Wm) == 2: - e0 = D[Wm,Wm][0,0].unit_part() - e1 = D[Wm,Wm][1,1].unit_part() - if mod(e1-e0,4) == 0: + e0 = D[Wm, Wm][0, 0].unit_part() + e1 = D[Wm, Wm][1, 1].unit_part() + if mod(e1 - e0, 4) == 0: R = Wm + [w] - B[R,:] = _relations(D[R,R],9) * B[R,:] + B[R, :] = _relations(D[R, R], 9) * B[R, :] D = B * G * B.T # condition a) - stay in homogeneous normal form R = UV + W - Dk = D[R,R] + Dk = D[R, R] Bk = _homogeneous_normal_form(Dk, len(W))[1] - B[R,:] = Bk * B[R,:] + B[R, :] = Bk * B[R, :] D = B * G * B.T # we need to restore the homogeneous normal form of k-1 - if len(Wm)>0: + if Wm: R = UVm + Wm - Dkm = D[R,R] + Dkm = D[R, R] Bkm = _homogeneous_normal_form(Dkm, len(Wm))[1] - B[R,:] = Bkm * B[R,:] + B[R, :] = Bkm * B[R, :] D = B * G * B.T return D, B diff --git a/src/sage/quadratic_forms/qfsolve.py b/src/sage/quadratic_forms/qfsolve.py index ddde95e04fe..d5e15d9f83e 100644 --- a/src/sage/quadratic_forms/qfsolve.py +++ b/src/sage/quadratic_forms/qfsolve.py @@ -70,7 +70,7 @@ def qfsolve(G): sage: M = Matrix(QQ, [[3, 0, 0, 0], [0, 5, 0, 0], [0, 0, -7, 0], [0, 0, 0, -11]]) sage: qfsolve(M) - (3, -4, -3, -2) + (3, 4, -3, -2) """ ret = G.__pari__().qfsolve() if ret.type() == 't_COL': diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index f5ac84bf401..c4e97d97c50 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -1,5 +1,5 @@ """ -Quadratic Forms Overview +Quadratic forms overview AUTHORS: @@ -1300,6 +1300,46 @@ def polynomial(self,names='x'): P = (V*M).dot_product(V) return P + @staticmethod + def from_polynomial(poly): + r""" + Construct a :class:`QuadraticForm` from a multivariate + polynomial. Inverse of :meth:`polynomial`. + + EXAMPLES:: + + sage: R.<x,y,z> = ZZ[] + sage: f = 5*x^2 - x*z - 3*y*z - 2*y^2 + 9*z^2 + sage: Q = QuadraticForm.from_polynomial(f); Q + Quadratic form in 3 variables over Integer Ring with coefficients: + [ 5 0 -1 ] + [ * -2 -3 ] + [ * * 9 ] + sage: Q.polynomial() + 5*x0^2 - 2*x1^2 - x0*x2 - 3*x1*x2 + 9*x2^2 + sage: Q.polynomial()(R.gens()) == f + True + + The method fails if the given polynomial is not a quadratic form:: + + sage: QuadraticForm.from_polynomial(x^3 + x*z + 5*y^2) + Traceback (most recent call last): + ... + ValueError: polynomial has monomials of degree != 2 + """ + R = poly.parent() + from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base + if not isinstance(R, MPolynomialRing_base): + raise TypeError(f'not a multivariate polynomial ring: {R}') + if not all(mon.degree() == 2 for mon in poly.monomials()): + raise ValueError(f'polynomial has monomials of degree != 2') + base = R.base_ring() + vs = R.gens() + coeffs = [] + for i,v in enumerate(vs): + for w in vs[i:]: + coeffs.append(poly.monomial_coefficient(v*w)) + return QuadraticForm(base, len(vs), coeffs) diff --git a/src/sage/quadratic_forms/quadratic_form__automorphisms.py b/src/sage/quadratic_forms/quadratic_form__automorphisms.py index c36c667e3b0..3d72cf3be11 100644 --- a/src/sage/quadratic_forms/quadratic_form__automorphisms.py +++ b/src/sage/quadratic_forms/quadratic_form__automorphisms.py @@ -300,9 +300,9 @@ def automorphism_group(self): sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.automorphism_group() Matrix group over Rational Field with 3 generators ( - [-1 0 0] [0 0 1] [ 0 0 1] - [ 0 -1 0] [0 1 0] [-1 0 0] - [ 0 0 -1], [1 0 0], [ 0 1 0] + [ 0 0 1] [1 0 0] [ 1 0 0] + [-1 0 0] [0 0 1] [ 0 -1 0] + [ 0 1 0], [0 1 0], [ 0 0 1] ) :: diff --git a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py index 8e1203debbf..97b07023060 100644 --- a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py +++ b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py @@ -247,7 +247,7 @@ def Watson_mass_at_2(self): eps_dict[j] = -1 # Step 4: Compute the quantities nu, q, P, E for the local mass at 2 - nu = sum([j * n_dict[j] * (ZZ(n_dict[j] + 1) / ZZ(2) + \ + nu = sum([j * n_dict[j] * (ZZ(n_dict[j] + 1) / ZZ(2) + sum([n_dict[r] for r in range(j+1, s_max+2)])) for j in range(s_min+1, s_max+2)]) q = sum([sgn(nu_dict[j-1] * (n_dict[j] + sgn(nu_dict[j]))) for j in range(s_min+1, s_max+2)]) P = prod([prod([1 - QQ(4)**(-k) for k in range(1, m_dict[j]+1)]) for j in range(s_min+1, s_max+2)]) diff --git a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py index bbb2d2cd49e..22334a8e857 100644 --- a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +++ b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py @@ -1,5 +1,5 @@ """ -Tornaria Methods for Computing with Quadratic Forms +Tornaria methods for computing with quadratic forms """ # **************************************************************************** # Copyright (C) 2007 Gonzalo Tornaria diff --git a/src/sage/quadratic_forms/random_quadraticform.py b/src/sage/quadratic_forms/random_quadraticform.py index cac77a2d099..368de819740 100644 --- a/src/sage/quadratic_forms/random_quadraticform.py +++ b/src/sage/quadratic_forms/random_quadraticform.py @@ -1,5 +1,5 @@ """ -Random Quadratic Forms +Random quadratic forms This file contains a set of routines to create a random quadratic form. """ diff --git a/src/sage/quadratic_forms/special_values.py b/src/sage/quadratic_forms/special_values.py index 2cf1f3be5c5..af80989032e 100644 --- a/src/sage/quadratic_forms/special_values.py +++ b/src/sage/quadratic_forms/special_values.py @@ -1,5 +1,5 @@ r""" -Routines for computing special values of L-functions +Routines for computing special values of `L`-functions - :func:`gamma__exact` -- Exact values of the `\Gamma` function at integers and half-integers - :func:`zeta__exact` -- Exact values of the Riemann `\zeta` function at critical values diff --git a/src/sage/quadratic_forms/ternary_qf.py b/src/sage/quadratic_forms/ternary_qf.py index 1b214724645..7932c432779 100644 --- a/src/sage/quadratic_forms/ternary_qf.py +++ b/src/sage/quadratic_forms/ternary_qf.py @@ -1,5 +1,5 @@ """ -Ternary Quadratic Form with integer coefficients +Ternary quadratic form with integer coefficients AUTHOR: diff --git a/src/sage/quivers/algebra_elements.pxi b/src/sage/quivers/algebra_elements.pxi index b1761809451..a6372328fdd 100644 --- a/src/sage/quivers/algebra_elements.pxi +++ b/src/sage/quivers/algebra_elements.pxi @@ -219,7 +219,7 @@ cdef int negdeglex(path_mon_t M1, path_mon_t M2) except -2: if M1.s_len < M2.s_len: return -1 return 1 - for index from 0 <= index < M1.path.length: + for index in range(M1.path.length): item1 = biseq_getitem(M1.path, index) item2 = biseq_getitem(M2.path, index) sig_check() @@ -256,7 +256,7 @@ cdef int deglex(path_mon_t M1, path_mon_t M2) except -2: if M1.s_len < M2.s_len: return 1 return -1 - for index from 0 <= index < M1.path.length: + for index in range(M1.path.length): item1 = biseq_getitem(M1.path, index) item2 = biseq_getitem(M2.path, index) sig_check() diff --git a/src/sage/quivers/homspace.py b/src/sage/quivers/homspace.py index 296ae15129c..cb9a36e3eed 100644 --- a/src/sage/quivers/homspace.py +++ b/src/sage/quivers/homspace.py @@ -17,6 +17,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations from sage.categories.homset import Homset from sage.quivers.morphism import QuiverRepHom from sage.misc.cachefunc import cached_method @@ -53,8 +54,8 @@ class QuiverHomSpace(Homset): sage: H.dimension() 2 sage: H.gens() - [Homomorphism of representations of Multi-digraph on 2 vertices, - Homomorphism of representations of Multi-digraph on 2 vertices] + (Homomorphism of representations of Multi-digraph on 2 vertices, + Homomorphism of representations of Multi-digraph on 2 vertices) """ Element = QuiverRepHom @@ -499,25 +500,25 @@ def dimension(self): """ return self._space.dimension() - def gens(self): + def gens(self) -> tuple: """ - Return a list of generators of the hom space (as a `k`-vector + Return a tuple of generators of the hom space (as a `k`-vector space). OUTPUT: - - list of :class:`QuiverRepHom` objects, the generators + - tuple of :class:`QuiverRepHom` objects, the generators EXAMPLES:: sage: Q = DiGraph({1:{2:['a', 'b']}}).path_semigroup() sage: H = Q.S(QQ, 2).Hom(Q.P(QQ, 1)) sage: H.gens() - [Homomorphism of representations of Multi-digraph on 2 vertices, - Homomorphism of representations of Multi-digraph on 2 vertices] + (Homomorphism of representations of Multi-digraph on 2 vertices, + Homomorphism of representations of Multi-digraph on 2 vertices) """ - return [self.element_class(self._domain, self._codomain, f) - for f in self._space.gens()] + return tuple([self.element_class(self._domain, self._codomain, f) + for f in self._space.gens()]) def coordinates(self, hom): """ diff --git a/src/sage/quivers/morphism.py b/src/sage/quivers/morphism.py index 04c45658d13..b8bb25657eb 100644 --- a/src/sage/quivers/morphism.py +++ b/src/sage/quivers/morphism.py @@ -562,7 +562,7 @@ def __ne__(self, other): # If all that holds just check the vectors return self._vector != other._vector - def __bool__(self): + def __bool__(self) -> bool: """ Return whether ``self`` is the zero morphism. @@ -586,8 +586,6 @@ def __bool__(self): """ return any(self._vector) - - def __mul__(self, other): """ This function overrides the ``*`` operator diff --git a/src/sage/quivers/representation.py b/src/sage/quivers/representation.py index 78ed8ce6d2a..859ff1b10b1 100644 --- a/src/sage/quivers/representation.py +++ b/src/sage/quivers/representation.py @@ -447,7 +447,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** - +from __future__ import annotations from sage.structure.factory import UniqueFactory from sage.modules.module import Module from sage.structure.element import ModuleElement @@ -1915,9 +1915,9 @@ def support(self): return sup - def gens(self, names='v'): + def gens(self, names='v') -> tuple: """ - Return a list of generators of ``self`` as a `k`-module. + Return a tuple of generators of ``self`` as a `k`-module. INPUT: @@ -1928,7 +1928,7 @@ def gens(self, names='v'): OUTPUT: - - list of :class:`QuiverRepElement` objects, the linear generators + - tuple of :class:`QuiverRepElement` objects, the linear generators of the module (over the base ring) .. NOTE:: @@ -1942,14 +1942,14 @@ def gens(self, names='v'): sage: Q = DiGraph({1:{2:['a', 'b']}}).path_semigroup() sage: M = Q.P(QQ, 1) sage: M.gens() - [v_0, v_1, v_2] + (v_0, v_1, v_2) If a string is given then it is used as the name of each generator, with the index of the generator appended in order to differentiate them:: sage: M.gens('generator') - [generator_0, generator_1, generator_2] + (generator_0, generator_1, generator_2) If a list or other iterable variable is given then each generator is named using the appropriate entry. The length of the variable @@ -1960,14 +1960,14 @@ def gens(self, names='v'): ... TypeError: can only concatenate list (not "str") to list sage: M.gens(['x', 'y', 'z']) - [x, y, z] + (x, y, z) Strings are iterable, so if the length of the string is equal to the number of generators then the characters of the string will be used as the names:: sage: M.gens('xyz') - [x, y, z] + (x, y, z) """ # Use names as a list if and only if it is the correct length uselist = (len(names) == self.dimension()) @@ -1986,7 +1986,7 @@ def gens(self, names='v'): basis.append(self({v: m}, names + "_" + str(i))) i += 1 - return basis + return tuple(basis) def coordinates(self, vector): """ diff --git a/src/sage/repl/attach.py b/src/sage/repl/attach.py index 39da6ee4acd..3ae3e7ad0cf 100644 --- a/src/sage/repl/attach.py +++ b/src/sage/repl/attach.py @@ -53,7 +53,7 @@ ... exec(code, globals) File ".../foobar.sage....py", line ..., in <module> - raise ValueError("third") # this should appear in the source snippet + raise ValueError("third") # this should appear in the source snippet... ValueError: third sage: detach(src) """ diff --git a/src/sage/repl/configuration.py b/src/sage/repl/configuration.py index 1f2aeb27692..99e51699868 100644 --- a/src/sage/repl/configuration.py +++ b/src/sage/repl/configuration.py @@ -81,7 +81,10 @@ def colors(self): sage: sage_ipython_config.simple_prompt() True """ - return 'LightBG' if self._allow_ansi() else 'NoColor' + if not self._allow_ansi(): + return 'NoColor' + from sage.repl.interpreter import SageTerminalInteractiveShell + return SageTerminalInteractiveShell.colors.default() def simple_prompt(self): """ diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 2910fb8ee19..7e06656d880 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -143,6 +143,9 @@ def format(self, obj, include=None, exclude=None): sage: import os sage: import importlib.resources + sage: import warnings + sage: warnings.filterwarnings('ignore', category=DeprecationWarning, + ....: message=r'path is deprecated\. Use files\(\) instead\.') sage: from sage.repl.rich_output.backend_ipython import BackendIPython sage: backend = BackendIPython() sage: shell = get_test_shell() @@ -169,8 +172,7 @@ def format(self, obj, include=None, exclude=None): sage: shell.run_cell('import ipywidgets') sage: shell.run_cell('slider = ipywidgets.IntSlider()') sage: shell.run_cell('get_ipython().display_formatter.format(slider)') - IntSlider(value=0) - ({}, {}) + ...IntSlider(value=0)..., {}) sage: shell.run_cell('%display default') sage: shell.quit() diff --git a/src/sage/repl/ipython_kernel/interact.py b/src/sage/repl/ipython_kernel/interact.py index 4a6960ece3a..4f96a212ab7 100644 --- a/src/sage/repl/ipython_kernel/interact.py +++ b/src/sage/repl/ipython_kernel/interact.py @@ -18,7 +18,7 @@ sage: @interact ....: def f(x=(0, 10)): ....: pass - Interactive function <function f at ...> with 1 widget + ...Interactive function <function f at ...> with 1 widget x: IntSlider(value=5, description='x', max=10) sage: f.widget.children (IntSlider(value=5, description='x', max=10), Output()) @@ -69,7 +69,7 @@ class sage_interactive(interactive): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: def myfunc(x=10, y="hello", z=None): pass sage: sage_interactive(myfunc, x=(0,100), z=["one", "two", "three"]) - Interactive function <function myfunc at ...> with 3 widgets + ...Interactive function <function myfunc at ...> with 3 widgets x: IntSlider(value=10, description='x') y: Text(value='hello', description='y') z: Dropdown(description='z', options=('one', 'two', 'three'), value=None) @@ -99,10 +99,10 @@ def __init__(self, *args, **kwds): sage: def myfunc(auto_update=False): pass sage: sage_interactive(myfunc) - Manual interactive function <function myfunc ...> with 0 widgets + ...Manual interactive function <function myfunc ...> with 0 widgets sage: def myfunc(auto_update=None): pass sage: sage_interactive(myfunc) - Interactive function <function myfunc ...> with 0 widgets + ...Interactive function <function myfunc ...> with 0 widgets """ # Use *args to avoid name clash with keyword arguments if len(args) < 2: @@ -126,7 +126,7 @@ def __init__(self, *args, **kwds): super().__init__(f, options, **kwds) if self.manual: # In Sage, manual interacts are always run once - self.on_displayed(self.update) + self.on_widget_constructed(self.update) else: # In automatic mode, clicking on a ToggleButtons button # should also run the interact @@ -143,7 +143,7 @@ def __repr__(self): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: def myfunc(): pass sage: sage_interactive(myfunc) - Interactive function <function myfunc ...> with 0 widgets + ...Interactive function <function myfunc ...> with 0 widgets """ s = "Manual interactive" if self.manual else "Interactive" widgets = [w for w in self.children if isinstance(w, ValueWidget)] @@ -164,7 +164,7 @@ def signature(self): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: def myfunc(x=[1,2,3], auto_update=False): pass sage: sage_interactive(myfunc).signature().parameters - mappingproxy({'x': <Parameter "x=[1, 2, 3]">}) + ...mappingproxy({'x': <Parameter "x=[1, 2, 3]">}) """ return self.__signature @@ -181,14 +181,14 @@ def widget_from_single_value(cls, abbrev, *args, **kwds): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_single_value("sin(x)") - Text(value='sin(x)') + ...Text(value='sin(x)') sage: sage_interactive.widget_from_single_value(sin(x)) - EvalText(value='sin(x)') + ...EvalText(value='sin(x)') sage: from sage.plot.colors import Color sage: sage_interactive.widget_from_single_value(matrix([[1, 2], [3, 4]])) - Grid(value=[[1, 2], [3, 4]], children=(Label(value=''), VBox(children=(EvalText(value='1', layout=Layout(max_width='5em')), EvalText(value='3', layout=Layout(max_width='5em')))), VBox(children=(EvalText(value='2', layout=Layout(max_width='5em')), EvalText(value='4', layout=Layout(max_width='5em')))))) + ...Grid(value=[[1, 2], [3, 4]], children=(Label(value=''), VBox(children=(EvalText(value='1', layout=Layout(max_width='5em')), EvalText(value='3', layout=Layout(max_width='5em')))), VBox(children=(EvalText(value='2', layout=Layout(max_width='5em')), EvalText(value='4', layout=Layout(max_width='5em')))))) sage: sage_interactive.widget_from_single_value(Color('cornflowerblue')) - SageColorPicker(value='#6495ed') + ...SageColorPicker(value='#6495ed') """ # Support Sage matrices and colors if isinstance(abbrev, Matrix): @@ -219,15 +219,15 @@ def widget_from_tuple(cls, abbrev, *args, **kwds): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_tuple( (0, 10) ) - IntSlider(value=5, max=10) + ...IntSlider(value=5, max=10) sage: sage_interactive.widget_from_tuple( ("number", (0, 10)) ) - IntSlider(value=5, description='number', max=10) + ...IntSlider(value=5, description='number', max=10) sage: sage_interactive.widget_from_tuple( (3, (0, 10)) ) - IntSlider(value=3, max=10) - sage: sage_interactive.widget_from_tuple((2, dict(one=1, two=2, three=3))) - Dropdown(index=1, options={'one': 1, 'two': 2, 'three': 3}, value=2) + ...IntSlider(value=3, max=10) + sage: sage_interactive.widget_from_tuple((2, [('one', 1), ('two', 2), ('three', 3)])) + ...Dropdown(index=1, options=(('one', 1), ('two', 2), ('three', 3)), value=2) sage: sage_interactive.widget_from_tuple( (sqrt(2), pi) ) - FloatSlider(value=2.277903107981444, max=3.141592653589793, min=1.4142135623730951) + ...FloatSlider(value=2.277903107981444, max=3.141592653589793, min=1.4142135623730951) TESTS: @@ -235,7 +235,7 @@ def widget_from_tuple(cls, abbrev, *args, **kwds): sage: SCR = SR.subring(no_variables=True) sage: sage_interactive.widget_from_tuple( (SCR(sqrt(2)), SCR(pi)) ) - FloatSlider(value=2.277903107981444, max=3.141592653589793, min=1.4142135623730951) + ...FloatSlider(value=2.277903107981444, max=3.141592653589793, min=1.4142135623730951) """ # Support (description, abbrev) if len(abbrev) == 2 and isinstance(abbrev[0], str): @@ -269,17 +269,17 @@ def widget_from_iterable(cls, abbrev, *args, **kwds): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_iterable([1..5]) - Dropdown(options=(1, 2, 3, 4, 5), value=1) + ...Dropdown(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(iter([1..5])) - SelectionSlider(options=(1, 2, 3, 4, 5), value=1) + ...SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable((1..5)) - SelectionSlider(options=(1, 2, 3, 4, 5), value=1) + ...SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(x for x in [1..5]) - SelectionSlider(options=(1, 2, 3, 4, 5), value=1) + ...SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: def gen(): ....: yield 1; yield 2; yield 3; yield 4; yield 5 sage: sage_interactive.widget_from_iterable(gen()) - SelectionSlider(options=(1, 2, 3, 4, 5), value=1) + ...SelectionSlider(options=(1, 2, 3, 4, 5), value=1) """ if isinstance(abbrev, Iterator): return SelectionSlider(options=list(abbrev)) diff --git a/src/sage/repl/ipython_kernel/widgets_sagenb.py b/src/sage/repl/ipython_kernel/widgets_sagenb.py index 2ce59d79a3b..76f4f52ac4a 100644 --- a/src/sage/repl/ipython_kernel/widgets_sagenb.py +++ b/src/sage/repl/ipython_kernel/widgets_sagenb.py @@ -470,15 +470,6 @@ def selector(values, label=None, default=None, nrows=None, ncols=None, width=Non sage: selector([(1,"one"), (2,"two"), (3,"three")], buttons=True) ToggleButtons(options=(('one', 1), ('two', 2), ('three', 3)), value=1) - A dict of ``label:value`` pairs is also allowed. Since a ``dict`` - is not ordered, it is better to use an :class:`OrderedDict`:: - - sage: from collections import OrderedDict - sage: selector(OrderedDict(one=1, two=2, three=3)) - Dropdown(options=OrderedDict([('one', 1), ('two', 2), ('three', 3)]), value=1) - sage: selector(OrderedDict(one=1, two=2, three=3), buttons=True) - ToggleButtons(options=OrderedDict([('one', 1), ('two', 2), ('three', 3)]), value=1) - The values can be any kind of object: sage: selector([sin(x^2), GF(29), EllipticCurve('37a1')]) diff --git a/src/sage/repl/user_globals.py b/src/sage/repl/user_globals.py index bc64972958b..92de7d87e0c 100644 --- a/src/sage/repl/user_globals.py +++ b/src/sage/repl/user_globals.py @@ -156,6 +156,8 @@ def initialize_globals(all, g=None): for key in dir(all): if key[0] != '_': user_globals[key] = getattr(all, key) + from sage.misc.lazy_import import clean_namespace + clean_namespace(user_globals) def get_global(name): diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index a0d5bac9d76..d45c3ec2cb0 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -956,7 +956,7 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algori ....: assert p(r).is_zero(), "r={} is not a root of p={}".format(r,p) """ - from sage.arith.all import lcm + from sage.arith.functions import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 500ab0b615a..a6090039db2 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -146,7 +146,7 @@ lazy_import('sage.rings.laurent_series_ring_element', 'LaurentSeries', deprecation=33602) # Lazy Laurent series ring -lazy_import('sage.rings.lazy_series_ring', ['LazyLaurentSeriesRing', 'LazyTaylorSeriesRing', +lazy_import('sage.rings.lazy_series_ring', ['LazyLaurentSeriesRing', 'LazyPowerSeriesRing', 'LazySymmetricFunctions', 'LazyDirichletSeriesRing']) # Tate algebras diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index 3100ed5356f..2a3d10df757 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -173,7 +173,7 @@ def Stirling(var, precision=None, skip_constant_factor=False): sage: set_series_precision(5) sage: asymptotic_expansions.Stirling('n') - sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + + sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + ... + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: set_series_precision(20) # restore series precision default """ @@ -363,7 +363,7 @@ def _log_StirlingNegativePowers_(var, precision): return A.zero() n = A.gen() - from sage.arith.all import bernoulli + from sage.arith.misc import bernoulli from sage.arith.srange import srange result = sum((bernoulli(k) / k / (k-1) / n**(k-1) @@ -462,7 +462,7 @@ def HarmonicNumber(var, precision=None, skip_constant_summand=False): result += 1 / (2 * n) from sage.arith.srange import srange - from sage.arith.all import bernoulli + from sage.arith.misc import bernoulli for k in srange(2, 2*precision - 4, 2): result += -bernoulli(k) / k / n**k @@ -912,7 +912,7 @@ def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, from .asymptotic_ring import AsymptoticRing from .growth_group import ExponentialGrowthGroup, \ MonomialGrowthGroup, GenericNonGrowthGroup - from sage.arith.all import falling_factorial + from sage.arith.misc import falling_factorial from sage.categories.cartesian_product import cartesian_product from sage.functions.other import binomial from sage.functions.gamma import gamma diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index ec4c5ce878f..0622d78c9c8 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -1345,7 +1345,7 @@ def cohomology_decomposition(self): (1/3, [(x*y - 1, 1), (x^2 + y^2 - 1, 1)]) """ from sage.calculus.functions import jacobian - from sage.arith.all import xgcd + from sage.arith.misc import XGCD as xgcd from sage.sets.set import Set R = self.denominator_ring diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 02520ec2efb..8681fc3f263 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -851,7 +851,7 @@ def closed_form(self, n='n'): sage: CFiniteSequence((x/(1-x-x^2))^2).closed_form() 1/5*(n - sqrt(1/5))*(1/2*sqrt(5) + 1/2)^n + 1/5*(n + sqrt(1/5))*(-1/2*sqrt(5) + 1/2)^n """ - from sage.arith.all import binomial + from sage.arith.misc import binomial from sage.rings.qqbar import QQbar from sage.symbolic.ring import SR diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 62841a9f8d6..797f5122d40 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -2984,7 +2984,7 @@ cdef class ComplexBall(RingElement): return res def rising_factorial(self, n): - """ + r""" Return the ``n``-th rising factorial of this ball. The `n`-th rising factorial of `x` is equal to `x (x+1) \cdots (x+n-1)`. @@ -3704,7 +3704,7 @@ cdef class ComplexBall(RingElement): return res def polylog(self, s): - """ + r""" Return the polylogarithm `\operatorname{Li}_s(\mathrm{self})`. EXAMPLES:: diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 5cd5cb1910f..ba0ec2b37d3 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -174,7 +174,7 @@ cdef class ComplexDoubleField_class(sage.rings.abc.ComplexDoubleField): (-1.0, -1.0 + 1.2246...e-16*I, False) """ from sage.categories.fields import Fields - ParentWithGens.__init__(self, self, ('I',), normalize=False, category=Fields().Metric().Complete()) + ParentWithGens.__init__(self, self, ('I',), normalize=False, category=Fields().Infinite().Metric().Complete()) self._populate_coercion_lists_() def __reduce__(self): @@ -380,7 +380,7 @@ cdef class ComplexDoubleField_class(sage.rings.abc.ComplexDoubleField): return x elif isinstance(x, tuple): return ComplexDoubleElement(x[0], x[1]) - elif isinstance(x, (float, int, long)): + elif isinstance(x, (float, int)): return ComplexDoubleElement(x, 0) elif isinstance(x, complex): return ComplexDoubleElement(x.real, x.imag) @@ -1637,7 +1637,7 @@ cdef class ComplexDoubleElement(FieldElement): return self.real().is_NaN() or self.imag().is_NaN() cpdef _pow_(self, other): - """ + r""" The complex number ``self`` raised to the power ``other``. This is computed using complex logarithms and exponentials @@ -2482,7 +2482,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(1,5).algdep(2) x^2 - 2*x + 26 """ - from sage.arith.all import algdep + from sage.arith.misc import algdep return algdep(self, n) cdef class FloatToCDF(Morphism): diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 49109af92c6..456883bb17a 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -137,15 +137,15 @@ cdef inline mpfr_rnd_t rnd_im(mpc_rnd_t rnd): sign = '[+-]' digit_ten = '[0123456789]' exponent_ten = '[e@]' + sign + '?[0123456789]+' -number_ten = 'inf(?:inity)?|@inf@|nan(?:\([0-9A-Z_]*\))?|@nan@(?:\([0-9A-Z_]*\))?'\ - '|(?:' + digit_ten + '*\.' + digit_ten + '+|' + digit_ten + '+\.?)(?:' + exponent_ten + ')?' -imaginary_ten = 'i(?:\s*\*\s*(?:' + number_ten + '))?|(?:' + number_ten + ')\s*\*\s*i' -complex_ten = '(?P<im_first>(?P<im_first_im_sign>' + sign + ')?\s*(?P<im_first_im_abs>' + imaginary_ten + ')' \ - '(\s*(?P<im_first_re_sign>' + sign + ')\s*(?P<im_first_re_abs>' + number_ten + '))?)' \ +number_ten = r'inf(?:inity)?|@inf@|nan(?:\([0-9A-Z_]*\))?|@nan@(?:\([0-9A-Z_]*\))?'\ + '|(?:' + digit_ten + r'*\.' + digit_ten + '+|' + digit_ten + r'+\.?)(?:' + exponent_ten + ')?' +imaginary_ten = r'i(?:\s*\*\s*(?:' + number_ten + '))?|(?:' + number_ten + r')\s*\*\s*i' +complex_ten = '(?P<im_first>(?P<im_first_im_sign>' + sign + r')?\s*(?P<im_first_im_abs>' + imaginary_ten + r')' \ + r'(\s*(?P<im_first_re_sign>' + sign + r')\s*(?P<im_first_re_abs>' + number_ten + '))?)' \ '|' \ - '(?P<re_first>(?P<re_first_re_sign>' + sign + ')?\s*(?P<re_first_re_abs>' + number_ten + ')' \ - '(\s*(?P<re_first_im_sign>' + sign + ')\s*(?P<re_first_im_abs>' + imaginary_ten + '))?)' -re_complex_ten = re.compile('^\s*(?:' + complex_ten + ')\s*$', re.I) + '(?P<re_first>(?P<re_first_re_sign>' + sign + r')?\s*(?P<re_first_re_abs>' + number_ten + r')' \ + r'(\s*(?P<re_first_im_sign>' + sign + r')\s*(?P<re_first_im_abs>' + imaginary_ten + '))?)' +re_complex_ten = re.compile(r'^\s*(?:' + complex_ten + r')\s*$', re.I) cpdef inline split_complex_string(string, int base=10): """ @@ -185,17 +185,17 @@ cpdef inline split_complex_string(string, int base=10): # Warning: number, imaginary, and complex should be enclosed in parentheses # when used as regexp because of alternatives '|' - number = '@nan@(?:\([0-9A-Z_]*\))?|@inf@|(?:' + digit + '*\.' + digit + '+|' + digit + '+\.?)(?:' + exponent + ')?' + number = r'@nan@(?:\([0-9A-Z_]*\))?|@inf@|(?:' + digit + r'*\.' + digit + '+|' + digit + r'+\.?)(?:' + exponent + ')?' if base <= 10: - number = 'nan(?:\([0-9A-Z_]*\))?|inf(?:inity)?|' + number - imaginary = 'i(?:\s*\*\s*(?:' + number + '))?|(?:' + number + ')\s*\*\s*i' - complex = '(?P<im_first>(?P<im_first_im_sign>' + sign + ')?\s*(?P<im_first_im_abs>' + imaginary + ')' \ - '(\s*(?P<im_first_re_sign>' + sign + ')\s*(?P<im_first_re_abs>' + number + '))?)' \ + number = r'nan(?:\([0-9A-Z_]*\))?|inf(?:inity)?|' + number + imaginary = r'i(?:\s*\*\s*(?:' + number + '))?|(?:' + number + r')\s*\*\s*i' + complex = '(?P<im_first>(?P<im_first_im_sign>' + sign + r')?\s*(?P<im_first_im_abs>' + imaginary + r')' \ + r'(\s*(?P<im_first_re_sign>' + sign + r')\s*(?P<im_first_re_abs>' + number + '))?)' \ '|' \ - '(?P<re_first>(?P<re_first_re_sign>' + sign + ')?\s*(?P<re_first_re_abs>' + number + ')' \ - '(\s*(?P<re_first_im_sign>' + sign + ')\s*(?P<re_first_im_abs>' + imaginary + '))?)' + '(?P<re_first>(?P<re_first_re_sign>' + sign + r')?\s*(?P<re_first_re_abs>' + number + r')' \ + r'(\s*(?P<re_first_im_sign>' + sign + r')\s*(?P<re_first_im_abs>' + imaginary + '))?)' - z = re.match('^\s*(?:' + complex + ')\s*$', string, re.I) + z = re.match(r'^\s*(?:' + complex + r')\s*$', string, re.I) x, y = None, None if z is not None: @@ -207,18 +207,18 @@ cpdef inline split_complex_string(string, int base=10): return None if z.group(prefix + '_re_abs') is not None: - x = z.expand('\g<' + prefix + '_re_abs>') + x = z.expand(r'\g<' + prefix + '_re_abs>') if z.group(prefix + '_re_sign') is not None: - x = z.expand('\g<' + prefix + '_re_sign>') + x + x = z.expand(r'\g<' + prefix + '_re_sign>') + x if z.group(prefix + '_im_abs') is not None: - y = re.search('(?P<im_part>' + number + ')', z.expand('\g<' + prefix + '_im_abs>'), re.I) + y = re.search('(?P<im_part>' + number + ')', z.expand(r'\g<' + prefix + '_im_abs>'), re.I) if y is None: y = '1' else: - y = y.expand('\g<im_part>') + y = y.expand(r'\g<im_part>') if z.group(prefix + '_im_sign') is not None: - y = z.expand('\g<' + prefix + '_im_sign>') + y + y = z.expand(r'\g<' + prefix + '_im_sign>') + y return x, y @@ -253,7 +253,7 @@ def MPComplexField(prec=53, rnd="RNDNN", names=None): if mykey in cache: X = cache[mykey] C = X() - if not C is None: + if C is not None: return C C = MPComplexField_class(prec, rnd) cache[mykey] = weakref.ref(C) @@ -869,7 +869,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): elif isinstance(z, Integer): mpc_set_z(self.value, (<Integer>z).value, rnd) return - elif isinstance(z, (int, long)): + elif isinstance(z, int): mpc_set_si(self.value, z, rnd) return else: @@ -1371,7 +1371,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: p(z) 1.11022302462516e-16 """ - from sage.arith.all import algdep + from sage.arith.misc import algdep return algdep(self, n, **kwds) ################################ @@ -1623,7 +1623,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): x = <MPComplexNumber>self z = x._new() - if isinstance(right, (int,long)): + if isinstance(right, int): mpc_pow_si(z.value, x.value, right, (<MPComplexField_class>x._parent).__rnd) elif isinstance(right, Integer): mpc_pow_z(z.value, x.value, (<Integer>right).value, (<MPComplexField_class>x._parent).__rnd) @@ -1701,7 +1701,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): return z def cosh(self): - """ + r""" Return the hyperbolic cosine of this complex number: .. MATH:: @@ -1721,7 +1721,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): return z def sinh(self): - """ + r""" Return the hyperbolic sine of this complex number: .. MATH:: @@ -2063,7 +2063,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): return z def exp(self): - """ + r""" Return the exponential of this complex number: .. MATH:: diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 21b32326aa3..bf88e122fee 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -206,7 +206,7 @@ def ComplexField(prec=53, names=None): if prec in cache: X = cache[prec] C = X() - if not C is None: + if C is not None: return C C = ComplexField_class(prec) cache[prec] = weakref.ref(C) @@ -1714,7 +1714,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: float(5)^(0.5 + 14.1347251*I) -1.62414637645790 - 1.53692828324508*I """ - if isinstance(right, (int, long, Integer)): + if isinstance(right, (int, Integer)): return RingElement.__pow__(self, right) try: @@ -2066,7 +2066,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): return Integer(4) elif self == -self._parent.gen(): return Integer(4) - elif not self._multiplicative_order is None: + elif self._multiplicative_order is not None: return Integer(self._multiplicative_order) elif abs(abs(self) - 1) > 0.1: # clearly not a root of unity return infinity.infinity @@ -2549,7 +2549,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): # Other special functions def agm(self, right, algorithm="optimal"): - """ + r""" Return the Arithmetic-Geometric Mean (AGM) of ``self`` and ``right``. INPUT: @@ -3279,7 +3279,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: p(z) 1.11022302462516e-16 """ - from sage.arith.all import algdep + from sage.arith.misc import algdep return algdep(self, n, **kwds) # Alias diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index e8e94e4331d..1715d2b910f 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -9,8 +9,6 @@ TESTS:: DeprecationWarning: the complex_number module is deprecated, please use sage.rings.complex_mpfr See http://trac.sagemath.org/24483 for details. """ - from sage.misc.superseded import deprecation from sage.rings.complex_mpfr import * deprecation(24483, "the complex_number module is deprecated, please use sage.rings.complex_mpfr") - diff --git a/src/sage/rings/continued_fraction.py b/src/sage/rings/continued_fraction.py index f4c10b009ad..a16137ff609 100644 --- a/src/sage/rings/continued_fraction.py +++ b/src/sage/rings/continued_fraction.py @@ -1018,7 +1018,7 @@ def __bool__(self): """ return bool(self.quotient(0)) or self.quotient(1) is not Infinity - + def is_zero(self): r""" diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index 11d8a76d3a8..29f3a18d662 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -1815,7 +1815,7 @@ def _rmul_(self, factor): """ factor = self.parent().codomain()(factor) base_derivation = factor * self._base_derivation - im = [ factor*x for x in self._images ] + im = [ factor*x for x in self._images ] return type(self)(self.parent(), [base_derivation] + im) def _lmul_(self, factor): diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index e46fe9b052f..3a7e4a3648b 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -75,7 +75,7 @@ cpdef aurifeuillian(n, m, F=None, bint check=True): There is no need to set `F`. It's only for increasing speed of :meth:`factor_aurifeuillian()`. """ - from sage.arith.all import euler_phi + from sage.arith.misc import euler_phi from sage.rings.real_mpfi import RealIntervalField if check: if not n.is_squarefree(): diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 8736e8dc416..df9b592cf97 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -207,7 +207,7 @@ cpdef prime_range(start, stop=None, algorithm=None, bint py_ints=False): res = pari_prime_range(start, stop, py_ints) elif (algorithm == "pari_isprime") or (algorithm == "pari_primes"): - from sage.arith.all import primes + from sage.arith.misc import primes res = list(primes(start, stop)) else: raise ValueError('algorithm must be "pari_primes" or "pari_isprime"') diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index d4dd6b22ee4..29ab05dd8ba 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -19,7 +19,9 @@ AUTHORS: from sage.structure.element cimport Element from sage.structure.parent cimport Parent +from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer +from sage.misc.superseded import deprecated_function_alias def is_FiniteFieldElement(x): """ @@ -213,7 +215,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def __getitem__(self, n): r""" - Return the `n`\th coefficient of this finite-field element when + Return the `n`\th coefficient of this finite field element when written as a polynomial in the generator. EXAMPLES:: @@ -250,7 +252,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def list(self): r""" Return the list of coefficients (in little-endian) of this - finite-field element when written as a polynomial in the + finite field element when written as a polynomial in the generator. Equivalent to calling ``list()`` on this element. @@ -266,7 +268,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): True The coefficients returned are those of a fully reduced - representative of the finite-field element:: + representative of the finite field element:: sage: b = u^777 sage: b.list() @@ -288,7 +290,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def __iter__(self): r""" - Return an iterator over the coefficients of this finite-field + Return an iterator over the coefficients of this finite field element, in the same order as :meth:`list`. EXAMPLES:: @@ -670,7 +672,6 @@ cdef class FinitePolyExtElement(FiniteRingElement): 1 """ if self.is_zero(): - from sage.rings.integer import Integer return Integer(1) return self.parent().characteristic() @@ -878,7 +879,6 @@ cdef class FinitePolyExtElement(FiniteRingElement): else: raise ValueError if extend: raise NotImplementedError - from sage.rings.integer import Integer n = Integer(n) return self._nth_root_common(n, all, algorithm, cunningham) @@ -983,9 +983,71 @@ cdef class FinitePolyExtElement(FiniteRingElement): return self.pth_power(k=k) + def to_integer(self, reverse=False): + r""" + Return an integer representation of this finite field element + obtained by lifting its representative polynomial to `\ZZ` and + evaluating it at the characteristic `p`. + + If ``reverse`` is set to ``True`` (default: ``False``), + the list of coefficients is reversed prior to evaluation. + + Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.from_integer`. + + EXAMPLES:: + + sage: F.<t> = GF(7^5) + sage: F(5).to_integer() + 5 + sage: t.to_integer() + 7 + sage: (t^2).to_integer() + 49 + sage: (t^2+1).to_integer() + 50 + sage: (t^2+t+1).to_integer() + 57 + + :: + + sage: F.<t> = GF(2^8) + sage: u = F.from_integer(0xd1) + sage: bin(u.to_integer(False)) + '0b11010001' + sage: bin(u.to_integer(True)) + '0b10001011' + + TESTS:: + + sage: p = random_prime(2^99) + sage: k = randrange(2,10) + sage: F.<t> = GF((p, k)) + sage: rev = bool(randrange(2)) + sage: u = F.random_element() + sage: 0 <= u.to_integer(rev) < F.cardinality() + True + sage: F.from_integer(u.to_integer(rev), rev) == u + True + sage: n = randrange(F.cardinality()) + sage: F.from_integer(n, rev).to_integer(rev) == n + True + """ + if not reverse: + try: + return self._integer_representation() + except AttributeError: + pass + p = self.parent().characteristic() + f = self.polynomial().change_ring(ZZ) + if reverse: + f = f.reverse(self.parent().degree() - 1) + return f(p) + + integer_representation = deprecated_function_alias(33941, to_integer) + cdef class Cache_base(SageObject): cpdef FinitePolyExtElement fetch_int(self, number): - """ + r""" Given an integer less than `p^n` with base `2` representation `a_0 + a_1 \cdot 2 + \cdots + a_k 2^k`, this returns `a_0 + a_1 x + \cdots + a_k x^k`, where `x` is the @@ -998,4 +1060,3 @@ cdef class Cache_base(SageObject): a^33 + a + 1 """ raise NotImplementedError("this must be implemented by subclasses") - diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 025e9fe80ce..bdb42614592 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -1,7 +1,7 @@ # distutils: libraries = givaro gmp m # distutils: language = c++ r""" -Givaro Field Elements +Givaro finite field elements Sage includes the Givaro finite field library, for highly optimized arithmetic in finite fields. @@ -66,13 +66,15 @@ import operator import sage.arith.all import sage.rings.finite_rings.finite_field_constructor as finite_field -import sage.interfaces.gap from sage.libs.pari.all import pari from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack from sage.structure.parent cimport Parent + +from sage.interfaces.abc import GapElement + cdef object is_IntegerMod cdef object Integer cdef object Rational @@ -378,7 +380,7 @@ cdef class Cache_givaro(Cache_base): else: raise TypeError("unable to coerce from a finite field other than the prime subfield") - elif isinstance(e, (int, Integer, long)) or is_IntegerMod(e): + elif isinstance(e, (int, Integer)) or is_IntegerMod(e): try: e_int = e % self.characteristic() self.objectptr.initi(res, e_int) @@ -431,9 +433,12 @@ cdef class Cache_givaro(Cache_base): # Reduce to pari e = e.__pari__() - elif sage.interfaces.gap.is_GapElement(e): - from sage.interfaces.gap import gfq_gap_to_sage - return gfq_gap_to_sage(e, self.parent) + elif isinstance(e, sage.libs.gap.element.GapElement_FiniteField): + return e.sage(ring=self.parent) + + elif isinstance(e, GapElement): + from sage.libs.gap.libgap import libgap + return libgap(e).sage(ring=self.parent) elif isinstance(e, list): if len(e) > self.exponent(): @@ -643,7 +648,7 @@ cdef class Cache_givaro(Cache_base): sage: k._cache._element_int_repr(a^20) '74' """ - return str(e.integer_representation()) + return str(e._integer_representation()) def _element_poly_repr(self, FiniteField_givaroElement e, varname=None): """ @@ -1350,7 +1355,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): raise TypeError("Cannot coerce element to an integer.") return self_int - def integer_representation(FiniteField_givaroElement self): + def _integer_representation(FiniteField_givaroElement self): r""" Return the integer representation of ``self``. When ``self`` is in the prime subfield, the integer returned is equal to ``self``. @@ -1362,15 +1367,19 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): OUTPUT: A Python ``int``. + .. SEEALSO:: + + :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer` + EXAMPLES:: sage: k.<b> = GF(5^2); k Finite Field in b of size 5^2 - sage: k(4).integer_representation() + sage: k(4)._integer_representation() 4 - sage: b.integer_representation() + sage: b._integer_representation() 5 - sage: type(b.integer_representation()) + sage: type(b._integer_representation()) <... 'int'> """ return self._cache.log_to_int(self.element) @@ -1637,6 +1646,8 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): 'Z(25)^3' sage: S(gap('Z(25)^3')) 4*b + 3 + sage: S(libgap.Z(25)^3) + 4*b + 3 """ cdef Cache_givaro cache = self._cache if self == 0: @@ -1669,7 +1680,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): sage: hash(a) 5 """ - return hash(self.integer_representation()) + return hash(self._integer_representation()) def _vector_(FiniteField_givaroElement self, reverse=False): """ diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index beda02aea78..109ce513b61 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ r""" -Finite Fields of characteristic 2. +Elements of finite fields of characteristic 2 This implementation uses NTL's GF2E class to perform the arithmetic and is the standard implementation for ``GF(2^n)`` for ``n >= 16``. @@ -46,8 +46,6 @@ from sage.libs.pari.all import pari from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack -from sage.interfaces.gap import is_GapElement - from sage.misc.randstate import current_randstate from sage.arith.long cimport pyobject_to_long @@ -56,6 +54,9 @@ from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.interfaces.abc import GapElement + + cdef object is_IntegerMod cdef object IntegerModRing_generic cdef object Integer @@ -305,6 +306,7 @@ cdef class Cache_ntl_gf2e(Cache_base): cdef FiniteField_ntl_gf2eElement x cdef FiniteField_ntl_gf2eElement g cdef Py_ssize_t i + from sage.libs.gap.element import GapElement_FiniteField if is_IntegerMod(e): e = e.lift() @@ -364,9 +366,13 @@ cdef class Cache_ntl_gf2e(Cache_base): # Reduce to pari e = e.__pari__() - elif is_GapElement(e): - from sage.interfaces.gap import gfq_gap_to_sage - return gfq_gap_to_sage(e, self._parent) + elif isinstance(e, GapElement_FiniteField): + return e.sage(ring=self._parent) + + elif isinstance(e, GapElement): + from sage.libs.gap.libgap import libgap + return libgap(e).sage(ring=self._parent) + else: raise TypeError("unable to coerce %r" % type(e)) @@ -401,7 +407,7 @@ cdef class Cache_ntl_gf2e(Cache_base): raise ValueError("Cannot coerce element %s to this field." % e) cpdef FiniteField_ntl_gf2eElement fetch_int(self, number): - """ + r""" Given an integer less than `p^n` with base `2` representation `a_0 + a_1 \cdot 2 + \cdots + a_k 2^k`, this returns `a_0 + a_1 x + \cdots + a_k x^k`, where `x` is the @@ -434,7 +440,7 @@ cdef class Cache_ntl_gf2e(Cache_base): if number < 0 or number >= self._order: raise TypeError("n must be between 0 and self.order()") - if isinstance(number, int) or isinstance(number, long): + if isinstance(number, int): if not number: n = 0 else: @@ -872,8 +878,8 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): rx = GF2X_deg(GF2E_rep((<FiniteField_ntl_gf2eElement>right).x)) if lx != rx: return richcmp_not_equal(lx, rx, op) - li = left.integer_representation() - ri = right.integer_representation() + li = left._integer_representation() + ri = right._integer_representation() return richcmp(li, ri, op) def _integer_(FiniteField_ntl_gf2eElement self, Integer): @@ -930,7 +936,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): else: raise TypeError("Cannot coerce element to an integer.") - def integer_representation(FiniteField_ntl_gf2eElement self): + def _integer_representation(FiniteField_ntl_gf2eElement self): r""" Return the int representation of ``self``. When ``self`` is in the prime subfield, the integer returned is equal to ``self`` and not @@ -940,15 +946,19 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): for `e \in \GF{p}[x]` with `e = a_0 + a_1 x + a_2 x^2 + \cdots`, `e` is represented as: `n = a_0 + a_1 p + a_2 p^2 + \cdots`. + .. SEEALSO:: + + :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer` + EXAMPLES:: sage: k.<a> = GF(2^20) - sage: a.integer_representation() + sage: a._integer_representation() 2 - sage: (a^2 + 1).integer_representation() + sage: (a^2 + 1)._integer_representation() 5 sage: k.<a> = GF(2^70) - sage: (a^65 + a^64 + 1).integer_representation() + sage: (a^65 + a^64 + 1)._integer_representation() 55340232221128654849 """ cdef unsigned int i = 0 @@ -1174,6 +1184,10 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: k.<b> = GF(2^16) sage: b._gap_init_() 'Z(65536)^1' + sage: k(gap('Z(2^16)^3+Z(2^16)^5')) + b^5 + b^3 + sage: k(libgap.Z(2^16)^3+libgap.Z(2^16)^5) + b^5 + b^3 """ F = self._parent if not F.is_conway(): @@ -1199,7 +1213,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: {a:1,a:0} # indirect doctest {a: 0} """ - return hash(self.integer_representation()) # todo, come up with a faster version + return hash(self._integer_representation()) # todo, come up with a faster version def _vector_(FiniteField_ntl_gf2eElement self, reverse=False): r""" @@ -1256,7 +1270,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): INPUT: - - ``base`` -- finite-field element. + - ``base`` -- finite field element OUTPUT: diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index ee0f9b3c4f5..be70aa56d39 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -29,7 +29,6 @@ from .element_base cimport FinitePolyExtElement from .integer_mod import IntegerMod_abstract import sage.rings.integer -from sage.interfaces.gap import is_GapElement from sage.modules.free_module_element import FreeModuleElement from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_element import Polynomial @@ -39,6 +38,8 @@ from sage.structure.element cimport Element, ModuleElement, RingElement from sage.structure.richcmp cimport rich_to_bool +from sage.interfaces.abc import GapElement + cdef GEN _INT_to_FFELT(GEN g, GEN x) except NULL: """ Convert the t_INT `x` to an element of the field of definition of @@ -395,7 +396,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): x_GEN = _new_GEN_from_mpz_t((<Integer>x).value) self.construct(_INT_to_FFELT(g, x_GEN)) - elif isinstance(x, int) or isinstance(x, long): + elif isinstance(x, int): g = (<pari_gen>self._parent._gen_pari).g x = objtogen(x) sig_on() @@ -504,10 +505,16 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): elif isinstance(x, str): self.construct_from(self._parent.polynomial_ring()(x)) - elif is_GapElement(x): - from sage.interfaces.gap import gfq_gap_to_sage + elif isinstance(x, GapElement): + try: + from sage.libs.gap.libgap import libgap + self.construct_from(libgap(x).sage(ring=self._parent)) + except (ValueError, IndexError, TypeError): + raise TypeError("no coercion defined") + + elif isinstance(x, sage.libs.gap.element.GapElement_FiniteField): try: - self.construct_from(gfq_gap_to_sage(x, self._parent)) + self.construct_from(x.sage(ring=self._parent)) except (ValueError, IndexError, TypeError): raise TypeError("no coercion defined") @@ -1258,9 +1265,9 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): EXAMPLES:: - sage: F = FiniteField(2^3, 'a', impl='pari_ffelt') - sage: a = F.multiplicative_generator() - sage: gap(a) # indirect doctest + sage: F = FiniteField(2^3, 'aa', impl='pari_ffelt') + sage: aa = F.multiplicative_generator() + sage: gap(aa) # indirect doctest Z(2^3) sage: b = F.multiplicative_generator() sage: a = b^3 @@ -1268,6 +1275,10 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): Z(2^3)^3 sage: gap(a^3) Z(2^3)^2 + sage: F(gap('Z(8)^3')) + aa + 1 + sage: F(libgap.Z(8)^3) + aa + 1 You can specify the instance of the Gap interpreter that is used:: diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index ffad0442389..285412aa5bd 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1,5 +1,5 @@ """ -Base Classes for Finite Fields +Base class for finite fields TESTS:: @@ -41,7 +41,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.prandom import randrange from sage.rings.integer cimport Integer import sage.rings.abc -from sage.misc.superseded import deprecation_cython as deprecation +from sage.misc.superseded import deprecation_cython as deprecation, deprecated_function_alias # Copied from sage.misc.fast_methods, used in __hash__() below. cdef int SIZEOF_VOID_P_SHIFT = 8*sizeof(void *) - 4 @@ -406,50 +406,60 @@ cdef class FiniteField(Field): if lim == <unsigned long>(-1): raise NotImplementedError("iterating over all elements of a large finite field is not supported") - def fetch_int(self, n): + def from_integer(self, n, reverse=False): r""" - Return the element of ``self`` that equals `n` under the condition that - :meth:`gen()` is set to the characteristic of the finite field ``self``. + Return the finite field element obtained by reinterpreting the base-`p` + expansion of `n` as a polynomial and evaluating it at the generator of + this finite field. + + If ``reverse`` is set to ``True`` (default: ``False``), + the list of digits is reversed prior to evaluation. + + Inverse of :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer`. INPUT: - - `n` -- integer. Must not be negative, and must be less than the - cardinality of ``self``. + - `n` -- integer between `0` and the cardinality of this field minus `1`. EXAMPLES:: sage: p = 4091 sage: F = GF(p^4, 'a') sage: n = 100*p^3 + 37*p^2 + 12*p + 6 - sage: F.fetch_int(n) + sage: F.from_integer(n) 100*a^3 + 37*a^2 + 12*a + 6 - sage: F.fetch_int(n) in F + sage: F.from_integer(n) in F True + sage: F.from_integer(n, reverse=True) + 6*a^3 + 12*a^2 + 37*a + 100 TESTS:: sage: F = GF(19^5) - sage: F.fetch_int(0) + sage: F.from_integer(0) 0 sage: _.parent() Finite Field in ... of size 19^5 - sage: F.fetch_int(-5) + sage: F.from_integer(-5) Traceback (most recent call last): ... - TypeError: n must be between 0 and self.order() - sage: F.fetch_int(F.cardinality()) + ValueError: n must be between 0 and self.order() + sage: F.from_integer(F.cardinality()) Traceback (most recent call last): ... - TypeError: n must be between 0 and self.order() + ValueError: n must be between 0 and self.order() """ n = Integer(n) - if (n < 0) or (n >= self.order()): - raise TypeError("n must be between 0 and self.order()") - if n == 0: - return self.zero() - cdef list digs = n.digits(base=self.characteristic()) + if not 0 <= n < self.order(): + raise ValueError("n must be between 0 and self.order()") + cdef list digs = n.digits(self.characteristic()) g = self.gen() - return self.sum(self(d) * g**i for i, d in enumerate(digs) if d) + r = self.zero() + for d in (digs if reverse else digs[::-1]): + r = r * g + self(d) + return r + + fetch_int = deprecated_function_alias(33941, from_integer) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ @@ -457,7 +467,7 @@ cdef class FiniteField(Field): ``self.0`` to the unique element of ``im_gens`` is a valid field homomorphism. Otherwise, return ``False``. - EXAMPLES:: + EXAMPLES: Between prime fields:: @@ -752,7 +762,7 @@ cdef class FiniteField(Field): a + 12 """ if self.degree() == 1: - from sage.arith.all import primitive_root + from sage.arith.misc import primitive_root return self(primitive_root(self.order())) F, = self.factored_unit_order() return self._element_of_factored_order(F) @@ -1021,7 +1031,7 @@ cdef class FiniteField(Field): return self._modulus def polynomial(self, name=None): - """ + r""" Return the minimal polynomial of the generator of ``self`` over the prime finite field. diff --git a/src/sage/rings/finite_rings/finite_field_constructor.py b/src/sage/rings/finite_rings/finite_field_constructor.py index 685c385cf2e..e42b2bed2dd 100644 --- a/src/sage/rings/finite_rings/finite_field_constructor.py +++ b/src/sage/rings/finite_rings/finite_field_constructor.py @@ -1,5 +1,5 @@ r""" -Finite Fields +Finite fields Sage supports arithmetic in finite prime and extension fields. Several implementation for prime fields are implemented natively in diff --git a/src/sage/rings/finite_rings/finite_field_givaro.py b/src/sage/rings/finite_rings/finite_field_givaro.py index 6b34b7e66be..b67df86bb30 100644 --- a/src/sage/rings/finite_rings/finite_field_givaro.py +++ b/src/sage/rings/finite_rings/finite_field_givaro.py @@ -1,5 +1,5 @@ """ -Givaro Finite Field +Givaro finite fields Finite fields that are implemented using Zech logs and the cardinality must be less than `2^{16}`. By default, Conway polynomials are @@ -23,6 +23,7 @@ from sage.rings.integer import Integer from sage.rings.finite_rings.element_givaro import Cache_givaro from sage.libs.pari.all import pari +from sage.misc.superseded import deprecated_function_alias class FiniteField_givaro(FiniteField): @@ -119,7 +120,7 @@ def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): sage: TestSuite(GF(2^3, 'a')).run() """ if repr not in ['int', 'log', 'poly']: - raise ValueError("Unknown representation %s"%repr) + raise ValueError("Unknown representation %s" % repr) q = Integer(q) if q < 2: @@ -130,7 +131,7 @@ def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): p = F[0][0] k = F[0][1] - if q >= 1<<16: + if q >= 1 << 16: raise ValueError("q must be < 2^16") from .finite_field_constructor import GF @@ -479,7 +480,7 @@ def int_to_log(self, n): """ return self._cache.int_to_log(n) - def fetch_int(self, n): + def from_integer(self, n): r""" Given an integer `n` return a finite field element in ``self`` which equals `n` under the condition that :meth:`gen()` is set to @@ -488,15 +489,17 @@ def fetch_int(self, n): EXAMPLES:: sage: k.<a> = GF(2^8) - sage: k.fetch_int(8) + sage: k.from_integer(8) a^3 - sage: e = k.fetch_int(151); e + sage: e = k.from_integer(151); e a^7 + a^4 + a^2 + a + 1 sage: 2^7 + 2^4 + 2^2 + 2 + 1 151 """ return self._cache.fetch_int(n) + fetch_int = deprecated_function_alias(33941, from_integer) + def _pari_modulus(self): """ Return the modulus of ``self`` in a format for PARI. diff --git a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py index ee47bba4d1c..82004502674 100644 --- a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py +++ b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py @@ -1,5 +1,5 @@ """ -Finite Fields of Characteristic 2 +Finite fields of characteristic 2 """ #***************************************************************************** @@ -18,6 +18,7 @@ from sage.rings.finite_rings.finite_field_base import FiniteField from sage.libs.pari.all import pari from sage.rings.integer import Integer +from sage.misc.superseded import deprecated_function_alias def late_import(): @@ -276,7 +277,7 @@ def prime_subfield(self): """ return GF2 - def fetch_int(self, number): + def from_integer(self, number): r""" Given an integer `n` less than :meth:`cardinality` with base `2` representation `a_0 + 2 \cdot a_1 + \cdots + 2^k a_k`, returns @@ -290,15 +291,17 @@ def fetch_int(self, number): EXAMPLES:: sage: k.<a> = GF(2^48) - sage: k.fetch_int(2^43 + 2^15 + 1) + sage: k.from_integer(2^43 + 2^15 + 1) a^43 + a^15 + 1 - sage: k.fetch_int(33793) + sage: k.from_integer(33793) a^15 + a^10 + 1 sage: 33793.digits(2) # little endian [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] """ return self._cache.fetch_int(number) + fetch_int = deprecated_function_alias(33941, from_integer) + def _pari_modulus(self): """ Return PARI object which is equivalent to the diff --git a/src/sage/rings/finite_rings/finite_field_prime_modn.py b/src/sage/rings/finite_rings/finite_field_prime_modn.py index 9129ecb56aa..afd18b7d64d 100644 --- a/src/sage/rings/finite_rings/finite_field_prime_modn.py +++ b/src/sage/rings/finite_rings/finite_field_prime_modn.py @@ -1,5 +1,5 @@ """ -Finite Prime Fields +Finite prime fields AUTHORS: @@ -111,7 +111,7 @@ def _coerce_map_from_(self, S): sage: RF13 = K.residue_field(pp) sage: RF13.hom([GF(13)(1)]) Ring morphism: - From: Residue field of Fractional ideal (w + 18) + From: Residue field of Fractional ideal (-w - 18) To: Finite Field of size 13 Defn: 1 |--> 1 @@ -221,7 +221,7 @@ def polynomial(self, name=None): name = self.variable_name() try: return self.__polynomial[name] - except AttributeError: + except AttributeError: f = self[name]([0, 1]) try: self.__polynomial[name] = f diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 81c7620d42d..86d2401ceec 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -826,31 +826,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return Morphism.__hash__(self) - cdef dict _extra_slots(self): - r""" - Helper function for copying and pickling - - TESTS:: - - sage: k.<t> = GF(5^3) - sage: Frob = k.frobenius_endomorphism(2) - sage: Frob.__reduce__() # indirect doctest - (<built-in function unpickle_map>, - (<class 'sage.rings.finite_rings.hom_finite_field_givaro.FrobeniusEndomorphism_givaro'>, - Automorphism group of Finite Field in t of size 5^3, - {}, - {'_codomain': Finite Field in t of size 5^3, - '_domain': Finite Field in t of size 5^3, - '_is_coercion': False, - '_lift': None, - '_power': 2, - '_repr_type_str': None})) - """ - cdef dict slots - slots = FrobeniusEndomorphism_generic._extra_slots(self) - slots['_power'] = self._power - return slots - cdef _update_slots(self, dict slots): r""" Helper function for copying and pickling @@ -869,7 +844,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): True """ FrobeniusEndomorphism_generic._update_slots(self, slots) - self._power = slots['_power'] domain = self.domain() self._degree = domain.degree() self._degree_fixed = domain.degree().gcd(self._power) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index d79ea68fc25..ad88a240355 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ """ -Finite field morphisms using Givaro +Givaro finite field morphisms Special implementation for givaro finite fields of: diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index ca4bd31571b..0a7578a092e 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -1,5 +1,5 @@ """ -Homset for Finite Fields +Homset for finite fields This is the set of all field homomorphisms between two finite fields. diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 400bf154522..97ae92d867b 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -91,14 +91,12 @@ from sage.libs.pari.all import pari, PariError import sage.rings.integer_ring as integer_ring import sage.rings.rational_field -import sage.interfaces.all - import sage.rings.integer cimport sage.rings.integer from sage.rings.integer cimport Integer from sage.structure.coerce cimport py_scalar_to_element -from sage.structure.richcmp import rich_to_bool_sgn, rich_to_bool +from sage.structure.richcmp cimport rich_to_bool_sgn, rich_to_bool import sage.structure.element cimport sage.structure.element coerce_binop = sage.structure.element.coerce_binop @@ -110,14 +108,16 @@ from sage.misc.persist import register_unpickle_override from sage.structure.parent cimport Parent -from sage.arith.all import crt, lcm +from sage.arith.misc import CRT as crt +from sage.arith.functions import lcm from sage.groups.generic import discrete_log cdef Integer one_Z = Integer(1) + def Mod(n, m, parent=None): - """ + r""" Return the equivalence class of `n` modulo `m` as an element of `\ZZ/m\ZZ`. @@ -1299,7 +1299,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): vmod.append(w) moduli.append(k) # Now combine in all possible ways using the CRT - from sage.arith.all import CRT_basis + from sage.arith.misc import CRT_basis basis = CRT_basis(moduli) from sage.misc.mrange import cartesian_product_iterator v = [] @@ -1598,7 +1598,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): return [a for a in self.parent() if a**n == self] def _balanced_abs(self): - """ + r""" This function returns `x` or `-x`, whichever has a positive representative in `-n/2 < x \leq n/2`. @@ -1608,8 +1608,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): """ if self.lift() > self.__modulus.sageInteger >> 1: return -self - else: - return self + return self def rational_reconstruction(self): """ @@ -1947,7 +1946,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): cdef class IntegerMod_gmp(IntegerMod_abstract): - """ + r""" Elements of `\ZZ/n\ZZ` for n not small enough to be operated on in word size. @@ -2354,7 +2353,7 @@ cdef class IntegerMod_gmp(IntegerMod_abstract): @coerce_binop def gcd(self, IntegerMod_gmp other): - """ + r""" Greatest common divisor Returns the "smallest" generator in `\ZZ / N\ZZ` of the ideal @@ -2387,7 +2386,7 @@ cdef class IntegerMod_gmp(IntegerMod_abstract): cdef class IntegerMod_int(IntegerMod_abstract): - """ + r""" Elements of `\ZZ/n\ZZ` for n small enough to be operated on in 32 bits @@ -3022,20 +3021,18 @@ cdef class IntegerMod_int(IntegerMod_abstract): # Either it failed but extend was True, or the generic algorithm is better return IntegerMod_abstract.sqrt(self, extend=extend, all=all) - def _balanced_abs(self): - """ + r""" This function returns `x` or `-x`, whichever has a positive representative in `-n/2 < x \leq n/2`. """ if self.ivalue > self.__modulus.int32 / 2: return -self - else: - return self + return self @coerce_binop def gcd(self, IntegerMod_int other): - """ + r""" Greatest common divisor Returns the "smallest" generator in `\ZZ / N\ZZ` of the ideal @@ -3213,7 +3210,7 @@ cdef int jacobi_int(int_fast32_t a, int_fast32_t m) except -2: ###################################################################### cdef class IntegerMod_int64(IntegerMod_abstract): - """ + r""" Elements of `\ZZ/n\ZZ` for n small enough to be operated on in 64 bits @@ -3688,22 +3685,20 @@ cdef class IntegerMod_int64(IntegerMod_abstract): sage: hash(a) 8943 """ - return hash(self.ivalue) def _balanced_abs(self): - """ + r""" This function returns `x` or `-x`, whichever has a positive representative in `-n/2 < x \leq n/2`. """ if self.ivalue > self.__modulus.int64 / 2: return -self - else: - return self + return self @coerce_binop def gcd(self, IntegerMod_int64 other): - """ + r""" Greatest common divisor Returns the "smallest" generator in `\ZZ / N\ZZ` of the ideal @@ -4470,7 +4465,7 @@ cdef class IntegerMod_to_Integer(Map): Set of Morphisms from Finite Field of size 2 to Integer Ring in Category of sets """ import sage.categories.homset - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Morphism.__init__(self, sage.categories.homset.Hom(R, integer_ring.ZZ, Sets())) cpdef Element _call_(self, x): diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 12c3c71c5ba..01b498eda92 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -62,7 +62,9 @@ import sage.misc.prandom as random -from sage.arith.all import factor, primitive_root, CRT_basis +from sage.arith.misc import factor +from sage.arith.misc import primitive_root +from sage.arith.misc import CRT_basis import sage.rings.ring as ring import sage.rings.abc from . import integer_mod @@ -72,12 +74,12 @@ from sage.libs.pari.all import pari, PariError -import sage.interfaces.all from sage.misc.cachefunc import cached_method from sage.structure.factory import UniqueFactory from sage.structure.richcmp import richcmp, richcmp_method +from sage.interfaces.abc import GapElement class IntegerModFactory(UniqueFactory): r""" @@ -1180,16 +1182,21 @@ def _element_constructor_(self, x): 4 sage: libgap(a.sage()) == a True + + better syntax for libgap interface:: + + sage: a = libgap.Z(13)^2 + sage: libgap(a.sage()) == a + True """ try: return integer_mod.IntegerMod(self, x) except (NotImplementedError, PariError): raise TypeError("error coercing to finite field") except TypeError: - if sage.interfaces.gap.is_GapElement(x): - from sage.interfaces.gap import intmod_gap_to_sage - y = intmod_gap_to_sage(x) - return integer_mod.IntegerMod(self, y) + if isinstance(x, GapElement): + from sage.libs.gap.libgap import libgap + return libgap(x).sage() raise # Continue up with the original TypeError def __iter__(self): diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index 7596f2a3028..83436bd6582 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -1,4 +1,4 @@ -""" +r""" Finite residue fields We can take the residue field of maximal ideals in the ring of integers @@ -20,13 +20,13 @@ monogenic (i.e., 2 is an essential discriminant divisor):: sage: K.<a> = NumberField(x^3 + x^2 - 2*x + 8) sage: F = K.factor(2); F - (Fractional ideal (1/2*a^2 - 1/2*a + 1)) * (Fractional ideal (-a^2 + 2*a - 3)) * (Fractional ideal (-3/2*a^2 + 5/2*a - 4)) + (Fractional ideal (-1/2*a^2 + 1/2*a - 1)) * (Fractional ideal (-a^2 + 2*a - 3)) * (Fractional ideal (3/2*a^2 - 5/2*a + 4)) sage: F[0][0].residue_field() - Residue field of Fractional ideal (1/2*a^2 - 1/2*a + 1) + Residue field of Fractional ideal (-1/2*a^2 + 1/2*a - 1) sage: F[1][0].residue_field() Residue field of Fractional ideal (-a^2 + 2*a - 3) sage: F[2][0].residue_field() - Residue field of Fractional ideal (-3/2*a^2 + 5/2*a - 4) + Residue field of Fractional ideal (3/2*a^2 - 5/2*a + 4) We can also form residue fields from `\ZZ`:: @@ -258,9 +258,9 @@ class ResidueFieldFactory(UniqueFactory): the index of ``ZZ[a]`` in the maximal order for all ``a``:: sage: K.<a> = NumberField(x^3 + x^2 - 2*x + 8); P = K.ideal(2).factor()[0][0]; P - Fractional ideal (1/2*a^2 - 1/2*a + 1) + Fractional ideal (-1/2*a^2 + 1/2*a - 1) sage: F = K.residue_field(P); F - Residue field of Fractional ideal (1/2*a^2 - 1/2*a + 1) + Residue field of Fractional ideal (-1/2*a^2 + 1/2*a - 1) sage: F(a) 0 sage: B = K.maximal_order().basis(); B @@ -270,7 +270,7 @@ class ResidueFieldFactory(UniqueFactory): sage: F(B[2]) 0 sage: F - Residue field of Fractional ideal (1/2*a^2 - 1/2*a + 1) + Residue field of Fractional ideal (-1/2*a^2 + 1/2*a - 1) sage: F.degree() 1 @@ -730,15 +730,15 @@ class ResidueField_generic(Field): EXAMPLES:: sage: I = QQ[3^(1/3)].factor(5)[1][0]; I - Fractional ideal (-a + 2) + Fractional ideal (a - 2) sage: k = I.residue_field(); k - Residue field of Fractional ideal (-a + 2) + Residue field of Fractional ideal (a - 2) sage: f = k.lift_map(); f Lifting map: - From: Residue field of Fractional ideal (-a + 2) + From: Residue field of Fractional ideal (a - 2) To: Maximal Order in Number Field in a with defining polynomial x^3 - 3 with a = 1.442249570307409? sage: f.domain() - Residue field of Fractional ideal (-a + 2) + Residue field of Fractional ideal (a - 2) sage: f.codomain() Maximal Order in Number Field in a with defining polynomial x^3 - 3 with a = 1.442249570307409? sage: f(k.0) @@ -768,7 +768,7 @@ class ResidueField_generic(Field): sage: K.<a> = NumberField(x^3-11) sage: F = K.ideal(37).factor(); F - (Fractional ideal (37, a + 9)) * (Fractional ideal (37, a + 12)) * (Fractional ideal (2*a - 5)) + (Fractional ideal (37, a + 9)) * (Fractional ideal (37, a + 12)) * (Fractional ideal (-2*a + 5)) sage: k = K.residue_field(F[0][0]) sage: l = K.residue_field(F[1][0]) sage: k == l @@ -846,7 +846,7 @@ cdef class ReductionMap(Map): sage: F.reduction_map() Partially defined reduction map: From: Number Field in a with defining polynomial x^3 + x^2 - 2*x + 8 - To: Residue field of Fractional ideal (1/2*a^2 - 1/2*a + 1) + To: Residue field of Fractional ideal (-1/2*a^2 + 1/2*a - 1) sage: K.<theta_5> = CyclotomicField(5) sage: F = K.factor(7)[0][0].residue_field() @@ -870,7 +870,7 @@ cdef class ReductionMap(Map): self._PBinv = PBinv self._to_order = to_order # used for lift self._PB = PB # used for lift - from sage.categories.all import SetsWithPartialMaps + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps self._repr_type_str = "Partially defined reduction" Map.__init__(self, Hom(K, F, SetsWithPartialMaps())) @@ -1846,7 +1846,7 @@ class ResidueFiniteField_ntl_gf2e(ResidueField_generic, FiniteField_ntl_gf2e): """ # we change the order for consistency with FiniteField_ntl_gf2e's __cinit__ def __init__(self, q, name, modulus, repr, p, to_vs, to_order, PB): - """ + r""" INPUT: - ``p`` -- the prime ideal defining this residue field @@ -1919,5 +1919,3 @@ class ResidueFiniteField_ntl_gf2e(ResidueField_generic, FiniteField_ntl_gf2e): return FiniteField_ntl_gf2e._element_constructor_(self, x) except TypeError: return ResidueField_generic._element_constructor_(self, x) - - diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 45ef3e63ae0..3f1964b13d2 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -1138,7 +1138,7 @@ def section(self): """ from sage.categories.sets_with_partial_maps import SetsWithPartialMaps - from sage.categories.all import Hom + from sage.categories.homset import Hom parent = Hom(self.codomain(), self.domain(), SetsWithPartialMaps()) return parent.__make_element_class__(FractionFieldEmbeddingSection)(self) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 58ee9bf3050..710aa930ca3 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: include_dirs = NTL_INCDIR # distutils: library_dirs = NTL_LIBDIR diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py index 80131156f33..10e06c248cd 100644 --- a/src/sage/rings/function_field/divisor.py +++ b/src/sage/rings/function_field/divisor.py @@ -51,7 +51,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.latex import latex -from sage.arith.all import lcm +from sage.arith.functions import lcm from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -1056,7 +1056,7 @@ def _coerce_map_from_(self, S): + Place (x^2 + 4*x + 1, y) """ if isinstance(S, PlaceSet): - func = lambda place: prime_divisor(self._field, place) + func = lambda place: prime_divisor(self._field, place) return SetMorphism(Hom(S,self), func) def function_field(self): diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 41a78f11846..6de39e743b2 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -281,13 +281,13 @@ cdef class FunctionFieldElement(FieldElement): Return the max degree between the denominator and numerator. EXAMPLES:: - + sage: FF.<t> = FunctionField(QQ) sage: f = (t^2 + 3) / (t^3 - 1/3); f (t^2 + 3)/(t^3 - 1/3) sage: f.degree() 3 - + sage: FF.<t> = FunctionField(QQ) sage: f = (t+8); f t + 8 @@ -1578,4 +1578,3 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): assert f.denominator() == 1 assert self._x.denominator() == 1 return self.parent()(self._x.numerator().inverse_mod(f.numerator())) - diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index e8cf51beda6..782aa99cc42 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -225,7 +225,7 @@ from sage.interfaces.singular import singular -from sage.arith.all import lcm +from sage.arith.functions import lcm from sage.rings.integer import Integer from sage.rings.ring import Field diff --git a/src/sage/rings/function_field/hermite_form_polynomial.pyx b/src/sage/rings/function_field/hermite_form_polynomial.pyx index 823ae0b55fa..dff24054110 100644 --- a/src/sage/rings/function_field/hermite_form_polynomial.pyx +++ b/src/sage/rings/function_field/hermite_form_polynomial.pyx @@ -184,4 +184,3 @@ def reversed_hermite_form(Matrix mat, bint transformation=False): if transformation: return U - diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index 75e6a826cd2..1ce0b885f56 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -1266,7 +1266,7 @@ def __bool__(self): """ return self._hnf.nrows() != 0 - + def __hash__(self): """ diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index 745690401cd..2ba5cd9ac66 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -108,7 +108,8 @@ from sage.misc.cachefunc import cached_method from sage.modules.free_module_element import vector -from sage.arith.all import lcm, gcd +from sage.arith.functions import lcm +from sage.arith.misc import GCD as gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.algebras.all import FiniteDimensionalAlgebra diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py index a05d9ae3fae..69993a4b744 100644 --- a/src/sage/rings/function_field/place.py +++ b/src/sage/rings/function_field/place.py @@ -57,7 +57,7 @@ from sage.misc.cachefunc import cached_method -from sage.arith.all import lcm +from sage.arith.functions import lcm from sage.rings.integer_ring import ZZ from sage.rings.qqbar import QQbar diff --git a/src/sage/rings/generic.py b/src/sage/rings/generic.py new file mode 100644 index 00000000000..83f923cee5c --- /dev/null +++ b/src/sage/rings/generic.py @@ -0,0 +1,259 @@ +r""" +Generic data structures and algorithms for rings + +AUTHORS: + +- Lorenz Panny (2022): :class:`ProductTree`, :func:`prod_with_derivative` +""" + +from sage.misc.misc_c import prod + + +class ProductTree: + r""" + A simple binary product tree, i.e., a tree of ring elements in + which every node equals the product of its children. + (In particular, the *root* equals the product of all *leaves*.) + + Product trees are a very useful building block for fast computer + algebra. For example, a quasilinear-time Discrete Fourier Transform + (the famous *Fast* Fourier Transform) can be implemented as follows + using the :meth:`remainders` method of this class:: + + sage: from sage.rings.generic import ProductTree + sage: F = GF(65537) + sage: a = F(1111) + sage: assert a.multiplicative_order() == 1024 + sage: R.<x> = F[] + sage: ms = [x - a^i for i in range(1024)] # roots of unity + sage: ys = [F.random_element() for _ in range(1024)] # input vector + sage: zs = ProductTree(ms).remainders(R(ys)) # compute FFT! + sage: zs == [R(ys) % m for m in ms] + True + + This class encodes the tree as *layers*: Layer `0` is just a tuple + of the leaves. Layer `i+1` is obtained from layer `i` by replacing + each pair of two adjacent elements by their product, starting from + the left. (If the length is odd, the unpaired element at the end is + simply copied as is.) This iteration stops as soon as it yields a + layer containing only a single element (the root). + + .. NOTE:: + + Use this class if you need the :meth:`remainders` method. + To compute just the product, :func:`prod` is likely faster. + + INPUT: + + - ``leaves`` -- an iterable of elements in a common ring + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: R.<x> = GF(101)[] + sage: vs = [x - i for i in range(1,10)] + sage: tree = ProductTree(vs) + sage: tree.root() + x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 + sage: tree.remainders(x^7 + x + 1) + [3, 30, 70, 27, 58, 72, 98, 98, 23] + sage: tree.remainders(x^100) + [1, 1, 1, 1, 1, 1, 1, 1, 1] + + :: + + sage: vs = prime_range(100) + sage: tree = ProductTree(vs) + sage: tree.root().factor() + 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71 * 73 * 79 * 83 * 89 * 97 + sage: tree.remainders(3599) + [1, 2, 4, 1, 2, 11, 12, 8, 11, 3, 3, 10, 32, 30, 27, 48, 0, 0, 48, 49, 22, 44, 30, 39, 10] + + We can access the individual layers of the tree:: + + sage: tree.layers + [(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97), + (6, 35, 143, 323, 667, 1147, 1763, 2491, 3599, 4757, 5767, 7387, 97), + (210, 46189, 765049, 4391633, 17120443, 42600829, 97), + (9699690, 3359814435017, 729345064647247, 97), + (32589158477190044730, 70746471270782959), + (2305567963945518424753102147331756070,)] + """ + def __init__(self, leaves): + r""" + Initialize a product tree having the given ring elements + as its leaves. + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: vs = prime_range(100) + sage: tree = ProductTree(vs) + """ + V = tuple(leaves) + self.layers = [V] + while len(V) > 1: + V = tuple(prod(V[i:i+2]) for i in range(0,len(V),2)) + self.layers.append(V) + + def __len__(self): + r""" + Return the number of leaves of this product tree. + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: R.<x> = GF(101)[] + sage: vs = [x - i for i in range(1,10)] + sage: tree = ProductTree(vs) + sage: len(tree) + 9 + sage: len(tree) == len(vs) + True + sage: len(tree.remainders(x^2)) + 9 + """ + return len(self.layers[0]) + + def __iter__(self): + r""" + Return an iterator over the leaves of this product tree. + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: R.<x> = GF(101)[] + sage: vs = [x - i for i in range(1,10)] + sage: tree = ProductTree(vs) + sage: next(iter(tree)) == vs[0] + True + sage: list(tree) == vs + True + """ + return iter(self.layers[0]) + + def root(self): + r""" + Return the value at the root of this product tree (i.e., the product of all leaves). + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: R.<x> = GF(101)[] + sage: vs = [x - i for i in range(1,10)] + sage: tree = ProductTree(vs) + sage: tree.root() + x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 + sage: tree.root() == prod(vs) + True + """ + assert len(self.layers[-1]) == 1 + return self.layers[-1][0] + + def leaves(self): + r""" + Return a tuple containing the leaves of this product tree. + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: R.<x> = GF(101)[] + sage: vs = [x - i for i in range(1,10)] + sage: tree = ProductTree(vs) + sage: tree.leaves() + (x + 100, x + 99, x + 98, ..., x + 93, x + 92) + sage: tree.leaves() == tuple(vs) + True + """ + return self.layers[0] + + def remainders(self, x): + r""" + Given a value `x`, return a list of all remainders of `x` + modulo the leaves of this product tree. + + The base ring must support the ``%`` operator for this + method to work. + + + INPUT: + + - ``x`` -- an element of the base ring of this product tree + + EXAMPLES:: + + sage: from sage.rings.generic import ProductTree + sage: vs = prime_range(100) + sage: tree = ProductTree(vs) + sage: n = 1085749272377676749812331719267 + sage: tree.remainders(n) + [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] + sage: [n % v for v in vs] + [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] + """ + X = [x] + for V in reversed(self.layers): + X = [X[i // 2] % V[i] for i in range(len(V))] + return X + + +def prod_with_derivative(pairs): + r""" + Given an iterable of pairs `(f, \partial f)` of ring elements, + return the pair `(\prod f, \partial \prod f)`, assuming `\partial` + is an operator obeying the standard product rule. + + This function is entirely algebraic, hence still works when the + elements `f` and `\partial f` are all passed through some ring + homomorphism first. One particularly useful instance of this is + evaluating the derivative of a product of polynomials at a point + without fully expanding the product; see the second example below. + + INPUT: + + - ``pairs`` -- an iterable of tuples `(f, \partial f)` of elements + of a common ring + + ALGORITHM: Repeated application of the product rule. + + EXAMPLES:: + + sage: from sage.rings.generic import prod_with_derivative + sage: R.<x> = ZZ[] + sage: fs = [x^2 + 2*x + 3, 4*x + 5, 6*x^7 + 8*x + 9] + sage: prod(fs) + 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 + sage: prod(fs).derivative() + 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 + sage: F, dF = prod_with_derivative((f, f.derivative()) for f in fs) + sage: F + 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 + sage: dF + 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 + + The main reason for this function to exist is that it allows us to + *evaluate* the derivative of a product of polynomials at a point + `\alpha` without ever fully expanding the product *as a polynomial*:: + + sage: alpha = 42 + sage: F(alpha) + 442943981574522759 + sage: dF(alpha) + 104645261461514994 + sage: us = [f(alpha) for f in fs] + sage: vs = [f.derivative()(alpha) for f in fs] + sage: prod_with_derivative(zip(us, vs)) + (442943981574522759, 104645261461514994) + """ + class _aux: + def __init__(self, f, df): + self.f, self.df = f, df + + def __mul__(self, other): + return _aux(self.f * other.f, self.df * other.f + self.f * other.df) + + def __iter__(self): + yield self.f + yield self.df + + return tuple(prod(_aux(*tup) for tup in pairs)) diff --git a/src/sage/rings/homset.py b/src/sage/rings/homset.py index c0db5c9210b..6f7a2d4b415 100644 --- a/src/sage/rings/homset.py +++ b/src/sage/rings/homset.py @@ -49,7 +49,9 @@ def RingHomset(R, S, category = None): """ if quotient_ring.is_QuotientRing(R): - return RingHomset_quo_ring(R, S, category = category) + from .polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if not is_PolynomialQuotientRing(R): # backwards compatibility + return RingHomset_quo_ring(R, S, category = category) return RingHomset_generic(R, S, category = category) diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 8e584ee0141..4a57584ff52 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -11,7 +11,7 @@ If `R` is non-commutative, the former creates a left and the latter a right ideal, and ``R*[a,b,...]*R`` creates a two-sided ideal. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) @@ -23,16 +23,23 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from types import GeneratorType -import sage.rings.ring +from sage.categories.rings import Rings +from sage.categories.fields import Fields from sage.structure.element import MonoidElement from sage.structure.richcmp import rich_to_bool, richcmp from sage.structure.sequence import Sequence + +# for efficiency +_Rings = Rings() +_Fields = Fields() + + def Ideal(*args, **kwds): r""" Create the ideal in ring with given generators. @@ -171,7 +178,7 @@ def Ideal(*args, **kwds): first = args[0] inferred_field = False - if not isinstance(first, sage.rings.ring.Ring): + if first not in _Rings: if isinstance(first, Ideal_generic) and len(args) == 1: R = first.ring() gens = first.gens() @@ -182,12 +189,12 @@ def Ideal(*args, **kwds): gens = args gens = Sequence(gens) R = gens.universe() - inferred_field = isinstance(R, sage.rings.ring.Field) + inferred_field = R in _Fields else: R = first gens = args[1:] - if not isinstance(R, sage.rings.ring.CommutativeRing): + if R not in _Rings.Commutative(): raise TypeError("R must be a commutative ring") I = R.ideal(*gens, **kwds) @@ -200,6 +207,7 @@ def Ideal(*args, **kwds): return I + def is_Ideal(x): r""" Return ``True`` if object is an ideal of a ring. @@ -1847,12 +1855,8 @@ def FieldIdeal(R): Multivariate Polynomial Ring in x1, x2, x3, x4 over Finite Field in alpha of size 2^4 """ - q = R.base_ring().order() - import sage.rings.infinity if q is sage.rings.infinity.infinity: raise TypeError("Cannot construct field ideal for R.base_ring().order()==infinity") - - return R.ideal([x**q - x for x in R.gens() ]) - + return R.ideal([x**q - x for x in R.gens()]) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 00b490ecb00..c43319942fb 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -632,11 +632,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if isinstance(x, Integer): set_from_Integer(self, <Integer>x) - elif isinstance(x, long): - mpz_set_pylong(self.value, x) - elif isinstance(x, int): - mpz_set_si(self.value, PyInt_AS_LONG(x)) + mpz_set_pylong(self.value, x) elif isinstance(x, float): n = long(x) @@ -683,7 +680,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if b == 2: # we use a faster method for j from 0 <= j < len(x): otmp = x[j] - if isinstance(otmp, (int, long)): + if isinstance(otmp, int): if (<long> otmp) == 1: mpz_setbit(self.value, j) if (<long> otmp) != 0: @@ -707,7 +704,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): elif is_numpy_type(type(x)): import numpy if isinstance(x, numpy.integer): - mpz_set_pylong(self.value, long(x)) + mpz_set_pylong(self.value, int(x)) return elif type(x) is gmpy2.mpz: @@ -1184,7 +1181,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): .. NOTE:: '0x' is *not* prepended to the result like is done by the - corresponding Python function on ``int`` or ``long``. This is for + corresponding Python function on ``int``. This is for efficiency sake--adding and stripping the string wastes time; since this function is used for conversions from integers to other C-library structures, it is important @@ -1208,7 +1205,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): .. NOTE:: '0' (or '0o') is *not* prepended to the result like is done by the - corresponding Python function on ``int`` or ``long``. This is for + corresponding Python function on ``int``. This is for efficiency sake--adding and stripping the string wastes time; since this function is used for conversions from integers to other C-library structures, it is important @@ -1257,7 +1254,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return self.str(2) def bits(self): - """ + r""" Return the bits in self as a list, least significant first. The result satisfies the identity @@ -1544,7 +1541,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): # set up digits for optimal access once we get inside the worker # functions - if not digits is None: + if digits is not None: # list objects have fastest access in the innermost loop if type(digits) is not list: digits = [digits[i] for i in range(_base)] @@ -2434,7 +2431,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): integer_ring.ZZ(n).ordinal_str())) cpdef size_t _exact_log_log2_iter(self,Integer m): - """ + r""" This is only for internal use only. You should expect it to crash and burn for negative or other malformed input. In particular, if the base `2 \leq m < 4` the log2 approximation of m is 1 and certain @@ -3463,7 +3460,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return q, r def powermod(self, exp, mod): - """ + r""" Compute self\*\*exp modulo mod. EXAMPLES:: @@ -6714,8 +6711,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): raise ArithmeticError("inverse does not exist") def inverse_mod(self, n): - """ - Returns the inverse of self modulo `n`, if this inverse exists. + r""" + Return the inverse of self modulo `n`, if this inverse exists. + Otherwise, raises a ``ZeroDivisionError`` exception. INPUT: diff --git a/src/sage/rings/invariants/invariant_theory.py b/src/sage/rings/invariants/invariant_theory.py index bb249b60560..0c0aa1c9f69 100644 --- a/src/sage/rings/invariants/invariant_theory.py +++ b/src/sage/rings/invariants/invariant_theory.py @@ -7,7 +7,7 @@ degree `d` in `n` variables. The special linear group `SL(n,\CC)` acts on the variables `(x_1,\dots, x_n)` linearly, -. MATH:: +.. MATH:: (x_1,\dots, x_n)^t \to A (x_1,\dots, x_n)^t ,\qquad diff --git a/src/sage/rings/invariants/reconstruction.py b/src/sage/rings/invariants/reconstruction.py index 2b7c90225c7..bde5c307ee4 100644 --- a/src/sage/rings/invariants/reconstruction.py +++ b/src/sage/rings/invariants/reconstruction.py @@ -328,8 +328,8 @@ def binary_quintic_coefficients_from_invariants(invariants, K=None, invariant_ch D = -M Delta = A a = [0] - a.append((2*K(3)**-1*A**2-B)*(N*A-M*B)*K(2)**-1 \ - - M*(N*K(2)**-1-M*A*K(3)**-1)) + a.append((2*K(3)**-1*A**2-B)*(N*A-M*B)*K(2)**-1 + - M*(N*K(2)**-1-M*A*K(3)**-1)) B0 = R B1 = K(2)**-1*(N*A-M*B) C0 = 0 diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 6bfb8730c39..e77b1a7cd2e 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -68,7 +68,7 @@ series are much faster. Moreover, these are the series where equality can be decided. For example:: - sage: L.<z> = LazyTaylorSeriesRing(ZZ) + sage: L.<z> = LazyPowerSeriesRing(ZZ) sage: f = 1 + 2*z^2 / (1 - z) sage: f - 2 / (1 - z) + 1 + 2*z 0 @@ -77,7 +77,7 @@ streams of multivariate polynomials. Therefore, the only exact series in this case are polynomials:: - sage: L.<x,y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) sage: 1 / (1-x) 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 + O(x,y)^7 @@ -100,6 +100,101 @@ sage: hinv.valuation() -1 +TESTS: + +We check that -- at least for some simple cases -- division, +composition and reversion do not raise exceptions for univariate lazy +Laurent series, lazy power series and lazy symmetric functions:: + + sage: def check(L, z, verbose=False): + ....: # division + ....: lf = [0, L(0), 1, L(1), z, 1 + z, 2 + z + z^2] + ....: lg = [3, L(3), 1 + z, 2 + z + z^2] + ....: for f in lf: + ....: for g in lg: + ....: try: + ....: h = f / g + ....: if verbose: print("(%s) / (%s) = %s" % (f, g, h)) + ....: except Exception as e: + ....: print("%s in (%s) / (%s)" % (e, f, g)) + ....: # composition + ....: f = L(0) + ....: l = [(f, 0), (f, L(0)), (f, 2), (f, L(2)), (f, 2 + z + z^2), (f, 3/(1 - 2*z))] + ....: f = L(1) + ....: l.extend([(f, 0), (f, L(0)), (f, 2), (f, L(2)), (f, 2 + z + z^2), (f, 3/(1 - 2*z))]) + ....: f = 2 + z + z^2 + ....: l.extend([(f, 0), (f, L(0)), (f, 2), (f, L(2)), (f, 2 + z + z^2), (f, 3/(1 - 2*z))]) + ....: f = 3/(2 - 3*z) + ....: l.extend([(f, 0), (f, L(0)), (f, 3*z/(1 - 2*z))]) + ....: for f, g in l: + ....: try: + ....: h = f(g) + ....: if verbose: print("(%s)(%s) = %s" % (f, g, h)) + ....: except Exception as e: + ....: print("%s in (%s)(%s)" % (e, f, g)) + ....: # reversion + ....: l = [2 + 3*z, 3*z + 2*z^2, 3*z/(1 - 2*z - 3*z^2)] + ....: for f in l: + ....: try: + ....: h = f.revert() + ....: if verbose: print("(%s)^{(-1)} = %s" % (f, h)) + ....: except Exception as e: + ....: print("%s in (%s).revert()" % (e, f)) + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: check(L, z) + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: check(L, z) + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(p) + sage: check(L, L(p[1])) + +We check that the elements in the cache of the stream of homogeneous +components are in the correct ring:: + + sage: def check(L, x, valuation, verbose=False): + ....: f = L(x, valuation=valuation) + ....: _ = f[2], f[5] + ....: if callable(x): + ....: assert len(f._coeff_stream._cache) == 2, "the cache is %s" % f._coeff_stream._cache + ....: else: + ....: m = 6 if valuation is None else 5 - valuation + 1 + ....: assert len(f._coeff_stream._cache) == m, "the cache is %s" % f._coeff_stream._cache + ....: P = f._coeff_stream._cache[2].parent() + ....: assert P is L._internal_poly_ring.base_ring(), "the cache is in %s" % P + ....: if verbose: + ....: print(P) + + sage: def gen(): + ....: n = 0 + ....: while True: + ....: yield n + ....: n += 1 + + sage: L.<z> = LazyLaurentSeriesRing(GF(2)) + sage: check(L, lambda n: n, valuation=-5) + sage: check(L, gen(), valuation=-5) + + sage: L = LazyDirichletSeriesRing(QQbar, "s") + sage: check(L, lambda n: n, valuation=2) + sage: check(L, gen(), valuation=2) + + sage: L.<z> = LazyPowerSeriesRing(GF(2)) + sage: check(L, lambda n: n, valuation=0) + sage: check(L, gen(), valuation=0) + + sage: L.<x,y> = LazyPowerSeriesRing(GF(2)) + sage: check(L, lambda n: (x + y)^n, valuation=None) + sage: def gen(): + ....: n = 0 + ....: while True: + ....: yield (x+y)^n + ....: n += 1 + sage: check(L, gen(), valuation=None) + + sage: s = SymmetricFunctions(GF(2)).s() + sage: L = LazySymmetricFunctions(s) + sage: check(L, lambda n: sum(k*s(la) for k, la in enumerate(Partitions(n))), valuation=0) """ # **************************************************************************** @@ -119,11 +214,17 @@ from sage.functions.other import factorial from sage.misc.misc_c import prod from sage.arith.power import generic_power +from sage.arith.functions import lcm +from sage.arith.misc import divisors, moebius +from sage.combinat.partition import Partition, Partitions +from sage.misc.derivative import derivative_parse +from sage.categories.integral_domains import IntegralDomains from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.categories.tensor import tensor from sage.data_structures.stream import ( Stream_add, Stream_cauchy_mul, @@ -139,11 +240,13 @@ Stream_uninitialized, Stream_shift, Stream_function, + Stream_derivative, Stream_dirichlet_convolve, Stream_dirichlet_invert, Stream_plethysm ) + class LazyModuleElement(Element): r""" A lazy sequence with a module structure given by term-wise @@ -154,9 +257,9 @@ class LazyModuleElement(Element): sage: L.<z> = LazyLaurentSeriesRing(ZZ) sage: M = L(lambda n: n, valuation=0) sage: N = L(lambda n: 1, valuation=0) - sage: M[:10] + sage: M[0:10] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - sage: N[:10] + sage: N[0:10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] Two sequences can be added:: @@ -168,19 +271,19 @@ class LazyModuleElement(Element): Two sequences can be subtracted:: sage: P = M - N - sage: P[:10] + sage: P[0:10] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] A sequence can be multiplied by a scalar:: sage: Q = 2 * M - sage: Q[:10] + sage: Q[0:10] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] The negation of a sequence can also be found:: sage: R = -M - sage: R[:10] + sage: R[0:10] [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] """ def __init__(self, parent, coeff_stream): @@ -195,27 +298,41 @@ def __init__(self, parent, coeff_stream): sage: L = LazyDirichletSeriesRing(QQbar, 'z') sage: g = L(constant=1) sage: TestSuite(g).run() - """ Element.__init__(self, parent) self._coeff_stream = coeff_stream def __getitem__(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. + r""" + Return the homogeneous degree ``n`` part of the series. INPUT: - - ``n`` -- integer; the exponent + - ``n`` -- integer; the degree + + For a series ``f``, the slice ``f[start:stop]`` produces the following: + + - if ``start`` and ``stop`` are integers, return the list of + terms with given degrees + + - if ``start`` is ``None``, return the list of terms + beginning with the valuation + + - if ``stop`` is ``None``, return a + :class:`~sage.misc.lazy_list.lazy_list_generic` instead. EXAMPLES:: - sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: L.<z> = LazyLaurentSeriesRing(ZZ) sage: f = z / (1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] sage: f[0:20] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: f[:20] + [1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: f[::3] + lazy list [1, 2, 4, ...] sage: M = L(lambda n: n, valuation=0) sage: [M[n] for n in range(20)] @@ -226,75 +343,154 @@ def __getitem__(self, n): sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + Similarly for multivariate series:: + + sage: L.<x,y> = LazyPowerSeriesRing(QQ) + sage: sin(x*y)[:11] + [x*y, 0, 0, 0, -1/6*x^3*y^3, 0, 0, 0, 1/120*x^5*y^5] + sage: sin(x*y)[2::4] + lazy list [x*y, -1/6*x^3*y^3, 1/120*x^5*y^5, ...] + Similarly for Dirichlet series:: sage: L = LazyDirichletSeriesRing(ZZ, "z") - sage: f = L(lambda n: n) - sage: [f[n] for n in range(1, 11)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - sage: f[1:11] + sage: L(lambda n: n)[1:11] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - sage: M = L(lambda n: n) - sage: [M[n] for n in range(1, 11)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - sage: L = LazyDirichletSeriesRing(ZZ, "z", sparse=True) - sage: M = L(lambda n: n) - sage: [M[n] for n in range(1, 11)] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + TESTS: + + Check that no more elements than necessary are computed:: + sage: L = LazyDirichletSeriesRing(ZZ, "z") + sage: f = L(lambda n: 0 if n < 5 else n) + sage: f[:3] + [] + sage: f._coeff_stream._cache + {1: 0, 2: 0} """ R = self.parent()._internal_poly_ring.base_ring() + coeff_stream = self._coeff_stream if isinstance(n, slice): - if n.stop is None: - raise NotImplementedError("cannot list an infinite set") - start = n.start if n.start is not None else self._coeff_stream._approximate_order + if n.start is None: + # WARNING: for Dirichlet series, 'degree' and + # valuation are different + if n.stop is None: + start = coeff_stream.order() + else: + start = coeff_stream._approximate_order + while start < n.stop and not coeff_stream[start]: + start += 1 + coeff_stream._approximate_order = start + else: + start = n.start step = n.step if n.step is not None else 1 + if n.stop is None: + from sage.misc.lazy_list import lazy_list + return lazy_list(lambda k: R(self._coeff_stream[start + k * step])) + return [R(self._coeff_stream[k]) for k in range(start, n.stop, step)] + return R(self._coeff_stream[n]) coefficient = __getitem__ - def map_coefficients(self, func, ring=None): + def coefficients(self, n=None): r""" - Return the series with ``func`` applied to each nonzero - coefficient of ``self``. + Return the first `n` non-zero coefficients of ``self``. INPUT: - - ``func`` -- function that takes in a coefficient and returns - a new coefficient + - ``n`` -- (optional) the number of non-zero coefficients to return - EXAMPLES: + If the series has fewer than `n` non-zero coefficients, only + these are returned. - Dense Implementation:: + If ``n`` is ``None``, a + :class:`~sage.misc.lazy_list.lazy_list_generic` with all + non-zero coefficients is returned instead. - sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=False) - sage: s = z/(1 - 2*z^2) - sage: t = s.map_coefficients(lambda c: c + 1) - sage: s - z + 2*z^3 + 4*z^5 + 8*z^7 + O(z^8) - sage: t - 2*z + 3*z^3 + 5*z^5 + 9*z^7 + O(z^8) - sage: m = L(lambda n: n, valuation=0); m - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) - sage: m.map_coefficients(lambda c: c + 1) - 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) + .. WARNING:: - Sparse Implementation:: + If there are fewer than `n` non-zero coefficients, but + this cannot be detected, this method will not return. - sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=True) + EXAMPLES:: + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: f = L([1,2,3]) + sage: f.coefficients(5) + doctest:...: DeprecationWarning: the method coefficients now only returns the non-zero coefficients. Use __getitem__ instead. + See https://trac.sagemath.org/32367 for details. + [1, 2, 3] + + sage: f = sin(x) + sage: f.coefficients(5) + [1, -1/6, 1/120, -1/5040, 1/362880] + + sage: L.<x, y> = LazyPowerSeriesRing(QQ) + sage: f = sin(x^2+y^2) + sage: f.coefficients(5) + [1, 1, -1/6, -1/2, -1/2] + + sage: f.coefficients() + lazy list [1, 1, -1/6, ...] + + sage: L.<x> = LazyPowerSeriesRing(GF(2)) + sage: f = L(lambda n: n) + sage: f.coefficients(5) + [1, 1, 1, 1, 1] + """ + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + return [] + from itertools import repeat, chain, islice + from sage.misc.lazy_list import lazy_list + # prepare a generator of the non-zero coefficients + P = self.parent() + if isinstance(coeff_stream, Stream_exact): + if coeff_stream._constant: + coeffs = chain((c for c in coeff_stream._initial_coefficients if c), + repeat(coeff_stream._constant)) + else: + coeffs = (c for c in coeff_stream._initial_coefficients if c) + else: + coeffs = filter(bool, coeff_stream.iterate_coefficients()) + + if n is None: + if P._internal_poly_ring.base_ring() is not P._laurent_poly_ring: + return lazy_list(coeffs) + + # flatten out the generator in the multivariate case + return lazy_list(chain.from_iterable(map(lambda coeff: coeff.coefficients(), coeffs))) + + if isinstance(self, LazyPowerSeries) and self.parent()._arity == 1: + from sage.misc.superseded import deprecation + deprecation(32367, 'the method coefficients now only returns the non-zero coefficients. Use __getitem__ instead.') + + if P._internal_poly_ring.base_ring() is not P._laurent_poly_ring: + return list(islice(coeffs, n)) + + # flatten out the generator in the multivariate case + return list(islice(chain.from_iterable(map(lambda coeff: coeff.coefficients(), coeffs)), n)) + + def map_coefficients(self, f): + r""" + Return the series with ``f`` applied to each nonzero + coefficient of ``self``. + + INPUT: + + - ``func`` -- function that takes in a coefficient and returns + a new coefficient + + EXAMPLES:: + + sage: L.<z> = LazyLaurentSeriesRing(ZZ) sage: m = L(lambda n: n, valuation=0); m z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) sage: m.map_coefficients(lambda c: c + 1) 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) - An example where the series is known to be exact:: - - sage: f = z + z^2 + z^3 - sage: f.map_coefficients(lambda c: c + 1) - 2*z + 2*z^2 + 2*z^3 - Similarly for Dirichlet series:: sage: L = LazyDirichletSeriesRing(ZZ, "z") @@ -303,7 +499,50 @@ def map_coefficients(self, func, ring=None): sage: s.map_coefficients(lambda c: c + 1) 2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + O(1/(8^z)) - TESTS:: + Similarly for multivariate power series:: + + sage: L.<x, y> = LazyPowerSeriesRing(QQ) + sage: f = 1/(1-(x+y)); f + 1 + (x+y) + (x^2+2*x*y+y^2) + (x^3+3*x^2*y+3*x*y^2+y^3) + + (x^4+4*x^3*y+6*x^2*y^2+4*x*y^3+y^4) + + (x^5+5*x^4*y+10*x^3*y^2+10*x^2*y^3+5*x*y^4+y^5) + + (x^6+6*x^5*y+15*x^4*y^2+20*x^3*y^3+15*x^2*y^4+6*x*y^5+y^6) + + O(x,y)^7 + sage: f.map_coefficients(lambda c: c^2) + 1 + (x+y) + (x^2+4*x*y+y^2) + (x^3+9*x^2*y+9*x*y^2+y^3) + + (x^4+16*x^3*y+36*x^2*y^2+16*x*y^3+y^4) + + (x^5+25*x^4*y+100*x^3*y^2+100*x^2*y^3+25*x*y^4+y^5) + + (x^6+36*x^5*y+225*x^4*y^2+400*x^3*y^3+225*x^2*y^4+36*x*y^5+y^6) + + O(x,y)^7 + + Similarly for lazy symmetric functions:: + + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(p) + sage: f = 1/(1-2*L(p[1])); f + p[] + 2*p[1] + (4*p[1,1]) + (8*p[1,1,1]) + (16*p[1,1,1,1]) + + (32*p[1,1,1,1,1]) + (64*p[1,1,1,1,1,1]) + O^7 + sage: f.map_coefficients(lambda c: log(c, 2)) + p[1] + (2*p[1,1]) + (3*p[1,1,1]) + (4*p[1,1,1,1]) + + (5*p[1,1,1,1,1]) + (6*p[1,1,1,1,1,1]) + O^7 + + TESTS: + + Dense implementation:: + + sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: s = z/(1 - 2*z^2) + sage: t = s.map_coefficients(lambda c: c + 1) + sage: s + z + 2*z^3 + 4*z^5 + 8*z^7 + O(z^8) + sage: t + 2*z + 3*z^3 + 5*z^5 + 9*z^7 + O(z^8) + sage: m = L(lambda n: n, valuation=0); m + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) + sage: m.map_coefficients(lambda c: c + 1) + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) + + Test the zero series:: sage: from sage.data_structures.stream import Stream_zero sage: L.<z> = LazyLaurentSeriesRing(ZZ) @@ -312,12 +551,22 @@ def map_coefficients(self, func, ring=None): sage: isinstance(s._coeff_stream, Stream_zero) True + An example where the series is known to be exact:: + + sage: f = z + z^2 + z^3 + sage: f.map_coefficients(lambda c: c + 1) + 2*z + 2*z^2 + 2*z^3 + """ P = self.parent() coeff_stream = self._coeff_stream if isinstance(coeff_stream, Stream_zero): return self - BR = P.base_ring() + R = P._internal_poly_ring.base_ring() + if R is P._laurent_poly_ring: + func = lambda c: R(c).map_coefficients(f) + else: + func = f if isinstance(coeff_stream, Stream_exact): initial_coefficients = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] @@ -325,13 +574,12 @@ def map_coefficients(self, func, ring=None): if not any(initial_coefficients) and not c: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - self._coeff_stream._is_sparse, - order=coeff_stream._approximate_order, - degree=coeff_stream._degree, - constant=BR(c)) + order=coeff_stream._approximate_order, + degree=coeff_stream._degree, + constant=P.base_ring()(c)) return P.element_class(P, coeff_stream) - R = P._internal_poly_ring.base_ring() - coeff_stream = Stream_map_coefficients(self._coeff_stream, func, R) + coeff_stream = Stream_map_coefficients(self._coeff_stream, func, + P.is_sparse()) return P.element_class(P, coeff_stream) def truncate(self, d): @@ -344,7 +592,7 @@ def truncate(self, d): EXAMPLES: - Dense Implementation:: + Dense implementation:: sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=False) sage: alpha = 1/(1-z) @@ -378,8 +626,7 @@ def truncate(self, d): coeff_stream = self._coeff_stream v = coeff_stream._approximate_order initial_coefficients = [coeff_stream[i] for i in range(v, d)] - return P.element_class(P, Stream_exact(initial_coefficients, P._sparse, - order=v)) + return P.element_class(P, Stream_exact(initial_coefficients, order=v)) def shift(self, n): r""" @@ -421,6 +668,11 @@ def shift(self, n): sage: f.shift(5).shift(-5) - f 0 + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: f = x.shift(-3); f + x^-2 + sage: f.parent() + Lazy Laurent Series Ring in x over Rational Field """ if isinstance(self._coeff_stream, Stream_zero): return self @@ -434,12 +686,18 @@ def shift(self, n): init_coeff = self._coeff_stream._initial_coefficients degree = self._coeff_stream._degree + n valuation = self._coeff_stream._approximate_order + n - coeff_stream = Stream_exact(init_coeff, self._coeff_stream._is_sparse, + coeff_stream = Stream_exact(init_coeff, constant=self._coeff_stream._constant, order=valuation, degree=degree) else: coeff_stream = Stream_shift(self._coeff_stream, n) P = self.parent() + # If we shift it too much, then it needs to go into the fraction field + # FIXME? This is different than the polynomial rings, which truncates the terms + if (coeff_stream._true_order + and P._minimal_valuation is not None + and coeff_stream._approximate_order < P._minimal_valuation): + P = P.fraction_field() return P.element_class(P, coeff_stream) __lshift__ = shift @@ -476,6 +734,35 @@ def prec(self): """ return infinity + def lift_to_precision(self, absprec=None): + """ + Return another element of the same parent with absolute + precision at least ``absprec``, congruent to this element + modulo the precision of this element. + + Since the precision of a lazy series is infinity, this method + returns the series itself, and the argument is ignored. + + EXAMPLES:: + + sage: P.<t> = PowerSeriesRing(QQ, default_prec=2) + sage: R.<z> = LazyPowerSeriesRing(P) + sage: f = R(lambda n: 1/(1-t)^n) + sage: f + 1 + ((1+t+O(t^2))*z) + ((1+2*t+O(t^2))*z^2) + + ((1+3*t+O(t^2))*z^3) + + ((1+4*t+O(t^2))*z^4) + + ((1+5*t+O(t^2))*z^5) + + ((1+6*t+O(t^2))*z^6) + O(z^7) + sage: f.lift_to_precision() + 1 + ((1+t+O(t^2))*z) + ((1+2*t+O(t^2))*z^2) + + ((1+3*t+O(t^2))*z^3) + + ((1+4*t+O(t^2))*z^4) + + ((1+5*t+O(t^2))*z^5) + + ((1+6*t+O(t^2))*z^6) + O(z^7) + """ + return self + def _richcmp_(self, other, op): r""" Compare ``self`` with ``other`` with respect to the comparison @@ -514,35 +801,50 @@ def _richcmp_(self, other, op): Traceback (most recent call last): ... ValueError: undecidable + + TESTS:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: f = L([0,0,1,0,1,0,0,1], constant=1) + sage: g = L([0,0,1,0,1,0,0], degree=7, constant=1) + sage: f == g + True + """ if op is op_EQ: - if isinstance(self._coeff_stream, Stream_zero): # self == 0 + if isinstance(self._coeff_stream, Stream_zero): if isinstance(other._coeff_stream, Stream_zero): return True if other._coeff_stream.is_nonzero(): return False - # other == 0 but self likely != 0 - elif (isinstance(other._coeff_stream, Stream_zero) - and self._coeff_stream.is_nonzero()): - return False - - if (not isinstance(self._coeff_stream, Stream_exact) - or not isinstance(other._coeff_stream, Stream_exact)): - # One of the lazy laurent series is not known to eventually be constant - # Implement the checking of the caches here. - n = min(self._coeff_stream._approximate_order, other._coeff_stream._approximate_order) - m = max(self._coeff_stream._approximate_order, other._coeff_stream._approximate_order) - for i in range(n, m): - if self[i] != other[i]: - return False + elif isinstance(other._coeff_stream, Stream_zero): + if self._coeff_stream.is_nonzero(): + return False + elif isinstance(self._coeff_stream, Stream_exact): + if isinstance(other._coeff_stream, Stream_exact): + return self._coeff_stream == other._coeff_stream + if self._coeff_stream != other._coeff_stream: + return False + elif isinstance(other._coeff_stream, Stream_exact): + if other._coeff_stream != self._coeff_stream: + return False + else: + # both streams are inexact, perhaps they are equal by + # construction if self._coeff_stream == other._coeff_stream: return True + # perhaps their caches are different if self._coeff_stream != other._coeff_stream: return False - raise ValueError("undecidable") - # Both are Stream_exact, which implements a full check - return self._coeff_stream == other._coeff_stream + # undecidable otherwise + prec = self.parent().options['halting_precision'] + if prec is None: + raise ValueError("undecidable") + # at least one of the approximate orders is not infinity + m = min(self._coeff_stream._approximate_order, + other._coeff_stream._approximate_order) + return all(self[i] == other[i] for i in range(m, m + prec)) if op is op_NE: return not (self == other) @@ -569,6 +871,10 @@ def __bool__(self): """ Test whether ``self`` is not zero. + An uninitialized series returns ``True`` as it is considered + as a formal variable, such as a generator of a polynomial + ring. + TESTS:: sage: L.<z> = LazyLaurentSeriesRing(GF(2)) @@ -603,21 +909,57 @@ def __bool__(self): 1 sage: bool(M) True + + Uninitialized series:: + + sage: g = L.undefined(valuation=0) + sage: bool(g) + True + sage: g.define(0) + sage: bool(g) + False + + sage: g = L.undefined(valuation=0) + sage: bool(g) + True + sage: g.define(1 + z) + sage: bool(g) + True + + sage: g = L.undefined(valuation=0) + sage: bool(g) + True + sage: g.define(1 + z*g) + sage: bool(g) + True """ if isinstance(self._coeff_stream, Stream_zero): return False if isinstance(self._coeff_stream, Stream_exact): return True - if self.parent()._sparse: + if isinstance(self._coeff_stream, Stream_uninitialized): + if self._coeff_stream._target is None: + return True + if isinstance(self._coeff_stream._target, Stream_zero): + return False + if isinstance(self._coeff_stream._target, Stream_exact): + return True + if self._coeff_stream._is_sparse: cache = self._coeff_stream._cache if any(cache[a] for a in cache): return True else: if any(a for a in self._coeff_stream._cache): return True - if self[self._coeff_stream._approximate_order]: + + v = self._coeff_stream._approximate_order + if self[v]: return True - raise ValueError("undecidable as lazy Laurent series") + + prec = self.parent().options['halting_precision'] + if prec is None: + raise ValueError("undecidable as lazy Laurent series") + return any(self[i] for i in range(v, v + prec)) def define(self, s): r""" @@ -625,42 +967,44 @@ def define(self, s): INPUT: - - ``s`` -- a Laurent polynomial + - ``s`` -- a lazy series EXAMPLES: We begin by constructing the Catalan numbers:: - sage: L.<z> = LazyLaurentSeriesRing(ZZ) - sage: C = L(None, valuation=0) + sage: L.<z> = LazyPowerSeriesRing(ZZ) + sage: C = L.undefined() sage: C.define(1 + z*C^2) sage: C 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + O(z^7) + sage: binomial(2000, 1000) / C[1000] + 1001 The Catalan numbers but with a valuation 1:: - sage: B = L(None, valuation=1) + sage: B = L.undefined(valuation=1) sage: B.define(z + B^2) sage: B z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) We can define multiple series that are linked:: - sage: s = L(None, valuation=0) - sage: t = L(None, valuation=0) + sage: s = L.undefined() + sage: t = L.undefined() sage: s.define(1 + z*t^3) sage: t.define(1 + z*s^2) - sage: s[:9] + sage: s[0:9] [1, 1, 3, 9, 34, 132, 546, 2327, 10191] - sage: t[:9] + sage: t[0:9] [1, 1, 2, 7, 24, 95, 386, 1641, 7150] A bigger example:: - sage: L.<z> = LazyLaurentSeriesRing(ZZ) - sage: A = L(None, valuation=5) - sage: B = L(None, valuation=0) - sage: C = L(None, valuation=2) + sage: L.<z> = LazyPowerSeriesRing(ZZ) + sage: A = L.undefined(valuation=5) + sage: B = L.undefined() + sage: C = L.undefined(valuation=2) sage: A.define(z^5 + B^2) sage: B.define(z^5 + C^2) sage: C.define(z^2 + C^2 + A^2) @@ -673,17 +1017,17 @@ def define(self, s): Counting binary trees:: - sage: L.<z> = LazyLaurentSeriesRing(QQ) - sage: s = L(None, valuation=1) + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: s = L.undefined(valuation=1) sage: s.define(z + (s^2+s(z^2))/2) - sage: [s[i] for i in range(9)] + sage: s[0:9] [0, 1, 1, 1, 2, 3, 6, 11, 23] The `q`-Catalan numbers:: sage: R.<q> = ZZ[] sage: L.<z> = LazyLaurentSeriesRing(R) - sage: s = L(None, valuation=0) + sage: s = L.undefined(valuation=0) sage: s.define(1+z*s*s(q*z)) sage: s 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 @@ -696,29 +1040,31 @@ def define(self, s): and number of internal nodes:: sage: R.<q> = QQ[] - sage: Q.<z> = LazyLaurentSeriesRing(R) + sage: Q.<z> = LazyPowerSeriesRing(R) sage: leaf = z sage: internal_node = q * z sage: L = Q(constant=1, degree=1) - sage: T = Q(None, valuation=1) + sage: T = Q.undefined(valuation=1) sage: T.define(leaf + internal_node * L(T)) - sage: [T[i] for i in range(6)] + sage: T[0:6] [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] Similarly for Dirichlet series:: sage: L = LazyDirichletSeriesRing(ZZ, "z") sage: g = L(constant=1, valuation=2) - sage: F = L(None); F.define(1 + g*F) - sage: [F[i] for i in range(1, 16)] + sage: F = L.undefined() + sage: F.define(1 + g*F) + sage: F[:16] [1, 1, 1, 2, 1, 3, 1, 4, 2, 3, 1, 8, 1, 3, 3] sage: oeis(_) # optional, internet 0: A002033: Number of perfect partitions of n. 1: A074206: Kalmár's [Kalmar's] problem: number of ordered factorizations of n. ... - sage: F = L(None); F.define(1 + g*F*F) - sage: [F[i] for i in range(1, 16)] + sage: F = L.undefined() + sage: F.define(1 + g*F*F) + sage: F[:16] [1, 1, 1, 3, 1, 5, 1, 10, 3, 5, 1, 24, 1, 5, 5] We can compute the Frobenius character of unlabeled trees:: @@ -728,10 +1074,10 @@ def define(self, s): sage: L = LazySymmetricFunctions(m) sage: E = L(lambda n: s[n], valuation=0) sage: X = L(s[1]) - sage: A = L(None); A.define(X*E(A, check=False)) + sage: A = L.undefined() + sage: A.define(X*E(A, check=False)) sage: A[:6] - [0, - m[1], + [m[1], 2*m[1, 1] + m[2], 9*m[1, 1, 1] + 5*m[2, 1] + 2*m[3], 64*m[1, 1, 1, 1] + 34*m[2, 1, 1] + 18*m[2, 2] + 13*m[3, 1] + 4*m[4], @@ -740,12 +1086,12 @@ def define(self, s): TESTS:: sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: s = L(None, valuation=0) - sage: s.define(1 + z*s^3) - sage: s[:10] + sage: s = L.undefined(valuation=-1) + sage: s.define(z^-1 + z^3*s^3) + sage: s[-1:9] [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] - sage: e = L(None, valuation=0) + sage: e = L.undefined(valuation=0) sage: e.define(1 + z*e) sage: e.define(1 + z*e) Traceback (most recent call last): @@ -756,18 +1102,124 @@ def define(self, s): ... ValueError: series already defined + sage: e = L.undefined(valuation=0) + sage: e.define(1) + sage: e + 1 + + sage: e = L.undefined(valuation=0) + sage: e.define((1 + z).polynomial()) + sage: e + 1 + z + sage: D = LazyDirichletSeriesRing(QQ, "s") sage: L.<z> = LazyLaurentSeriesRing(QQ) sage: e = L(lambda n: 1/factorial(n), 0) - sage: g = D(None, valuation=2) + sage: g = D.undefined(valuation=2) sage: o = D(constant=1, valuation=2) sage: g.define(o * e(g)) sage: g 1/(2^s) + 1/(3^s) + 2/4^s + 1/(5^s) + 3/6^s + 1/(7^s) + 9/2/8^s + O(1/(9^s)) + + For Laurent series there is no minimal valuation, so it has + to be specified:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: L.undefined() + Traceback (most recent call last): + ... + ValueError: the valuation must be specified for undefined series + + For power series and Dirichlet series there is a minimal + valuation, which is why the following work:: + + sage: P.<x> = LazyPowerSeriesRing(QQ) + sage: f = P.undefined() + sage: f.define(1 - ~f*x) + sage: f + 1 - x - x^2 - 2*x^3 - 5*x^4 - 14*x^5 - 42*x^6 + O(x^7) + + sage: D = LazyDirichletSeriesRing(QQ, "s") + sage: g = D([0, 1]) + sage: f = D.undefined() + sage: f.define(1 + ~f*g) + sage: f + 1 + 1/(2^s) - 1/(4^s) + O(1/(8^s)) + + sage: oeis(f[:30]) # optional, internet + 0: A122698: a(1)=a(2)=1 then a(n) = Sum_{d|n, 1<d<n} a(d)*a(n/d). + + Note that we cannot use division in the examples above. + Since we allow division by series with positive valuation, + the valuation of `x / f` might be zero:: + + sage: f = P.undefined() + sage: f.define(1 - x / f) + sage: f[0] + Traceback (most recent call last): + ... + ValueError: inverse does not exist + + Check that reversion is lazy enough:: + + sage: L.<t> = LazyPowerSeriesRing(QQ) + sage: f = L.undefined() + sage: f.define(1+(t*f).revert()) + sage: f + 1 + t - t^2 + 3*t^3 - 13*t^4 + 69*t^5 - 419*t^6 + O(t^7) + + sage: L.<t> = LazyLaurentSeriesRing(QQ) + sage: f = L.undefined(valuation=0) + sage: f.define(1+(t*f).revert()) + sage: f + 1 + t - t^2 + 3*t^3 - 13*t^4 + 69*t^5 - 419*t^6 + O(t^7) + + sage: f = L.undefined(valuation=0) + sage: f.define(1+(t*~f).revert()) + sage: f + 1 + t + t^2 + 2*t^3 + 6*t^4 + 23*t^5 + 104*t^6 + O(t^7) + sage: oeis(f[1:20]) # optional, internet + 0: A030266: Shifts left under COMPOSE transform with itself. + 1: A110447: Permutations containing 3241 patterns only as part of 35241 patterns. + + The following can only work for power series, where we have a + minimal valuation of `0`:: + + sage: L.<t> = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(valuation=0) + sage: f.define(1 - t*~(-f) - (-t*f).revert()) + sage: f + 1 + 2*t + 12*t^3 + 32*t^4 + 368*t^5 + 2192*t^6 + O(t^7) + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LazySymmetricFunctions(s) + sage: f = L.undefined() + sage: f.define(1+(s[1]*f).revert()) + sage: f + s[] + s[1] + (-s[1,1]-s[2]) + + (3*s[1,1,1]+6*s[2,1]+3*s[3]) + + (-13*s[1,1,1,1]-39*s[2,1,1]-26*s[2,2]-39*s[3,1]-13*s[4]) + + (69*s[1,1,1,1,1]+276*s[2,1,1,1]+345*s[2,2,1]+414*s[3,1,1]+345*s[3,2]+276*s[4,1]+69*s[5]) + + (-419*s[1,1,1,1,1,1]-2095*s[2,1,1,1,1]-3771*s[2,2,1,1]-2095*s[2,2,2]-4190*s[3,1,1,1]-6704*s[3,2,1]-2095*s[3,3]-4190*s[4,1,1]-3771*s[4,2]-2095*s[5,1]-419*s[6]) + + O^7 + + sage: (f*s[1]).revert() + 1 - f + O^7 + """ if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") - self._coeff_stream._target = s._coeff_stream + + if not isinstance(s, LazyModuleElement): + s = self.parent()(s) + + coeff_stream = s._coeff_stream + # Special case when it has a trivial definition + if isinstance(coeff_stream, (Stream_zero, Stream_exact)): + self._coeff_stream = coeff_stream + return + + self._coeff_stream._target = coeff_stream # an alias for compatibility with padics set = define @@ -800,7 +1252,7 @@ def _repr_(self): sage: L(lambda x: x if x > 0 else 0, valuation=-10) O(z^-3) - sage: L(None, valuation=0) + sage: L.undefined(valuation=0) Uninitialized Lazy Laurent Series sage: L(0) 0 @@ -847,7 +1299,7 @@ def _latex_(self): sage: latex(L(lambda x: x if x > 0 else 0, valuation=-10)) O(\frac{1}{z^{3}}) - sage: latex(L(None, valuation=0)) + sage: latex(L.undefined(valuation=0)) \text{\texttt{Undef}} sage: latex(L(0)) 0 @@ -958,7 +1410,7 @@ def change_ring(self, ring): A Taylor series example:: - sage: L.<z> = LazyTaylorSeriesRing(ZZ) + sage: L.<z> = LazyPowerSeriesRing(ZZ) sage: s = 2 + z sage: t = s.change_ring(QQ) sage: t^-1 @@ -967,7 +1419,10 @@ def change_ring(self, ring): Lazy Taylor Series Ring in z over Rational Field """ P = self.parent() - Q = type(P)(ring, names=P.variable_names(), sparse=P._sparse) + if P._names is None: + Q = type(P)(ring, sparse=P._sparse) + else: + Q = type(P)(ring, names=P.variable_names(), sparse=P._sparse) return Q.element_class(Q, self._coeff_stream) # === module structure === @@ -1053,13 +1508,13 @@ def _add_(self, other): if not any(initial_coefficients) and not constant: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=degree, order=approximate_order) return P.element_class(P, coeff_stream) return P.element_class(P, Stream_add(self._coeff_stream, - other._coeff_stream)) + other._coeff_stream, + P.is_sparse())) def _sub_(self, other): """ @@ -1131,14 +1586,15 @@ def _sub_(self, other): if not any(initial_coefficients) and not constant: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=degree, order=approximate_order) return P.element_class(P, coeff_stream) if left == right: return P.zero() - return P.element_class(P, Stream_sub(self._coeff_stream, other._coeff_stream)) + return P.element_class(P, Stream_sub(self._coeff_stream, + other._coeff_stream, + P.is_sparse())) def _acted_upon_(self, scalar, self_on_left): r""" @@ -1276,13 +1732,14 @@ def _acted_upon_(self, scalar, self_on_left): c = scalar * coeff_stream._constant initial_coefficients = [scalar * val for val in init_coeffs] return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, order=v, constant=c, degree=coeff_stream._degree)) if self_on_left or R.is_commutative(): - return P.element_class(P, Stream_lmul(coeff_stream, scalar)) - return P.element_class(P, Stream_rmul(coeff_stream, scalar)) + return P.element_class(P, Stream_lmul(coeff_stream, scalar, + P.is_sparse())) + return P.element_class(P, Stream_rmul(coeff_stream, scalar, + P.is_sparse())) def _neg_(self): """ @@ -1338,7 +1795,6 @@ def _neg_(self): initial_coefficients = [-v for v in coeff_stream._initial_coefficients] constant = -coeff_stream._constant coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=coeff_stream._degree, order=coeff_stream.order()) @@ -1346,7 +1802,7 @@ def _neg_(self): # -(-f) = f if isinstance(coeff_stream, Stream_neg): return P.element_class(P, coeff_stream._series) - return P.element_class(P, Stream_neg(coeff_stream)) + return P.element_class(P, Stream_neg(coeff_stream, P.is_sparse())) # === special functions === @@ -1356,33 +1812,14 @@ def exp(self): EXAMPLES:: - sage: L.<z> = LazyLaurentSeriesRing(QQ) - sage: exp(z) - 1 + z + 1/2*z^2 + 1/6*z^3 + 1/24*z^4 + 1/120*z^5 + 1/720*z^6 + O(z^7) - sage: exp(z + z^2) - 1 + z + 3/2*z^2 + 7/6*z^3 + 25/24*z^4 + 27/40*z^5 + 331/720*z^6 + O(z^7) - sage: exp(0) - 1 - sage: exp(1 + z) - Traceback (most recent call last): - ... - ValueError: can only compose with a positive valuation series - - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) - sage: exp(x+y)[4].factor() - (1/24) * (x + y)^4 - sage: exp(x/(1-y)).polynomial(3) - 1/6*x^3 + x^2*y + x*y^2 + 1/2*x^2 + x*y + x + 1 - - TESTS:: - - sage: L.<z> = LazyLaurentSeriesRing(QQ); x = var("x") - sage: exp(z)[0:6] == exp(x).series(x, 6).coefficients(sparse=False) - True + sage: L = LazyDirichletSeriesRing(QQ, "s") + sage: Z = L(constant=1, valuation=2) + sage: exp(Z) + 1 + 1/(2^s) + 1/(3^s) + 3/2/4^s + 1/(5^s) + 2/6^s + 1/(7^s) + O(1/(8^s)) """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: 1/factorial(ZZ(n)), valuation=0) + f = P(coefficients=lambda n: 1/factorial(ZZ(n)), valuation=0) return f(self) def log(self): @@ -1391,28 +1828,14 @@ def log(self): EXAMPLES:: - sage: L.<z> = LazyLaurentSeriesRing(QQ) - sage: log(1/(1-z)) - z + 1/2*z^2 + 1/3*z^3 + 1/4*z^4 + 1/5*z^5 + 1/6*z^6 + 1/7*z^7 + O(z^8) - - sage: L.<x, y> = LazyTaylorSeriesRing(QQ) - sage: log((1 + x/(1-y))).polynomial(3) - 1/3*x^3 - x^2*y + x*y^2 - 1/2*x^2 + x*y + x - - TESTS:: - - sage: L.<z> = LazyLaurentSeriesRing(QQ); x = var("x") - sage: log(1+z)[0:6] == log(1+x).series(x, 6).coefficients(sparse=False) - True - - sage: log(z) - Traceback (most recent call last): - ... - ValueError: can only compose with a positive valuation series + sage: L = LazyDirichletSeriesRing(QQ, "s") + sage: Z = L(constant=1) + sage: log(Z) + 1/(2^s) + 1/(3^s) + 1/2/4^s + 1/(5^s) + 1/(7^s) + O(1/(8^s)) """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: ((-1) ** (n + 1))/ZZ(n), valuation=1) + f = P(coefficients=lambda n: ((-1) ** (n + 1))/ZZ(n), valuation=1) return f(self-1) # trigonometric functions @@ -1432,7 +1855,7 @@ def sin(self): ... ValueError: can only compose with a positive valuation series - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: sin(x/(1-y)).polynomial(3) -1/6*x^3 + x*y^2 + x*y + x @@ -1444,8 +1867,8 @@ def sin(self): """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: (n % 2)/factorial(ZZ(n)) if n % 4 == 1 else -(n % 2)/factorial(ZZ(n)), - valuation=1) + c = lambda n: (n % 2)/factorial(ZZ(n)) if n % 4 == 1 else -(n % 2)/factorial(ZZ(n)) + f = P(coefficients=c, valuation=1) return f(self) def cos(self): @@ -1458,7 +1881,7 @@ def cos(self): sage: cos(z) 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: cos(x/(1-y)).polynomial(4) 1/24*x^4 - 3/2*x^2*y^2 - x^2*y - 1/2*x^2 + 1 @@ -1470,8 +1893,8 @@ def cos(self): """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: 1/factorial(ZZ(n)) if n % 4 == 0 else (n % 2 - 1)/factorial(ZZ(n)), - valuation=0) + c = lambda n: 1/factorial(ZZ(n)) if n % 4 == 0 else (n % 2 - 1)/factorial(ZZ(n)) + f = P(coefficients=c, valuation=0) return f(self) def tan(self): @@ -1484,7 +1907,7 @@ def tan(self): sage: tan(z) z + 1/3*z^3 + 2/15*z^5 + 17/315*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: tan(x/(1-y)).polynomial(5) 2/15*x^5 + 2*x^3*y^2 + x*y^4 + x^3*y + x*y^3 + 1/3*x^3 + x*y^2 + x*y + x @@ -1550,7 +1973,7 @@ def sec(self): sage: sec(z) 1 + 1/2*z^2 + 5/24*z^4 + 61/720*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: sec(x/(1-y)).polynomial(4) 5/24*x^4 + 3/2*x^2*y^2 + x^2*y + 1/2*x^2 + 1 @@ -1574,7 +1997,7 @@ def arcsin(self): sage: arcsin(z) z + 1/6*z^3 + 3/40*z^5 + 5/112*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: asin(x/(1-y)) x + x*y + (1/6*x^3+x*y^2) + (1/2*x^3*y+x*y^3) + (3/40*x^5+x^3*y^2+x*y^4) + (3/8*x^5*y+5/3*x^3*y^3+x*y^5) @@ -1612,7 +2035,7 @@ def arccos(self): sage: arccos(z/(1-z)) 1/2*pi - z - z^2 - 7/6*z^3 - 3/2*z^4 - 83/40*z^5 - 73/24*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(SR) + sage: L.<x,y> = LazyPowerSeriesRing(SR) sage: arccos(x/(1-y)) 1/2*pi + (-x) + (-x*y) + ((-1/6)*x^3-x*y^2) + ((-1/2)*x^3*y-x*y^3) + ((-3/40)*x^5-x^3*y^2-x*y^4) + ((-3/8)*x^5*y+(-5/3)*x^3*y^3-x*y^5) + O(x,y)^7 @@ -1636,7 +2059,7 @@ def arctan(self): sage: arctan(z) z - 1/3*z^3 + 1/5*z^5 - 1/7*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: atan(x/(1-y)) x + x*y + (-1/3*x^3+x*y^2) + (-x^3*y+x*y^3) + (1/5*x^5-2*x^3*y^2+x*y^4) + (x^5*y-10/3*x^3*y^3+x*y^5) + (-1/7*x^7+3*x^5*y^2-5*x^3*y^4+x*y^6) + O(x,y)^8 @@ -1675,7 +2098,7 @@ def arccot(self): sage: arccot(z/(1-z)) 1/2*pi - z - z^2 - 2/3*z^3 + 4/5*z^5 + 4/3*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(SR) + sage: L.<x,y> = LazyPowerSeriesRing(SR) sage: acot(x/(1-y)) 1/2*pi + (-x) + (-x*y) + (1/3*x^3-x*y^2) + (x^3*y-x*y^3) + ((-1/5)*x^5+2*x^3*y^2-x*y^4) + (-x^5*y+10/3*x^3*y^3-x*y^5) + O(x,y)^7 @@ -1701,7 +2124,7 @@ def sinh(self): sage: sinh(z) z + 1/6*z^3 + 1/120*z^5 + 1/5040*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: sinh(x/(1-y)) x + x*y + (1/6*x^3+x*y^2) + (1/2*x^3*y+x*y^3) + (1/120*x^5+x^3*y^2+x*y^4) + (1/24*x^5*y+5/3*x^3*y^3+x*y^5) @@ -1715,7 +2138,7 @@ def sinh(self): """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: 1/factorial(ZZ(n)) if n % 2 else ZZ.zero(), + f = P(coefficients=lambda n: 1/factorial(ZZ(n)) if n % 2 else ZZ.zero(), valuation=1) return f(self) @@ -1729,7 +2152,7 @@ def cosh(self): sage: cosh(z) 1 + 1/2*z^2 + 1/24*z^4 + 1/720*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: cosh(x/(1-y)) 1 + 1/2*x^2 + x^2*y + (1/24*x^4+3/2*x^2*y^2) + (1/6*x^4*y+2*x^2*y^3) + (1/720*x^6+5/12*x^4*y^2+5/2*x^2*y^4) + O(x,y)^7 @@ -1742,7 +2165,7 @@ def cosh(self): """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: ZZ.zero() if n % 2 else 1/factorial(ZZ(n)), + f = P(coefficients=lambda n: ZZ.zero() if n % 2 else 1/factorial(ZZ(n)), valuation=0) return f(self) @@ -1756,7 +2179,7 @@ def tanh(self): sage: tanh(z) z - 1/3*z^3 + 2/15*z^5 - 17/315*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: tanh(x/(1-y)) x + x*y + (-1/3*x^3+x*y^2) + (-x^3*y+x*y^3) + (2/15*x^5-2*x^3*y^2+x*y^4) + (2/3*x^5*y-10/3*x^3*y^3+x*y^5) + (-17/315*x^7+2*x^5*y^2-5*x^3*y^4+x*y^6) + O(x,y)^8 @@ -1775,7 +2198,7 @@ def f(n): n = ZZ(n) if n % 2: h = 4 ** ((n + 1) // 2) - return bernoulli(n + 1) * h * (h -1) / factorial(n + 1) + return bernoulli(n + 1) * h * (h - 1) / factorial(n + 1) return ZZ.zero() return P(f, valuation=1)(self) @@ -1819,7 +2242,7 @@ def sech(self): sage: sech(z) 1 - 1/2*z^2 + 5/24*z^4 - 61/720*z^6 + O(z^7) - sage: L.<x, y> = LazyTaylorSeriesRing(QQ) + sage: L.<x, y> = LazyPowerSeriesRing(QQ) sage: sech(x/(1-y)) 1 + (-1/2*x^2) + (-x^2*y) + (5/24*x^4-3/2*x^2*y^2) + (5/6*x^4*y-2*x^2*y^3) + (-61/720*x^6+25/12*x^4*y^2-5/2*x^2*y^4) + O(x,y)^7 @@ -1889,7 +2312,7 @@ def arcsinh(self): sage: arcsinh(z) z - 1/6*z^3 + 3/40*z^5 - 5/112*z^7 + O(z^8) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: asinh(x/(1-y)) x + x*y + (-1/6*x^3+x*y^2) + (-1/2*x^3*y+x*y^3) + (3/40*x^5-x^3*y^2+x*y^4) + (3/8*x^5*y-5/3*x^3*y^3+x*y^5) + (-5/112*x^7+9/8*x^5*y^2-5/2*x^3*y^4+x*y^6) + O(x,y)^8 @@ -1927,7 +2350,7 @@ def arctanh(self): sage: arctanh(z) z + 1/3*z^3 + 1/5*z^5 + 1/7*z^7 + O(z^8) - sage: L.<x, y> = LazyTaylorSeriesRing(QQ) + sage: L.<x, y> = LazyPowerSeriesRing(QQ) sage: atanh(x/(1-y)) x + x*y + (1/3*x^3+x*y^2) + (x^3*y+x*y^3) + (1/5*x^5+2*x^3*y^2+x*y^4) + (x^5*y+10/3*x^3*y^3+x*y^5) + (1/7*x^7+3*x^5*y^2+5*x^3*y^4+x*y^6) + O(x,y)^8 @@ -1941,7 +2364,7 @@ def arctanh(self): """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - f = P(lambda n: 1/ZZ(n) if n % 2 else ZZ.zero(), valuation=1) + f = P(coefficients=lambda n: 1/ZZ(n) if n % 2 else ZZ.zero(), valuation=1) return f(self) def hypergeometric(self, a, b): @@ -1962,7 +2385,7 @@ def hypergeometric(self, a, b): sage: z.hypergeometric([], []) - exp(z) O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: (x+y).hypergeometric([1, 1], [1]).polynomial(4) x^4 + 4*x^3*y + 6*x^2*y^2 + 4*x*y^3 + y^4 + x^3 + 3*x^2*y + 3*x*y^2 + y^3 + x^2 + 2*x*y + y^2 + x + y + 1 @@ -1983,29 +2406,87 @@ def coeff(n, c): for term in range(len(c)): num *= rising_factorial(c[term], n) return num - f = P(lambda n: coeff(n, a) / (coeff(n, b) * factorial(ZZ(n))), + f = P(coefficients=lambda n: coeff(n, a) / (coeff(n, b) * factorial(ZZ(n))), valuation=0) return f(self) - # === powers === + # === named special functions === - def __pow__(self, n): + def q_pochhammer(self, q=None): r""" - Return the ``n``-th power of the series. + Return the infinite ``q``-Pochhammer symbol `(a; q)_{\infty}`, + where `a` is ``self``. + + This is also one version of the quantum dilogarithm or + the `q`-Exponential function. + + .. SEEALSO:: + + :meth:`sage.rings.lazy_series_ring.LazyLaurentSeriesRing.q_pochhammer` INPUT: - - ``n`` -- the power to which to raise the series; this may be a - rational number, an element of the base ring, or an other series + - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` EXAMPLES:: - sage: D = LazyDirichletSeriesRing(QQ, 's') - sage: Z = D(constant=1) - sage: Z^2 - 1 + 2/2^s + 2/3^s + 3/4^s + 2/5^s + 4/6^s + 2/7^s + O(1/(8^s)) - sage: f = Z^(1/3) - sage: f + sage: q = ZZ['q'].fraction_field().gen() + sage: L.<z> = LazyLaurentSeriesRing(q.parent()) + sage: qp = L.q_pochhammer(q) + sage: (z + z^2).q_pochhammer(q) - qp(z + z^2) + O(z^7) + """ + from .lazy_series_ring import LazyLaurentSeriesRing + P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) + f = P.q_pochhammer(q) + return f(self) + + def euler(self): + r""" + Return the Euler function evaluated at ``self``. + + The *Euler function* is defined as + + .. MATH:: + + \phi(z) = (z; z)_{\infty} + = \sum_{n=0}^{\infty} (-1)^n q^{(3n^2-n)/2}. + + .. SEEALSO:: + + :meth:`sage.rings.lazy_series_ring.LazyLaurentSeriesRing.euler` + + EXAMPLES:: + + sage: L.<q> = LazyLaurentSeriesRing(ZZ) + sage: phi = L.euler() + sage: (q + q^2).euler() - phi(q + q^2) + O(q^7) + """ + from .lazy_series_ring import LazyLaurentSeriesRing + P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) + phi = P.euler() + return phi(self) + + # === powers === + + def __pow__(self, n): + r""" + Return the ``n``-th power of the series. + + INPUT: + + - ``n`` -- the power to which to raise the series; this may be a + rational number, an element of the base ring, or an other series + + EXAMPLES:: + + sage: D = LazyDirichletSeriesRing(QQ, 's') + sage: Z = D(constant=1) + sage: Z^2 + 1 + 2/2^s + 2/3^s + 3/4^s + 2/5^s + 4/6^s + 2/7^s + O(1/(8^s)) + sage: f = Z^(1/3) + sage: f 1 + 1/3/2^s + 1/3/3^s + 2/9/4^s + 1/3/5^s + 1/9/6^s + 1/3/7^s + O(1/(8^s)) sage: f^2 1 + 2/3/2^s + 2/3/3^s + 5/9/4^s + 2/3/5^s + 4/9/6^s + 2/3/7^s + O(1/(8^s)) @@ -2037,10 +2518,10 @@ def __pow__(self, n): P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) if n in QQ or n in self.base_ring(): - f = P(lambda k: prod(n - i for i in range(k)) / ZZ(k).factorial(), valuation=0) + f = P(coefficients=lambda k: prod(n - i for i in range(k)) / ZZ(k).factorial(), valuation=0) return f(self - 1) - exp = P(lambda k: 1 / ZZ(k).factorial(), valuation=0) + exp = P(coefficients=lambda k: 1 / ZZ(k).factorial(), valuation=0) return exp(self.log() * n) def sqrt(self): @@ -2053,7 +2534,7 @@ def sqrt(self): sage: sqrt(1+z) 1 + 1/2*z - 1/8*z^2 + 1/16*z^3 - 5/128*z^4 + 7/256*z^5 - 21/1024*z^6 + O(z^7) - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: sqrt(1+x/(1-y)) 1 + 1/2*x + (-1/8*x^2+1/2*x*y) + (1/16*x^3-1/4*x^2*y+1/2*x*y^2) + (-5/128*x^4+3/16*x^3*y-3/8*x^2*y^2+1/2*x*y^3) @@ -2186,6 +2667,14 @@ def _mul_(self, other): sage: t1 = t.approximate_series(44) sage: s1 * t1 - (s * t).approximate_series(42) O(z^42) + + Check products with exact series:: + + sage: L([1], constant=3)^2 + 1 + 6*z + 15*z^2 + 24*z^3 + 33*z^4 + 42*z^5 + 51*z^6 + O(z^7) + + sage: (1+z) * L([1,0,1], constant=1) + 1 + z + z^2 + 2*z^3 + 2*z^4 + 2*z^5 + O(z^6) """ P = self.parent() left = self._coeff_stream @@ -2194,15 +2683,21 @@ def _mul_(self, other): # Check some trivial products if isinstance(left, Stream_zero) or isinstance(right, Stream_zero): return P.zero() - if isinstance(left, Stream_exact) and left._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) and left.order() == 0: + if (isinstance(left, Stream_exact) + and left._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) + and left.order() == 0 + and not left._constant): return other # self == 1 - if isinstance(right, Stream_exact) and right._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) and right.order() == 0: + if (isinstance(right, Stream_exact) + and right._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) + and right.order() == 0 + and not right._constant): return self # right == 1 - # The product is exact if and only if both factors are exact # and one of the factors has eventually 0 coefficients: # (p + a x^d/(1-x))(q + b x^e/(1-x)) # = p q + (a x^d q + b x^e p)/(1-x) + a b x^(d+e)/(1-x)^2 + # TODO: this is not true in characteristic 2 if (isinstance(left, Stream_exact) and isinstance(right, Stream_exact) and not (left._constant and right._constant)): @@ -2220,7 +2715,8 @@ def _mul_(self, other): # coefficients of q. if right._constant: d = right._degree - c = left._constant # this is zero + c = left._constant # this is zero + initial_coefficients.extend([c]*(d - rv - len(ir))) # left._constant must be 0 and thus len(il) >= 1 for k in range(len(il)-1): c += il[k] * right._constant @@ -2228,21 +2724,21 @@ def _mul_(self, other): c += il[-1] * right._constant elif left._constant: d = left._degree - c = right._constant # this is zero + c = right._constant # this is zero + initial_coefficients.extend([c]*(d - lv - len(il))) # left._constant must be 0 and thus len(il) >= 1 for k in range(len(ir)-1): c += left._constant * ir[k] initial_coefficients[d - lv + k] += c c += left._constant * ir[-1] else: - c = left._constant # this is zero + c = left._constant # this is zero coeff_stream = Stream_exact(initial_coefficients, - P._sparse, order=lv + rv, constant=c) return P.element_class(P, coeff_stream) - return P.element_class(P, Stream_cauchy_mul(left, right)) + return P.element_class(P, Stream_cauchy_mul(left, right, P.is_sparse())) def __pow__(self, n): r""" @@ -2318,7 +2814,6 @@ def __pow__(self, n): deg = ret.degree() + 1 initial_coefficients = [ret[i] for i in range(val, deg)] return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, constant=cs._constant, degree=deg, order=val)) @@ -2357,17 +2852,38 @@ def __invert__(self): sage: ~z z^-1 + We can also compute the multiplicative inverse of a symmetric + function:: + + sage: h = SymmetricFunctions(QQ).h() + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(p) + sage: E = L(lambda n: h[n]) + sage: (~E)[:4] + [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]] + + sage: (E * ~E)[:6] + [p[], 0, 0, 0, 0, 0] + TESTS:: sage: L.<x> = LazyLaurentSeriesRing(QQ) sage: g = L([2], valuation=-1, constant=1); g 2*x^-1 + 1 + x + x^2 + O(x^3) - sage: g*g^-1 + sage: g * g^-1 1 + O(x^7) + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: ~(x + x^2) + Traceback (most recent call last): + ... + ZeroDivisionError: cannot divide by a series of positive valuation """ P = self.parent() coeff_stream = self._coeff_stream + if P._minimal_valuation is not None and coeff_stream._approximate_order > 0: + raise ZeroDivisionError("cannot divide by a series of positive valuation") + # the inverse is exact if and only if coeff_stream corresponds to one of # cx^d/(1-x) ... (c, ...) # cx^d ... (c, 0, ...) @@ -2379,7 +2895,6 @@ def __invert__(self): v = -coeff_stream.order() c = P._internal_poly_ring.base_ring().zero() coeff_stream = Stream_exact((i, -i), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) @@ -2388,17 +2903,15 @@ def __invert__(self): v = -coeff_stream.order() c = P._internal_poly_ring.base_ring().zero() coeff_stream = Stream_exact((i,), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) if (len(initial_coefficients) == 2 and not (initial_coefficients[0] + initial_coefficients[1]) - and not coeff_stream._constant): + and not coeff_stream._constant): v = -coeff_stream.order() c = ~initial_coefficients[0] coeff_stream = Stream_exact((), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) @@ -2406,7 +2919,10 @@ def __invert__(self): # (f^-1)^-1 = f if isinstance(coeff_stream, Stream_cauchy_invert): return P.element_class(P, coeff_stream._series) - return P.element_class(P, Stream_cauchy_invert(coeff_stream)) + + coeff_stream_inverse = Stream_cauchy_invert(coeff_stream, + approximate_order=P._minimal_valuation) + return P.element_class(P, coeff_stream_inverse) def _div_(self, other): r""" @@ -2465,25 +2981,74 @@ def _div_(self, other): Examples for multivariate Taylor series:: - sage: L.<x, y> = LazyTaylorSeriesRing(QQ) + sage: L.<x, y> = LazyPowerSeriesRing(QQ) sage: 1 / (1 - y) 1 + y + y^2 + y^3 + y^4 + y^5 + y^6 + O(x,y)^7 sage: (x + y) / (1 - y) (x+y) + (x*y+y^2) + (x*y^2+y^3) + (x*y^3+y^4) + (x*y^4+y^5) + (x*y^5+y^6) + (x*y^6+y^7) + O(x,y)^8 + TESTS:: + + sage: L.<t> = LazyPowerSeriesRing(QQ) + sage: t / L(1) + t + + sage: t^3 * (1+2*t+3*t^2+4*t^3) / (t-t^2) + t^2 + 3*t^3 + 6*t^4 + 10*t^5 + 10*t^6 + 10*t^7 + O(t^8) + + sage: t^3 * ((1+2*t+3*t^2+4*t^3) / (t-t^2)) + t^2 + 3*t^3 + 6*t^4 + 10*t^5 + 10*t^6 + 10*t^7 + O(t^8) + + sage: L(lambda n: n) / (t + t^2) + 1 + t + 2*t^2 + 2*t^3 + 3*t^4 + 3*t^5 + O(t^6) + + Check that division by one does nothing, and division by + itself gives one:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: S = LazySymmetricFunctions(s) + sage: f = S(lambda n: s(Partitions(n).random_element())) + sage: f / S.one() is f + True + + sage: f / f + s[] + """ if isinstance(other._coeff_stream, Stream_zero): raise ZeroDivisionError("cannot divide by 0") P = self.parent() left = self._coeff_stream + # self == 0 if isinstance(left, Stream_zero): return P.zero() right = other._coeff_stream + + # right == 1 + if (isinstance(right, Stream_exact) + and right._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) + and right.order() == 0 + and not right._constant): + return self + + # self is right + if left is right: + return P.one() + + if (P._minimal_valuation is not None + and left._true_order + and left._approximate_order < right._approximate_order): + F = P.fraction_field() + num = F.element_class(F, left) + den = F.element_class(F, right) + return num / den + R = P._internal_poly_ring if (isinstance(left, Stream_exact) and isinstance(right, Stream_exact) + and hasattr(R.base_ring(), "fraction_field") and hasattr(R, "_gcd_univariate_polynomial")): z = R.gen() num = left._polynomial_part(R) * (1-z) + left._constant * z**left._degree @@ -2501,17 +3066,19 @@ def _div_(self, other): den = den // g exponents = den.exponents() if len(exponents) == 1: + # dividing by z^k d = den[exponents[0]] - initial_coefficients = [c / d for c in num] - order = num.valuation() - den.valuation() + v = num.valuation() + initial_coefficients = [num[i] / d + for i in range(v, num.degree() + 1)] + order = v - den.valuation() return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, order=order, constant=0)) - if (len(exponents) == 2 and exponents[0] + 1 == exponents[1] and den[exponents[0]] == -den[exponents[1]]): + # dividing by z^k (1-z) quo, rem = num.quo_rem(den) # rem is a unit, i.e., in the Laurent case c*z^v v_rem = rem.exponents()[0] @@ -2528,18 +3095,167 @@ def _div_(self, other): order = quo.valuation() else: order = 0 - return P.element_class(P, Stream_exact(list(quo), - P._sparse, + initial_coefficients = [quo[i] for i in range(order, quo.degree() + 1)] + return P.element_class(P, Stream_exact(initial_coefficients, order=order, degree=v, constant=constant)) return P.element_class(P, Stream_exact([], - P._sparse, order=v, degree=v, constant=constant)) - return P.element_class(P, Stream_cauchy_mul(left, Stream_cauchy_invert(right))) + # we cannot pass the approximate order here, even when + # P._minimal_valuation is zero, because we allow division by + # series of positive valuation + right_inverse = Stream_cauchy_invert(right) + return P.element_class(P, Stream_cauchy_mul(left, right_inverse, P.is_sparse())) + + + def _floordiv_(self, other): + r""" + Return ``self`` floor divided by ``other``. + + INPUT: + + - ``other`` -- nonzero series + + EXAMPLES:: + + sage: L.<x> = LazyLaurentSeriesRing(QQ) + sage: g = (x + 2*x^2) / (1 - x - x^2) + sage: x // g + 1 - 3*x + 5*x^2 - 10*x^3 + 20*x^4 - 40*x^5 + 80*x^6 + O(x^7) + sage: 1 // g + x^-1 - 3 + 5*x - 10*x^2 + 20*x^3 - 40*x^4 + 80*x^5 + O(x^6) + sage: x^-3 // g + x^-4 - 3*x^-3 + 5*x^-2 - 10*x^-1 + 20 - 40*x + 80*x^2 + O(x^3) + sage: f = (x + x^2) / (1 - x) + sage: f // g + 1 - x + x^2 - 4*x^3 + 6*x^4 - 14*x^5 + 26*x^6 + O(x^7) + sage: g // f + 1 + x + 3*x^3 + x^4 + 6*x^5 + 5*x^6 + O(x^7) + """ + if isinstance(other._coeff_stream, Stream_zero): + raise ZeroDivisionError("cannot divide by 0") + P = self.parent() + if P not in IntegralDomains(): + raise TypeError("must be an integral domain") + return P(self / other) + + # === fast special functions === + + def exp(self): + r""" + Return the exponential series of ``self``. + + We use the identity + + .. MATH:: + + \exp(s) = 1 + \int s' \exp(s). + + EXAMPLES:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: exp(z) + 1 + z + 1/2*z^2 + 1/6*z^3 + 1/24*z^4 + 1/120*z^5 + 1/720*z^6 + O(z^7) + sage: exp(z + z^2) + 1 + z + 3/2*z^2 + 7/6*z^3 + 25/24*z^4 + 27/40*z^5 + 331/720*z^6 + O(z^7) + sage: exp(0) + 1 + sage: exp(1 + z) + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series + + sage: L.<x,y> = LazyPowerSeriesRing(QQ) + sage: exp(x+y)[4].factor() + (1/24) * (x + y)^4 + sage: exp(x/(1-y)).polynomial(3) + 1/6*x^3 + x^2*y + x*y^2 + 1/2*x^2 + x*y + x + 1 + + TESTS:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ); x = var("x") + sage: exp(z)[0:6] == exp(x).series(x, 6).coefficients(sparse=False) + True + + Check the exponential when the base ring is a lazy ring:: + + sage: L.<t> = LazyPowerSeriesRing(QQ) + sage: M.<x> = LazyPowerSeriesRing(L) + sage: exp(x) + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) + """ + P = self.parent() + R = self.base_ring() + coeff_stream = self._coeff_stream + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! + if any(coeff_stream[i] for i in range(coeff_stream._approximate_order, 1)): + raise ValueError("can only compose with a positive valuation series") + # WARNING: d_self need not be a proper element of P, e.g. for + # multivariate power series + # We make the streams dense, because all coefficients have to be computed anyway + d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], + False, 0) + f = P.undefined(valuation=0) + d_self_f = Stream_cauchy_mul(d_self, f._coeff_stream, False) + int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), + False, 0) + f._coeff_stream._target = int_d_self_f + return f + + def log(self): + r""" + Return the series for the natural logarithm of ``self``. + + We use the identity + + .. MATH:: + + \log(s) = \int s' / s. + + EXAMPLES:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: log(1/(1-z)) + z + 1/2*z^2 + 1/3*z^3 + 1/4*z^4 + 1/5*z^5 + 1/6*z^6 + 1/7*z^7 + O(z^8) + + sage: L.<x, y> = LazyPowerSeriesRing(QQ) + sage: log((1 + x/(1-y))).polynomial(3) + 1/3*x^3 - x^2*y + x*y^2 - 1/2*x^2 + x*y + x + + TESTS:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ); x = var("x") + sage: log(1+z)[0:6] == log(1+x).series(x, 6).coefficients(sparse=False) + True + + sage: log(z) + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series + """ + P = self.parent() + R = self.base_ring() + coeff_stream = self._coeff_stream + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! + if (any(coeff_stream[i] for i in range(coeff_stream._approximate_order, 0)) + or coeff_stream[0] != R.one()): + raise ValueError("can only compose with a positive valuation series") + # WARNING: d_self need not be a proper element of P, e.g. for + # multivariate power series + d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], + P.is_sparse(), 0) + d_self_quo_self = Stream_cauchy_mul(d_self, + Stream_cauchy_invert(coeff_stream), + P.is_sparse()) + int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), + P.is_sparse(), 1) + return P.element_class(P, int_d_self_quo_self) class LazyLaurentSeries(LazyCauchyProductSeries): @@ -2613,12 +3329,63 @@ class LazyLaurentSeries(LazyCauchyProductSeries): sage: f = 1 / (1 - z - z^2) sage: TestSuite(f).run() """ + def is_unit(self): + """ + Return whether this element is a unit in the ring. + + EXAMPLES:: + + sage: L.<z> = LazyLaurentSeriesRing(ZZ) + sage: (2*z).is_unit() + False + + sage: (1 + 2*z).is_unit() + True + + sage: (1 + 2*z^-1).is_unit() + False + + sage: (z^3 + 4 - z^-2).is_unit() + True + """ + if self.is_zero(): # now 0 != 1 + return False + a = self[self.valuation()] + return a.is_unit() + + def _im_gens_(self, codomain, im_gens, base_map=None): + """ + Return the image of ``self`` under the map that sends the + generators of the parent of ``self`` to the elements of the + tuple ``im_gens``. + + EXAMPLES:: + + sage: Z.<x> = ZZ[] + sage: K.<i> = NumberField(x^2 + 1) + sage: R.<t> = LazyLaurentSeriesRing(K) + sage: f = R(lambda n: i^n, valuation=-2) + sage: f + -t^-2 - i*t^-1 + 1 + i*t - t^2 - i*t^3 + t^4 + O(t^5) + sage: f._im_gens_(R, [t + t^2]) + -t^-2 + (-i + 2)*t^-1 + (i - 2) + 4*t + (2*i - 6)*t^2 + + (-2*i + 4)*t^3 + (-2*i - 7)*t^4 + O(t^5) + + sage: cc = K.hom([-i]) + sage: f._im_gens_(R, [t + t^2], base_map=cc) + -t^-2 + (i + 2)*t^-1 + (-i - 2) + 4*t + (-2*i - 6)*t^2 + + (2*i + 4)*t^3 + (2*i - 7)*t^4 + O(t^5) + """ + if base_map is None: + return codomain(self(im_gens[0])) + + return codomain(self.map_coefficients(base_map)(im_gens[0])) def __call__(self, g, *, check=True): r""" Return the composition of ``self`` with ``g``. - Given two Laurent Series `f` and `g` over the same base ring, the + Given two Laurent series `f` and `g` over the same base ring, the composition `(f \circ g)(z) = f(g(z))` is defined if and only if: - `g = 0` and `val(f) >= 0`, @@ -2819,7 +3586,7 @@ def __call__(self, g, *, check=True): ZeroDivisionError: the valuation of the series must be nonnegative `g \neq 0` and `val(g) \leq 0` and `f` has infinitely many - non-zero coefficients`:: + non-zero coefficients:: sage: g = z^-1 + z^-2 sage: g.valuation() <= 0 @@ -2888,8 +3655,17 @@ def __call__(self, g, *, check=True): 3 sage: parent(three) Univariate Polynomial Ring in x over Rational Field + + Consistency check when `g` is an uninitialized series between a + polynomial `f` as both a polynomial and a lazy series:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + sage: g = L.undefined(valuation=0) + sage: f(g) == f.polynomial()(g) + True """ - # f = self and compute f(g) + # Find a good parent for the result from sage.structure.element import get_coercion_model cm = get_coercion_model() P = cm.common_parent(self.base_ring(), parent(g)) @@ -2936,7 +3712,6 @@ def __call__(self, g, *, check=True): deg = ret.degree() + 1 initial_coefficients = [ret[i] for i in range(val, deg)] coeff_stream = Stream_exact(initial_coefficients, - self._coeff_stream._is_sparse, constant=P.base_ring().zero(), degree=deg, order=val) return P.element_class(P, coeff_stream) @@ -3000,11 +3775,14 @@ def __call__(self, g, *, check=True): def coefficient(n): return sum(self[i] * (g**i)[n] for i in range(n+1)) + R = P._internal_poly_ring.base_ring() - coeff_stream = Stream_function(coefficient, R, P._sparse, 1) + coeff_stream = Stream_function(coefficient, P._sparse, 1) return P.element_class(P, coeff_stream) - coeff_stream = Stream_cauchy_compose(self._coeff_stream, g._coeff_stream) + coeff_stream = Stream_cauchy_compose(self._coeff_stream, + g._coeff_stream, + P.is_sparse()) return P.element_class(P, coeff_stream) compose = __call__ @@ -3013,45 +3791,71 @@ def revert(self): r""" Return the compositional inverse of ``self``. - Given a Laurent Series `f`. the compositional inverse is a - Laurent Series `g` over the same base ring, such that + Given a Laurent series `f`, the compositional inverse is a + Laurent series `g` over the same base ring, such that `(f \circ g)(z) = f(g(z)) = z`. The compositional inverse exists if and only if: - `val(f) = 1`, or - - `f = a + b z` with `a b \neq 0`, or + - `f = a + b z` with `a, b \neq 0`, or - `f = a/z` with `a \neq 0` EXAMPLES:: - sage: L.<z> = LazyLaurentSeriesRing(ZZ) - sage: z.revert() - z + O(z^8) - sage: (1/z).revert() - z^-1 + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: (2*z).revert() + 1/2*z + sage: (2/z).revert() + 2*z^-1 sage: (z-z^2).revert() z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) + sage: s = L(degree=1, constant=-1) + sage: s.revert() + -z - z^2 - z^3 + O(z^4) + + sage: s = L(degree=1, constant=1) + sage: s.revert() + z - z^2 + z^3 - z^4 + z^5 - z^6 + z^7 + O(z^8) + TESTS:: sage: L.<z> = LazyLaurentSeriesRing(QQ) - sage: s = L(lambda n: 1 if n == 1 else 0, valuation=1); s - z + O(z^8) + sage: s = L(lambda n: 2 if n == 1 else 0, valuation=1); s + 2*z + O(z^8) sage: s.revert() - z + O(z^8) + 1/2*z + O(z^8) sage: (2+3*z).revert() -2/3 + 1/3*z sage: s = L(lambda n: 2 if n == 0 else 3 if n == 1 else 0, valuation=0); s 2 + 3*z + O(z^7) + sage: f = s.revert() + sage: f[1] + Traceback (most recent call last): + ... + ValueError: inverse does not exist + + sage: s = L(lambda n: 1, valuation=-2); s + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + O(z^5) + sage: f = s.revert() + sage: f[1] + Traceback (most recent call last): + ... + ValueError: inverse does not exist + + sage: R.<q,t> = QQ[] + sage: L.<z> = LazyLaurentSeriesRing(R.fraction_field()) + sage: s = L([q], valuation=0, constant=t); s + q + t*z + t*z^2 + t*z^3 + O(z^4) sage: s.revert() Traceback (most recent call last): ... - ValueError: cannot determine whether the compositional inverse exists + ValueError: compositional inverse does not exist We look at some cases where the compositional inverse does not exist: @@ -3066,7 +3870,7 @@ def revert(self): ... ValueError: compositional inverse does not exist - `val(f) ! = 1` and `f(0) * f(1) = 0`:: + `val(f) != 1` and `f(0) * f(1) = 0`:: sage: (z^2).revert() Traceback (most recent call last): @@ -3077,65 +3881,210 @@ def revert(self): Traceback (most recent call last): ... ValueError: compositional inverse does not exist + + Reversion of exact series:: + + sage: f = L([2], valuation=-1, constant=2) + sage: f.revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + sage: f = L([1, 2], valuation=0, constant=1) + sage: f.revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + sage: f = L([-1, -1], valuation=1, constant=-1) + sage: f.revert() + -z - z^2 - z^3 + O(z^4) + + sage: f = L([-1, 0, -1], valuation=1, constant=-1) + sage: f.revert() + (1/(-1))*z + z^3 - z^4 - 2*z^5 + 6*z^6 + z^7 + O(z^8) + + sage: f = L([-1], valuation=1, degree=3, constant=-1) + sage: f.revert() + (1/(-1))*z + z^3 - z^4 - 2*z^5 + 6*z^6 + z^7 + O(z^8) """ P = self.parent() - if self.valuation() == 1: - z = P.gen() - g = P(None, valuation=1) - g.define(z/((self/z)(g))) - return g - if self.valuation() not in [-1, 0]: - raise ValueError("compositional inverse does not exist") coeff_stream = self._coeff_stream - if isinstance(coeff_stream, Stream_exact): - if (coeff_stream.order() == 0 - and coeff_stream._degree == 2): - a = coeff_stream[0] - b = coeff_stream[1] - coeff_stream = Stream_exact((-a/b, 1/b), - coeff_stream._is_sparse, - order=0) - return P.element_class(P, coeff_stream) - if (coeff_stream.order() == -1 - and coeff_stream._degree == 0): - return self + if isinstance(coeff_stream, Stream_zero): raise ValueError("compositional inverse does not exist") - raise ValueError("cannot determine whether the compositional inverse exists") + if isinstance(coeff_stream, Stream_exact): + if coeff_stream._constant: + if coeff_stream.order() == 1: + R = P.base_ring() + # we cannot assume that the last initial coefficient + # and the constant differ, see stream.Stream_exact + if (coeff_stream._degree == 1 + len(coeff_stream._initial_coefficients) + and coeff_stream._constant == -R.one() + and all(c == -R.one() for c in coeff_stream._initial_coefficients)): + # self = -z/(1-z); self.revert() = -z/(1-z) + return self + else: + raise ValueError("compositional inverse does not exist") + else: + if (coeff_stream.order() == -1 + and coeff_stream._degree == 0): + # self = a/z; self.revert() = a/z + return self + + if (coeff_stream.order() >= 0 + and coeff_stream._degree == 2): + # self = a + b*z; self.revert() = -a/b + 1/b * z + a = coeff_stream[0] + b = coeff_stream[1] + coeff_stream = Stream_exact((-a/b, 1/b), + order=0) + return P.element_class(P, coeff_stream) - def approximate_series(self, prec, name=None): - r""" - Return the Laurent series with absolute precision ``prec`` approximated - from this series. + if coeff_stream.order() != 1: + raise ValueError("compositional inverse does not exist") - INPUT: + g = P.undefined(valuation=1) + # the following is mathematically equivalent to + # z / ((self / z)(g)) + # but more efficient and more lazy + g.define((~self.shift(-1)(g)).shift(1)) + return g - - ``prec`` -- an integer - - ``name`` -- name of the variable; if it is ``None``, the name of - the variable of the series is used + compositional_inverse = revert - OUTPUT: a Laurent series with absolute precision ``prec`` + def derivative(self, *args): + """ + Return the derivative of the Laurent series. + + Multiple variables and iteration counts may be supplied; see + the documentation of + :func:`sage.calculus.functional.derivative` function for + details. EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z = L.gen() - sage: f = (z - 2*z^3)^5/(1 - 2*z) - sage: f - z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + 32*z^10 - 16*z^11 + O(z^12) - sage: g = f.approximate_series(10) - sage: g - z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) - sage: g.parent() - Power Series Ring in z over Integer Ring - sage: h = (f^-1).approximate_series(3) - sage: h - z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) - sage: h.parent() - Laurent Series Ring in z over Integer Ring - """ - S = self.parent() + sage: L.<z> = LazyLaurentSeriesRing(ZZ) + sage: z.derivative() + 1 + sage: (1+z+z^2).derivative(3) + 0 + sage: (1/z).derivative() + -z^-2 + sage: (1/(1-z)).derivative(z) + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) - if name is None: + TESTS: + + Check the derivative of the logarithm: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: -log(1-z).derivative() + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7) + + Check that differentiation of 'exact' series with nonzero + constant works:: + + sage: L.<z> = LazyLaurentSeriesRing(ZZ) + sage: f = L([1,2], valuation=-2, constant=1) + sage: f + z^-2 + 2*z^-1 + 1 + z + z^2 + O(z^3) + sage: f.derivative() + -2*z^-3 - 2*z^-2 + 1 + 2*z + 3*z^2 + 4*z^3 + O(z^4) + + Check that differentiation with respect to a variable other + than the series variable works:: + + sage: R.<q> = QQ[] + sage: L.<z> = LazyLaurentSeriesRing(R) + sage: (z*q).derivative() + q + + sage: (z*q).derivative(q) + z + + sage: (z*q).derivative(q, z) + 1 + + sage: f = 1/(1-q*z+z^2) + sage: f + 1 + q*z + (q^2 - 1)*z^2 + (q^3 - 2*q)*z^3 + (q^4 - 3*q^2 + 1)*z^4 + (q^5 - 4*q^3 + 3*q)*z^5 + (q^6 - 5*q^4 + 6*q^2 - 1)*z^6 + O(z^7) + sage: f.derivative(q)[3] + 3*q^2 - 2 + + """ + P = self.parent() + R = P._laurent_poly_ring + v = R.gen() + order = 0 + vars = [] + for x in derivative_parse(args): + if x is None or x == v: + order += 1 + else: + vars.append(x) + + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + return self + if (isinstance(coeff_stream, Stream_exact) + and not coeff_stream._constant): + if coeff_stream._approximate_order >= 0 and coeff_stream._degree <= order: + return P.zero() + if vars: + coeffs = [prod(i-k for k in range(order)) * c.derivative(vars) + for i, c in enumerate(coeff_stream._initial_coefficients, + coeff_stream._approximate_order)] + else: + coeffs = [prod(i-k for k in range(order)) * c + for i, c in enumerate(coeff_stream._initial_coefficients, + coeff_stream._approximate_order)] + coeff_stream = Stream_exact(coeffs, + order=coeff_stream._approximate_order - order, + constant=coeff_stream._constant) + return P.element_class(P, coeff_stream) + + coeff_stream = Stream_derivative(self._coeff_stream, order, + P.is_sparse()) + if vars: + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: c.derivative(vars), + P.is_sparse()) + return P.element_class(P, coeff_stream) + + def approximate_series(self, prec, name=None): + r""" + Return the Laurent series with absolute precision ``prec`` approximated + from this series. + + INPUT: + + - ``prec`` -- an integer + - ``name`` -- name of the variable; if it is ``None``, the name of + the variable of the series is used + + OUTPUT: a Laurent series with absolute precision ``prec`` + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z = L.gen() + sage: f = (z - 2*z^3)^5/(1 - 2*z) + sage: f + z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + 32*z^10 - 16*z^11 + O(z^12) + sage: g = f.approximate_series(10) + sage: g + z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) + sage: g.parent() + Power Series Ring in z over Integer Ring + sage: h = (f^-1).approximate_series(3) + sage: h + z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) + sage: h.parent() + Laurent Series Ring in z over Integer Ring + """ + S = self.parent() + + if name is None: name = S.variable_name() if self.valuation() < 0: @@ -3163,10 +4112,10 @@ def polynomial(self, degree=None, name=None): A Laurent polynomial if the valuation of the series is negative or a polynomial otherwise. - If ``degree`` is not ``None``, the terms of the series of degree - greater than ``degree`` are truncated first. If ``degree`` is ``None`` - and the series is not a polynomial or a Laurent polynomial, a - ``ValueError`` is raised. + If ``degree`` is not ``None``, the terms of the series of + degree greater than ``degree`` are first truncated. If + ``degree`` is ``None`` and the series is not a polynomial or + a Laurent polynomial, a ``ValueError`` is raised. EXAMPLES:: @@ -3207,6 +4156,7 @@ def polynomial(self, degree=None, name=None): sage: L.zero().polynomial() 0 + """ S = self.parent() @@ -3272,13 +4222,13 @@ 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 LazyTaylorSeries(LazyCauchyProductSeries): +class LazyPowerSeries(LazyCauchyProductSeries): r""" A Taylor series where the coefficients are computed lazily. EXAMPLES:: - sage: L.<x, y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x, y> = LazyPowerSeriesRing(ZZ) sage: f = 1 / (1 - x^2 + y^3); f 1 + x^2 + (-y^3) + x^4 + (-2*x^2*y^3) + (x^6+y^6) + O(x,y)^7 sage: P.<x, y> = PowerSeriesRing(ZZ, default_prec=101) @@ -3293,6 +4243,96 @@ class LazyTaylorSeries(LazyCauchyProductSeries): sage: g == f True """ + def is_unit(self): + """ + Return whether this element is a unit in the ring. + + EXAMPLES:: + + sage: L.<z> = LazyPowerSeriesRing(ZZ) + sage: (2*z).is_unit() + False + + sage: (1 + 2*z).is_unit() + True + + sage: (3 + 2*z).is_unit() + False + + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) + sage: (-1 + 2*x + 3*x*y).is_unit() + True + """ + if self.is_zero(): # now 0 != 1 + return False + return self[0].is_unit() + + def exponential(self): + r""" + Return the exponential series of ``self``. + + This method is deprecated, use :meth:`exp` instead. + + TESTS:: + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: lazy_exp = x.exponential(); lazy_exp + doctest:...: DeprecationWarning: the method exponential is deprecated. Use exp instead. + See https://trac.sagemath.org/32367 for details. + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) + """ + from sage.misc.superseded import deprecation + deprecation(32367, 'the method exponential is deprecated. Use exp instead.') + return self.exp() + + def compute_coefficients(self, i): + r""" + Computes all the coefficients of self up to i. + + This method is deprecated, it has no effect anymore. + + TESTS:: + + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: a = L([1,2,3], constant=3) + sage: a.compute_coefficients(5) + doctest:...: DeprecationWarning: the method compute_coefficients obsolete and has no effect. + See https://trac.sagemath.org/32367 for details. + sage: a + 1 + 2*z + 3*z^2 + 3*z^3 + 3*z^4 + O(z^5) + """ + from sage.misc.superseded import deprecation + deprecation(32367, "the method compute_coefficients obsolete and has no effect.") + return + + def _im_gens_(self, codomain, im_gens, base_map=None): + """ + Return the image of ``self`` under the map that sends the + generators of the parent of ``self`` to the elements of the + tuple ``im_gens``. + + EXAMPLES:: + + sage: Z.<x> = QQ[] + sage: R.<q, t> = LazyPowerSeriesRing(Z) + sage: f = 1/(1-q-t) + sage: f + 1 + (q+t) + (q^2+2*q*t+t^2) + (q^3+3*q^2*t+3*q*t^2+t^3) + (q^4+4*q^3*t+6*q^2*t^2+4*q*t^3+t^4) + (q^5+5*q^4*t+10*q^3*t^2+10*q^2*t^3+5*q*t^4+t^5) + (q^6+6*q^5*t+15*q^4*t^2+20*q^3*t^3+15*q^2*t^4+6*q*t^5+t^6) + O(q,t)^7 + sage: S.<s> = LazyPowerSeriesRing(Z) + sage: f._im_gens_(S, [s, x*s]) + 1 + ((x+1)*s) + ((x^2+2*x+1)*s^2) + ((x^3+3*x^2+3*x+1)*s^3) + ((x^4+4*x^3+6*x^2+4*x+1)*s^4) + ((x^5+5*x^4+10*x^3+10*x^2+5*x+1)*s^5) + ((x^6+6*x^5+15*x^4+20*x^3+15*x^2+6*x+1)*s^6) + O(s^7) + + sage: cc = Z.hom([-x]) + sage: f = 1/(1+x*q-t) + sage: f._im_gens_(S, [s, x*s], base_map=cc) + 1 + 2*x*s + 4*x^2*s^2 + 8*x^3*s^3 + 16*x^4*s^4 + 32*x^5*s^5 + 64*x^6*s^6 + O(s^7) + + """ + if base_map is None: + return codomain(self(*im_gens)) + + return codomain(self.map_coefficients(base_map)(*im_gens)) + def __call__(self, *g, check=True): r""" Return the composition of ``self`` with ``g``. @@ -3300,12 +4340,21 @@ def __call__(self, *g, check=True): The arity of ``self`` must be equal to the number of arguments provided. - Given two Taylor Series `f` and `g` over the same base ring, the - composition `(f \circ g)(z) = f(g(z))` is defined if and only if: + Given a Taylor series `f` of arity `n` and a tuple of Taylor + series `g = (g_1,\dots, g_n)` over the same base ring, the + composition `f \circ g` is defined if and only if for each + `1\leq k\leq n`: - - `g = 0` and `val(f) >= 0`, - - `g` is non-zero and `f` has only finitely many non-zero coefficients, - - `g` is non-zero and `val(g) > 0`. + - `g_i` is zero, or + - setting all variables except the `i`th in `f` to zero + yields a polynomial, or + - `val(g_i) > 0`. + + If `f` is a univariate 'exact' series, we can check whether + `f` is a actually a polynomial. However, if `f` is a + multivariate series, we have no way to test whether setting + all but one variable of `f` to zero yields a polynomial, + except if `f` itself is 'exact' and therefore a polynomial. INPUT: @@ -3313,8 +4362,8 @@ def __call__(self, *g, check=True): EXAMPLES:: - sage: L.<x, y, z> = LazyTaylorSeriesRing(QQ) - sage: M.<a, b> = LazyTaylorSeriesRing(ZZ) + sage: L.<x, y, z> = LazyPowerSeriesRing(QQ) + sage: M.<a, b> = LazyPowerSeriesRing(ZZ) sage: g1 = 1 / (1 - x) sage: g2 = x + y^2 sage: p = a^2 + b + 1 @@ -3324,7 +4373,7 @@ def __call__(self, *g, check=True): The number of mappings from a set with `m` elements to a set with `n` elements:: - sage: M.<a> = LazyTaylorSeriesRing(QQ) + sage: M.<a> = LazyPowerSeriesRing(QQ) sage: Ea = M(lambda n: 1/factorial(n)) sage: Ex = L(lambda n: 1/factorial(n)*x^n) sage: Ea(Ex*y)[5] @@ -3368,7 +4417,7 @@ def __call__(self, *g, check=True): of `f` and the parent of `g` or extended to the corresponding lazy series:: - sage: T.<x,y> = LazyTaylorSeriesRing(QQ) + sage: T.<x,y> = LazyPowerSeriesRing(QQ) sage: R.<a,b,c> = ZZ[] sage: S.<v> = R[] sage: L.<z> = LaurentPolynomialRing(ZZ) @@ -3397,7 +4446,7 @@ def __call__(self, *g, check=True): TESTS:: - sage: L.<x,y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) sage: f = 1 / (1 - x - y) sage: f(f) Traceback (most recent call last): @@ -3417,8 +4466,50 @@ def __call__(self, *g, check=True): ... TypeError: no common canonical parent for objects with parents: ... + Consistency check when `g` is an uninitialized series between a + polynomial `f` as both a polynomial and a lazy series:: + + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: f = 1 - z + sage: g = L.undefined(valuation=1) + sage: f(g) == f.polynomial()(g) + True + + sage: g = L.undefined(valuation=1) + sage: g.define(z / (1 - g)) + sage: g + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) + sage: gp = L.undefined(valuation=1) + sage: gp.define(z / f(gp)) + sage: gp + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) + + Check that composing the zero series with anything yields zero:: + + sage: T.<x,y> = LazyPowerSeriesRing(QQ) + sage: M.<a, b> = LazyPowerSeriesRing(QQ) + sage: T(0)(1/(1-a), a+b) + 0 + + Check that composing `f` with zero series yields the constant term of `f`:: + + sage: T(3/(1-x-2*y))(0, 0) + 3 + + Check that we can compose a polynomial with anything:: + + sage: T(1-x-2*y + x*y^2)(1, 3) + 3 + + sage: T(1-x-2*y + x*y^2)(1 + a, 3) + 3 + 8*a + + sage: T(1-x-2*y + x*y^2)(1/(1-a), 3) + 3 + 8*a + 8*a^2 + 8*a^3 + 8*a^4 + 8*a^5 + 8*a^6 + O(a,b)^7 + """ - if len(g) != len(self.parent().variable_names()): + fP = parent(self) + if len(g) != fP._arity: raise ValueError("arity of must be equal to the number of arguments provided") # Find a good parent for the result @@ -3426,8 +4517,20 @@ def __call__(self, *g, check=True): cm = get_coercion_model() P = cm.common_parent(self.base_ring(), *[parent(h) for h in g]) - # f has finite length - if isinstance(self._coeff_stream, Stream_exact) and not self._coeff_stream._constant: + # f = 0 + if isinstance(self._coeff_stream, Stream_zero): + return P.zero() + + # g = (0, ..., 0) + if all((not isinstance(h, LazyModuleElement) and not h) + or (isinstance(h, LazyModuleElement) + and isinstance(h._coeff_stream, Stream_zero)) + for h in g): + return P(self[0]) + + # f has finite length and f != 0 + if (isinstance(self._coeff_stream, Stream_exact) + and not self._coeff_stream._constant): # constant polynomial poly = self.polynomial() if poly.is_constant(): @@ -3442,18 +4545,17 @@ def __call__(self, *g, check=True): from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_univariate from sage.rings.lazy_series_ring import LazySeriesRing if not isinstance(P, LazySeriesRing): - fP = parent(self) if fP._laurent_poly_ring.has_coerce_map_from(P): S = fP._laurent_poly_ring P = fP if isinstance(P, (PolynomialRing_general, MPolynomialRing_base)): - from sage.rings.lazy_series_ring import LazyTaylorSeriesRing + from sage.rings.lazy_series_ring import LazyPowerSeriesRing S = P try: sparse = S.is_sparse() except AttributeError: sparse = fP.is_sparse() - P = LazyTaylorSeriesRing(S.base_ring(), S.variable_names(), sparse) + P = LazyPowerSeriesRing(S.base_ring(), S.variable_names(), sparse) elif isinstance(P, LaurentPolynomialRing_univariate): from sage.rings.lazy_series_ring import LazyLaurentSeriesRing S = P @@ -3478,8 +4580,7 @@ def __call__(self, *g, check=True): raise ValueError("can only compose with a positive valuation series") h._coeff_stream._approximate_order = 2 - - # We now ahave that every element of g has a _coeff_stream + # We now have that every element of g has a _coeff_stream sorder = self._coeff_stream._approximate_order if len(g) == 1: g0 = g[0] @@ -3487,10 +4588,13 @@ def __call__(self, *g, check=True): # we assume that the valuation of self[i](g) is at least i def coefficient(n): return sum(self[i] * (g0**i)[n] for i in range(n+1)) - coeff_stream = Stream_function(coefficient, R, P._sparse, 1) + + coeff_stream = Stream_function(coefficient, P._sparse, 1) return P.element_class(P, coeff_stream) - coeff_stream = Stream_cauchy_compose(self._coeff_stream, g0._coeff_stream) + coeff_stream = Stream_cauchy_compose(self._coeff_stream, + g0._coeff_stream, + P.is_sparse()) return P.element_class(P, coeff_stream) # The arity is at least 2 @@ -3498,25 +4602,267 @@ def coefficient(n): def coefficient(n): r = R.zero() - for i in range(n//gv+1): + for i in range(n // gv + 1): # Make sure the element returned from the composition is in P r += P(self[i](g))[n] return r - coeff_stream = Stream_function(coefficient, R, P._sparse, sorder * gv) + coeff_stream = Stream_function(coefficient, P._sparse, sorder * gv) return P.element_class(P, coeff_stream) compose = __call__ + def revert(self): + r""" + Return the compositional inverse of ``self``. + + Given a Taylor series `f`, the compositional inverse is a + Laurent series `g` over the same base ring, such that + `(f \circ g)(z) = f(g(z)) = z`. + + The compositional inverse exists if and only if: + + - `val(f) = 1`, or + + - `f = a + b z` with `a, b \neq 0` + + EXAMPLES:: + + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: (2*z).revert() + 1/2*z + sage: (z-z^2).revert() + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) + + sage: s = L(degree=1, constant=-1) + sage: s.revert() + -z - z^2 - z^3 + O(z^4) + + sage: s = L(degree=1, constant=1) + sage: s.revert() + z - z^2 + z^3 - z^4 + z^5 - z^6 + z^7 + O(z^8) + + TESTS:: + + sage: L.<z> = LazyPowerSeriesRing(QQ) + sage: s = L(lambda n: 2 if n == 1 else 0, valuation=1); s + 2*z + O(z^8) + sage: s.revert() + 1/2*z + O(z^8) + + sage: (2+3*z).revert() + -2/3 + 1/3*z + + sage: s = L(lambda n: 2 if n == 0 else 3 if n == 1 else 0, valuation=0); s + 2 + 3*z + O(z^7) + sage: s.revert() + Traceback (most recent call last): + ... + ValueError: cannot determine whether the compositional inverse exists + + sage: R.<q,t> = QQ[] + sage: L.<z> = LazyPowerSeriesRing(R.fraction_field()) + sage: s = L([q], valuation=0, constant=t); s + q + t*z + t*z^2 + t*z^3 + O(z^4) + sage: s.revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + We look at some cases where the compositional inverse does not exist: + + `f = 0`:: + + sage: L(0).revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + sage: (z - z).revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + `val(f) != 1` and `f(0) * f(1) = 0`:: + + sage: (z^2).revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + sage: L(1).revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + Reversion of exact series:: + + sage: f = L([1, 2], valuation=0, constant=1) + sage: f.revert() + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + sage: f = L([-1, -1], valuation=1, constant=-1) + sage: f.revert() + (-z) + (-z^2) + (-z^3) + O(z^4) + + sage: f = L([-1, 0, -1], valuation=1, constant=-1) + sage: f.revert() + (-z) + z^3 + (-z^4) + (-2*z^5) + 6*z^6 + z^7 + O(z^8) + + sage: f = L([-1], valuation=1, degree=3, constant=-1) + sage: f.revert() + (-z) + z^3 + (-z^4) + (-2*z^5) + 6*z^6 + z^7 + O(z^8) + """ + P = self.parent() + if P._arity != 1: + raise ValueError("arity must be equal to 1") + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + raise ValueError("compositional inverse does not exist") + if isinstance(coeff_stream, Stream_exact): + if coeff_stream._constant: + if coeff_stream.order() == 1: + R = P.base_ring() + # we cannot assume that the last initial coefficient + # and the constant differ, see stream.Stream_exact + if (coeff_stream._degree == 1 + len(coeff_stream._initial_coefficients) + and coeff_stream._constant == -R.one() + and all(c == -R.one() for c in coeff_stream._initial_coefficients)): + # self = -z/(1-z); self.revert() = -z/(1-z) + return self + else: + raise ValueError("compositional inverse does not exist") + else: + if coeff_stream._degree == 2: + # self = a + b*z; self.revert() = -a/b + 1/b * z + a = coeff_stream[0] + b = coeff_stream[1] + coeff_stream = Stream_exact((-a/b, 1/b), + order=0) + return P.element_class(P, coeff_stream) + + if coeff_stream.order() != 1: + raise ValueError("compositional inverse does not exist") + + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! + if coeff_stream[0]: + raise ValueError("cannot determine whether the compositional inverse exists") + + g = P.undefined(valuation=1) + # the following is mathematically equivalent to + # z / ((self / z)(g)) + # but more efficient and more lazy + g.define((~self.shift(-1)(g)).shift(1)) + return g + + compositional_inverse = revert + + def derivative(self, *args): + """ + Return the derivative of the Taylor series. + + Multiple variables and iteration counts may be supplied; see + the documentation of + :func:`sage.calculus.functional.derivative` function for + details. + + EXAMPLES:: + + sage: T.<z> = LazyPowerSeriesRing(ZZ) + sage: z.derivative() + 1 + sage: (1+z+z^2).derivative(3) + 0 + sage: (1/(1-z)).derivative() + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) + + sage: R.<q> = QQ[] + sage: L.<x, y> = LazyPowerSeriesRing(R) + sage: f = 1/(1-q*x+y); f + 1 + (q*x-y) + (q^2*x^2+(-2*q)*x*y+y^2) + + (q^3*x^3+(-3*q^2)*x^2*y+3*q*x*y^2-y^3) + + (q^4*x^4+(-4*q^3)*x^3*y+6*q^2*x^2*y^2+(-4*q)*x*y^3+y^4) + + (q^5*x^5+(-5*q^4)*x^4*y+10*q^3*x^3*y^2+(-10*q^2)*x^2*y^3+5*q*x*y^4-y^5) + + (q^6*x^6+(-6*q^5)*x^5*y+15*q^4*x^4*y^2+(-20*q^3)*x^3*y^3+15*q^2*x^2*y^4+(-6*q)*x*y^5+y^6) + + O(x,y)^7 + sage: f.derivative(q) + x + (2*q*x^2+(-2)*x*y) + (3*q^2*x^3+(-6*q)*x^2*y+3*x*y^2) + + (4*q^3*x^4+(-12*q^2)*x^3*y+12*q*x^2*y^2+(-4)*x*y^3) + + (5*q^4*x^5+(-20*q^3)*x^4*y+30*q^2*x^3*y^2+(-20*q)*x^2*y^3+5*x*y^4) + + (6*q^5*x^6+(-30*q^4)*x^5*y+60*q^3*x^4*y^2+(-60*q^2)*x^3*y^3+30*q*x^2*y^4+(-6)*x*y^5) + + O(x,y)^7 + + """ + P = self.parent() + R = P._laurent_poly_ring + V = R.gens() + order = 0 + vars = [] + gen_vars = [] + for x in derivative_parse(args): + if x is None: + order += 1 + elif x in V: + gen_vars.append(x) + else: + vars.append(x) + + if P._arity > 1 and order: + raise ValueError("for multivariate series you have to specify the variable with respect to which the derivative should be taken") + else: + order += len(gen_vars) + + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + return self + + if P._arity > 1: + v = gen_vars + vars + d = -len(gen_vars) + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: R(c).derivative(v), + P.is_sparse()) + coeff_stream = Stream_shift(coeff_stream, d) + return P.element_class(P, coeff_stream) + + if (isinstance(coeff_stream, Stream_exact) + and not coeff_stream._constant): + if coeff_stream._degree <= order: + return P.zero() + if vars: + coeffs = [prod(i-k for k in range(order)) * c.derivative(vars) + for i, c in enumerate(coeff_stream._initial_coefficients, + coeff_stream._approximate_order)] + else: + coeffs = [prod(i-k for k in range(order)) * c + for i, c in enumerate(coeff_stream._initial_coefficients, + coeff_stream._approximate_order)] + coeff_stream = Stream_exact(coeffs, + order=coeff_stream._approximate_order - order, + constant=coeff_stream._constant) + return P.element_class(P, coeff_stream) + + coeff_stream = Stream_derivative(self._coeff_stream, order, + P.is_sparse()) + if vars: + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: c.derivative(vars), + P.is_sparse()) + return P.element_class(P, coeff_stream) + def _format_series(self, formatter, format_strings=False): """ Return nonzero ``self`` formatted by ``formatter``. TESTS:: - sage: L.<x,y> = LazyTaylorSeriesRing(QQ) + sage: L.<x,y> = LazyPowerSeriesRing(QQ) sage: f = 1 / (2 - x^2 + y) sage: f._format_series(repr) - '1/2 + (-1/4*y) + (1/4*x^2+1/8*y^2) + (-1/4*x^2*y-1/16*y^3) + (1/8*x^4+3/16*x^2*y^2+1/32*y^4) + (-3/16*x^4*y-1/8*x^2*y^3-1/64*y^5) + (1/16*x^6+3/16*x^4*y^2+5/64*x^2*y^4+1/128*y^6) + O(x,y)^7' + '1/2 + (-1/4*y) + (1/4*x^2+1/8*y^2) + (-1/4*x^2*y-1/16*y^3) + + (1/8*x^4+3/16*x^2*y^2+1/32*y^4) + (-3/16*x^4*y-1/8*x^2*y^3-1/64*y^5) + + (1/16*x^6+3/16*x^4*y^2+5/64*x^2*y^4+1/128*y^6) + O(x,y)^7' sage: f = (2 - x^2 + y) sage: f._format_series(repr) @@ -3588,13 +4934,14 @@ def polynomial(self, degree=None, names=None): OUTPUT: - If ``degree`` is not ``None``, the terms of the series of degree greater - than ``degree`` are truncated first. If ``degree`` is ``None`` and the - series is not a polynomial polynomial, a ``ValueError`` is raised. + If ``degree`` is not ``None``, the terms of the series of + degree greater than ``degree`` are first truncated. If + ``degree`` is ``None`` and the series is not a polynomial + polynomial, a ``ValueError`` is raised. EXAMPLES:: - sage: L.<x,y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) sage: f = x^2 + y*x - x + 2; f 2 + (-x) + (x^2+x*y) sage: f.polynomial() @@ -3617,6 +4964,11 @@ def polynomial(self, degree=None, names=None): True sage: g3.polynomial(0) 1 + + sage: L.<z> = LazyPowerSeriesRing(ZZ) + sage: f = z-z^2 + sage: f.polynomial() + -z^2 + z """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing S = self.parent() @@ -3628,18 +4980,183 @@ def polynomial(self, degree=None, names=None): if degree is None: if (isinstance(self._coeff_stream, Stream_exact) - and not self._coeff_stream._constant): + and not self._coeff_stream._constant): m = self._coeff_stream._degree else: raise ValueError("not a polynomial") else: m = degree + 1 - if names is None: - names = S.variable_names() + if S._arity == 1: + return R(self[0:m]) + return R.sum(self[0:m]) - return R.sum(self[:m]) + def _floordiv_(self, other): + r""" + Return ``self`` floor divided by ``other``. + + INPUT: + + - ``other`` -- nonzero series + + EXAMPLES:: + + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) + sage: g = x^2 + y*x + sage: x // g + 0 + sage: g = (x^2 + y*x) / (1 - x + x*y) + sage: x // g + 0 + sage: f = (x + y) / (1 - x - y + x*y) + sage: f // g + 0 + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: g = (x + 2*x^2) / (1 - x - x^2) + sage: 3 // g + 0 + sage: x // g + 1 - 3*x + 5*x^2 - 10*x^3 + 20*x^4 - 40*x^5 + 80*x^6 + O(x^7) + sage: x^2 // g + x - 3*x^2 + 5*x^3 - 10*x^4 + 20*x^5 - 40*x^6 + 80*x^7 + O(x^8) + sage: f = (x + x^2) / (1 - x) + sage: f // g + 1 - x + x^2 - 4*x^3 + 6*x^4 - 14*x^5 + 26*x^6 + O(x^7) + """ + if isinstance(other._coeff_stream, Stream_zero): + raise ZeroDivisionError("cannot divide by 0") + P = self.parent() + if P not in IntegralDomains(): + raise TypeError("must be an integral domain") + left = self._coeff_stream + right_order = other._coeff_stream._approximate_order + if left._approximate_order < right_order: + if left._true_order: + return P.zero() + while left._approximate_order < right_order: + # TODO: Implement a bound on computing the order of a Stream + if left[left._approximate_order]: + left._true_order = True + return P.zero() + left._approximate_order += 1 + return super()._floordiv_(other) + +class LazyPowerSeries_gcd_mixin: + """ + A lazy power series that also implements the GCD algorithm. + """ + def gcd(self, other): + r""" + Return the greatest common divisor of ``self`` and ``other``. + + EXAMPLES:: + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: a = 16*x^5 / (1 - 5*x) + sage: b = (22*x^2 + x^8) / (1 - 4*x^2) + sage: a.gcd(b) + x^2 + """ + P = self.parent() + if P._arity != 1: + raise NotImplementedError("only implemented for arity one") + if not self or not other: + return P.zero() + sv = self.valuation() + ov = other.valuation() + val = min(sv, ov) + assert val is not infinity + # This assumes the base ring is a field + return P.gen(0) ** val + + def xgcd(self, f): + r""" + Return the extended gcd of ``self`` and ``f``. + + OUTPUT: + + A triple ``(g, s, t)`` such that ``g`` is the gcd of ``self`` + and ``f``, and ``s`` and ``t`` are cofactors satisfying the + Bezout identity + + .. MATH:: + + g = s \cdot \mathrm{self} + t \cdot f. + + EXAMPLES:: + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: a = 16*x^5 / (1 - 2*x) + sage: b = (22*x^3 + x^8) / (1 - 3*x^2) + sage: g, s, t = a.xgcd(b) + sage: g + x^3 + sage: s + 1/22 - 41/242*x^2 - 8/121*x^3 + 120/1331*x^4 + 1205/5324*x^5 + 316/14641*x^6 + O(x^7) + sage: t + 1/22 - 41/242*x^2 - 8/121*x^3 + 120/1331*x^4 + 1205/5324*x^5 + 316/14641*x^6 + O(x^7) + + sage: LazyPowerSeriesRing.options.halting_precision(20) # verify up to degree 20 + + sage: g == s * a + t * b + True + sage: a = 16*x^5 / (1 - 2*x) + sage: b = (-16*x^5 + x^8) / (1 - 3*x^2) + sage: g, s, t = a.xgcd(b) + sage: g + x^5 + sage: s + 1/16 - 1/16*x - 3/16*x^2 + 1/8*x^3 - 17/256*x^4 + 9/128*x^5 + 1/128*x^6 + O(x^7) + sage: t + 1/16*x - 1/16*x^2 - 3/16*x^3 + 1/8*x^4 - 17/256*x^5 + 9/128*x^6 + 1/128*x^7 + O(x^8) + sage: g == s * a + t * b + True + + sage: L.<x> = LazyPowerSeriesRing(GF(2)) + sage: a = L(lambda n: n % 2, valuation=3); a + x^3 + x^5 + x^7 + x^9 + O(x^10) + sage: b = L(lambda n: binomial(n,2) % 2, valuation=3); b + x^3 + x^6 + x^7 + O(x^10) + sage: g, s, t = a.xgcd(b) + sage: g + x^3 + sage: s + 1 + x + x^3 + x^4 + x^5 + O(x^7) + sage: t + x + x^2 + x^4 + x^5 + x^6 + O(x^8) + sage: g == s * a + t * b + True + + sage: LazyPowerSeriesRing.options._reset() # reset the options + """ + P = self.parent() + if P._arity != 1: + raise NotImplementedError("only implemented for arity one") + # one of the elements is zero + if not self: + return (P.zero(), P.zero(), P.one()) + if not f: + return (P.zero(), P.one(), P.zero()) + # get the valuations + sv = self.valuation() + fv = f.valuation() + val = min(sv, fv) + assert val is not infinity + # This assumes the base ring is a field + x = P.gen(0) + unit = (self + f).shift(-val) + if not unit[0]: + # this only happens if they have the same valuation + # we multiply f by the generator to avoid any cancellations + unit = (self + f.shift(1)).shift(-val) + unit = ~unit + return (x**val, + unit, + unit * x) + unit = ~unit + return (x**val, unit, unit) class LazyCompletionGradedAlgebraElement(LazyCauchyProductSeries): """ @@ -3729,24 +5246,67 @@ class LazySymmetricFunction(LazyCompletionGradedAlgebraElement): sage: s = SymmetricFunctions(ZZ).s() sage: L = LazySymmetricFunctions(s) """ + def is_unit(self): + """ + Return whether this element is a unit in the ring. + + EXAMPLES:: + + sage: m = SymmetricFunctions(ZZ).m() + sage: L = LazySymmetricFunctions(m) + + sage: L(2*m[1]).is_unit() + False + + sage: L(-1 + 2*m[1]).is_unit() + True + + sage: L(2 + m[1]).is_unit() + False + + sage: m = SymmetricFunctions(QQ).m() + sage: L = LazySymmetricFunctions(m) + + sage: L(2 + 3*m[1]).is_unit() + True + """ + if self.is_zero(): # now 0 != 1 + return False + return self[0].is_unit() + def __call__(self, *args, check=True): r""" - Return the composition of ``self`` with ``args``. + Return the composition of ``self`` with ``g``. The arity of ``self`` must be equal to the number of arguments provided. - Given two lazy symmetric functions `f` and `g` over the same - base ring, the composition (or plethysm) `(f \circ g)` is - defined if and only if: - - - `g = 0`, - - `g` is non-zero and `f` has only finitely many non-zero coefficients, - - `g` is non-zero and `val(g) > 0`. + Given a lazy symmetric function `f` of arity `n` and a tuple + of lazy symmetric functions `g = (g_1,\dots, g_n)` over the + same base ring, the composition (or plethysm) `(f \circ g)` + is defined if and only if for each `1\leq k\leq n`: + + - `g_i = 0`, or + - setting all alphabets except the `i`th in `f` to zero + yields a symmetric function with only finitely many + non-zero coefficients, or + - `val(g) > 0`. + + If `f` is a univariate 'exact' lazy symmetric function, we + can check whether `f` has only finitely many non-zero + coefficients. However, if `f` has larger arity, we have no + way to test whether setting all but one alphabets of `f` to + zero yields a polynomial, except if `f` itself is 'exact' and + therefore a symmetric function with only finitely many + non-zero coefficients. INPUT: - - ``args`` -- other (lazy) symmetric functions + - ``g`` -- other (lazy) symmetric functions + + .. TODO:: + + Allow specification of degree one elements. EXAMPLES:: @@ -3779,9 +5339,33 @@ def __call__(self, *args, check=True): sage: E1 = S(lambda n: s[n], valuation=1) sage: E = 1 + E1 sage: P = E(E1) - sage: [s(x) for x in P[:5]] + sage: P[:5] [s[], s[1], 2*s[2], s[2, 1] + 3*s[3], 2*s[2, 2] + 2*s[3, 1] + 5*s[4]] + The plethysm with a tensor product is also implemented:: + + sage: s = SymmetricFunctions(QQ).s() + sage: X = tensor([s[1],s[[]]]) + sage: Y = tensor([s[[]],s[1]]) + sage: S = LazySymmetricFunctions(s) + sage: S2 = LazySymmetricFunctions(tensor([s, s])) + sage: A = S(s[1,1,1]) + sage: B = S2(X+Y) + sage: A(B) + (s[]#s[1,1,1]+s[1]#s[1,1]+s[1,1]#s[1]+s[1,1,1]#s[]) + + sage: H = S(lambda n: s[n]) + sage: H(S2(X*Y)) + (s[]#s[]) + (s[1]#s[1]) + (s[1,1]#s[1,1]+s[2]#s[2]) + + (s[1,1,1]#s[1,1,1]+s[2,1]#s[2,1]+s[3]#s[3]) + O^7 + sage: H(S2(X+Y)) + (s[]#s[]) + (s[]#s[1]+s[1]#s[]) + (s[]#s[2]+s[1]#s[1]+s[2]#s[]) + + (s[]#s[3]+s[1]#s[2]+s[2]#s[1]+s[3]#s[]) + + (s[]#s[4]+s[1]#s[3]+s[2]#s[2]+s[3]#s[1]+s[4]#s[]) + + (s[]#s[5]+s[1]#s[4]+s[2]#s[3]+s[3]#s[2]+s[4]#s[1]+s[5]#s[]) + + (s[]#s[6]+s[1]#s[5]+s[2]#s[4]+s[3]#s[3]+s[4]#s[2]+s[5]#s[1]+s[6]#s[]) + + O^7 + TESTS:: sage: s = SymmetricFunctions(QQ).s() @@ -3793,8 +5377,7 @@ def __call__(self, *args, check=True): True sage: f = 1 / (1 - S(s[2])) sage: g = S(s[1]) / (1 - S(s[1])) - sage: h = f(g) - sage: h + sage: f(g) s[] + s[2] + (s[1,1,1]+2*s[2,1]+s[3]) + (2*s[1,1,1,1]+4*s[2,1,1]+5*s[2,2]+5*s[3,1]+3*s[4]) + (2*s[1,1,1,1,1]+10*s[2,1,1,1]+14*s[2,2,1]+18*s[3,1,1]+16*s[3,2]+14*s[4,1]+4*s[5]) @@ -3806,30 +5389,64 @@ def __call__(self, *args, check=True): Traceback (most recent call last): ... ValueError: can only compose with a positive valuation series + + Check that composing the zero series with anything yields + zero in the correct parent:: + + sage: e = SymmetricFunctions(QQ).e() + sage: h = SymmetricFunctions(QQ).h() + sage: s = SymmetricFunctions(QQ).s() + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(tensor([e, h])) + sage: r = (L(0)(s[1], p[1])); r + 0 + sage: r.parent() + Symmetric Functions over Rational Field in the Schur basis + + Check that composing `f` with zero series yields the constant term of `f`:: + + sage: f = 3*L(tensor([s[1], s[1]])) + sage: f(0, 0) + 0 + sage: (3+f)(0, 0) + 3 """ - if len(args) != self.parent()._arity: + fP = parent(self) + if len(args) != fP._arity: raise ValueError("arity must be equal to the number of arguments provided") - from sage.combinat.sf.sfa import is_SymmetricFunction - if not all(isinstance(g, LazySymmetricFunction) or is_SymmetricFunction(g) or not g for g in args): - raise ValueError("all arguments must be (possibly lazy) symmetric functions") + # Find a good parent for the result + from sage.structure.element import get_coercion_model + cm = get_coercion_model() + P = cm.common_parent(self.base_ring(), *[parent(h) for h in args]) + + # f = 0 if isinstance(self._coeff_stream, Stream_zero): - return self + return P.zero() + + # g = (0, ..., 0) + if all((not isinstance(h, LazyModuleElement) and not h) + or (isinstance(h, LazyModuleElement) + and isinstance(h._coeff_stream, Stream_zero)) + for h in args): + f = self[0] + # FIXME: TypeError: unable to convert 0 to a rational + if f: + return P(f.leading_coefficient()) + return P.zero() if len(args) == 1: g = args[0] - P = g.parent() - - # Handle other types of 0s - if not isinstance(g, LazySymmetricFunction) and not g: - return P(self[0].leading_coefficient()) + if (isinstance(self._coeff_stream, Stream_exact) + and not self._coeff_stream._constant): - if isinstance(self._coeff_stream, Stream_exact) and not self._coeff_stream._constant: - f = self.symmetric_function() - if is_SymmetricFunction(g): + if not isinstance(g, LazySymmetricFunction): + f = self.symmetric_function() return f(g) - # g must be a LazySymmetricFunction - if isinstance(g._coeff_stream, Stream_exact) and not g._coeff_stream._constant: + + if (isinstance(g._coeff_stream, Stream_exact) + and not g._coeff_stream._constant): + f = self.symmetric_function() gs = g.symmetric_function() return P(f(gs)) @@ -3841,21 +5458,608 @@ def __call__(self, *args, check=True): P = LazySymmetricFunctions(R) g = P(g) - # self has (potentially) infinitely many terms - if check: + if check and not (isinstance(self._coeff_stream, Stream_exact) + and not self._coeff_stream._constant): if g._coeff_stream._approximate_order == 0: if g[0]: raise ValueError("can only compose with a positive valuation series") g._coeff_stream._approximate_order = 1 - ps = P._laurent_poly_ring.realization_of().p() - coeff_stream = Stream_plethysm(self._coeff_stream, g._coeff_stream, ps) + if P._arity == 1: + ps = R.realization_of().p() + else: + ps = tensor([R._sets[0].realization_of().p()]*P._arity) + coeff_stream = Stream_plethysm(self._coeff_stream, g._coeff_stream, + P.is_sparse(), ps, R) + return P.element_class(P, coeff_stream) + else: raise NotImplementedError("only implemented for arity 1") + plethysm = __call__ + + def revert(self): + r""" + Return the compositional inverse of ``self``. + + Given a symmetric function `f`, the compositional inverse is + a symmetric function `g` over the same base ring, such that + `f \circ g = p_1`. Thus, it is the inverse with respect to + plethystic substitution. + + The compositional inverse exists if and only if: + + - `val(f) = 1`, or + + - `f = a + b p_1` with `a, b \neq 0`. + + EXAMPLES:: + + sage: h = SymmetricFunctions(QQ).h() + sage: L = LazySymmetricFunctions(h) + sage: f = L(lambda n: h[n]) - 1 + sage: f(f.revert()) + h[1] + O^7 + + TESTS:: + + sage: f = L(lambda n: h[n]) - 1 - h[1] + sage: g = f.revert() + sage: g[1] + Traceback (most recent call last): + ... + ValueError: compositional inverse does not exist + + sage: R.<a,b> = QQ[] + sage: p = SymmetricFunctions(R.fraction_field()).p() + sage: L = LazySymmetricFunctions(p) + sage: f = L(a + b*p[1]) + sage: f.revert() + (((-a)/b)*p[]) + 1/b*p[1] + + sage: f = L(2*p[1]) + sage: f.revert() + 1/2*p[1] + + sage: f = L(2*p[1] + p[1,1]) + sage: f.revert() + 1/2*p[1] + (-1/8*p[1,1]) + (1/16*p[1,1,1]) + (-5/128*p[1,1,1,1]) + + (7/256*p[1,1,1,1,1]) + (-21/1024*p[1,1,1,1,1,1]) + + (33/2048*p[1,1,1,1,1,1,1]) + O^8 + + sage: f.revert()(f) + p[1] + O^8 + + ALGORITHM: + + Let `F` be a symmetric function with valuation `1`, i.e., + whose constant term vanishes and whose degree one term equals + `b p_1`. Then + + .. MATH:: + + (F - b p_1) \circ G = F \circ G - b p_1 \circ G = p_1 - b G, + + and therefore `G = (p_1 - (F - b p_1) \circ G) / b`, which + allows recursive computation of `G`. + + .. SEEALSO:: + + The compositional inverse `\Omega` of the symmetric + function `h_1 + h_2 + \dots` can be handled much more + efficiently using specialized methods. See + :func:`~sage.combinat.species.generating_series.LogarithmCycleIndexSeries` + + AUTHORS: + + - Andrew Gainer-Dewar + - Martin Rubey + + """ + P = self.parent() + if P._arity != 1: + raise ValueError("arity must be equal to 1") + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + raise ValueError("compositional inverse does not exist") + R = P._laurent_poly_ring + if (isinstance(coeff_stream, Stream_exact) + and coeff_stream.order() >= 0 + and coeff_stream._degree == 2): + # self = a + b * p_1; self.revert() = -a/b + 1/b * p_1 + a = coeff_stream[0] + b = coeff_stream[1][Partition([1])] + X = R(Partition([1])) + coeff_stream = Stream_exact((-a/b, 1/b * X), + order=0) + return P.element_class(P, coeff_stream) + + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! + if coeff_stream[0]: + raise ValueError("cannot determine whether the compositional inverse exists") + + la = Partition([1]) + X = R(la) + + def coefficient(n): + if n: + return 0 + c = coeff_stream[1][la] + if c.is_unit(): + return ~c + raise ValueError("compositional inverse does not exist") + + b = P(lambda n: 0 if n else coeff_stream[1][la]) # TODO: we want a lazy version of Stream_exact + b_inv = P(coefficient) # TODO: we want a lazy version of Stream_exact + g = P.undefined(valuation=1) + g.define(b_inv * (X - (self - b * X)(g))) + return g + + plethystic_inverse = revert + + compositional_inverse = revert + + def derivative_with_respect_to_p1(self, n=1): + r""" + Return the symmetric function obtained by taking the + derivative of ``self`` with respect to the power-sum + symmetric function `p_1` when the expansion of ``self`` in + the power-sum basis is considered as a polynomial in `p_k`'s + (with `k \geq 1`). + + This is the same as skewing ``self`` by the first power-sum + symmetric function `p_1`. + + INPUT: + + - ``n`` -- (default: 1) nonnegative integer which determines + which power of the derivative is taken + + EXAMPLES: + + The species `E` of sets satisfies the relationship `E' = E`:: + + sage: h = SymmetricFunctions(QQ).h() + sage: T = LazySymmetricFunctions(h) + sage: E = T(lambda n: h[n]) + sage: E - E.derivative_with_respect_to_p1() + O^6 + + The species `C` of cyclic orderings and the species `L` of linear + orderings satisfy the relationship `C' = L`:: + + sage: p = SymmetricFunctions(QQ).p() + sage: C = T(lambda n: (sum(euler_phi(k)*p([k])**(n//k) for k in divisors(n))/n if n > 0 else 0)) + sage: L = T(lambda n: p([1]*n)) + sage: L - C.derivative_with_respect_to_p1() + O^6 + + TESTS:: + + sage: T = LazySymmetricFunctions(p) + sage: a = T(p([1,1,1])) + sage: a.derivative_with_respect_to_p1() + (3*p[1,1]) + O^9 + sage: a.derivative_with_respect_to_p1(1) + (3*p[1,1]) + O^9 + sage: a.derivative_with_respect_to_p1(2) + 6*p[1] + O^8 + sage: a.derivative_with_respect_to_p1(3) + 6*p[] + O^7 + """ + P = self.parent() + if P._arity != 1: + raise ValueError("arity must be equal to 1") + + coeff_stream = Stream_map_coefficients(self._coeff_stream, + lambda c: c.derivative_with_respect_to_p1(n), + P.is_sparse()) + coeff_stream = Stream_shift(coeff_stream, -n) return P.element_class(P, coeff_stream) - plethysm = __call__ + def functorial_composition(self, *args): + r""" + Return the functorial composition of ``self`` and ``g``. + + Let `X` be a finite set of cardinality `m`. For a group + action of the symmetric group `g: S_n \to S_X` and a + (possibly virtual) representation of the symmetric group on + `X`, `f: S_X \to GL(V)`, the functorial composition is the + (virtual) representation of the symmetric group `f \Box g: + S_n \to GL(V)` given by `\sigma \mapsto f(g(\sigma))`. + + This is more naturally phrased in the language of + combinatorial species. Let `F` and `G` be species, then + their functorial composition is the species `F \Box G` with + `(F \Box G) [A] = F[ G[A] ]`. In other words, an `(F \Box + G)`-structure on a set `A` of labels is an `F`-structure + whose labels are the set of all `G`-structures on `A`. + + The Frobenius character (or cycle index series) of `F \Box G` + can be computed as follows, see section 2.2 of [BLL]_): + + .. MATH:: + + \sum_{n \geq 0} \frac{1}{n!} \sum_{\sigma \in + \mathfrak{S}_{n}} \operatorname{fix} F[ (G[\sigma])_{1}, + (G[\sigma])_{2}, \ldots ] \, p_{1}^{\sigma_{1}} + p_{2}^{\sigma_{2}} \cdots. + + .. WARNING:: + + The operation `f \Box g` only makes sense when `g` + corresponds to a permutation representation, i.e., a + group action. + + EXAMPLES: + + The species `G` of simple graphs can be expressed in terms of + a functorial composition: `G = \mathfrak{p} \Box + \mathfrak{p}_{2}`, where `\mathfrak{p}` is the + :class:`~sage.combinat.species.subset_species.SubsetSpecies`.:: + + sage: R.<q> = QQ[] + sage: h = SymmetricFunctions(R).h() + sage: m = SymmetricFunctions(R).m() + sage: L = LazySymmetricFunctions(m) + sage: P = L(lambda n: sum(q^k*h[n-k]*h[k] for k in range(n+1))) + sage: P2 = L(lambda n: h[2]*h[n-2], valuation=2) + sage: P.functorial_composition(P2)[:4] + [m[], + m[1], + (q+1)*m[1, 1] + (q+1)*m[2], + (q^3+3*q^2+3*q+1)*m[1, 1, 1] + (q^3+2*q^2+2*q+1)*m[2, 1] + (q^3+q^2+q+1)*m[3]] + + For example, there are:: + + sage: P.functorial_composition(P2)[4].coefficient([4])[3] + 3 + + unlabelled graphs on 4 vertices and 3 edges, and:: + + sage: P.functorial_composition(P2)[4].coefficient([2,2])[3] + 8 + + labellings of their vertices with two 1's and two 2's. + + The symmetric function `h_1 \sum_n h_n` is the neutral + element with respect to functorial composition:: + + sage: p = SymmetricFunctions(QQ).p() + sage: h = SymmetricFunctions(QQ).h() + sage: e = SymmetricFunctions(QQ).e() + sage: L = LazySymmetricFunctions(h) + sage: E = L(lambda n: h[n]) + sage: Ep = p[1]*E.derivative_with_respect_to_p1(); Ep + h[1] + (h[1,1]) + (h[2,1]) + (h[3,1]) + (h[4,1]) + (h[5,1]) + O^7 + sage: f = L(lambda n: h[n-n//2, n//2]) + sage: f - Ep.functorial_composition(f) + O^7 + + The functorial composition distributes over the sum:: + + sage: F1 = L(lambda n: h[n]) + sage: F2 = L(lambda n: e[n]) + sage: f1 = F1.functorial_composition(f) + sage: f2 = F2.functorial_composition(f) + sage: (F1 + F2).functorial_composition(f) - f1 - f2 # long time + O^7 + + TESTS: + + Check a corner case:: + + sage: h = SymmetricFunctions(QQ).h() + sage: L = LazySymmetricFunctions(h) + sage: L(h[2,1]).functorial_composition(3*h[0]) + 3*h[] + O^7 + + Check an instance of a non-group action:: + + sage: s = SymmetricFunctions(QQ).s() + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(p) + sage: f = L(lambda n: s[n]) + sage: g = 2*s[2, 1, 1] + s[2, 2] + 3*s[4] + sage: r = f.functorial_composition(g); r[4] + Traceback (most recent call last): + ... + ValueError: the argument is not the Frobenius character of a permutation representation + + """ + if len(args) != self.parent()._arity: + raise ValueError("arity must be equal to the number of arguments provided") + from sage.combinat.sf.sfa import is_SymmetricFunction + if not all(isinstance(g, LazySymmetricFunction) + or is_SymmetricFunction(g) + or not g for g in args): + raise ValueError("all arguments must be (possibly lazy) symmetric functions") + + if len(args) == 1: + g = args[0] + P = g.parent() + if isinstance(g, LazySymmetricFunction): + R = P._laurent_poly_ring + else: + from sage.rings.lazy_series_ring import LazySymmetricFunctions + R = g.parent() + P = LazySymmetricFunctions(R) + g = P(g) + + p = R.realization_of().p() + # TODO: does the following introduce a memory leak? + g = Stream_map_coefficients(g._coeff_stream, p, P.is_sparse()) + f = Stream_map_coefficients(self._coeff_stream, p, P.is_sparse()) + + def g_cycle_type(s, n): + # the cycle type of G[sigma] of any permutation sigma + # with cycle type s, which is a partition of n + if not n: + if g[0]: + return Partition([1]*ZZ(g[0].coefficient([]))) + return Partition([]) + res = [] + # in the species case, k is at most + # factorial(n) * g[n].coefficient([1]*n) + for k in range(1, lcm(s) + 1): + e = 0 + for d in divisors(k): + m = moebius(d) + if not m: + continue + u = s.power(k // d) + # it could be, that we never need to compute + # g[n], so we only do this here + g_u = g[n] + if g_u: + e += m * u.aut() * g_u.coefficient(u) + # e / k might not be an integer if g is not a + # group action, so it is good to check + res.extend([k] * ZZ(e / k)) + res.reverse() + return Partition(res) + + def coefficient(n): + terms = {} + t_size = None + for s in Partitions(n): + t = g_cycle_type(s, n) + if t_size is None: + t_size = sum(t) + f_t = f[t_size] + if not f_t: + break + elif t_size != sum(t): + raise ValueError("the argument is not the Frobenius character of a permutation representation") + + terms[s] = t.aut() * f_t.coefficient(t) / s.aut() + return R(p.element_class(p, terms)) + + coeff_stream = Stream_function(coefficient, P._sparse, 0) + return P.element_class(P, coeff_stream) + else: + raise NotImplementedError("only implemented for arity 1") + + def arithmetic_product(self, *args, check=True): + r""" + Return the arithmetic product of ``self`` with ``g``. + + The arithmetic product is a binary operation `\boxdot` on the + ring of symmetric functions which is bilinear in its two + arguments and satisfies + + .. MATH:: + + p_{\lambda} \boxdot p_{\mu} = \prod\limits_{i \geq 1, j \geq 1} + p_{\mathrm{lcm}(\lambda_i, \mu_j)}^{\mathrm{gcd}(\lambda_i, \mu_j)} + + for any two partitions `\lambda = (\lambda_1, \lambda_2, \lambda_3, + \dots )` and `\mu = (\mu_1, \mu_2, \mu_3, \dots )` (where `p_{\nu}` + denotes the power-sum symmetric function indexed by the partition + `\nu`, and `p_i` denotes the `i`-th power-sum symmetric function). + This is enough to define the arithmetic product if the base ring + is torsion-free as a `\ZZ`-module; for all other cases the + arithmetic product is uniquely determined by requiring it to be + functorial in the base ring. See + http://mathoverflow.net/questions/138148/ for a discussion of + this arithmetic product. + + .. WARNING:: + + The operation `f \boxdot g` was originally defined only + for symmetric functions `f` and `g` without constant + term. We extend this definition using the convention + that the least common multiple of any integer with `0` is + `0`. + + If `f` and `g` are two symmetric functions which are homogeneous + of degrees `a` and `b`, respectively, then `f \boxdot g` is + homogeneous of degree `ab`. + + The arithmetic product is commutative and associative and has + unity `e_1 = p_1 = h_1`. + + For species `M` and `N` such that `M[\varnothing] = + N[\varnothing] = \varnothing`, their arithmetic product is + the species `M \boxdot N` of "`M`-assemblies of cloned + `N`-structures". This operation is defined and several + examples are given in [MM2008]_. + + INPUT: + + - ``g`` -- a cycle index series having the same parent as ``self`` + + - ``check`` -- (default: ``True``) a Boolean which, when set + to ``False``, will cause input checks to be skipped + + OUTPUT: + + The arithmetic product of ``self`` with ``g``. + + .. SEEALSO:: + + :meth:`sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.arithmetic_product` + + EXAMPLES: + + For `C` the species of (oriented) cycles and `L_{+}` the + species of nonempty linear orders, `C \boxdot L_{+}` + corresponds to the species of "regular octopuses"; a `(C + \boxdot L_{+})`-structure is a cycle of some length, each of + whose elements is an ordered list of a length which is + consistent for all the lists in the structure. :: + + sage: R.<q> = QQ[] + sage: p = SymmetricFunctions(R).p() + sage: m = SymmetricFunctions(R).m() + sage: L = LazySymmetricFunctions(m) + + sage: C = species.CycleSpecies().cycle_index_series() + sage: c = L(lambda n: C[n]) + sage: Lplus = L(lambda n: p([1]*n), valuation=1) + sage: r = c.arithmetic_product(Lplus); r + m[1] + (3*m[1,1]+2*m[2]) + + (8*m[1,1,1]+4*m[2,1]+2*m[3]) + + (42*m[1,1,1,1]+21*m[2,1,1]+12*m[2,2]+7*m[3,1]+3*m[4]) + + (144*m[1,1,1,1,1]+72*m[2,1,1,1]+36*m[2,2,1]+24*m[3,1,1]+12*m[3,2]+6*m[4,1]+2*m[5]) + + ... + + O^7 + + In particular, the number of regular octopuses is:: + + sage: [r[n].coefficient([1]*n) for n in range(8)] + [0, 1, 3, 8, 42, 144, 1440, 5760] + + It is shown in [MM2008]_ that the exponential generating + function for regular octopuses satisfies `(C \boxdot L_{+}) + (x) = \sum_{n \geq 1} \sigma (n) (n - 1)! \frac{x^{n}}{n!}` + (where `\sigma (n)` is the sum of the divisors of `n`). :: + + sage: [sum(divisors(i))*factorial(i-1) for i in range(1,8)] + [1, 3, 8, 42, 144, 1440, 5760] + + AUTHORS: + + - Andrew Gainer-Dewar (2013) + + REFERENCES: + + - [MM2008]_ + + TESTS: + + Check that the product with zero works:: + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LazySymmetricFunctions(s) + sage: L(0).arithmetic_product(s[2]) + 0 + sage: L(s[2]).arithmetic_product(0) + 0 + + Check that the arithmetic product of symmetric functions of + finite support works:: + + sage: L(s([2])).arithmetic_product(s([1,1,1])) + s[2, 2, 1, 1] + s[3, 1, 1, 1] + s[3, 2, 1] + s[3, 3] + 2*s[4, 1, 1] + + sage: f = 1/(1-L(s[1])) + sage: f.arithmetic_product(s[1]) - f + O^7 + + Check that the arithmetic product of symmetric functions with + constant a term works as advertised:: + + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(p) + sage: L(5).arithmetic_product(3*p[2,1]) + 15*p[] + + Check the arithmetic product of symmetric functions over a + finite field works:: + + sage: s = SymmetricFunctions(FiniteField(2)).s() + sage: L = LazySymmetricFunctions(s) + sage: L(s([2])).arithmetic_product(s([1,1,1])) + s[2, 2, 1, 1] + s[3, 1, 1, 1] + s[3, 2, 1] + s[3, 3] + + """ + if len(args) != self.parent()._arity: + raise ValueError("arity must be equal to the number of arguments provided") + from sage.combinat.sf.sfa import is_SymmetricFunction + if not all(isinstance(g, LazySymmetricFunction) + or is_SymmetricFunction(g) + or not g for g in args): + raise ValueError("all arguments must be (possibly lazy) symmetric functions") + + if len(args) == 1: + g = args[0] + P = g.parent() + + # f = 0 or g = (0, ..., 0) + if (isinstance(self._coeff_stream, Stream_zero) + or (not isinstance(g, LazyModuleElement) and not g) + or (isinstance(g, LazyModuleElement) + and isinstance(g._coeff_stream, Stream_zero))): + return P.zero() + + if (isinstance(self._coeff_stream, Stream_exact) + and not self._coeff_stream._constant): + + if not isinstance(g, LazySymmetricFunction): + f = self.symmetric_function() + return f.arithmetic_product(g) + + if (isinstance(g._coeff_stream, Stream_exact) + and not g._coeff_stream._constant): + f = self.symmetric_function() + gs = g.symmetric_function() + return P(f.arithmetic_product(gs)) + + if isinstance(g, LazySymmetricFunction): + R = P._laurent_poly_ring + else: + from sage.rings.lazy_series_ring import LazySymmetricFunctions + R = g.parent() + P = LazySymmetricFunctions(R) + g = P(g) + + # compute the constant term in the case where not both f + # and g have finite support + # TODO: this should be done lazily if possible + c = R.zero() + if self[0]: + if (isinstance(g._coeff_stream, Stream_exact) + and not g._coeff_stream._constant): + gs = g.symmetric_function() + c += self[0].arithmetic_product(gs) + elif check: + raise ValueError("can only take the arithmetic product with a positive valuation series") + if g[0]: + if (isinstance(self._coeff_stream, Stream_exact) + and not self._coeff_stream._constant): + fs = self.symmetric_function() + c += fs.arithmetic_product(g[0]) + elif check: + raise ValueError("can only take the arithmetic product with a positive valuation series") + + p = R.realization_of().p() + # TODO: does the following introduce a memory leak? + g = Stream_map_coefficients(g._coeff_stream, p, P.is_sparse()) + f = Stream_map_coefficients(self._coeff_stream, p, P.is_sparse()) + + def coefficient(n): + if not n: + return c + index_set = ((d, n // d) for d in divisors(n)) + return sum(f[i].arithmetic_product(g[j]) + for i, j in index_set if f[i] and g[j]) + + coeff_stream = Stream_function(coefficient, P._sparse, 0) + return P.element_class(P, coeff_stream) + else: + raise NotImplementedError("only implemented for arity 1") def symmetric_function(self, degree=None): r""" @@ -3867,9 +6071,10 @@ def symmetric_function(self, degree=None): OUTPUT: - If ``degree`` is not ``None``, the terms of the series of degree greater - than ``degree`` are truncated first. If ``degree`` is ``None`` and the - series is not a polynomial polynomial, a ``ValueError`` is raised. + If ``degree`` is not ``None``, the terms of the series of + degree greater than ``degree`` are first truncated. If + ``degree`` is ``None`` and the series is not a polynomial + polynomial, a ``ValueError`` is raised. EXAMPLES:: @@ -3904,6 +6109,7 @@ def symmetric_function(self, degree=None): 0 sage: f4.symmetric_function(0) s[] + """ S = self.parent() R = S._laurent_poly_ring @@ -3913,7 +6119,7 @@ def symmetric_function(self, degree=None): if degree is None: if (isinstance(self._coeff_stream, Stream_exact) - and not self._coeff_stream._constant): + and not self._coeff_stream._constant): m = self._coeff_stream._degree else: raise ValueError("not a symmetric function") @@ -3922,6 +6128,7 @@ def symmetric_function(self, degree=None): return R.sum(self[:m]) + class LazyDirichletSeries(LazyModuleElement): r""" A Dirichlet series where the coefficients are computed lazily. @@ -3942,6 +6149,30 @@ class LazyDirichletSeries(LazyModuleElement): sage: g == f True """ + def is_unit(self): + """ + Return whether this element is a unit in the ring. + + EXAMPLES:: + + sage: D = LazyDirichletSeriesRing(ZZ, "s") + sage: D([0, 2]).is_unit() + False + + sage: D([-1, 2]).is_unit() + True + + sage: D([3, 2]).is_unit() + False + + sage: D = LazyDirichletSeriesRing(QQ, "s") + sage: D([3, 2]).is_unit() + True + """ + if self.is_zero(): # now 0 != 1 + return False + return self[1].is_unit() + def valuation(self): r""" Return the valuation of ``self``. @@ -4028,13 +6259,13 @@ def _mul_(self, other): and not left._constant and left._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) and left.order() == 1): - return other # self == 1 + return other # self == 1 if (isinstance(right, Stream_exact) and not right._constant and right._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) and right.order() == 1): - return self # other == 1 - coeff = Stream_dirichlet_convolve(left, right) + return self # other == 1 + coeff = Stream_dirichlet_convolve(left, right, P.is_sparse()) # Performing exact arithmetic is slow because the series grow large # very quickly as we are multiplying the degree #if (isinstance(left, Stream_exact) and not left._constant @@ -4044,7 +6275,7 @@ def _mul_(self, other): # deg = (left._degree - 1) * (right._degree - 1) + 1 # order = left._approximate_order * right._approximate_order # coeff_vals = [coeff[i] for i in range(order, deg)] - # return P.element_class(P, Stream_exact(coeff_vals, coeff._is_sparse, + # return P.element_class(P, Stream_exact(coeff_vals, # constant=left._constant, order=order, degree=deg)) return P.element_class(P, coeff) @@ -4061,9 +6292,24 @@ def __invert__(self): sage: ~L(constant=1) - L(moebius) O(1/(8^z)) + Trying to invert a non-invertible 'exact' series raises a + ``ZeroDivisionError``:: + + sage: f = ~L([0,1], constant=1) + sage: f[1] + Traceback (most recent call last): + ... + ZeroDivisionError: the Dirichlet inverse only exists if the coefficient with index 1 is non-zero + + sage: f = ~L(lambda n: n-1) + sage: f[1] + Traceback (most recent call last): + ... + ZeroDivisionError: rational division by zero """ P = self.parent() - return P.element_class(P, Stream_dirichlet_invert(self._coeff_stream)) + return P.element_class(P, Stream_dirichlet_invert(self._coeff_stream, + P.is_sparse())) def __call__(self, p, *, check=True): r""" @@ -4165,7 +6411,7 @@ def coefficient(m): except ValueError: return ZZ.zero() R = P._internal_poly_ring.base_ring() - return P.element_class(P, Stream_function(coefficient, R, P._sparse, 1)) + return P.element_class(P, Stream_function(coefficient, P._sparse, 1)) def _format_series(self, formatter, format_strings=False): """ diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 94a3c4ac465..b8613ba2b9c 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -9,11 +9,16 @@ :delim: | :class:`LazyLaurentSeriesRing` | The ring of lazy Laurent series. - :class:`LazyTaylorSeriesRing` | The ring of (possibly multivariate) lazy Taylor series. - :class:`LazyCompletionGradedAlgebra` | The completion of a graded alebra consisting of formal series. + :class:`LazyPowerSeriesRing` | The ring of (possibly multivariate) lazy Taylor series. + :class:`LazyCompletionGradedAlgebra` | The completion of a graded algebra consisting of formal series. :class:`LazySymmetricFunctions` | The ring of (possibly multivariate) lazy symmetric functions. :class:`LazyDirichletSeriesRing` | The ring of lazy Dirichlet series. +.. SEEALSO:: + + :class:`sage.rings.padics.generic_nodes.pAdicRelaxedGeneric`, + :func:`sage.rings.padics.factory.ZpER` + AUTHORS: - Kwankyu Lee (2019-02-24): initial version @@ -38,7 +43,9 @@ from sage.structure.element import parent from sage.categories.algebras import Algebras +from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis from sage.categories.rings import Rings +from sage.categories.unique_factorization_domains import UniqueFactorizationDomains from sage.categories.integral_domains import IntegralDomains from sage.categories.fields import Fields from sage.categories.complete_discrete_valuation import (CompleteDiscreteValuationFields, @@ -51,7 +58,8 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.lazy_series import (LazyModuleElement, LazyLaurentSeries, - LazyTaylorSeries, + LazyPowerSeries, + LazyPowerSeries_gcd_mixin, LazyCompletionGradedAlgebraElement, LazySymmetricFunction, LazyDirichletSeries) @@ -61,14 +69,35 @@ from sage.data_structures.stream import ( Stream_zero, Stream_function, + Stream_iterator, Stream_exact, Stream_uninitialized ) +from types import GeneratorType + class LazySeriesRing(UniqueRepresentation, Parent): """ Abstract base class for lazy series. """ + # This will never be called directly (as it is an ABC), but we copy it + # for use in other subclasses. + @staticmethod + def __classcall_private__(cls, base_ring, names, sparse=True, *args, **kwds): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: Lp = LazyLaurentSeriesRing(QQ, 'z') + sage: L is Lp + True + """ + from sage.structure.category_object import normalize_names + names = normalize_names(-1, names) + return super().__classcall__(cls, base_ring, names, sparse, *args, **kwds) + def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, coefficients=None): r""" Construct a lazy series from ``x``. @@ -115,7 +144,16 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No coefficients when evaluated at integers. Examples are provided below. - EXAMPLES:: + .. WARNING:: + + If ``x`` is provided as a list, any trailing zeros are + ignored, because ``x`` is immediately converted into a + polynomial. + + EXAMPLES: + + If ``x`` can be converted into an element of the underlying + Laurent polynomial ring, we do this:: sage: L = LazyLaurentSeriesRing(GF(2), 'z') sage: L(2) @@ -123,18 +161,29 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: L(3) 1 - sage: L.<z> = LazyLaurentSeriesRing(ZZ) + In particular, ``x`` can be a Laurent polynomial:: + + sage: P.<x> = LaurentPolynomialRing(QQ) + sage: p = x^-2 + 3*x^3 + sage: L.<x> = LazyLaurentSeriesRing(ZZ) + sage: L(p) + x^-2 + 3*x^3 + + sage: L(p, valuation=0) + 1 + 3*x^5 + + sage: L(p, valuation=1) + x + 3*x^6 + + If ``x`` is callable, its evaluation at the integers, + beginning at ``valuation``, defines the coefficients of the series:: + sage: L.<z> = LazyLaurentSeriesRing(ZZ) sage: L(lambda i: i, valuation=5, constant=1, degree=10) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + O(z^13) sage: L(lambda i: i, valuation=5, constant=(1, 10)) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + O(z^13) - sage: X = L(constant=5, degree=2); X - 5*z^2 + 5*z^3 + 5*z^4 + O(z^5) - sage: X.valuation() - 2 - sage: def g(i): ....: if i < 0: ....: return 1 @@ -151,6 +200,13 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: f[30] -219 + We can omit ``x``, when defining a series with constant coefficients:: + + sage: X = L(constant=5, degree=2); X + 5*z^2 + 5*z^3 + 5*z^4 + O(z^5) + sage: X.valuation() + 2 + sage: L(valuation=2, constant=1) z^2 + z^3 + z^4 + O(z^5) sage: L(constant=1) @@ -171,19 +227,15 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + O(z^13) - Finally, ``x`` can be a Laurent polynomial:: - - sage: P.<x> = LaurentPolynomialRing(QQ) - sage: p = x^-2 + 3*x^3 - sage: L.<x> = LazyLaurentSeriesRing(ZZ) - sage: L(p) - x^-2 + 3*x^3 - - sage: L(p, valuation=0) - 1 + 3*x^5 + If ``x`` is explicitly passed as ``None``, the resulting + series is undefined. This can be used to define it + implicitly, see + :meth:`sage.rings.lazy_series.LazyModuleElement.define`:: - sage: L(p, valuation=1) - x + 3*x^6 + sage: f = L(None, valuation=-1) + sage: f.define(z^-1 + z^2*f^2) + sage: f + z^-1 + 1 + 2*z + 5*z^2 + 14*z^3 + 42*z^4 + 132*z^5 + O(z^6) We construct a lazy Laurent series over another lazy Laurent series:: @@ -234,6 +286,49 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: s[1] 0 + Converting various series from a univariate power series:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: R = LazyPowerSeriesRing(ZZ, 'z') + sage: L.has_coerce_map_from(R) + True + sage: L(R(lambda n: n)) + z + z^3 + z^5 + O(z^7) + sage: L(R([2,4,6])) == L.zero() + True + sage: L(R([2,4,6], valuation=2, constant=4)) == L.zero() + True + sage: L(R([2,4,6], valuation=2, constant=5)) + z^5 + z^6 + z^7 + O(z^8) + sage: L(R([2,3,4], valuation=2, constant=4)) + z^3 + sage: L(R([2,3,4], valuation=2, constant=5)) + z^3 + z^5 + z^6 + z^7 + O(z^8) + + Can only convert from known to be constant multivariate power series:: + + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: R.<x,y> = LazyPowerSeriesRing(QQ) + sage: L(R(2)) + 2 + sage: L(R.zero()) + 0 + sage: L(x) + Traceback (most recent call last): + ... + ValueError: unable to convert ... + sage: L(1 / (1 - x - y)) + Traceback (most recent call last): + ... + ValueError: unable to convert ... + sage: P.<x,y> = QQ[] + sage: f = R(lambda n: (x+y)^n if n == 0 else P.zero()); f + 1 + O(x,y)^7 + sage: L(f) + Traceback (most recent call last): + ... + ValueError: unable to convert ... + TESTS: Checking the valuation is consistent:: @@ -260,7 +355,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: L(z^3/(1-z), valuation=0) 1 + z + z^2 + O(z^3) - sage: L = LazyLaurentSeriesRing(ZZ, 'z') sage: L(lambda n: 1/(n+1), degree=3) Traceback (most recent call last): ... @@ -328,10 +422,10 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: f == g True - .. TODO:: - - Add a method to change the sparse/dense implementation. + We support passing a generator:: + sage: L(filter(is_odd, NN), -3) + z^-3 + 3*z^-2 + 5*z^-1 + 7 + 9*z + 11*z^2 + 13*z^3 + O(z^4) """ if valuation is not None and valuation not in ZZ: raise ValueError("the valuation must be an integer") @@ -339,8 +433,10 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if x is None and coefficients is None: if valuation is None: raise ValueError("the valuation must be specified") - return self.element_class(self, Stream_uninitialized(self._sparse, valuation)) + return self.element_class(self, Stream_uninitialized(valuation)) + # WARNING: if x is not explicitly specified as None, it is + # set to 0 by Parent.__call__ if coefficients is not None and (x is not None and (not isinstance(x, int) or x)): raise ValueError("coefficients must be None if x is provided") @@ -355,6 +451,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if coefficients is None: # Try to build stuff using the internal polynomial ring constructor R = self._internal_poly_ring + try: x = R(x) except (TypeError, ValueError): @@ -371,19 +468,15 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No raise ValueError("you must specify the degree for the polynomial 0") degree = valuation if x == R.zero(): - coeff_stream = Stream_exact([], self._sparse, order=degree, constant=constant) + coeff_stream = Stream_exact([], order=degree, constant=constant) return self.element_class(self, coeff_stream) initial_coefficients = [x[i] for i in range(x.valuation(), x.degree() + 1)] - coeff_stream = Stream_exact(initial_coefficients, self._sparse, - order=x.valuation(), constant=constant, degree=degree) + coeff_stream = Stream_exact(initial_coefficients, + order=x.valuation(), degree=degree, constant=constant) return self.element_class(self, coeff_stream) # Handle when it is a lazy series if isinstance(x, self.Element): - if x._coeff_stream._is_sparse is not self._sparse: - # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") - # If x is known to be 0 if isinstance(x._coeff_stream, Stream_zero): if not constant: @@ -394,8 +487,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if valuation is None: raise ValueError("you must specify the degree for the polynomial 0") degree = valuation - coeff_stream = Stream_exact([], self._sparse, order=degree, - constant=constant) + coeff_stream = Stream_exact([], order=degree, constant=constant) return self.element_class(self, coeff_stream) # Make the result exact @@ -411,8 +503,8 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No # We learned some stuff about x; pass it along x._coeff_stream._approximate_order += len(initial_coefficients) initial_coefficients = [] - coeff_stream = Stream_exact(initial_coefficients, self._sparse, - order=valuation, constant=constant, degree=degree) + coeff_stream = Stream_exact(initial_coefficients, + order=valuation, degree=degree, constant=constant) return self.element_class(self, coeff_stream) # We are just possibly shifting the result @@ -421,26 +513,61 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No return ret return ret.shift(valuation - x._coeff_stream.order()) + # Handle when it is a power series + if isinstance(x, LazyPowerSeries): + stream = x._coeff_stream + if isinstance(stream, Stream_zero): + return self.zero() + elif isinstance(stream, Stream_exact): + BR = self.base_ring() + if x.parent()._arity != 1: + # Special case for constant series + if stream._degree == 1: + return self(BR(stream[0])) + else: + coeffs = [BR(val) for val in stream._initial_coefficients] + valuation = stream._approximate_order + for i, c in enumerate(coeffs): + if c: + valuation += i + coeffs = coeffs[i:] + break + else: + valuation += len(coeffs) + coeffs = [] + return self(coeffs, + degree=stream._degree, + constant=BR(stream._constant), + valuation=valuation) + elif x.parent()._arity == 1: + return self.element_class(self, stream) + raise ValueError(f"unable to convert {x} into {self}") + else: x = coefficients - if callable(x): + if callable(x) or isinstance(x, (GeneratorType, map, filter)): if valuation is None: raise ValueError("the valuation must be specified") if degree is None: if constant is not None: raise ValueError("constant may only be specified if the degree is specified") - coeff_stream = Stream_function(x, self.base_ring(), self._sparse, valuation) + if callable(x): + coeff_stream = Stream_function(lambda i: BR(x(i)), self._sparse, valuation) + else: + coeff_stream = Stream_iterator(map(BR, _skip_leading_zeros(x)), valuation) return self.element_class(self, coeff_stream) # degree is not None if constant is None: constant = BR.zero() - p = [BR(x(i)) for i in range(valuation, degree)] + if callable(x): + p = [BR(x(i)) for i in range(valuation, degree)] + else: + p = [BR(c) for c, _ in zip(_skip_leading_zeros(x), range(valuation, degree))] if not any(p) and not constant: return self.zero() - coeff_stream = Stream_exact(p, self._sparse, order=valuation, - constant=constant, degree=degree) + coeff_stream = Stream_exact(p, order=valuation, constant=constant, degree=degree) return self.element_class(self, coeff_stream) raise ValueError(f"unable to convert {x} into {self}") @@ -454,18 +581,33 @@ def undefined(self, valuation=None): - ``valuation`` -- integer; a lower bound for the valuation of the series Power series can be defined recursively (see - :meth:`sage.rings.lazy_series.LazyModuleElement.define()` for + :meth:`sage.rings.lazy_series.LazyModuleElement.define` for more examples). + .. SEEALSO:: + + :meth:`sage.rings.padics.generic_nodes.pAdicRelaxedGeneric.unknown` + EXAMPLES:: - sage: L.<z> = LazyTaylorSeriesRing(QQ) + sage: L.<z> = LazyPowerSeriesRing(QQ) sage: s = L.undefined(1) sage: s.define(z + (s^2+s(z^2))/2) sage: s z + z^2 + z^3 + 2*z^4 + 3*z^5 + 6*z^6 + 11*z^7 + O(z^8) + + Alternatively:: + + sage: L.<z> = LazyLaurentSeriesRing(QQ) + sage: f = L(None, valuation=-1) + sage: f.define(z^-1 + z^2*f^2) + sage: f + z^-1 + 1 + 2*z + 5*z^2 + 14*z^3 + 42*z^4 + 132*z^5 + O(z^6) """ - return self(None, valuation=valuation) + if valuation is None: + valuation = self._minimal_valuation + coeff_stream = Stream_uninitialized(valuation) + return self.element_class(self, coeff_stream) unknown = undefined @@ -486,8 +628,9 @@ class options(GlobalOptions): sage: LLS.<z> = LazyLaurentSeriesRing(QQ) sage: LLS.options Current options for lazy series rings - - constant_length: 3 - - display_length: 7 + - constant_length: 3 + - display_length: 7 + - halting_precision: None sage: LLS.options.display_length 7 @@ -520,6 +663,9 @@ class options(GlobalOptions): constant_length = dict(default=3, description='the number of coefficients to display for nonzero constant series', checker=lambda x: x in ZZ and x > 0) + halting_precision = dict(default=None, + description='the number of coefficients, beginning with the approximate valuation, to check in equality tests', + checker=lambda x: x is None or x in ZZ and x > 0) @cached_method def one(self): @@ -532,7 +678,7 @@ def one(self): sage: L.one() 1 - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') sage: L.one() 1 @@ -543,7 +689,7 @@ def one(self): """ R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, constant=R.zero(), order=0) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=0) return self.element_class(self, coeff_stream) @cached_method @@ -566,11 +712,11 @@ def zero(self): sage: L.zero() 0 - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') sage: L.zero() 0 """ - return self.element_class(self, Stream_zero(self._sparse)) + return self.element_class(self, Stream_zero()) def characteristic(self): """ @@ -588,7 +734,7 @@ def characteristic(self): sage: R.characteristic() 11 - sage: R.<x, y> = LazyTaylorSeriesRing(GF(7)); R + sage: R.<x, y> = LazyPowerSeriesRing(GF(7)); R Multivariate Lazy Taylor Series Ring in x, y over Finite Field of size 7 sage: R.characteristic() 7 @@ -610,8 +756,22 @@ def _coerce_map_from_(self, S): True sage: L.has_coerce_map_from(GF(2)) True + sage: R = LazyPowerSeriesRing(ZZ, 'z') + sage: L.has_coerce_map_from(R) + True + + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: R = LazyPowerSeriesRing(QQ, 'z') + sage: L.has_coerce_map_from(R) + True + sage: R = LazyPowerSeriesRing(ZZ, 'z') + sage: L.has_coerce_map_from(R) + True + sage: R = LazyPowerSeriesRing(ZZ['t'], 'z') + sage: L.has_coerce_map_from(R) + False - sage: L = LazyTaylorSeriesRing(GF(2), 'z') + sage: L = LazyPowerSeriesRing(GF(2), 'z') sage: L.has_coerce_map_from(ZZ) True sage: L.has_coerce_map_from(GF(2)) @@ -628,7 +788,14 @@ def _coerce_map_from_(self, S): return True R = self._laurent_poly_ring - return R.has_coerce_map_from(S) + if R.has_coerce_map_from(S): + return True + + if (isinstance(S, LazySeriesRing) + and self._laurent_poly_ring.has_coerce_map_from(S._laurent_poly_ring)): + return True + + return None def _coerce_map_from_base_ring(self): """ @@ -690,8 +857,142 @@ def is_exact(self): """ return self.base_ring().is_exact() + def _test_invert(self, **options): + """ + Test multiplicative inversion of elements of ``self``. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES:: + + sage: LazyLaurentSeriesRing.options.halting_precision(5) + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: L._test_invert() + sage: LazyLaurentSeriesRing.options._reset() # reset the options + + .. SEEALSO:: + + :class:`TestSuite` + """ + tester = self._tester(**options) + + elements = tester.some_elements() + for x in elements: + # because of laziness, creating the inverse of x should + # always succeed except if the series is 'exact' + if not x.is_unit(): + continue + y = ~x + e = y * x + tester.assertFalse(x.is_zero(), "zero should not be invertible") + tester.assertTrue(e.is_one(), "an element (%s) times its inverse should be 1" % x) + tester.assertEqual(y.valuation(), -x.valuation(), "the valuation of the inverse should be the negative of the valuation of the element (%s)" % x) + + def _test_div(self, **options): + r""" + Test division of elements of this ring. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES:: + + sage: LazyLaurentSeriesRing.options.halting_precision(5) + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: L._test_div() + sage: LazyLaurentSeriesRing.options._reset() # reset the options + + .. SEEALSO:: + + :class:`TestSuite` + """ + from sage.misc.misc import some_tuples + tester = self._tester(**options) + + elements = list(tester.some_elements()) + for x, y in some_tuples(elements, 2, tester._max_runs): + # because of laziness, creating the inverse of x should + # always succeed except if the series is 'exact' + if not y.is_unit(): + continue + z = x / y + xx = z * y + try: + v_z = z.valuation() + except Exception as error: + raise ValueError("could not compute the valuation of the quotient (%s)/(%s): %s" % (x, y, error)) + else: + v_x = x.valuation() + v_y = y.valuation() + tester.assertEqual(v_z, v_x - v_y, "the valuation of the quotient should be the difference of the valuations of the elements (%s and %s)" % (x, y)) + tester.assertEqual(xx, x, "the element (%s) should be the quotient times the divisor (%s)" % (x, y)) + + def _test_revert(self, **options): + """ + Test compositional inverse of elements of this ring. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES:: + + sage: LazyLaurentSeriesRing.options.halting_precision(5) + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: L._test_revert() + sage: LazyLaurentSeriesRing.options._reset() + + .. SEEALSO:: + + :class:`TestSuite` + """ + if not hasattr(self.element_class, "revert") or self._arity != 1: + return + tester = self._tester(**options) + + elements = tester.some_elements() + count = 0 + for x in elements: + # because of laziness, creating the compositional inverse + # of x should always succeed, except if the series is + # 'exact' or if it has negative valuation + vx = x.valuation() + if (vx != 1 + and not (isinstance(x._coeff_stream, Stream_exact) + and ((vx == 0 + and x._coeff_stream._degree == 2 + and not x._coeff_stream._constant) + or (vx == -1 + and x._coeff_stream._degree == 0 + and not x._coeff_stream._constant)))): + continue + try: + y = x.revert() + except Exception as error: + raise AssertionError("compositional inverse of %s should exist: %s" % (x, error)) + try: + vy = y.valuation() + _ = y[vy] + except NotImplementedError: + pass + except (ValueError, TypeError): + tester.assertFalse(vx == 1 and x[vx].is_unit(), + ("the series %s should be reversible " + "- its valuation is one and its leading coefficient is a unit") % x) + else: + count += 1 + e1 = y(x) + e2 = x(y) + tester.assertEqual(e1, e2, "y(x) and x(y) differ for x = %s and y = %s" %(x, y)) + # tester.assertEqual(e1, self.gen()) + # 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. The ring of Laurent series over a ring with the usual arithmetic @@ -725,8 +1026,7 @@ class LazyLaurentSeriesRing(LazySeriesRing): Finite Field of size 3 Series can be defined by specifying a coefficient function - along with a valuation or a degree where after the series - is evenutally constant:: + and a valuation:: sage: R.<x,y> = QQ[] sage: L.<z> = LazyLaurentSeriesRing(R) @@ -746,9 +1046,9 @@ class LazyLaurentSeriesRing(LazySeriesRing): -5*z^-3 - 4*z^-2 - 3*z^-1 + 6 + (x + y)*z + (y^2 + x)*z^2 + x*z^3 + x*z^4 + x*z^5 + O(z^6) - Similarly, we can specify a polynomial or the initial - coefficients with anything that converts into the - corresponding Laurent polynomial ring:: + We can also specify a polynomial or the initial coefficients. + Additionally, we may specify that all coefficients are equal to a + given constant, beginning at a given degree:: sage: L([1, x, y, 0, x+y]) 1 + x*z + y*z^2 + (x + y)*z^4 @@ -779,8 +1079,8 @@ class LazyLaurentSeriesRing(LazySeriesRing): sage: L(x^-2 + 3 + x, valuation=-5, degree=0, constant=2) z^-5 + 3*z^-3 + z^-2 + 2 + 2*z + 2*z^2 + O(z^3) - We can also truncate, shift, and make eventually constant any - Laurent series:: + We can truncate a series, shift its coefficients, or replace all + coefficients beginning with a given degree by a constant:: sage: f = 1 / (z + z^2) sage: f @@ -797,20 +1097,20 @@ class LazyLaurentSeriesRing(LazySeriesRing): z - z^2 + z^3 + 5*z^4 + 5*z^5 + 5*z^6 + O(z^7) Power series can be defined recursively (see - :meth:`sage.rings.lazy_series.LazyModuleElement.define()` for + :meth:`sage.rings.lazy_series.LazyModuleElement.define` for more examples):: sage: L.<z> = LazyLaurentSeriesRing(ZZ) - sage: s = L(None, valuation=0) + sage: s = L.undefined(valuation=0) sage: s.define(1 + z*s^2) sage: s 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + O(z^7) - If we do not explcitly know the exact value of every coefficient, - then equality checking will depend on the computed coefficients. - If at a certain point we cannot prove two series are different - (which involves the coefficients we have computed), then we will - raise an error:: + If the series is not specified by a finite number of initial + coefficients and a constant for the remaining coefficients, then + equality checking will depend on the coefficients which have + already been computed. If this information is not enough to + check that two series are different we raise an error:: sage: f = 1 / (z + z^2); f z^-1 - 1 + z - z^2 + z^3 - z^4 + z^5 + O(z^6) @@ -836,50 +1136,77 @@ class LazyLaurentSeriesRing(LazySeriesRing): sage: L.<z> = LazyLaurentSeriesRing(ZZ, sparse=False) sage: L.is_sparse() False - """ Element = LazyLaurentSeries + # Follow the "generic" normalization + __classcall_private__ = LazySeriesRing.__classcall_private__ + def __init__(self, base_ring, names, sparse=True, category=None): """ Initialize ``self``. TESTS:: + sage: LazyLaurentSeriesRing.options.halting_precision(12) + sage: L = LazyLaurentSeriesRing(ZZ, 't') - sage: elts = L.some_elements()[:-2] # skip the non-exact elements - sage: TestSuite(L).run(elements=elts, skip=['_test_elements', '_test_associativity', '_test_distributivity', '_test_zero']) + sage: TestSuite(L).run() sage: L.category() Category of infinite commutative no zero divisors algebras over (euclidean domains and infinite enumerated sets and metric spaces) sage: L = LazyLaurentSeriesRing(QQ, 't') + sage: TestSuite(L).run() sage: L.category() Join of Category of complete discrete valuation fields and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets - sage: L = LazyLaurentSeriesRing(ZZ['x,y'], 't') + + sage: L = LazyLaurentSeriesRing(ZZ['x, y'], 't') + sage: TestSuite(L).run() sage: L.category() Category of infinite commutative no zero divisors algebras over (unique factorization domains and commutative algebras over (euclidean domains and infinite enumerated sets and metric spaces) and infinite sets) + + sage: L = LazyLaurentSeriesRing(GF(5), 't') + sage: TestSuite(L).run() + + sage: L = LazyLaurentSeriesRing(GF(5)['x'], 't') + sage: TestSuite(L).run() + + sage: L = LazyLaurentSeriesRing(GF(5)['x, y'], 't') + sage: TestSuite(L).run() + + sage: L = LazyLaurentSeriesRing(Zmod(6), 't') + sage: TestSuite(L).run(skip=['_test_revert']) + sage: L.category() + Category of infinite commutative algebras over + (finite commutative rings and subquotients of monoids + and quotients of semigroups and finite enumerated sets) + sage: E.<x,y> = ExteriorAlgebra(QQ) sage: L = LazyLaurentSeriesRing(E, 't') # not tested + + sage: LazyLaurentSeriesRing.options._reset() # reset the options """ self._sparse = sparse - # We always use the dense because our CS_exact is implemented densely - self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names) + if len(names) != 1: + raise ValueError("only univariate lazy Laurent series are implemented") + self._arity = 1 + self._minimal_valuation = None + self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names, sparse=sparse) self._internal_poly_ring = self._laurent_poly_ring category = Algebras(base_ring.category()) if base_ring in Fields(): category &= CompleteDiscreteValuationFields() - else: - if "Commutative" in base_ring.category().axioms(): - category = category.Commutative() - if base_ring in IntegralDomains(): - category &= IntegralDomains() + elif base_ring in IntegralDomains(): + category &= IntegralDomains() + elif "Commutative" in base_ring.category().axioms(): + category = category.Commutative() if base_ring.is_zero(): category = category.Finite() @@ -930,8 +1257,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, - constant=R.zero(), order=1) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=1) return self.element_class(self, coeff_stream) def ngens(self): @@ -971,12 +1297,12 @@ def _an_element_(self): sage: L = LazyLaurentSeriesRing(ZZ, 'z') sage: L.an_element() - z^-2 + 3*z^-1 + 2*z + z^2 + z^3 + z^4 + z^5 + O(z^6) + z^-2 + z^3 + z^4 + z^5 + O(z^6) """ - R = self.base_ring() - coeff_stream = Stream_exact([R.an_element(), 3, 0, 2*R.an_element(), 1], - self._sparse, order=-2, constant=R.one()) - return self.element_class(self, coeff_stream) + return self(self._laurent_poly_ring.an_element(), + valuation=-2, + degree=3, + constant=self.base_ring().an_element()) def some_elements(self): """ @@ -985,32 +1311,35 @@ def some_elements(self): EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.some_elements() + sage: L.some_elements()[:7] [0, 1, z, -3*z^-4 + z^-3 - 12*z^-2 - 2*z^-1 - 10 - 8*z + z^2 + z^3, - z^-2 + 3*z^-1 + 2*z + z^2 + z^3 + z^4 + z^5 + O(z^6), + z^-2 + z^3 + z^4 + z^5 + O(z^6), -2*z^-3 - 2*z^-2 + 4*z^-1 + 11 - z - 34*z^2 - 31*z^3 + O(z^4), 4*z^-2 + z^-1 + z + 4*z^2 + 9*z^3 + 16*z^4 + O(z^5)] sage: L = LazyLaurentSeriesRing(GF(2), 'z') - sage: L.some_elements() + sage: L.some_elements()[:7] [0, 1, z, z^-4 + z^-3 + z^2 + z^3, - z^-1 + z^2 + z^3 + z^4 + z^5 + O(z^6), + z^-2, 1 + z + z^3 + z^4 + z^6 + O(z^7), z^-1 + z + z^3 + O(z^5)] sage: L = LazyLaurentSeriesRing(GF(3), 'z') - sage: L.some_elements() + sage: L.some_elements()[:7] [0, 1, z, z^-3 + z^-1 + 2 + z + z^2 + z^3, - z^2 + z^3 + z^4 + z^5 + O(z^6), + z^-2, z^-3 + z^-2 + z^-1 + 2 + 2*z + 2*z^2 + O(z^3), z^-2 + z^-1 + z + z^2 + z^4 + O(z^5)] """ z = self.gen() elts = [self.zero(), self.one(), z, (z-3)*(z**-2+2+z)**2, self.an_element(), - (1 - 2*z**-3)/(1 - z + 3*z**2), self(lambda n: n**2, valuation=-2)] + (1 - 2*z**-3)/(1 - z + 3*z**2), + self(lambda n: n**2, valuation=-2), + self(lambda n: n**2, valuation=1), + self([3, 2, 1], valuation=1, constant=1)] return elts def series(self, coefficient, valuation, degree=None, constant=None): @@ -1088,7 +1417,7 @@ def series(self, coefficient, valuation, degree=None, constant=None): constant = self.base_ring().zero() if degree is None: degree = valuation + len(coefficient) - coeff_stream = Stream_exact(coefficient, self._sparse, order=valuation, + coeff_stream = Stream_exact(coefficient, order=valuation, constant=constant, degree=degree) return self.element_class(self, coeff_stream) @@ -1114,9 +1443,159 @@ def _monomial(self, c, n): """ return self._laurent_poly_ring(c).shift(n) + def uniformizer(self): + """ + Return a uniformizer of ``self``.. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: L.uniformizer() + z + """ + R = self.base_ring() + if R not in Fields(): + raise TypeError("the base ring is not a field") + return self.gen() + + def residue_field(self): + """ + Return the residue field of the ring of integers of ``self``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(QQ, 'z') + sage: L.residue_field() + Rational Field + """ + R = self.base_ring() + if R not in Fields(): + raise TypeError("the base ring is not a field") + return R + + # === special functions === + + + def q_pochhammer(self, q=None): + r""" + Return the infinite ``q``-Pochhammer symbol `(a; q)_{\infty}`, + where `a` is the variable of ``self``. + + This is also one version of the quantum dilogarithm or + the `q`-Exponential function. + + INPUT: + + - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: L.<z> = LazyLaurentSeriesRing(q.parent()) + sage: qpoch = L.q_pochhammer(q) + sage: qpoch + 1 + + (-1/(-q + 1))*z + + (q/(q^3 - q^2 - q + 1))*z^2 + + (-q^3/(-q^6 + q^5 + q^4 - q^2 - q + 1))*z^3 + + (q^6/(q^10 - q^9 - q^8 + 2*q^5 - q^2 - q + 1))*z^4 + + (-q^10/(-q^15 + q^14 + q^13 - q^10 - q^9 - q^8 + q^7 + q^6 + q^5 - q^2 - q + 1))*z^5 + + (q^15/(q^21 - q^20 - q^19 + q^16 + 2*q^14 - q^12 - q^11 - q^10 - q^9 + 2*q^7 + q^5 - q^2 - q + 1))*z^6 + + O(z^7) + + We show that `(z; q)_n = \frac{(z; q)_{\infty}}{(q^n z; q)_{\infty}}`:: + + sage: qpoch / qpoch(q*z) + 1 - z + O(z^7) + sage: qpoch / qpoch(q^2*z) + 1 + (-q - 1)*z + q*z^2 + O(z^7) + sage: qpoch / qpoch(q^3*z) + 1 + (-q^2 - q - 1)*z + (q^3 + q^2 + q)*z^2 - q^3*z^3 + O(z^7) + sage: qpoch / qpoch(q^4*z) + 1 + (-q^3 - q^2 - q - 1)*z + (q^5 + q^4 + 2*q^3 + q^2 + q)*z^2 + + (-q^6 - q^5 - q^4 - q^3)*z^3 + q^6*z^4 + O(z^7) + + We can also construct part of Euler's function:: + + sage: M.<a> = LazyLaurentSeriesRing(QQ) + sage: phi = sum(qpoch[i](q=a)*a^i for i in range(10)) + sage: phi[:20] == M.euler()[:20] + True + + TESTS:: + + sage: R = ZZ['q'].fraction_field() + sage: q = R.gen() + sage: L.<z> = LazyLaurentSeriesRing(LazyDirichletSeriesRing(R, "s")) + sage: z.q_pochhammer(q) + 1 + ((1/(q-1)))*z + ((q/(q^3-q^2-q+1)))*z^2 + ... + O(z^7) + + REFERENCES: + + - :wikipedia:`Q-Pochhammer_symbol` + - :wikipedia:`Quantum_dilogarithm` + - :wikipedia:`Q-exponential` + """ + if q is None: + q = ZZ['q'].fraction_field().gen() + if q not in self.base_ring(): + raise ValueError("q must be in the base ring") + from sage.arith.misc import binomial + qP = q.parent() + one = qP.one() + + def coeff(n): + return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) + return self(coefficients=coeff, valuation=0) + + def euler(self): + r""" + Return the Euler function as an element of ``self``. + + The *Euler function* is defined as + + .. MATH:: + + \phi(z) = (z; z)_{\infty} + = \sum_{n=0}^{\infty} (-1)^n q^{(3n^2-n)/2}. + + EXAMPLES:: + + sage: L.<q> = LazyLaurentSeriesRing(ZZ) + sage: phi = q.euler() + sage: phi + 1 - q - q^2 + q^5 + O(q^7) + + We verify that `1 / phi` gives the generating function + for all partitions:: + + sage: P = 1 / phi; P + 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + O(q^7) + sage: P[:20] == [Partitions(n).cardinality() for n in range(20)] + True + + TESTS:: + + sage: L.<q> = LazyLaurentSeriesRing(LazyDirichletSeriesRing(QQ, "s")) + sage: q.euler() + 1 - q - q^2 + q^5 + O(q^7) + + REFERENCES: + + - :wikipedia:`Euler_function` + """ + def coeff(n): + k = ZZ(24 * n + 1) + m, rem = k.sqrtrem() + if rem: + return ZZ.zero() + return (-1) ** ((m + 1) // 6) + return self(coefficients=coeff, valuation=0) + ###################################################################### -class LazyTaylorSeriesRing(LazySeriesRing): + +class LazyPowerSeriesRing(LazySeriesRing): """ The ring of (possibly multivariate) lazy Taylor series. @@ -1128,13 +1607,16 @@ class LazyTaylorSeriesRing(LazySeriesRing): EXAMPLES:: - sage: LazyTaylorSeriesRing(ZZ, 't') + sage: LazyPowerSeriesRing(ZZ, 't') Lazy Taylor Series Ring in t over Integer Ring - sage: L.<x, y> = LazyTaylorSeriesRing(QQ); L + sage: L.<x, y> = LazyPowerSeriesRing(QQ); L Multivariate Lazy Taylor Series Ring in x, y over Rational Field """ - Element = LazyTaylorSeries + Element = LazyPowerSeries + + # Follow the "generic" normalization + __classcall_private__ = LazySeriesRing.__classcall_private__ def __init__(self, base_ring, names, sparse=True, category=None): """ @@ -1142,26 +1624,91 @@ def __init__(self, base_ring, names, sparse=True, category=None): TESTS:: - sage: L = LazyTaylorSeriesRing(ZZ, 't') - sage: TestSuite(L).run(skip=['_test_elements', '_test_associativity', '_test_distributivity', '_test_zero']) + sage: LazyPowerSeriesRing.options.halting_precision(12) + + sage: L = LazyPowerSeriesRing(ZZ, 't') + sage: TestSuite(L).run(skip="_test_fraction_field") + sage: L = LazyPowerSeriesRing(ZZ, 's, t') + sage: TestSuite(L).run(skip="_test_fraction_field") + + sage: L = LazyPowerSeriesRing(QQ, 't') + sage: TestSuite(L).run(skip="_test_fraction_field") + sage: L = LazyPowerSeriesRing(QQ, 's, t') + sage: TestSuite(L).run(skip="_test_fraction_field") + + sage: L = LazyPowerSeriesRing(GF(5), 't') + sage: TestSuite(L).run() + + sage: L = LazyPowerSeriesRing(GF(5), 's, t') + sage: TestSuite(L).run(skip=['_test_fraction_field']) + + sage: L = LazyPowerSeriesRing(Zmod(6), 't') + sage: TestSuite(L).run(skip=['_test_revert']) + sage: L = LazyPowerSeriesRing(Zmod(6), 's, t') + sage: TestSuite(L).run(skip=['_test_revert']) + + sage: L = LazyPowerSeriesRing(QQ['q'], 't') + sage: TestSuite(L).run(skip="_test_fraction_field") + sage: L = LazyPowerSeriesRing(QQ['q'], 's, t') + sage: TestSuite(L).run(skip="_test_fraction_field") # long time + + sage: L = LazyPowerSeriesRing(ZZ['q'], 't') + sage: TestSuite(L).run(skip="_test_fraction_field") + sage: L = LazyPowerSeriesRing(ZZ['q'], 's, t') + sage: TestSuite(L).run(skip="_test_fraction_field") # long time + + sage: LazyPowerSeriesRing.options._reset() # reset the options + + Check that :trac:`34470` is fixed:: + + sage: L.<t> = LazyPowerSeriesRing(QQ) + sage: L in CompleteDiscreteValuationRings + True + sage: L.uniformizer() + t + sage: lcm(1/(1 - t^2) - 1, t) + t^2 + + sage: L.<t> = LazyPowerSeriesRing(ZZ) + sage: L in PrincipalIdealDomains + False + + The ideal generated by `s` and `t` is not principal:: + + sage: L = LazyPowerSeriesRing(QQ, 's, t') + sage: L in PrincipalIdealDomains + False """ - from sage.structure.category_object import normalize_names - names = normalize_names(-1, names) self._sparse = sparse - self._laurent_poly_ring = PolynomialRing(base_ring, names) - if len(names) == 1: + self._minimal_valuation = 0 + self._arity = len(names) + if self._arity == 1: + self._laurent_poly_ring = PolynomialRing(base_ring, names, sparse=sparse) self._internal_poly_ring = self._laurent_poly_ring else: - coeff_ring = PolynomialRing(base_ring, names) - self._internal_poly_ring = PolynomialRing(coeff_ring, "DUMMY_VARIABLE") + self._laurent_poly_ring = PolynomialRing(base_ring, names) + self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE", sparse=sparse) category = Algebras(base_ring.category()) - if base_ring in Fields(): - category &= CompleteDiscreteValuationRings() - elif base_ring in IntegralDomains(): + mixin_gcd = False + if self._arity == 1: + if base_ring in Fields(): + category &= CompleteDiscreteValuationRings() + mixin_gcd = True + elif base_ring in Fields(): + category &= UniqueFactorizationDomains() + mixin_gcd = True + if base_ring in IntegralDomains(): category &= IntegralDomains() elif base_ring in Rings().Commutative(): category = category.Commutative() + if mixin_gcd: + from sage.structure.dynamic_class import dynamic_class + self.Element = dynamic_class( + f"{self.Element.__name__}_gcd", + (self.Element, LazyPowerSeries_gcd_mixin), + doccls=self.Element) + if base_ring.is_zero(): category = category.Finite() else: @@ -1175,7 +1722,7 @@ def _repr_(self): EXAMPLES:: - sage: LazyTaylorSeriesRing(GF(2), 'z') + sage: LazyPowerSeriesRing(GF(2), 'z') Lazy Taylor Series Ring in z over Finite Field of size 2 """ BR = self.base_ring() @@ -1190,7 +1737,7 @@ def _latex_(self): EXAMPLES:: - sage: L = LazyTaylorSeriesRing(GF(2), 'z') + sage: L = LazyPowerSeriesRing(GF(2), 'z') sage: latex(L) \Bold{F}_{2} [\![z]\!] """ @@ -1204,7 +1751,7 @@ def _monomial(self, c, n): EXAMPLES:: - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') sage: L._monomial(2, 3) 2*z^3 """ @@ -1221,7 +1768,7 @@ def gen(self, n=0): EXAMPLES:: - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') sage: L.gen() z sage: L.gen(3) @@ -1238,9 +1785,9 @@ def gen(self, n=0): R = self._laurent_poly_ring BR = self.base_ring() if len(self.variable_names()) == 1: - coeff_stream = Stream_exact([BR.one()], self._sparse, constant=BR.zero(), order=1) + coeff_stream = Stream_exact([BR.one()], constant=BR.zero(), order=1) else: - coeff_stream = Stream_exact([R.gen(n)], self._sparse, constant=BR.zero(), order=1) + coeff_stream = Stream_exact([R.gen(n)], constant=BR.zero(), order=1) return self.element_class(self, coeff_stream) def ngens(self): @@ -1249,7 +1796,7 @@ def ngens(self): EXAMPLES:: - sage: L.<z> = LazyTaylorSeriesRing(ZZ) + sage: L.<z> = LazyPowerSeriesRing(ZZ) sage: L.ngens() 1 """ @@ -1262,13 +1809,13 @@ def gens(self): EXAMPLES:: - sage: L = LazyTaylorSeriesRing(ZZ, 'x,y') + sage: L = LazyPowerSeriesRing(ZZ, 'x,y') sage: L.gens() (x, y) """ return tuple([self.gen(n) for n in range(self.ngens())]) - def _element_constructor_(self, x=None, valuation=None, constant=None, degree=None, check=True): + def _element_constructor_(self, x=None, valuation=None, constant=None, degree=None, coefficients=None, check=True): """ Construct a Taylor series from ``x``. @@ -1280,15 +1827,22 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No - ``degree`` -- (optional) the degree when the series is ``constant`` - ``check`` -- (optional) check that coefficients are homogeneous of the correct degree when they are retrieved + .. WARNING:: + + The behaviour of ``LazyPowerSeries(c)`` for a list ``c`` + with non-zero last element `e` changed with + :trac:`32367`. To obtain the old behaviour, use + ``LazyPowerSeries(c, constant=e)``. + EXAMPLES:: - sage: L = LazyTaylorSeriesRing(GF(2), 'z') + sage: L = LazyPowerSeriesRing(GF(2), 'z') sage: L(2) 0 sage: L(3) 1 - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') sage: L(lambda i: i, 5, 1, 10) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + O(z^13) sage: L(lambda i: i, 5, (1, 10)) @@ -1325,15 +1879,11 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: g = L([1,3,5,7,9], 5, -1); g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + O(z^13) - .. TODO:: - - Add a method to change the sparse/dense implementation. - Finally, ``x`` can be a polynomial:: sage: P.<x> = QQ[] sage: p = x + 3*x^2 + x^5 - sage: L.<x> = LazyTaylorSeriesRing(ZZ) + sage: L.<x> = LazyPowerSeriesRing(ZZ) sage: L(p) x + 3*x^2 + x^5 @@ -1342,13 +1892,13 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: P.<x, y> = QQ[] sage: p = x + y^2 + x*y - sage: L.<x,y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) sage: L(p) x + (x*y+y^2) TESTS:: - sage: L.<x,y> = LazyTaylorSeriesRing(ZZ) + sage: L.<x,y> = LazyPowerSeriesRing(ZZ) sage: L(constant=1) Traceback (most recent call last): ... @@ -1375,18 +1925,20 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No """ if valuation is not None: if valuation < 0: - raise ValueError("the valuation of a Taylor series must be positive") - if len(self.variable_names()) > 1: + raise ValueError("the valuation of a Taylor series must be non-negative") + # TODO: the following is nonsense, think of an iterator + if self._arity > 1: raise ValueError("valuation must not be specified for multivariate Taylor series") - if len(self.variable_names()) > 1: + if self._arity > 1: valuation = 0 R = self._laurent_poly_ring BR = self.base_ring() if x is None: assert degree is None - coeff_stream = Stream_uninitialized(self._sparse, valuation) + coeff_stream = Stream_uninitialized(valuation) return self.element_class(self, coeff_stream) + try: # Try to build stuff using the polynomial ring constructor x = R(x) @@ -1395,21 +1947,22 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if isinstance(constant, (tuple, list)): constant, degree = constant if constant is not None: - if len(self.variable_names()) > 1 and constant: + if self._arity > 1 and constant: raise ValueError("constant must be zero for multivariate Taylor series") constant = BR(constant) + if x in R: if not x and not constant: - coeff_stream = Stream_zero(self._sparse) + coeff_stream = Stream_zero() else: if not x: - coeff_stream = Stream_exact([], self._sparse, + coeff_stream = Stream_exact([], order=valuation, degree=degree, constant=constant) return self.element_class(self, coeff_stream) - if len(self.variable_names()) == 1: + if self._arity == 1: v = x.valuation() d = x.degree() p_list = [x[i] for i in range(v, d + 1)] @@ -1421,45 +1974,72 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No d = max(p_dict.keys()) p_list = [p_dict.get(i, 0) for i in range(v, d + 1)] - coeff_stream = Stream_exact(p_list, self._sparse, + coeff_stream = Stream_exact(p_list, order=v, constant=constant, degree=degree) return self.element_class(self, coeff_stream) - if isinstance(x, LazyTaylorSeries): - if x._coeff_stream._is_sparse is self._sparse: - return self.element_class(self, x._coeff_stream) - # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") - if callable(x): + if isinstance(x, LazyPowerSeries): + stream = x._coeff_stream + if isinstance(stream, Stream_exact): + if self._arity == 1: + BR = self.base_ring() + else: + BR = self._laurent_poly_ring + coeffs = [BR(val) for val in stream._initial_coefficients] + valuation = stream._approximate_order + for i, c in enumerate(coeffs): + if c: + valuation += i + coeffs = coeffs[i:] + break + else: + valuation += len(coeffs) + coeffs = [] + return self(coeffs, + degree=stream._degree, + constant=self.base_ring()(stream._constant), + valuation=valuation) + return self.element_class(self, stream) + + if callable(x) or isinstance(x, (GeneratorType, map, filter)): if valuation is None: valuation = 0 if degree is not None: if constant is None: constant = ZZ.zero() - if len(self.variable_names()) == 1: - p = [BR(x(i)) for i in range(valuation, degree)] + if callable(x): + p = [x(i) for i in range(valuation, degree)] + else: + p = [c for c, _ in zip(_skip_leading_zeros(x), range(valuation, degree))] + if self._arity == 1: + p = [BR(c) for c in p] else: - p = [R(x(i)) for i in range(valuation, degree)] + p = [R(c) for c in p] if not all(e.is_homogeneous() and e.degree() == i for i, e in enumerate(p, valuation)): raise ValueError("coefficients must be homogeneous polynomials of the correct degree") - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=constant, degree=degree) return self.element_class(self, coeff_stream) - coeff_ring = self._internal_poly_ring.base_ring() - if check and len(self.variable_names()) > 1: - def y(n): - e = R(x(n)) - if not e or e.is_homogeneous() and e.degree() == n: - return e - raise ValueError("coefficient %s at degree %s is not a homogeneous polynomial" % (e, n)) - coeff_stream = Stream_function(y, coeff_ring, self._sparse, valuation) + if check and self._arity > 1: + if callable(x): + def y(n): + e = R(x(n)) + if not e or e.is_homogeneous() and e.degree() == n: + return e + raise ValueError("coefficient %s at degree %s is not a homogeneous polynomial" % (e, n)) + coeff_stream = Stream_function(y, self._sparse, valuation) + else: + coeff_stream = Stream_iterator(map(R, _skip_leading_zeros(x)), valuation) else: - coeff_stream = Stream_function(x, coeff_ring, self._sparse, valuation) + if callable(x): + coeff_stream = Stream_function(lambda i: BR(x(i)), self._sparse, valuation) + else: + coeff_stream = Stream_iterator(map(BR, _skip_leading_zeros(x)), valuation) return self.element_class(self, coeff_stream) raise ValueError(f"unable to convert {x} into a lazy Taylor series") @@ -1469,21 +2049,123 @@ def _an_element_(self): EXAMPLES:: - sage: L = LazyTaylorSeriesRing(ZZ, 'z') + sage: L = LazyPowerSeriesRing(ZZ, 'z') + sage: L.an_element() + z + z^2 + z^3 + O(z^4) + + sage: L = LazyPowerSeriesRing(ZZ, 'x, y') sage: L.an_element() - z + z^2 + z^3 + z^4 + O(z^5) + x """ - c = self.base_ring().an_element() - R = self._laurent_poly_ring - coeff_stream = Stream_exact([R.one()], self._sparse, order=1, constant=c) - return self.element_class(self, coeff_stream) + if self._arity == 1: + return self(self._laurent_poly_ring.an_element(), + constant=self.base_ring().an_element()) + return self(self._laurent_poly_ring.an_element()) + + def uniformizer(self): + """ + Return a uniformizer of ``self``. + + EXAMPLES:: + + sage: L = LazyPowerSeriesRing(QQ, 'x') + sage: L.uniformizer() + x + """ + R = self.base_ring() + if R not in Fields(): + raise TypeError("the base ring is not a field") + if self._arity != 1: + raise TypeError("the arity must be one") + return self.gen() + + def residue_field(self): + """ + Return the residue field of the ring of integers of ``self``. + + EXAMPLES:: + + sage: L = LazyPowerSeriesRing(QQ, 'x') + sage: L.residue_field() + Rational Field + """ + R = self.base_ring() + if R not in Fields(): + raise TypeError("the base ring is not a field") + if self._arity != 1: + raise TypeError("the arity must be one") + return R + def fraction_field(self): + """ + Return the fraction field of ``self``. + + If this is with a single variable over a field, then the fraction + field is the field of (lazy) formal Laurent series. + + .. TODO:: + + Implement other fraction fields. + + EXAMPLES:: + + sage: L.<x> = LazyPowerSeriesRing(QQ) + sage: L.fraction_field() + Lazy Laurent Series Ring in x over Rational Field + """ + if self not in IntegralDomains(): + raise TypeError("must be an integral domain") + R = self.base_ring() + if self._arity == 1 and R in Fields(): + return LazyLaurentSeriesRing(R, names=self.variable_names()) + raise NotImplementedError("the fraction field is not yet implemented") + + def some_elements(self): + """ + Return a list of elements of ``self``. + + EXAMPLES:: + + sage: L = LazyPowerSeriesRing(ZZ, 'z') + sage: L.some_elements()[:6] + [0, 1, z + z^2 + z^3 + O(z^4), + -12 - 8*z + z^2 + z^3, + 1 + z - 2*z^2 - 7*z^3 - z^4 + 20*z^5 + 23*z^6 + O(z^7), + z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + O(z^7)] + + sage: L = LazyPowerSeriesRing(GF(3)["q"], 'z') + sage: L.some_elements()[:6] + [0, 1, z + q*z^2 + q*z^3 + q*z^4 + O(z^5), + z + z^2 + z^3, + 1 + z + z^2 + 2*z^3 + 2*z^4 + 2*z^5 + O(z^6), + z + z^2 + z^4 + z^5 + O(z^7)] + + sage: L = LazyPowerSeriesRing(GF(3), 'q, t') + sage: L.some_elements()[:6] + [0, 1, q, + q + q^2 + q^3, + 1 + q + q^2 + (-q^3) + (-q^4) + (-q^5) + (-q^6) + O(q,t)^7, + 1 + (q+t) + (q^2-q*t+t^2) + (q^3+t^3) + + (q^4+q^3*t+q*t^3+t^4) + + (q^5-q^4*t+q^3*t^2+q^2*t^3-q*t^4+t^5) + + (q^6-q^3*t^3+t^6) + O(q,t)^7] + """ + z = self.gen(0) + elts = [self.zero(), self.one(), self.an_element()] + if self._arity == 1: + elts.extend([(z-3)*(2+z)**2, (1 - 2*z**3)/(1 - z + 3*z**2), self(lambda n: n**2)]) + else: + PR = self._laurent_poly_ring + sum_gens = PR.sum(PR.gens()) + elts.extend([(z-3)*(2+z)**2, (1 - 2*z**3)/(1 - z + 3*z**2), self(lambda n: sum_gens**n)]) + return elts ###################################################################### + class LazyCompletionGradedAlgebra(LazySeriesRing): r""" - The completion of a graded alebra consisting of formal series. + The completion of a graded algebra consisting of formal series. For a graded algebra `A`, we can form a completion of `A` consisting of all formal series of `A` such that each homogeneous component is @@ -1531,21 +2213,57 @@ def __init__(self, basis, sparse=True, category=None): TESTS:: + sage: LazySymmetricFunctions.options.halting_precision(6) + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LazySymmetricFunctions(s) + sage: TestSuite(L).run() + + sage: p = SymmetricFunctions(GF(5)).p() + sage: L = LazySymmetricFunctions(p) + sage: TestSuite(L).run() + + Reversion will only work when the base ring is a field:: + sage: s = SymmetricFunctions(ZZ).s() sage: L = LazySymmetricFunctions(s) - sage: TestSuite(L).run(skip=['_test_elements', '_test_associativity', '_test_distributivity', '_test_zero']) + sage: TestSuite(L).run(skip=['_test_revert']) + + sage: s = SymmetricFunctions(QQ["q"]).s() + sage: L = LazySymmetricFunctions(s) + sage: TestSuite(L).run(skip=['_test_revert']) + + Options are remembered across doctests:: + + sage: LazySymmetricFunctions.options._reset() + + Check that :trac:`34470` is fixed. The ideal generated by + `p[1]` and `p[2]` is not principal:: + + sage: p = SymmetricFunctions(QQ).p() + sage: L = LazySymmetricFunctions(s) + sage: L in PrincipalIdealDomains + False + + Check that a basis which is not graded is not enough:: + + sage: ht = SymmetricFunctions(ZZ).ht() + sage: L = LazySymmetricFunctions(ht) + Traceback (most recent call last): + ... + ValueError: basis should be in GradedAlgebrasWithBasis + """ base_ring = basis.base_ring() + self._minimal_valuation = 0 if basis in Algebras.TensorProducts: self._arity = len(basis._sets) else: - if basis not in Algebras.Graded: - raise ValueError("basis should be a graded algebra") + if basis not in GradedAlgebrasWithBasis: + raise ValueError("basis should be in GradedAlgebrasWithBasis") self._arity = 1 category = Algebras(base_ring.category()) - if base_ring in Fields(): - category &= CompleteDiscreteValuationRings() - elif base_ring in IntegralDomains(): + if base_ring in IntegralDomains(): category &= IntegralDomains() elif base_ring in Rings().Commutative(): category = category.Commutative() @@ -1561,7 +2279,7 @@ def __init__(self, basis, sparse=True, category=None): from sage.algebras.free_algebra import FreeAlgebra self._internal_poly_ring = FreeAlgebra(self._laurent_poly_ring, 1, "DUMMY_VARIABLE") else: - self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE") + self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE", sparse=sparse) def _repr_(self): """ @@ -1604,7 +2322,7 @@ def _monomial(self, c, n): L = self._laurent_poly_ring return L(c) - def _element_constructor_(self, x=None, valuation=None, degree=None, check=True): + def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, check=True): r""" Construct a lazy element in ``self`` from ``x``. @@ -1644,10 +2362,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) sage: f = L([m[1],m[2],m[3]], valuation=1); f m[1] + m[2] + m[3] - .. TODO:: - - Add a method to change the sparse/dense implementation. - Finally, ``x`` can be a symmetric function:: sage: m = SymmetricFunctions(ZZ).m() @@ -1690,7 +2404,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) R = self._laurent_poly_ring if x is None: assert degree is None - coeff_stream = Stream_uninitialized(self._sparse, valuation) + coeff_stream = Stream_uninitialized(valuation) return self.element_class(self, coeff_stream) try: # Try to build stuff using the polynomial ring constructor @@ -1699,7 +2413,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) pass if x in R: if not x: - coeff_stream = Stream_zero(self._sparse) + coeff_stream = Stream_zero() else: p_dict = {} if self._arity == 1: @@ -1712,23 +2426,20 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) d = f.degree() except (TypeError, ValueError, AttributeError): # FIXME: Fallback for symmetric functions in multiple variables - d = sum(p.size() for p in f.support()) + d = sum(sum(mu.size() for mu in p) for p in f.support()) p_dict[d] = p_dict.get(d, 0) + f v = min(p_dict) d = max(p_dict) p_list = [p_dict.get(i, 0) for i in range(v, d + 1)] - coeff_stream = Stream_exact(p_list, self._sparse, + coeff_stream = Stream_exact(p_list, order=v, constant=0, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, self.Element): - if x._coeff_stream._is_sparse is self._sparse: - return self.element_class(self, x._coeff_stream) - # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") + return self.element_class(self, x._coeff_stream) if self._arity == 1: def check_homogeneous_of_degree(f, d): @@ -1765,7 +2476,7 @@ def check_homogeneous_of_degree(f, d): p = [R(e) for e in x] for i, e in enumerate(p, valuation): check_homogeneous_of_degree(e, i) - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=0, degree=degree) @@ -1775,21 +2486,20 @@ def check_homogeneous_of_degree(f, d): p = [R(x(i)) for i in range(valuation, degree)] for i, e in enumerate(p, valuation): check_homogeneous_of_degree(e, i) - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=0, degree=degree) return self.element_class(self, coeff_stream) - coeff_ring = self._internal_poly_ring.base_ring() if check: def y(n): e = R(x(n)) check_homogeneous_of_degree(e, n) return e - coeff_stream = Stream_function(y, coeff_ring, self._sparse, valuation) + coeff_stream = Stream_function(y, self._sparse, valuation) else: - coeff_stream = Stream_function(x, coeff_ring, self._sparse, valuation) + coeff_stream = Stream_function(x, self._sparse, valuation) return self.element_class(self, coeff_stream) raise ValueError(f"unable to convert {x} into a lazy completion element") @@ -1802,12 +2512,54 @@ def _an_element_(self): sage: m = SymmetricFunctions(ZZ).m() sage: L = LazySymmetricFunctions(m) sage: L.an_element() - m[] + 2*m[] + 2*m[1] + 3*m[2] """ - R = self._laurent_poly_ring - coeff_stream = Stream_exact([R.one()], self._sparse, order=1, constant=0) - return self.element_class(self, coeff_stream) + return self(self._laurent_poly_ring.an_element()) + def some_elements(self): + """ + Return a list of elements of ``self``. + + EXAMPLES:: + + sage: m = SymmetricFunctions(GF(5)).m() + sage: L = LazySymmetricFunctions(m) + sage: L.some_elements()[:5] + [0, m[], 2*m[] + 2*m[1] + 3*m[2], 2*m[1] + 3*m[2], + 3*m[] + 2*m[1] + (m[1,1]+m[2]) + + (2*m[1,1,1]+m[3]) + + (2*m[1,1,1,1]+4*m[2,1,1]+2*m[2,2]) + + (3*m[2,1,1,1]+3*m[3,1,1]+4*m[3,2]+m[5]) + + (2*m[2,2,1,1]+m[2,2,2]+2*m[3,2,1]+2*m[3,3]+m[4,1,1]+3*m[4,2]+4*m[5,1]+4*m[6]) + + O^7] + + sage: NCSF = NonCommutativeSymmetricFunctions(QQ) + sage: S = NCSF.Complete() + sage: L = S.formal_series_ring() + sage: L.some_elements()[:4] + [0, S[], 2*S[] + 2*S[1] + (3*S[1,1]), 2*S[1] + (3*S[1,1])] + + """ + elt = self.an_element() + elts = [self.zero(), self.one(), elt] + # an element with no constant term + elts.append(elt - elt[0]) + # the inverse of an element + try: + if elt.is_unit(): + elts.append(~elt) + else: + elts.append(~(1 - elt[0] + elt)) + except NotImplementedError: + pass + # an element with no constant term and an invertible + # coefficient of the linear term + it = iter(self._laurent_poly_ring.basis()) + temp = self.sum(b for _ in range(4) if (b := next(it)).degree()) + if temp: + elts.append(temp) + + return elts ###################################################################### @@ -1837,7 +2589,7 @@ class LazySymmetricFunctions(LazyCompletionGradedAlgebra): ###################################################################### class LazyDirichletSeriesRing(LazySeriesRing): - """ + r""" The ring of lazy Dirichlet series. INPUT: @@ -1846,29 +2598,88 @@ class LazyDirichletSeriesRing(LazySeriesRing): - ``names`` -- name of the generator of this Dirichlet series ring - ``sparse`` -- (default: ``True``) whether this series is sparse or not + Unlike formal univariate Laurent/power series (over a field), + the ring of formal Dirichlet series is not a + :wikipedia:`discrete_valuation_ring`. On the other hand, it + is a :wikipedia:`local_ring`. The unique maximal ideal + consists of all non-invertible series, i.e., series with + vanishing constant term. + + .. TODO:: + + According to the answers in + https://mathoverflow.net/questions/5522/dirichlet-series-with-integer-coefficients-as-a-ufd, + (which, in particular, references :arxiv:`math/0105219`) + the ring of formal Dirichlet series is actually a + :wikipedia:`Unique_factorization_domain` over `\ZZ`. + + .. NOTE:: + + An interesting valuation is described in Emil Daniel + Schwab; Gheorghe Silberberg *A note on some discrete + valuation rings of arithmetical functions*, Archivum + Mathematicum, Vol. 36 (2000), No. 2, 103-109, + http://dml.cz/dmlcz/107723. Let `J_k` be the ideal of + Dirichlet series whose coefficient `f[n]` of `n^s` + vanishes if `n` has less than `k` prime factors, counting + multiplicities. For any Dirichlet series `f`, let `D(f)` + be the largest integer `k` such that `f` is in `J_k`. + Then `D` is surjective, `D(f g) = D(f) + D(g)` for + nonzero `f` and `g`, and `D(f + g) \geq \min(D(f), D(g))` + provided that `f + g` is nonzero. + + For example, `J_1` are series with no constant term, and + `J_2` are series such that `f[1]` and `f[p]` for prime + `p` vanish. + + Since this is a chain of increasing ideals, the ring of + formal Dirichlet series is not a + :wikipedia:`Noetherian_ring`. + + Evidently, this valuation cannot be computed for a given + series. + EXAMPLES:: sage: LazyDirichletSeriesRing(ZZ, 't') Lazy Dirichlet Series Ring in t over Integer Ring + + The ideal generated by `2^-s` and `3^-s` is not principal:: + + sage: L = LazyDirichletSeriesRing(QQ, 's') + sage: L in PrincipalIdealDomains + False """ Element = LazyDirichletSeries + # Follow the "generic" normalization + __classcall_private__ = LazySeriesRing.__classcall_private__ + def __init__(self, base_ring, names, sparse=True, category=None): - """ + r""" Initialize the ring. TESTS:: + sage: LazyDirichletSeriesRing.options.halting_precision(12) + sage: L = LazyDirichletSeriesRing(ZZ, 't') - sage: TestSuite(L).run(skip=['_test_elements', '_test_associativity', '_test_distributivity', '_test_zero']) + sage: TestSuite(L).run() + + sage: L = LazyDirichletSeriesRing(QQ, 't') + sage: TestSuite(L).run() + + sage: LazyDirichletSeriesRing.options._reset() # reset the options + """ if base_ring.characteristic() > 0: raise ValueError("positive characteristic not allowed for Dirichlet series") self._sparse = sparse - # TODO: it would be good to have something better than the symbolic ring - self._laurent_poly_ring = SR - self._internal_poly_ring = PolynomialRing(base_ring, names, sparse=True) + self._minimal_valuation = 1 + self._arity = 1 + self._laurent_poly_ring = SR # TODO: it would be good to have something better than the symbolic ring + self._internal_poly_ring = PolynomialRing(base_ring, names, sparse=sparse) category = Algebras(base_ring.category()) if base_ring in IntegralDomains(): @@ -1904,7 +2715,7 @@ def one(self): 1 + O(1/(8^z)) """ R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, constant=R.zero(), order=1) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=1) return self.element_class(self, coeff_stream) def _coerce_map_from_(self, S): @@ -1997,10 +2808,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: D = LazyDirichletSeriesRing(QQ, 't') sage: D(m) -1/(2^t) - 1/(3^t) - 1/(5^t) + 1/(6^t) - 1/(7^t) + O(1/(9^t)) - - .. TODO:: - - Add a method to make a copy of ``self._sparse``. """ if isinstance(x, (list, tuple)): p = self._internal_poly_ring(x) @@ -2048,7 +2855,37 @@ def _an_element_(self): 1/(4^z) + 1/(5^z) + 1/(6^z) + O(1/(7^z)) """ c = self.base_ring().an_element() - return self.element_class(self, Stream_exact([], self._sparse, constant=c, order=4)) + return self.element_class(self, Stream_exact([], constant=c, order=4)) + + def some_elements(self): + """ + Return a list of elements of ``self``. + + EXAMPLES:: + + sage: L = LazyDirichletSeriesRing(ZZ, 'z') + sage: L.some_elements() + [0, 1, + 1/(4^z) + 1/(5^z) + 1/(6^z) + O(1/(7^z)), + 1/(2^z) - 1/(3^z) + 2/4^z - 2/5^z + 3/6^z - 3/7^z + 4/8^z - 4/9^z, + 1/(2^z) - 1/(3^z) + 2/4^z - 2/5^z + 3/6^z - 3/7^z + 4/8^z - 4/9^z + 1/(10^z) + 1/(11^z) + 1/(12^z) + O(1/(13^z)), + 1 + 4/2^z + 9/3^z + 16/4^z + 25/5^z + 36/6^z + 49/7^z + O(1/(8^z))] + + sage: L = LazyDirichletSeriesRing(QQ, 'z') + sage: L.some_elements() + [0, 1, + 1/2/4^z + 1/2/5^z + 1/2/6^z + O(1/(7^z)), + 1/2 - 1/2/2^z + 2/3^z - 2/4^z + 1/(6^z) - 1/(7^z) + 42/8^z + 2/3/9^z, + 1/2 - 1/2/2^z + 2/3^z - 2/4^z + 1/(6^z) - 1/(7^z) + 42/8^z + 2/3/9^z + 1/2/10^z + 1/2/11^z + 1/2/12^z + O(1/(13^z)), + 1 + 4/2^z + 9/3^z + 16/4^z + 25/5^z + 36/6^z + 49/7^z + O(1/(8^z))] + """ + R = self.base_ring() + some_numbers = [c for c, _ in zip(R.some_elements(), range(9))] + elts = [self.zero(), self.one(), self.an_element(), + self(some_numbers), + self(some_numbers, constant=R.an_element()), + self(lambda n: n**2)] + return elts def _monomial(self, c, n): r""" @@ -2066,3 +2903,27 @@ 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. + + EXAMPLES:: + + sage: from sage.rings.lazy_series_ring import _skip_leading_zeros + sage: it = map(lambda x: 0 if x < 10 else x, NN) + sage: [x for x, _ in zip(_skip_leading_zeros(it), range(10))] + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + sage: it = map(GF(3), NN) + sage: [x for x, _ in zip(it, range(10))] + [0, 1, 2, 0, 1, 2, 0, 1, 2, 0] + sage: it = map(GF(3), NN) + sage: [x for x, _ in zip(_skip_leading_zeros(it), range(10))] + [1, 2, 0, 1, 2, 0, 1, 2, 0, 1] + """ + while True: + c = next(iterator) + if c: + yield c + break + yield from iterator diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 96a55bffd69..70fc5aacc73 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -412,7 +412,7 @@ def is_RingHomomorphism(phi): sage.misc.superseded.deprecation(23204, "is_RingHomomorphism() should not be used anymore. Check whether the category_for() your morphism is a subcategory of Rings() instead.") # We use the category framework to determine whether something is a ring homomorphism. from sage.categories.map import Map - from sage.categories.all import Rings + from sage.categories.rings import Rings return isinstance(phi, Map) and phi.category_for().is_subcategory(Rings()) @@ -2939,6 +2939,48 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): self._q = self._p ** self._power RingHomomorphism.__init__(self, Hom(domain, domain)) + cdef _update_slots(self, dict _slots): + """ + Update information with the given slots. + + Helper function for copying or pickling. + + EXAMPLES:: + + sage: K = Frac(GF(5)['T']) + sage: phi = K.frobenius_endomorphism() + sage: psi = copy(phi) + sage: phi == psi + True + """ + self._p = _slots['_domain'].characteristic() + self._power = _slots['_power'] + self._q = self._p ** self._power + RingHomomorphism._update_slots(self, _slots) + + cdef dict _extra_slots(self): + """ + Return additional information about this morphism + as a dictionary. + + Helper function for copying or pickling. + + EXAMPLES:: + + sage: K = Frac(GF(25)['T']) + sage: phi = K.frobenius_endomorphism(2) + sage: phi + Frobenius endomorphism x |--> x^(5^2) of Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 + + sage: psi = loads(dumps(phi)); psi + Frobenius endomorphism x |--> x^(5^2) of Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 + sage: phi == psi + True + """ + slots = RingHomomorphism._extra_slots(self) + slots['_power'] = self._power + return slots + def _repr_(self): """ Return a string representation of this endomorphism. diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index b1c1a3ffb67..91ac18389c2 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -1668,8 +1668,10 @@ def _integral(self, xx): """ Formal integral for multivariate power series. - INPUT: ``xx`` - a generator of the power series ring (the - one with respect to which to integrate) + INPUT: + + - ``xx`` -- a generator of the power series ring (the + one with respect to which to integrate) EXAMPLES:: diff --git a/src/sage/rings/noncommutative_ideals.pyx b/src/sage/rings/noncommutative_ideals.pyx index 29599c2ac0f..0661987be2a 100644 --- a/src/sage/rings/noncommutative_ideals.pyx +++ b/src/sage/rings/noncommutative_ideals.pyx @@ -385,7 +385,7 @@ class Ideal_nc(Ideal_generic): of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: IL * IR - Twosided Ideal + Twosided Ideal ( [0 3] [0 0] @@ -402,7 +402,7 @@ class Ideal_nc(Ideal_generic): gens = [z for z in (x * y for x in self.gens() for y in other.gens()) if z] return self.ring().ideal(gens, side='twosided') raise NotImplementedError("cannot multiply non-commutative ideals") - + if not isinstance(other, Ideal_nc): # Perhaps other is a ring and thus has its own # multiplication. @@ -418,4 +418,3 @@ class Ideal_nc(Ideal_generic): return other.ring().ideal(other.gens(), side='twosided') raise NotImplementedError("cannot multiply non-commutative ideals") - diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index e99dff850fb..a0eb41b75b3 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -1595,7 +1595,7 @@ def p_adic_LLL_bound_one_prime(prime, B0, M, M_logp, m0, c3, prec=106): m = e * f u = 1 while True: - if prec <= u + c8: + if prec <= u + c8: return 0, True # We construct the matrix A as a block matrix @@ -1781,20 +1781,20 @@ def sieve_ordering(SUK, q): sage: SUK = K.S_unit_group(S=3) sage: sieve_data = list(sieve_ordering(SUK, 19)) sage: sieve_data[0] - (Fractional ideal (xi - 3), - Fractional ideal (-2*xi^2 + 3), + (Fractional ideal (-2*xi^2 + 3), + Fractional ideal (-xi + 3), Fractional ideal (2*xi + 1)) sage: sieve_data[1] - (Residue field of Fractional ideal (xi - 3), - Residue field of Fractional ideal (-2*xi^2 + 3), + (Residue field of Fractional ideal (-2*xi^2 + 3), + Residue field of Fractional ideal (-xi + 3), Residue field of Fractional ideal (2*xi + 1)) sage: sieve_data[2] - ([18, 7, 16, 4], [18, 9, 12, 8], [18, 3, 10, 10]) + ([18, 12, 16, 8], [18, 16, 10, 4], [18, 10, 12, 10]) sage: sieve_data[3] - (486, 648, 11664) + (648, 2916, 3888) """ K = SUK.number_field() diff --git a/src/sage/rings/number_field/bdd_height.py b/src/sage/rings/number_field/bdd_height.py index beb047ae026..b7c8c33d0be 100644 --- a/src/sage/rings/number_field/bdd_height.py +++ b/src/sage/rings/number_field/bdd_height.py @@ -248,7 +248,7 @@ def bdd_norm_pr_ideal_gens(K, norm_list): sage: from sage.rings.number_field.bdd_height import bdd_norm_pr_ideal_gens sage: K.<g> = QuadraticField(123) sage: bdd_norm_pr_ideal_gens(K, range(5)) - {0: [0], 1: [1], 2: [-g - 11], 3: [], 4: [2]} + {0: [0], 1: [1], 2: [g + 11], 3: [], 4: [2]} :: diff --git a/src/sage/rings/number_field/class_group.py b/src/sage/rings/number_field/class_group.py index da255ee6fa2..46b98c242e8 100644 --- a/src/sage/rings/number_field/class_group.py +++ b/src/sage/rings/number_field/class_group.py @@ -221,11 +221,11 @@ def reduce(self): Class group of order 76 with structure C38 x C2 of Number Field in a with defining polynomial x^2 + 20072 sage: I = (G.0)^11; I - Fractional ideal class (41, 1/2*a + 5) + Fractional ideal class (33, 1/2*a + 8) sage: J = G(I.ideal()^5); J - Fractional ideal class (115856201, 1/2*a + 40407883) + Fractional ideal class (39135393, 1/2*a + 13654253) sage: J.reduce() - Fractional ideal class (57, 1/2*a + 44) + Fractional ideal class (73, 1/2*a + 47) sage: J == I^5 True """ diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 79acd053bbc..e060148e4dd 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -944,7 +944,7 @@ def artin_symbol(self, P): sage: K.<b> = NumberField(x^4 - 2*x^2 + 2, 'a').galois_closure() sage: G = K.galois_group() sage: [G.artin_symbol(P) for P in K.primes_above(7)] - [(1,5)(2,6)(3,7)(4,8), (1,5)(2,6)(3,7)(4,8), (1,4)(2,3)(5,8)(6,7), (1,4)(2,3)(5,8)(6,7)] + [(1,4)(2,3)(5,8)(6,7), (1,4)(2,3)(5,8)(6,7), (1,5)(2,6)(3,7)(4,8), (1,5)(2,6)(3,7)(4,8)] sage: G.artin_symbol(17) Traceback (most recent call last): ... diff --git a/src/sage/rings/number_field/homset.py b/src/sage/rings/number_field/homset.py index 7072a4ddf8a..6353353afe1 100644 --- a/src/sage/rings/number_field/homset.py +++ b/src/sage/rings/number_field/homset.py @@ -51,7 +51,8 @@ def __init__(self, R, S, category=None): Category of homsets of number fields """ if category is None: - from sage.categories.all import Fields, NumberFields + from sage.categories.fields import Fields + from sage.categories.number_fields import NumberFields if S in NumberFields(): category = NumberFields() elif S in Fields(): @@ -102,7 +103,8 @@ def _element_constructor_(self, x, check=True): """ if not isinstance(x, NumberFieldHomomorphism_im_gens): return self.element_class(self, x, check=check) - from sage.categories.all import NumberFields, Rings + from sage.categories.number_fields import NumberFields + from sage.categories.rings import Rings if (x.parent() == self or (x.domain() == self.domain() and x.codomain() == self.codomain() and # This would be the better check, however it returns False currently: diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 5a38cb794b6..1dd1bcbc973 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -153,6 +153,13 @@ from collections import Counter from builtins import zip +from sage.categories.homset import Hom +from sage.categories.sets_cat import Sets +from sage.modules.free_module import VectorSpace +from sage.modules.free_module_element import vector +from sage.rings.real_mpfr import RR + +from sage.interfaces.abc import GapElement _NumberFields = NumberFields() @@ -560,10 +567,10 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, latex_name = latex_names for key, val in kwds.items(): if key not in ['implementation', 'prec']: - raise TypeError("NumberField() got an unexpected keyword argument '%s'"%key) + raise TypeError("NumberField() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): - raise NotImplementedError("Number field with prescribed %s is not implemented"%key) - if isinstance(polynomial, (list,tuple)): + raise NotImplementedError("Number field with prescribed %s is not implemented" % key) + if isinstance(polynomial, (list, tuple)): return NumberFieldTower(polynomial, names=name, check=check, embeddings=embedding, latex_names=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structures=structure) return NumberField_version2(polynomial=polynomial, name=name, check=check, embedding=embedding, latex_name=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structure=structure) @@ -650,7 +657,7 @@ def create_key_and_extra_args(self, polynomial, name, check, embedding, latex_na polynomial = polynomial.change_ring(polynomial.base_ring().fraction_field()) # normalize embedding - if isinstance(embedding, (list,tuple)): + if isinstance(embedding, (list, tuple)): if len(embedding) != 1: raise TypeError("embedding must be a list of length 1") embedding = embedding[0] @@ -676,7 +683,7 @@ def create_key_and_extra_args(self, polynomial, name, check, embedding, latex_na raise TypeError("structure must be a list of length 1") structure = structure[0] - return (polynomial.base_ring(), polynomial, name, embedding, latex_name, maximize_at_primes, assume_disc_small, structure), {"check":check} + return (polynomial.base_ring(), polynomial, name, embedding, latex_name, maximize_at_primes, assume_disc_small, structure), {"check": check} def create_object(self, version, key, check): r""" @@ -838,7 +845,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name except IndexError: names = normalize_names(1, names) if len(polynomials) > 1: - names = ['%s%s'%(names[0], i) for i in range(len(polynomials))] + names = ['%s%s' % (names[0], i) for i in range(len(polynomials))] if embeddings is None: embeddings = [None] * len(polynomials) @@ -864,7 +871,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name var = f.variable_name() if is_Polynomial(f) else 'x' R = w[var] # polynomial ring - return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0], latex_name=latex_names[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes + return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0], latex_name=latex_names[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes def QuadraticField(D, name='a', check=True, embedding=True, latex_name='sqrt', **args): @@ -1037,7 +1044,7 @@ def is_AbsoluteNumberField(x): return isinstance(x, NumberField_absolute) -def is_QuadraticField(x): +def is_QuadraticField(x) -> bool: r""" Return True if x is of the quadratic *number* field type. @@ -1231,7 +1238,7 @@ def create_object(self, version, key, **extra_args): CyclotomicField = CyclotomicFieldFactory("sage.rings.number_field.number_field.CyclotomicField") -def is_CyclotomicField(x): +def is_CyclotomicField(x) -> bool: """ Return True if x is a cyclotomic field, i.e., of the special cyclotomic field class. This function does not return True for a @@ -1443,17 +1450,17 @@ def __init__(self, polynomial, name, latex_name, if category is None: category = default_category else: - assert category.is_subcategory(default_category), "%s is not a subcategory of %s"%(category, default_category) + assert category.is_subcategory(default_category), "%s is not a subcategory of %s" % (category, default_category) ParentWithGens.__init__(self, QQ, name, category=category) if not isinstance(polynomial, polynomial_element.Polynomial): - raise TypeError("polynomial (=%s) must be a polynomial"%repr(polynomial)) + raise TypeError("polynomial (=%s) must be a polynomial" % repr(polynomial)) if check: if not polynomial.parent().base_ring() == QQ: raise TypeError("polynomial must be defined over rational field") if not polynomial.is_irreducible(): - raise ValueError("defining polynomial (%s) must be irreducible"%polynomial) + raise ValueError("defining polynomial (%s) must be irreducible" % polynomial) self._assign_names(name) self._latex_names = (latex_name,) @@ -1579,12 +1586,12 @@ def _magma_init_(self, magma): """ # Get magma version of defining polynomial of this number field f = self._magma_polynomial_(magma) - s = 'NumberField(%s)'%f.name() + s = 'NumberField(%s)' % f.name() return magma._with_names(s, self.variable_names()) def construction(self): r""" - Construction of self + Construction of self. EXAMPLES:: @@ -1808,21 +1815,21 @@ def _element_constructor_(self, x, check=True): % (x, self.pari_polynomial())) beta = self._pari_absolute_structure()[2] x = x(beta).lift() - else: # constant polynomial + else: # constant polynomial x = x[0] else: raise TypeError("%s has unsupported PARI type %s" % (x, x.type())) x = self.absolute_polynomial().parent()(x) return self._element_class(self, x) - elif sage.interfaces.gap.is_GapElement(x): + elif isinstance(x, GapElement): s = x._sage_repr() if self.variable_name() in s: return self._convert_from_str(s) return self._convert_from_str(s.replace('!', '')) - elif isinstance(x,str): + elif isinstance(x, str): return self._convert_from_str(x) elif (isinstance(x, (tuple, list)) or - isinstance(x, sage.modules.free_module_element.FreeModuleElement)): + isinstance(x, sage.modules.free_module_element.FreeModuleElement)): if len(x) != self.relative_degree(): raise ValueError("Length must be equal to the degree of this number field") base = self.base_ring() @@ -1896,9 +1903,9 @@ def _convert_non_number_field_element(self, x): return self._element_class(self, x) if isinstance(x, sage.rings.polynomial.polynomial_quotient_ring_element.PolynomialQuotientRingElement)\ - and (x in self.polynomial_quotient_ring()): + and (x in self.polynomial_quotient_ring()): y = self.polynomial_ring().gen() - return x.lift().subs({y:self.gen()}) + return x.lift().subs({y: self.gen()}) if isinstance(x, (sage.rings.qqbar.AlgebraicNumber, sage.rings.qqbar.AlgebraicReal)): return self._convert_from_qqbar(x) @@ -2072,7 +2079,7 @@ def structure(self): """ if self._structure is None: f = self.hom(self) - return f,f + return f, f else: return self._structure.create_structure(self) @@ -2366,7 +2373,7 @@ def change_generator(self, alpha, name=None, names=None): alpha = self(alpha) K, from_K = self.subfield(alpha, name=name) if K.degree() != self.degree(): - raise ValueError("alpha must generate a field of degree %s, but alpha generates a subfield of degree %s"%(self.degree(), K.degree())) + raise ValueError("alpha must generate a field of degree %s, but alpha generates a subfield of degree %s" % (self.degree(), K.degree())) # Now compute to_K, which is an isomorphism # from self to K such that from_K(to_K(x)) == x for all x, # and to_K(from_K(y)) == y. @@ -2608,7 +2615,7 @@ def quadratic_defect(self, a, p, check=True): raise TypeError(str(a) + " must be an element of " + str(self)) if not self == QQ and not p.parent() == self.ideal_monoid(): raise TypeError(str(p) + " is not a prime ideal in " - + str(self.ideal_monoid())) + + str(self.ideal_monoid())) if check and not p.is_prime(): raise ValueError(str(p) + " must be prime") if a.is_zero(): @@ -2630,7 +2637,7 @@ def quadratic_defect(self, a, p, check=True): return Infinity return v # The dyadic case - s = self(F.lift((1/F(a)).sqrt())) + s = self(F.lift((1 / F(a)).sqrt())) a = self(s**2) * a u = self(4).valuation(p) w = (a - 1).valuation(p) @@ -2641,7 +2648,7 @@ def quadratic_defect(self, a, p, check=True): s = self(q((a - 1) / pi**w)**(1/2)) a = a / (1 + s*(pi**(w/2)))**2 w = (a - 1).valuation(p) - if w < u and w % 2 ==1: + if w < u and w % 2: return v + w if w == u and (f + F((a-1) / 4)).is_irreducible(): return v + w @@ -2671,7 +2678,7 @@ def absolute_field(self, names): """ return NumberField(self.defining_polynomial(), names, check=False, structure=structure.NameChange(self)) - def is_isomorphic(self, other, isomorphism_maps = False): + def is_isomorphic(self, other, isomorphism_maps=False) -> bool: """ Return True if self is isomorphic as a number field to other. @@ -2845,16 +2852,14 @@ def is_CM(self): True sage: E.is_CM_extension() False - """ - - #Return cached answer if available + # Return cached answer if available try: return self.__is_CM except(AttributeError): pass - #Then, deal with simple cases + # Then, deal with simple cases if is_odd(self.absolute_degree()): self.__is_CM = False return False @@ -2880,8 +2885,8 @@ def is_CM(self): return True K = self.absolute_field('z') - #Check for index 2 subextensions that are totally real - possibilities = K.subfields(K.absolute_degree()/2) + # Check for index 2 subextensions that are totally real + possibilities = K.subfields(K.absolute_degree() / 2) for F, phi, _ in possibilities: if F.is_totally_real(): self.__is_CM = True @@ -2928,16 +2933,14 @@ def complex_conjugation(self): -a sage: cc(b) -b - """ - - #Return cached answer if available + # Return cached answer if available try: return self.__complex_conjugation except(AttributeError): pass - #Then, deal with simple cases + # Then, deal with simple cases if isinstance( self, sage.rings.number_field.number_field.NumberField_quadratic): disc = self.discriminant() @@ -2962,7 +2965,7 @@ def complex_conjugation(self): if not self.is_CM(): raise ValueError('Complex conjugation is only well-defined for fields contained in CM fields.') - #In the remaining case, self.is_CM() should have cached __max_tot_real_sub + # In the remaining case, self.is_CM() should have cached __max_tot_real_sub try: F, phi = self.__max_tot_real_sub except(AttributeError): @@ -2970,8 +2973,8 @@ def complex_conjugation(self): if self.is_absolute(): K_rel = self.relativize(phi, self.variable_name() * 2) to_abs, from_abs = K_rel.structure() - self.__complex_conjugation = K_rel.automorphisms()[1].pre_compose( \ - from_abs).post_compose(to_abs) + self.__complex_conjugation = K_rel.automorphisms()[1].pre_compose( + from_abs).post_compose(to_abs) self.__complex_conjugation = self.hom([self.__complex_conjugation(self.gen())], check=False) return self.__complex_conjugation else: @@ -3405,17 +3408,17 @@ def dirichlet_group(self): Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3 sage: X[4]^2 in X True - """ - #todo : turn this into an abelian group rather than a list. + # todo : turn this into an abelian group rather than a list. from sage.modular.dirichlet import DirichletGroup m = self.conductor() d = self.degree() - A = _splitting_classes_gens_(self,m,d) - # d could be improved to be the exponent of the Galois group rather than the degree, but I do not see how to go about that yet. + A = _splitting_classes_gens_(self, m, d) + # d could be improved to be the exponent of the Galois group + # rather than the degree, but I do not see how to go about that yet. G = DirichletGroup(m, CyclotomicField(d)) - H = [G(1)] + H = [G.one()] for chi in G: if len(H) == d: break @@ -3486,8 +3489,8 @@ def _latex_(self): \Bold{Q}[\theta_{25}]/(\theta_{25}^{25} + \theta_{25} + 1) """ latex_name = self.latex_variable_names()[0] - return "%s[%s]/(%s)"%(latex(QQ), latex_name, - self.polynomial()._latex_(latex_name)) + return "%s[%s]/(%s)" % (latex(QQ), latex_name, + self.polynomial()._latex_(latex_name)) def _ideal_class_(self, n=0): """ @@ -3544,14 +3547,13 @@ def ideal(self, *gens, **kwds): sage: K.<a> = NumberField(x^6 - x^5 - 5*x^4 + 4*x^3 + 6*x^2 - 3*x - 1) sage: K.ideal(1,1) Fractional ideal (1) - """ try: return self.fractional_ideal(*gens, **kwds) except ValueError: return sage.rings.ring.Ring.ideal(self, gens, **kwds) - def idealchinese(self,ideals,residues): + def idealchinese(self, ideals, residues): r""" Return a solution of the Chinese Remainder Theorem problem for ideals in a number field. @@ -3599,16 +3601,15 @@ def idealchinese(self,ideals,residues): ....: for P,k in I.factor() ....: ) True - """ factorizations = [I.factor() for I in ideals] - y = [a for a,f in zip(residues,factorizations) for _ in f] + y = [a for a, f in zip(residues, factorizations) for _ in f] x = pari.Mat([ - pari.Col([p.pari_prime(),k]) + pari.Col([p.pari_prime(), k]) for f in factorizations - for p,k in f + for p, k in f ]).mattranspose() - r = self.pari_nf().idealchinese(x,y) + r = self.pari_nf().idealchinese(x, y) return self(r) def fractional_ideal(self, *gens, **kwds): @@ -3643,7 +3644,7 @@ def fractional_ideal(self, *gens, **kwds): sage: L.<b> = K.extension(x^2 - 3, x^2 + 1) sage: M.<c> = L.extension(x^2 + 1) sage: L.ideal(K.ideal(2, a)) - Fractional ideal (-a) + Fractional ideal (a) sage: M.ideal(K.ideal(2, a)) == M.ideal(a*(b - c)/2) True @@ -3819,11 +3820,12 @@ def primes_above(self, x, degree=None): """ if degree is not None: degree = ZZ(degree) - facs = sorted([ (id.residue_class_degree(), id.absolute_norm(), id) for id in self.prime_factors(x) ]) + facs = sorted((id.residue_class_degree(), id.absolute_norm(), id) + for id in self.prime_factors(x)) if degree is None: - return [ id for d, n, id in facs ] + return [id for d, n, id in facs] else: - return [ id for d, n, id in facs if d == degree ] + return [id for d, n, id in facs if d == degree] def prime_above(self, x, degree=None): r""" @@ -3967,18 +3969,19 @@ def primes_of_bounded_norm(self, B): B = ZZ(B.ceil()) except (TypeError, AttributeError): raise TypeError("%s is not valid bound on prime ideals" % B) - if B<2: + if B < 2: return [] from sage.rings.fast_arith import prime_range if self is QQ: - #return arith.primes(B+1) - return prime_range(B+1, algorithm="pari_isprime") + # return arith.primes(B+1) + return prime_range(B + 1, algorithm="pari_isprime") else: - #P = [pp for p in arith.primes(B+1) for pp in self.primes_above(p)] - P = [pp for p in prime_range(B+1, algorithm="pari_isprime") for pp in self.primes_above(p)] + # P = [pp for p in arith.primes(B+1) for pp in self.primes_above(p)] + P = (pp for p in prime_range(B + 1, algorithm="pari_isprime") + for pp in self.primes_above(p)) P = [p for p in P if p.norm() <= B] - P.sort(key=lambda P: (P.norm(),P)) + P.sort(key=lambda P: (P.norm(), P)) return P def primes_of_bounded_norm_iter(self, B): @@ -4025,12 +4028,12 @@ def primes_of_bounded_norm_iter(self, B): from sage.rings.fast_arith import prime_range if self is QQ: - #for p in arith.primes(B+1): - for p in prime_range(B+1,algorithm="pari_isprime"): + # for p in arith.primes(B+1): + for p in prime_range(B + 1, algorithm="pari_isprime"): yield p else: - #for p in arith.primes(B+1): - for p in prime_range(B+1,algorithm="pari_isprime"): + # for p in arith.primes(B+1): + for p in prime_range(B + 1, algorithm="pari_isprime"): for pp in self.primes_above(p): if pp.norm() <= B: yield pp @@ -4094,7 +4097,6 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= INPUT: - - ``num_integer_primes (default: 10000)`` - an integer. We try to find primes of absolute norm no greater than the num_integer_primes-th prime number. For example, if @@ -4103,8 +4105,7 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= - ``max_iterations (default: 100)`` - an integer. We test max_iterations integers to find small primes before raising - StopIteration. - + ``StopIteration``. EXAMPLES:: @@ -4118,9 +4119,9 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= [1, 1, 1] """ it = self.primes_of_degree_one_iter() - return [ next(it) for i in range(n) ] + return [next(it) for i in range(n)] - def completely_split_primes(self, B = 200): + def completely_split_primes(self, B=200): r""" Return a list of rational primes which split completely in the number field `K`. @@ -4141,11 +4142,11 @@ def completely_split_primes(self, B = 200): from sage.rings.fast_arith import prime_range from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.arith.all import factor + from sage.arith.misc import factor split_primes = [] for p in prime_range(B): Fp = GF(p) - FpT = PolynomialRing(Fp,'T') + FpT = PolynomialRing(Fp, 'T') g = FpT(self.defining_polynomial()) if len(factor(g)) == self.degree(): split_primes.append(p) @@ -4227,7 +4228,8 @@ def _pari_absolute_structure(self): (y^2 + 6, Mod(1/6*y, y^2 + 6), Mod(6*y, y^2 + 1/6)) """ f = self.absolute_polynomial()._pari_with_name('y') - if f.pollead() == f.content().denominator() == 1: + f = f * f.content().denominator() + if f.pollead() == 1: g = f alpha = beta = g.variable().Mod(g) else: @@ -4525,16 +4527,15 @@ def _gap_init_(self): [ tau ] sage: gap(tau)^3 !2 - """ if not self.is_absolute(): raise NotImplementedError("Currently, only simple algebraic extensions are implemented in gap") G = sage.interfaces.gap.gap q = self.polynomial() - if q.variable_name()!='E': - return 'CallFuncList(function() local %s,E; %s:=Indeterminate(%s,"%s"); E:=AlgebraicExtension(%s,%s,"%s"); return E; end,[])'%(q.variable_name(),q.variable_name(),G(self.base_ring()).name(),q.variable_name(),G(self.base_ring()).name(),repr(self.polynomial()),str(self.gen())) - else: - return 'CallFuncList(function() local %s,F; %s:=Indeterminate(%s,"%s"); F:=AlgebraicExtension(%s,%s,"%s"); return F; end,[])'%(q.variable_name(),q.variable_name(),G(self.base_ring()).name(),q.variable_name(),G(self.base_ring()).name(),repr(self.polynomial()),str(self.gen())) + if q.variable_name() != 'E': + return 'CallFuncList(function() local %s,E; %s:=Indeterminate(%s,"%s"); E:=AlgebraicExtension(%s,%s,"%s"); return E; end,[])' % (q.variable_name(), q.variable_name(), G(self.base_ring()).name(), q.variable_name(), G(self.base_ring()).name(), repr(self.polynomial()), str(self.gen())) + + return 'CallFuncList(function() local %s,F; %s:=Indeterminate(%s,"%s"); F:=AlgebraicExtension(%s,%s,"%s"); return F; end,[])' % (q.variable_name(), q.variable_name(), G(self.base_ring()).name(), q.variable_name(), G(self.base_ring()).name(), repr(self.polynomial()), str(self.gen())) def characteristic(self): """ @@ -4619,10 +4620,10 @@ def class_group(self, proof=None, names='c'): except AttributeError: self.__class_group = {} k = self.pari_bnf(proof) - cycle_structure = tuple( ZZ(c) for c in k.bnf_get_cyc() ) + cycle_structure = tuple(ZZ(c) for c in k.bnf_get_cyc()) # Gens is a list of ideals (the generators) - gens = tuple( self.ideal(hnf) for hnf in k.bnf_get_gen() ) + gens = tuple(self.ideal(hnf) for hnf in k.bnf_get_gen()) G = ClassGroup(cycle_structure, names, self, gens, proof=proof) self.__class_group[proof, names] = G @@ -4821,7 +4822,7 @@ def _S_class_group_and_units(self, S, proof=True): sage: K.<a> = NumberField(2*x^2 - 1/3) sage: K._S_class_group_and_units(tuple(K.primes_above(2) + K.primes_above(3))) - ([-6*a + 2, 6*a + 3, -1, 12*a + 5], []) + ([6*a + 2, 6*a + 3, -1, -12*a + 5], []) """ K_pari = self.pari_bnf(proof=proof) S_pari = [p.pari_prime() for p in sorted(set(S))] @@ -5025,7 +5026,7 @@ def selmer_generators(self, S, m, proof=True, orders=False): ords.append(m) else: m1 = order.gcd(m) - if m1!= 1: + if m1 != 1: gens.append(unit) ords.append(m1) card_S = len(S) @@ -5166,7 +5167,7 @@ def selmer_space(self, S, p, proof=None): sage: [K.ideal(g).factor() for g in gens] [(Fractional ideal (2, a + 1)) * (Fractional ideal (3, a + 1)), - Fractional ideal (-a), + Fractional ideal (a), (Fractional ideal (2, a + 1))^2, 1] @@ -5579,8 +5580,8 @@ def composite_fields(self, other, names=None, both_maps=False, preserve_embeddin i -= 1 else: self_to_F = self.hom([a_in_F]) - other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F*to_other_abs) - rets.append( (F, self_to_F, other_to_F, k) ) + other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F * to_other_abs) + rets.append((F, self_to_F, other_to_F, k)) else: rets.append(F) return rets @@ -5751,7 +5752,7 @@ def elements_of_norm(self, n, proof=None) -> list: sage: K.elements_of_norm(3) [] sage: K.elements_of_norm(50) - [-7*a + 1, 5*a - 5, 7*a + 1] + [-a - 7, 5*a - 5, 7*a + 1] TESTS: @@ -5813,7 +5814,7 @@ def extension(self, poly, name=None, names=None, latex_name=None, latex_names=No try: poly = poly.polynomial(self) except (AttributeError, TypeError): - raise TypeError("polynomial (=%s) must be a polynomial."%repr(poly)) + raise TypeError("polynomial (=%s) must be a polynomial" % repr(poly)) if poly.base_ring() is not self: poly = poly.change_ring(self) if names is not None: @@ -5863,7 +5864,7 @@ def factor(self, n): sage: K.factor(1+a) Fractional ideal (a + 1) sage: K.factor(1+a/5) - (Fractional ideal (a + 1)) * (Fractional ideal (-a - 2))^-1 * (Fractional ideal (2*a + 1))^-1 * (Fractional ideal (-3*a - 2)) + (Fractional ideal (a + 1)) * (Fractional ideal (-a - 2))^-1 * (Fractional ideal (2*a + 1))^-1 * (Fractional ideal (-2*a + 3)) An example over a relative number field:: @@ -6134,7 +6135,6 @@ def is_abelian(self): sage: NumberField(x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1, 'a').is_abelian() True """ - if not self.is_galois(): return False @@ -6147,7 +6147,7 @@ def is_abelian(self): return self.galois_group().is_abelian() pari_pol = pari(self.polynomial()) - return pari_pol.galoisinit().galoisisabelian(1)==1 + return pari_pol.galoisinit().galoisisabelian(1) == 1 @cached_method def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=None): @@ -6283,7 +6283,7 @@ def _normalize_prime_list(self, v): v = [] elif not isinstance(v, (list, tuple)): v = [v] - + v = [ZZ(p).abs() for p in v] v = sorted(set(v)) @@ -6319,7 +6319,7 @@ def power_basis(self): [1, zeta15, zeta15^2, zeta15^3, zeta15^4, zeta15^5, zeta15^6, zeta15^7] """ g = self.gen() - return [ g**i for i in range(self.relative_degree()) ] + return [g**i for i in range(self.relative_degree())] def integral_basis(self, v=None): """ @@ -6328,11 +6328,9 @@ def integral_basis(self, v=None): INPUT: - - ``v`` - None, a prime, or a list of primes. See the documentation for self.maximal_order. - EXAMPLES:: sage: K.<a> = NumberField(x^5 + 10*x + 1) @@ -6416,7 +6414,7 @@ def _pari_integral_basis(self, v=None, important=True): trialdivlimit2 = pari(10**12) trialdivlimit3 = pari(10**18) if all(p < trialdivlimit2 or (e == 1 and p < trialdivlimit3) or p.isprime() for p, e in zip(m[0], m[1])): - B = f.nfbasis(fa = m) + B = f.nfbasis(fa=m) else: raise RuntimeError("Unable to factor discriminant with trial division") else: @@ -6469,9 +6467,9 @@ def reduced_basis(self, prec=None): sage: new_basis = k.reduced_basis(prec=120) sage: [c.minpoly() for c in new_basis] [x - 1, - x^2 - x + 1, + x^2 + x + 1, + x^6 + 3*x^5 - 102*x^4 - 103*x^3 + 10572*x^2 - 59919*x + 127657, x^6 + 3*x^5 - 102*x^4 - 103*x^3 + 10572*x^2 - 59919*x + 127657, - x^6 - 3*x^5 - 102*x^4 + 315*x^3 + 10254*x^2 - 80955*x + 198147, x^3 - 171*x + 848, x^6 + 171*x^4 + 1696*x^3 + 29241*x^2 + 145008*x + 719104] sage: R = k.order(new_basis) @@ -6573,25 +6571,25 @@ def reduced_gram_matrix(self, prec=None): if self.is_totally_real(): B = self.reduced_basis() self.__reduced_gram_matrix = matrix(ZZ, d, d, - [[(x*y).trace() for x in B] + [[(x * y).trace() for x in B] for y in B]) else: M = self.minkowski_embedding(prec=prec) - T = matrix(d, flatten([ a.vector().list() - for a in self.reduced_basis(prec=prec) ])) - A = M*(T.transpose()) - self.__reduced_gram_matrix = A.transpose()*A + T = matrix(d, flatten([a.vector().list() + for a in self.reduced_basis(prec=prec)])) + A = M * T.transpose() + self.__reduced_gram_matrix = A.transpose() * A if prec is None: - ## this is the default choice for minkowski_embedding + # this is the default choice for minkowski_embedding self.__reduced_gram_matrix_prec = 53 else: self.__reduced_gram_matrix_prec = prec return self.__reduced_gram_matrix - #****************************************************** - # Supplementary algorithm to enumerate lattice points - #****************************************************** + # ****************************************************** + # Supplementary algorithm to enumerate lattice points + # ****************************************************** def _positive_integral_elements_with_trace(self, C): r""" @@ -6627,14 +6625,14 @@ def _positive_integral_elements_with_trace(self, C): B = self.reduced_basis() T = self.reduced_gram_matrix() - P = pari(T).qfminim((C[1]**2)*(1./2), 10**6)[2] + P = pari(T).qfminim((C[1]**2) * 0.5, 10**6)[2] S = [] for p in P: - theta = sum([ p.list()[i]*B[i] for i in range(self.degree())]) + theta = sum([p.list()[i] * B[i] for i in range(self.degree())]) if theta.trace() < 0: theta *= -1 - if theta.trace() >= C[0] and theta.trace() <= C[1]: + if C[0] <= theta.trace() <= C[1]: if self(theta).is_totally_positive(): S.append(self(theta)) return S @@ -6881,7 +6879,7 @@ def residue_field(self, prime, names=None, check=True): """ from sage.rings.number_field.number_field_ideal import is_NumberFieldIdeal if is_NumberFieldIdeal(prime) and prime.number_field() is not self: - raise ValueError("%s is not an ideal of %s"%(prime,self)) + raise ValueError("%s is not an ideal of %s" % (prime, self)) # This allows principal ideals to be specified using a generator: try: prime = self.ideal(prime) @@ -6889,9 +6887,9 @@ def residue_field(self, prime, names=None, check=True): pass if not is_NumberFieldIdeal(prime) or prime.number_field() is not self: - raise ValueError("%s is not an ideal of %s"%(prime,self)) + raise ValueError("%s is not an ideal of %s" % (prime, self)) if check and not prime.is_prime(): - raise ValueError("%s is not a prime ideal"%prime) + raise ValueError("%s is not a prime ideal" % prime) from sage.rings.finite_rings.residue_field import ResidueField return ResidueField(prime, names=names, check=False) @@ -6925,10 +6923,10 @@ def trace_pairing(self, v): import sage.matrix.matrix_space A = sage.matrix.matrix_space.MatrixSpace(self.base_ring(), len(v))(0) for i in range(len(v)): - for j in range(i,len(v)): + for j in range(i, len(v)): t = (self(v[i]*v[j])).trace() - A[i,j] = t - A[j,i] = t + A[i, j] = t + A[j, i] = t return A def uniformizer(self, P, others="positive"): @@ -7067,7 +7065,7 @@ def units(self, proof=None): -a^16 - a^15 - a^14 - a^13 - a^12 - a^11 - a^10 - a^9 - a^8 - a^7 - a^6 - a^5 - a^4 - a^3 - a^2 + 2, -2*a^16 + 3*a^15 - 3*a^14 + 3*a^13 - 3*a^12 + a^11 - a^9 + 3*a^8 - 4*a^7 + 5*a^6 - 6*a^5 + 4*a^4 - 3*a^3 + 2*a^2 + 2*a - 4, a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, - -a^14 - a^13 + a^12 + 2*a^10 + a^8 - 2*a^7 - 2*a^6 + 2*a^3 - a^2 + 2*a - 2) + 2*a^16 + a^15 - a^11 - 3*a^10 - 4*a^9 - 4*a^8 - 4*a^7 - 5*a^6 - 7*a^5 - 8*a^4 - 6*a^3 - 5*a^2 - 6*a - 7) TESTS: @@ -7076,7 +7074,7 @@ def units(self, proof=None): sage: K.<a> = NumberField(1/2*x^2 - 1/6) sage: K.units() - (-3*a + 2,) + (3*a - 2,) """ proof = proof_flag(proof) @@ -7155,7 +7153,7 @@ def unit_group(self, proof=None): sage: U.gens() (u0, u1, u2, u3, u4, u5, u6, u7, u8) sage: U.gens_values() # result not independently verified - [-1, -a^9 - a + 1, -a^16 + a^15 - a^14 + a^12 - a^11 + a^10 + a^8 - a^7 + 2*a^6 - a^4 + 3*a^3 - 2*a^2 + 2*a - 1, 2*a^16 - a^14 - a^13 + 3*a^12 - 2*a^10 + a^9 + 3*a^8 - 3*a^6 + 3*a^5 + 3*a^4 - 2*a^3 - 2*a^2 + 3*a + 4, a^15 + a^14 + 2*a^11 + a^10 - a^9 + a^8 + 2*a^7 - a^5 + 2*a^3 - a^2 - 3*a + 1, -a^16 - a^15 - a^14 - a^13 - a^12 - a^11 - a^10 - a^9 - a^8 - a^7 - a^6 - a^5 - a^4 - a^3 - a^2 + 2, -2*a^16 + 3*a^15 - 3*a^14 + 3*a^13 - 3*a^12 + a^11 - a^9 + 3*a^8 - 4*a^7 + 5*a^6 - 6*a^5 + 4*a^4 - 3*a^3 + 2*a^2 + 2*a - 4, a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, -a^14 - a^13 + a^12 + 2*a^10 + a^8 - 2*a^7 - 2*a^6 + 2*a^3 - a^2 + 2*a - 2] + [-1, -a^9 - a + 1, -a^16 + a^15 - a^14 + a^12 - a^11 + a^10 + a^8 - a^7 + 2*a^6 - a^4 + 3*a^3 - 2*a^2 + 2*a - 1, 2*a^16 - a^14 - a^13 + 3*a^12 - 2*a^10 + a^9 + 3*a^8 - 3*a^6 + 3*a^5 + 3*a^4 - 2*a^3 - 2*a^2 + 3*a + 4, a^15 + a^14 + 2*a^11 + a^10 - a^9 + a^8 + 2*a^7 - a^5 + 2*a^3 - a^2 - 3*a + 1, -a^16 - a^15 - a^14 - a^13 - a^12 - a^11 - a^10 - a^9 - a^8 - a^7 - a^6 - a^5 - a^4 - a^3 - a^2 + 2, -2*a^16 + 3*a^15 - 3*a^14 + 3*a^13 - 3*a^12 + a^11 - a^9 + 3*a^8 - 4*a^7 + 5*a^6 - 6*a^5 + 4*a^4 - 3*a^3 + 2*a^2 + 2*a - 4, a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, 2*a^16 + a^15 - a^11 - 3*a^10 - 4*a^9 - 4*a^8 - 4*a^7 - 5*a^6 - 7*a^5 - 8*a^4 - 6*a^3 - 5*a^2 - 6*a - 7] """ proof = proof_flag(proof) @@ -7170,7 +7168,7 @@ def unit_group(self, proof=None): except AttributeError: pass - U = UnitGroup(self,proof) + U = UnitGroup(self, proof) if proof: self._unit_group = U else: @@ -7272,14 +7270,14 @@ def S_unit_group(self, proof=None, S=None): try: S = tuple(self.ideal(S).prime_factors()) except (NameError, TypeError, ValueError): - raise ValueError("Cannot make a set of primes from %s"%(S,)) + raise ValueError(f"Cannot make a set of primes from {S}") else: try: S = tuple(self.ideal(P) for P in S) except (NameError, TypeError, ValueError): - raise ValueError("Cannot make a set of primes from %s"%(S,)) + raise ValueError(f"Cannot make a set of primes from {S}") if not all(P.is_prime() for P in S): - raise ValueError("Not all elements of %s are prime ideals"%(S,)) + raise ValueError(f"Not all elements of {S} are prime ideals") try: return self._S_unit_group_cache[S] @@ -7296,7 +7294,7 @@ def S_unit_group(self, proof=None, S=None): except KeyError: pass - U = UnitGroup(self,proof,S=S) + U = UnitGroup(self, proof, S=S) if proof: self._S_unit_group_cache[S] = U else: @@ -7383,13 +7381,13 @@ def zeta(self, n=2, all=False): sage: K.zeta(2, all=True) [-1] sage: K.zeta(3) - 1/2*z - 1/2 + -1/2*z - 1/2 sage: K.zeta(3, all=True) - [1/2*z - 1/2, -1/2*z - 1/2] + [-1/2*z - 1/2, 1/2*z - 1/2] sage: K.zeta(4) Traceback (most recent call last): ... - ValueError: There are no 4th roots of unity in self. + ValueError: there are no 4th roots of unity in self :: @@ -7402,7 +7400,7 @@ def zeta(self, n=2, all=False): sage: K.zeta(3) Traceback (most recent call last): ... - ValueError: There are no 3rd roots of unity in self. + ValueError: there are no 3rd roots of unity in self sage: K.zeta(3,all=True) [] @@ -7412,6 +7410,12 @@ def zeta(self, n=2, all=False): sage: K.<a> = NumberField(1/2*x^2 + 1/6) sage: K.zeta(3) -3/2*a - 1/2 + + TESTS:: + + sage: K = NumberField(x**60+691*x**12-25,'a') + sage: K.zeta(15,all=True) + [] """ try: return self._unit_group.zeta(n, all) @@ -7429,28 +7433,26 @@ def zeta(self, n=2, all=False): if n == 1: if all: return [K.one()] - else: - return K.one() + return K.one() elif n == 2: if all: return [K(-1)] - else: - return K(-1) + return K(-1) - # First check if the degree of K is compatible with an - # inclusion QQ(\zeta_n) -> K. + # First check if the degree of K is compatible + # with an inclusion QQ(\zeta_n) -> K. if sage.arith.all.euler_phi(n).divides(K.absolute_degree()): - # Factor the n-th cyclotomic polynomial over K. - f = K.pari_polynomial('y') - factors = f.nffactor(pari.polcyclo(n)).component(1) - roots = (K(-g.polcoef(0)) for g in factors if g.poldegree() == 1) - if all: - return list(roots) - try: - return next(roots) - except StopIteration: - pass - raise ValueError("There are no %s roots of unity in self." % n.ordinal_str()) + w, zeta_w = self.pari_nf().nfrootsof1() + w = w.sage() + zeta_w = K(zeta_w) + if not w % n: + zeta_n = zeta_w**(w // n) + if all: + return [zeta_n**i for i in n.coprime_integers(n)] + return zeta_n + if all: + return [] + raise ValueError("there are no %s roots of unity in self" % n.ordinal_str()) def zeta_order(self): r""" @@ -7587,7 +7589,7 @@ def roots_of_unity(self): """ z = self.primitive_root_of_unity() n = self.zeta_order() - return [ z**k for k in range(1, n+1) ] + return [z**k for k in range(1, n + 1)] def zeta_coefficients(self, n): """ @@ -7643,19 +7645,19 @@ def solve_CRT(self, reslist, Ilist, check=True): reslist = [self(x) for x in reslist] except ValueError: raise ValueError("solve_CRT requires a list of arguments in the field") - if n==0: + if n == 0: return self.zero() - if n==1: + if n == 1: return reslist[0] - if n==2: + if n == 2: try: r = Ilist[0].element_1_mod(Ilist[1]) except TypeError: raise ArithmeticError("ideals in solve_CRT() must be pairwise coprime") x = ((1-r)*reslist[0]+r*reslist[1]).mod(prod(Ilist)) else: # n>2;, use induction / recursion - x = self.solve_CRT([reslist[0],self.solve_CRT(reslist[1:],Ilist[1:])], - [Ilist[0],prod(Ilist[1:])], check=check) + x = self.solve_CRT([reslist[0], self.solve_CRT(reslist[1:], Ilist[1:])], + [Ilist[0], prod(Ilist[1:])], check=check) if check and not all(x - xi in Ii for xi, Ii in zip(reslist, Ilist)): raise RuntimeError("Error in number field solve_CRT()") return self(x) @@ -7975,7 +7977,7 @@ def __init__(self, polynomial, name, latex_name=None, check=True, embedding=None assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structure=structure) self._element_class = number_field_element.NumberFieldElement_absolute self._zero_element = self._element_class(self, 0) - self._one_element = self._element_class(self, 1) + self._one_element = self._element_class(self, 1) self._init_embedding_approx() @@ -8148,22 +8150,22 @@ def _coerce_from_other_number_field(self, x): F = LF Kgen = F(Kgen) else: - raise TypeError("No compatible natural embeddings found for %s and %s"%(KF,LF)) + raise TypeError("No compatible natural embeddings found for %s and %s" % (KF, LF)) # List of candidates for K(x) f = x.minpoly() ys = f.roots(ring=K, multiplicities=False) if not ys: - raise ValueError("Cannot convert %s to %s (regardless of embeddings)"%(x,K)) + raise ValueError("Cannot convert %s to %s (regardless of embeddings)" % (x, K)) # Define a function are_roots_equal to determine whether two # roots of f are equal. A simple a == b does not suffice for # inexact fields because of floating-point errors. if F.is_exact(): - are_roots_equal = lambda a,b: a == b + are_roots_equal = lambda a, b: a == b else: - ### Compute a lower bound on the distance between the roots of f. - ### This essentially gives the precision to work with. + # Compute a lower bound on the distance between the roots of f. + # This essentially gives the precision to work with. # A function # log2abs: F --> RR @@ -8175,7 +8177,7 @@ def _coerce_from_other_number_field(self, x): # Compute half Fujiwara's bound on the roots of f n = f.degree() log_half_root_bound = log2abs(f[0]/2)/n - for i in range(1,n): + for i in range(1, n): bd = log2abs(f[i])/(n-i) if bd > log_half_root_bound: log_half_root_bound = bd @@ -8192,7 +8194,7 @@ def _coerce_from_other_number_field(self, x): # they are equal. The factor 128 is arbitrary, it is an extra # safety margin. eps = (log_root_diff - 7.0).exp2() - are_roots_equal = lambda a,b: (a-b).abs() <= eps + are_roots_equal = lambda a, b: (a-b).abs() <= eps if F is CC: # Adjust the precision of F, sufficient to represent all # the temporaries in the computation with a precision @@ -8211,7 +8213,7 @@ def _coerce_from_other_number_field(self, x): emb_y = y.polynomial()(Kgen) if are_roots_equal(emb_x, emb_y): return y - raise ValueError("Cannot convert %s to %s (using the specified embeddings)"%(x,K)) + raise ValueError("Cannot convert %s to %s (using the specified embeddings)" % (x, K)) def _coerce_map_from_(self, R): """ @@ -8310,7 +8312,7 @@ def _coerce_map_from_(self, R): if self.coerce_embedding() is not None: try: return number_field_morphisms.EmbeddedNumberFieldMorphism(R, self) - except ValueError: # no common embedding found + except ValueError: # no common embedding found return None else: # R is embedded, self isn't. So, we could only have @@ -8562,8 +8564,8 @@ def optimized_subfields(self, degree=0, name=None, both_maps=True): Defn: a |--> -1/2*a6^3 + a6^2 - 1/2*a6) ] """ - return self._subfields_helper(degree=degree,name=name, - both_maps=both_maps,optimize=True) + return self._subfields_helper(degree=degree, name=name, + both_maps=both_maps, optimize=True) def change_names(self, names): r""" @@ -8739,12 +8741,12 @@ def _subfields_helper(self, degree=0, name=None, both_maps=True, optimize=False) embedding = self.coerce_embedding()(a) # trac 7695 add a _ to prevent zeta70 etc. if name[-1].isdigit(): - new_name= name+ '_' + str(i) + new_name = name + '_' + str(i) else: new_name = name + str(i) K = NumberField(f, names=new_name, embedding=embedding) - from_K = K.hom([a]) # check=False here ?? would be safe unless there are bugs. + from_K = K.hom([a]) # check=False here ?? would be safe unless there are bugs. if both_maps and K.degree() == self.degree(): g = K['x'](self.polynomial()) @@ -9002,7 +9004,7 @@ def _galois_closure_and_embedding(self, names=None): L = self.__galois_closure.change_names(names) L_to_orig, orig_to_L = L.structure() # "flatten" the composition by hand - self_into_L = self.hom([ (orig_to_L * self.__galois_closure_embedding)(self.gen()) ]) + self_into_L = self.hom([(orig_to_L * self.__galois_closure_embedding)(self.gen())]) return (L, self_into_L) except AttributeError: pass @@ -9076,17 +9078,14 @@ def galois_closure(self, names=None, map=False): Defn: a |--> 1/240*cc^5 - 41/120*cc """ L, self_into_L = self._galois_closure_and_embedding(names) - if map: - return (L, self_into_L) - else: - return L + return (L, self_into_L) if map else L def automorphisms(self): r""" Compute all Galois automorphisms of self. - This uses PARI's :pari:`nfgaloisconj` and is much faster than root finding - for many fields. + This uses PARI's :pari:`nfgaloisconj` and is much faster than + root finding for many fields. EXAMPLES:: @@ -9139,12 +9138,12 @@ def embeddings(self, K): not even be a number field, e.g., it could be the complex numbers). This will return an identical result when given K as input again. - If possible, the most natural embedding of this field into K is put first - in the list. + If possible, the most natural embedding of this field into K + is put first in the list. INPUT: - - ``K`` - a field + - ``K`` -- a field EXAMPLES:: @@ -9220,7 +9219,7 @@ def embeddings(self, K): # If there is an embedding that preserves variable names # then it is most natural, so we put it first. put_natural_embedding_first(v) - return Sequence(v, cr=v!=[], immutable=True, + return Sequence(v, cr=bool(v), immutable=True, check=False, universe=self.Hom(K)) def minkowski_embedding(self, B=None, prec=None): @@ -9282,14 +9281,14 @@ def minkowski_embedding(self, B=None, prec=None): R = sage.rings.real_double.RDF else: R = sage.rings.real_mpfr.RealField(prec) - r,s = self.signature() + r, s = self.signature() places = self.places(prec=prec) if B is None: - B = [ (self.gen(0))**i for i in range(n) ] + B = [(self.gen(0))**i for i in range(n)] A = ZZ['x'] - f = A.gen(0)**2-2 + f = A.gen(0)**2 - 2 sqrt2 = f.roots(R)[1][0] d = {} @@ -9297,15 +9296,87 @@ def minkowski_embedding(self, B=None, prec=None): for col in range(n): for row in range(r): - d[(row,col)] = places[row](B[col]) + d[(row, col)] = places[row](B[col]) for i in range(s): z = places[r+i](B[col]) - d[(r+2*i,col)] = z.real()*sqrt2 - d[(r+2*i+1,col)] = z.imag()*sqrt2 + d[(r+2*i, col)] = z.real()*sqrt2 + d[(r+2*i+1, col)] = z.imag()*sqrt2 return sage.matrix.all.matrix(d) + def logarithmic_embedding(self, prec=53): + r""" + Return the morphism of ``self`` under the logarithmic embedding + in the category Set. + + The logarithmic embedding is defined as a map from the number field ``self`` to `\RR^n`. + + It is defined under Definition 4.9.6 in [Coh1993]_. + + INPUT: + + - ``prec`` -- desired floating point precision. + + OUTPUT: + + - the morphism of ``self`` under the logarithmic embedding in the category Set. + + EXAMPLES:: + + sage: CF.<a> = CyclotomicField(5) + sage: f = CF.logarithmic_embedding() + sage: f(0) + (-1, -1) + sage: f(7) + (3.89182029811063, 3.89182029811063) + + :: + + sage: K.<a> = NumberField(x^3 + 5) + sage: f = K.logarithmic_embedding() + sage: f(0) + (-1, -1) + sage: f(7) + (1.94591014905531, 3.89182029811063) + + :: + + sage: F.<a> = NumberField(x^4 - 8*x^2 + 3) + sage: f = F.logarithmic_embedding() + sage: f(0) + (-1, -1, -1, -1) + sage: f(7) + (1.94591014905531, 1.94591014905531, 1.94591014905531, 1.94591014905531) + """ + def closure_map(x, prec=53): + """ + The function closure of the logarithmic embedding. + """ + K = self + K_embeddings = K.places(prec) + r1, r2 = K.signature() + r = r1 + r2 - 1 + + from sage.rings.real_mpfr import RealField + Reals = RealField(prec) + + if x == 0: + return vector([-1 for _ in range(r + 1)]) + + x_logs = [] + for i in range(r1): + sigma = K_embeddings[i] + x_logs.append(Reals(abs(sigma(x))).log()) + for i in range(r1, r + 1): + tau = K_embeddings[i] + x_logs.append(2 * Reals(abs(tau(x))).log()) + + return vector(x_logs) + + hom = Hom(self, VectorSpace(RR, len(closure_map(self(0), prec))), Sets()) + return hom(closure_map) + def places(self, all_complex=False, prec=None): r""" Return the collection of all infinite places of self. @@ -9391,27 +9462,28 @@ def places(self, all_complex=False, prec=None): R = sage.rings.real_mpfr.RealField(prec) C = sage.rings.complex_mpfr.ComplexField(prec) - ## first, find the intervals with roots, and see how much - ## precision we need to approximate the roots - ## - all_intervals = [ x[0] for x in self.defining_polynomial().roots(C) ] + # first, find the intervals with roots, and see how much + # precision we need to approximate the roots + # + all_intervals = [x[0] for x in self.defining_polynomial().roots(C)] - ## first, set up the real places + # first, set up the real places if all_complex: - real_intervals = [ x for x in all_intervals if x.imag().is_zero() ] + real_intervals = [x for x in all_intervals if x.imag().is_zero()] else: - real_intervals = [ x[0] for x in self.defining_polynomial().roots(R) ] + real_intervals = [x[0] for x in self.defining_polynomial().roots(R)] if prec is None: - real_places = [ self.hom([i.center()], check=False) for i in real_intervals ] + real_places = [self.hom([i.center()], check=False) + for i in real_intervals] - complex_places = [ self.hom([i.center()], check=False) for i in - all_intervals if i.imag() > 0 ] + complex_places = [self.hom([i.center()], check=False) + for i in all_intervals if i.imag() > 0] else: - real_places = [ self.hom([i], check=False) for i in real_intervals ] + real_places = [self.hom([i], check=False) for i in real_intervals] - complex_places = [ self.hom([i], check=False) for i in - all_intervals if i.imag() > 0 ] + complex_places = [self.hom([i], check=False) + for i in all_intervals if i.imag() > 0] return real_places + complex_places @@ -9691,12 +9763,12 @@ def relativize(self, alpha, names, structure=None): if alpha.codomain() != self: raise ValueError("Co-domain of morphism must be self") L = alpha.domain() - alpha = alpha(L.gen()) # relativize over phi's domain + alpha = alpha(L.gen()) # relativize over phi's domain if L is QQ: from sage.rings.polynomial.polynomial_ring import polygen f = polygen(QQ) else: - f = L.defining_polynomial() # = alpha.minpoly() + f = L.defining_polynomial() # = alpha.minpoly() names = normalize_names(len(names), names) else: # alpha must be an element coercible to self @@ -9708,13 +9780,14 @@ def relativize(self, alpha, names, structure=None): # now we do some linear algebra to find the minpoly of self.gen() over L L_into_self = L.hom([alpha]) - extdeg = self.absolute_degree() // L.absolute_degree() # [ L : self ] + extdeg = self.absolute_degree() // L.absolute_degree() # [ L : self ] a = self.gen() # we will find a linear relation between small powers of a over L - basis = [ a**i * b for i in range(extdeg) for b in map(L_into_self, L.power_basis()) ] - basis.append(a**extdeg) # this one makes the basis no longer a basis - mat = matrix([ b.vector() for b in basis ]) + basis = [a**i * b for i in range(extdeg) + for b in map(L_into_self, L.power_basis())] + basis.append(a**extdeg) # this one makes the basis no longer a basis + mat = matrix([b.vector() for b in basis]) soln_space = mat.left_kernel(mat.row_space()(0)) # the solution space is one dimensional and the last entry is non-zero # because a satisfies no smaller linear relation @@ -9724,8 +9797,8 @@ def relativize(self, alpha, names, structure=None): reln = reln * ~reln[-1] # now we need to get those coeffs in L - coeff_mat = matrix(extdeg, f.degree(), list(reln)[:-1]) # easy way to divide into the correct lengths - coeffs_in_L = [ r*vector(L.power_basis()) for r in coeff_mat.rows() ] + coeff_mat = matrix(extdeg, f.degree(), list(reln)[:-1]) # easy way to divide into the correct lengths + coeffs_in_L = [r * vector(L.power_basis()) for r in coeff_mat.rows()] # f is the minimal polynomial of a over L f = L['x'](coeffs_in_L + [1]) # sanity check... @@ -9846,7 +9919,7 @@ def relative_different(self): """ return self.different() - def hilbert_symbol(self, a, b, P = None): + def hilbert_symbol(self, a, b, P=None): r""" Return the Hilbert symbol `(a,b)_P` for a prime P of self and non-zero elements a and b of self. @@ -10057,7 +10130,7 @@ def hilbert_symbol(self, a, b, P = None): codom = P.codomain() from sage.rings.qqbar import AA, QQbar if isinstance(codom, (sage.rings.abc.ComplexField, sage.rings.abc.ComplexDoubleField, sage.rings.abc.ComplexIntervalField)) or \ - codom is QQbar: + codom is QQbar: if P(self.gen()).imag() == 0: raise ValueError("Possibly real place (=%s) given as complex embedding in hilbert_symbol. Is it real or complex?" % P) return 1 @@ -10142,7 +10215,7 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True): # input checks if not type(S) is list: - raise TypeError( "first argument must be a list") + raise TypeError("first argument must be a list") if b not in self: raise TypeError("second argument must be an element of this field") b = self(b) @@ -10156,15 +10229,15 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True): if not p.is_prime(): raise ValueError("not a prime ideal") if self.quadratic_defect(b, p) == infinity.Infinity: - raise ValueError("%s is a square in the completion "%b + - "with respect to %s"%p) + raise ValueError(f"{b} is a square in the completion " + f"with respect to {p}") else: if p not in self.real_places(): raise ValueError("entries of the list must be " "prime ideals or real places") if p(b) > 0: - raise ValueError("%s is a square in the completion " - "with respect to %s" % (b, p)) + raise ValueError(f"{b} is a square in the completion " + f"with respect to {p}") # L is the list of primes that we need to consider, b must have # nonzero valuation for each prime in L, this is the set S' @@ -10248,7 +10321,7 @@ def phi(x): assert phi(a) == v, "oops" return a - def hilbert_conductor(self,a,b): + def hilbert_conductor(self, a, b): """ This is the product of all (finite) primes where the Hilbert symbol is -1. What is the same, this is the (reduced) discriminant of the quaternion @@ -10276,12 +10349,11 @@ def hilbert_conductor(self,a,b): AUTHOR: - Aly Deines - """ a, b = self(a), self(b) d = self.ideal(1) for p in set(self.ideal(2).prime_factors()).union(self.ideal(a).prime_factors()).union(self.ideal(b).prime_factors()): - if self.hilbert_symbol(a,b,p) == -1: + if self.hilbert_symbol(a, b, p) == -1: d *= p return d @@ -10432,11 +10504,21 @@ def _factor_univariate_polynomial(self, poly, **kwargs): sage: x = polygen(K,'x') sage: factor(x*x+4) # indirect doctest (x - 2*i) * (x + 2*i) + + TESTS:: + + sage: with seed(0): + ....: P.<y> = NumberField(QQ['a'].random_element(30,10^100), 'a')[] + sage: (3*y^4/4).factor() + (3/4) * y^4 """ if self.degree() == 1: factors = poly.change_ring(QQ).factor() return Factorization([(p.change_ring(self), e) for p, e in factors], self(factors.unit())) + elif poly.is_term(): + return Factorization([(poly.parent().gen(), poly.degree())], + poly.leading_coefficient()) # Convert the polynomial we want to factor to PARI f = poly._pari_with_name() @@ -10548,22 +10630,22 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a """ f = QQ['x'].cyclotomic_polynomial(n) if names[0].startswith('zeta'): - latex_name = "\\zeta_{%s}"%n + latex_name = "\\zeta_{%s}" % n else: latex_name = latex_variable_name(names[0]) self.__n = n = Integer(n) NumberField_absolute.__init__(self, f, - name= names, + name=names, latex_name=latex_name, check=False, - embedding = embedding, + embedding=embedding, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes) if n % 2: self.__zeta_order = 2 * n else: self.__zeta_order = n - ## quadratic number fields require this: + # quadratic number fields require this: if f.degree() == 2: # define a boolean flag as for NumberField_quadratic to know, which # square root we choose (True means no embedding or positive @@ -10579,12 +10661,12 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a if n == 4: self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian self._D = ZZ(-1) - self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ(1))) + self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ.one())) else: - ## n is 3 or 6 + # n is 3 or 6 self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic self._D = ZZ(-3) - one_half = ZZ(1)/ZZ(2) + one_half = QQ((1, 2)) if n == 3: self._NumberField_generic__gen = self._element_class(self, (one_half-1, one_half)) else: @@ -10594,8 +10676,8 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a # _one_element to NumberFieldElement_absolute values, which is # wrong (and dangerous; such elements can actually be used to # crash Sage: see #5316). Overwrite them with correct values. - self._zero_element = self._element_class(self, (QQ(0),QQ(0))) - self._one_element = self._element_class(self, (QQ(1),QQ(0))) + self._zero_element = self._element_class(self, (QQ(0), QQ(0))) + self._one_element = self._element_class(self, (QQ.one(), QQ(0))) zeta = self.gen() zeta._set_multiplicative_order(n) @@ -10676,7 +10758,7 @@ def _magma_init_(self, magma): sage: K._magma_init_(magma) # optional - magma 'SageCreateWithNames(CyclotomicField(7),["zeta"])' """ - s = 'CyclotomicField(%s)'%self.__n + s = 'CyclotomicField(%s)' % self.__n return magma._with_names(s, self.variable_names()) def _gap_init_(self): @@ -10709,7 +10791,7 @@ def _gap_init_(self): zeta3 -zeta3 - 1 """ - return 'CyclotomicField(%s)'%self.__n + return 'CyclotomicField(%s)' % self.__n def _libgap_(self): """ @@ -10742,8 +10824,8 @@ def _repr_(self): sage: CyclotomicField(400)._repr_() 'Cyclotomic Field of order 400 and degree 160' """ - return "Cyclotomic Field of order %s and degree %s"%( - self.__n, self.degree()) + n = self.__n + return f"Cyclotomic Field of order {n} and degree {self.degree()}" def _n(self): """ @@ -10794,9 +10876,8 @@ def _latex_(self): """ v = self.latex_variable_names()[0] if v.startswith('\\zeta_'): - return "%s(%s)"%(latex(QQ), v) - else: - return NumberField_generic._latex_(self) + return "%s(%s)" % (latex(QQ), v) + return NumberField_generic._latex_(self) def _coerce_map_from_(self, K): r""" @@ -11075,106 +11156,18 @@ def _element_constructor_(self, x, check=True): return NumberField_absolute._element_constructor_(self, x) elif isinstance(x, pari_gen): return NumberField_absolute._element_constructor_(self, x, check=check) - elif (sage.interfaces.gap.is_GapElement(x) or - isinstance(x, sage.libs.gap.element.GapElement)): + elif isinstance(x, (sage.libs.gap.element.GapElement, GapElement)): return self._coerce_from_gap(x) - elif isinstance(x,str): + elif isinstance(x, str): return self._convert_from_str(x) # late import because of speed from sage.rings.universal_cyclotomic_field import UniversalCyclotomicFieldElement - if isinstance(x,UniversalCyclotomicFieldElement): + if isinstance(x, UniversalCyclotomicFieldElement): return x.to_cyclotomic_field(self) else: return self._convert_non_number_field_element(x) - # TODO: - # The following is very nice and much more flexible / powerful. - # However, it is simply not *consistent*, since it totally - # breaks the doctests in eisenstein_submodule.py. - # FIX THIS. - -## def _will_be_better_coerce_from_other_cyclotomic_field(self, x, only_canonical=False): -## """ -## Coerce an element x of a cyclotomic field into self, if at all possible. - -## INPUT: -## x -- number field element - -## only_canonical -- bool (default: False); Attempt to work, -## even in some cases when x is not in a subfield of -## the cyclotomics (as long as x is a root of unity). - -## EXAMPLES:: - -## sage: k5 = CyclotomicField(5) -## sage: k3 = CyclotomicField(3) -## sage: k15 = CyclotomicField(15) -## sage: k15._coerce_from_other_cyclotomic_field(k3.gen()) -## zeta15^5 -## sage: k15._coerce_from_other_cyclotomic_field(k3.gen()^2 + 17/3) -## -zeta15^5 + 14/3 -## sage: k3._coerce_from_other_cyclotomic_field(k15.gen()^5) -## zeta3 -## sage: k3._coerce_from_other_cyclotomic_field(-2/3 * k15.gen()^5 + 2/3) -## -2/3*zeta3 + 2/3 -## """ - -## K = x.parent() - -## if K is self: -## return x -## n = K.zeta_order() -## m = self.zeta_order() - -## self_gen = self.gen() - -## if m % n == 0: # easy case -## # pass this off to a method in the element class -## # it can be done very quickly and easily by the cython<->NTL -## # interface there -## return x._lift_cyclotomic_element(self) - -## # Whatever happens below, it has to be consistent with -## # zeta_r |---> (zeta_s)^m - -## if m % 2 and not n%2: -## m *= 2 -## self_gen = -self_gen - -## if only_canonical and m % n: -## raise TypeError, "no canonical coercion" - -## if not is_CyclotomicField(K): -## raise TypeError, "x must be in a cyclotomic field" - -## v = x.list() - -## # Find the smallest power r >= 1 of the generator g of K that is in self, -## # i.e., find the smallest r such that g^r has order dividing m. - -## d = sage.arith.all.gcd(m,n) -## r = n // d - -## # Since we use the power basis for cyclotomic fields, if every -## # v[i] with i not divisible by r is 0, then we're good. - -## # If h generates self and has order m, then the element g^r -## # maps to the power of self of order gcd(m,n)., i.e., h^(m/gcd(m,n)) -## # -## z = self_gen**(m // d) -## w = self(1) - -## a = self(0) -## for i in range(len(v)): -## if i%r: -## if v[i]: -## raise TypeError, "element does not belong to cyclotomic field" -## else: -## a += w*v[i] -## w *= z -## return a - def _coerce_from_other_cyclotomic_field(self, x, only_canonical=False): """ Coerce an element x of a cyclotomic field into self, if at all @@ -11280,7 +11273,7 @@ def _coerce_from_gap(self, x): return self(QQ(x)) coeffs = x.CoeffsCyc(self.__n) zeta = self.gen() - return sum(QQ(c)*zeta**i for i,c in enumerate(coeffs)) + return sum(QQ(c) * zeta**i for i, c in enumerate(coeffs)) def _Hom_(self, codomain, cat=None): """ @@ -11431,7 +11424,7 @@ def embeddings(self, K): # zeta not defined return super().embeddings(K) else: - X = [m for m in range(n) if arith.gcd(m,n) == 1] + X = (m for m in range(n) if arith.gcd(m, n) == 1) v = [self.hom([z**i], check=False) for i in X] else: v = [] @@ -11511,9 +11504,9 @@ def signature(self): """ m = ZZ(self.degree()) if m == 1: - return (ZZ(1), ZZ(0)) + return (ZZ.one(), ZZ(0)) else: - return (ZZ(0), ZZ(m/2)) + return (ZZ(0), m // 2) def different(self): """ @@ -11577,17 +11570,17 @@ def discriminant(self, v=None): except AttributeError: n = self._n() deg = self.degree() - d = ZZ(1) # so that CyclotomicField(1).disc() has the right type + d = ZZ.one() # so that CyclotomicField(1).disc() has the right type factors = n.factor() for (p, r) in factors: - e = (r*p - r - 1) * deg // (p-1) + e = (r * p - r - 1) * deg // (p - 1) d *= p**e sign = 1 if len(factors) == 1 and (n == 4 or factors[0][0].mod(4) == 3): sign = -1 elif len(factors) == 2 and factors[0] == (2, 1) and factors[1][0].mod(4) == 3: sign = -1 - self.__disc = sign*d + self.__disc = sign * d return self.__disc else: return NumberField_generic.discriminant(self, v) @@ -11679,7 +11672,7 @@ def _multiplicative_order_table(self): zeta = self.zeta(n) # todo: this desperately needs to be optimized!!! for i in range(n): - t[x.polynomial()] = n//arith.GCD(m,n) # multiplicative_order of (zeta_n)**m + t[x.polynomial()] = n // arith.GCD(m, n) # multiplicative_order of (zeta_n)**m x *= zeta m += 1 self.__multiplicative_order_table = t @@ -11765,18 +11758,18 @@ def zeta(self, n=None, all=False): if n % 2 == 0 and m % 2 == 1: # In the n-th cyclotomic field, n odd, there are # actually 2*n-th roots of unity, so we include them. - z = -z**((m+1)//2) # -z - m = 2*m + z = -z**((m+1)//2) # -z + m = 2 * m if m % n != 0: raise ValueError("%s does not divide order of generator (%s)" % - (n, self.zeta_order())) - a = z**(m//n) + (n, self.zeta_order())) + a = z**(m // n) if not all: return a v = [a] - b = a*a - for i in range(2,n): + b = a * a + for i in range(2, n): if n.gcd(i).is_one(): v.append(b) b = b * a @@ -11793,7 +11786,7 @@ def number_of_roots_of_unity(self): 42 """ n = self._n() - if n%2: + if n % 2: n *= 2 return n @@ -11807,13 +11800,12 @@ def roots_of_unity(self): sage: K.<a> = CyclotomicField(3) sage: zs = K.roots_of_unity(); zs [1, a, -a - 1, -1, -a, a + 1] - sage: [ z**K.number_of_roots_of_unity() for z in zs ] + sage: [z**K.number_of_roots_of_unity() for z in zs] [1, 1, 1, 1, 1, 1] """ - z = self.gen() n = self._n() - v = [z**k for k in range(n)] - if n%2: + v = self.gen().powers(n) + if n % 2: v += [-x for x in v] return v @@ -11888,7 +11880,7 @@ def __init__(self, polynomial, name=None, latex_name=None, check=True, embedding # we must set the flag _standard_embedding *before* any element creation # Note that in the following code, no element is built. if self.coerce_embedding() is not None and CDF.has_coerce_map_from(self): - rootD = CDF(number_field_element_quadratic.NumberFieldElement_quadratic(self, (QQ(0),QQ(1)))) + rootD = CDF(number_field_element_quadratic.NumberFieldElement_quadratic(self, (QQ(0), QQ(1)))) if D > 0: self._standard_embedding = rootD.real() > 0 else: @@ -11931,7 +11923,7 @@ def _coerce_map_from_(self, K): if K is ZZ: return number_field_element_quadratic.Z_to_quadratic_field_element(self) if K is int: - return self._coerce_map_via([ZZ], int) # faster than direct + return self._coerce_map_via([ZZ], int) # faster than direct if K is QQ: return number_field_element_quadratic.Q_to_quadratic_field_element(self) return NumberField_absolute._coerce_map_from_(self, K) @@ -11952,7 +11944,7 @@ def _latex_(self): """ v = self.latex_variable_names()[0] if v.startswith('\\sqrt'): - return "%s(%s)"%(latex(QQ), v) + return "%s(%s)" % (latex(QQ), v) else: return NumberField_generic._latex_(self) @@ -12204,8 +12196,8 @@ def is_fundamental_discriminant(D): if d not in [0, 1]: return False return D != 1 and D != 0 and \ - (arith.is_squarefree(D) or \ - (d == 0 and (D//4)%4 in [2,3] and arith.is_squarefree(D//4))) + (arith.is_squarefree(D) or + (d == 0 and (D // 4) % 4 in [2, 3] and arith.is_squarefree(D // 4))) ################### @@ -12484,7 +12476,7 @@ def is_real_place(v): return False -def _splitting_classes_gens_(K,m,d): +def _splitting_classes_gens_(K, m, d): r""" Given a number field `K` of conductor `m` and degree `d`, this returns a set of multiplicative generators of the @@ -12533,7 +12525,7 @@ def map_Zmstar_to_Zm(h): Hgens = [] H = Zmstar.subgroup([]) p = 0 - Horder = arith.euler_phi(m)/d + Horder = arith.euler_phi(m) / d for g in Zmstar: if H.order() == Horder: break diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 784c239dc10..bbe5b90fb34 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -308,7 +308,7 @@ cdef class NumberFieldElement(FieldElement): self.__fld_numerator, self.__fld_denominator = parent.absolute_polynomial_ntl() cdef ZZ_c coeff - if isinstance(f, (int, long, Integer_sage)): + if isinstance(f, (int, Integer_sage)): # set it up and exit immediately # fast pathway mpz_to_ZZ(&coeff, (<Integer>ZZ(f)).value) @@ -493,7 +493,7 @@ cdef class NumberFieldElement(FieldElement): return codomain(f(im_gens[0])) def _latex_(self): - """ + r""" Returns the latex representation for this element. EXAMPLES:: @@ -1637,10 +1637,10 @@ cdef class NumberFieldElement(FieldElement): sage: K.<a> = NumberField(x^3 + x + 1) sage: Q.<X> = K[] sage: L.<b> = NumberField(X^4 + a) - sage: t = (-a).is_norm(L, element=True); t - (True, -b^3 - 1) - sage: t[1].norm(K) - -a + sage: t, u = (-a).is_norm(L, element=True); u # random (not unique) + b^3 + 1 + sage: t and u.norm(K) == -a + True Verify that :trac:`27469` has been fixed:: @@ -1648,8 +1648,7 @@ cdef class NumberFieldElement(FieldElement): Cyclotomic Field of order 24 and degree 8 sage: K = L.subfield(z24^3, 'z8')[0]; K Number Field in z8 with defining polynomial x^4 + 1 with z8 = 0.7071067811865475? + 0.7071067811865475?*I - sage: flag, c = K(-7).is_norm(K, element=True) - sage: flag + sage: flag, c = K(-7).is_norm(K, element=True); flag True sage: c.norm(K) -7 @@ -1746,30 +1745,34 @@ cdef class NumberFieldElement(FieldElement): sage: K.<a> = NumberField(x^3 + x^2 - 2*x - 1, 'a') sage: P.<X> = K[] sage: L = NumberField(X^2 + a^2 + 2*a + 1, 'b') - sage: K(17)._rnfisnorm(L) # representation depends, not tested - ((a^2 - 2)*b - 4, 1) + sage: y, q = K(17)._rnfisnorm(L) + sage: q==1 + True + sage: y # random (not unique) + (a^2 - 2)*b - 4 sage: K.<a> = NumberField(x^3 + x + 1) sage: Q.<X> = K[] sage: L.<b> = NumberField(X^4 + a) - sage: t = (-a)._rnfisnorm(L); t - (-b^3 - 1, 1) - sage: t[0].norm(K) - -a - sage: t = K(3)._rnfisnorm(L); t + sage: y, q = (-a)._rnfisnorm(L) + sage: y # random (not unique) + b^3 + 1 + sage: q == 1 and y.norm(K) == -a + True + sage: y, q = K(3)._rnfisnorm(L); y, q # random (not unique) (b^3 + a*b^2 + a^2*b - 1, 3*a^2 - 3*a + 6) - sage: t[0].norm(K)*t[1] - 3 + sage: y.norm(K)*q == 3 + True An example where the base field is a relative field:: sage: K.<a, b> = NumberField([x^2 - 2, x^2 - 3]) sage: L.<c> = K.extension(x^3 + 2) sage: s = 2*a + b - sage: t = s._rnfisnorm(L) - sage: t[1] == 1 # True iff s is a norm + sage: y, q = s._rnfisnorm(L) + sage: q == 1 # True iff s is a norm False - sage: s == t[0].norm(K)*t[1] + sage: s == y.norm(K)*q True TESTS: @@ -1779,7 +1782,9 @@ cdef class NumberFieldElement(FieldElement): sage: K.<a> = NumberField(x^2 + 1/2) sage: L.<b> = K.extension(x^2 - 1/2) - sage: a._rnfisnorm(L) + sage: y, q = a._rnfisnorm(L) + sage: y # random (not unique) + q == 1 and y.norm(K) == a (a*b + a + 1/2, 1) We test the above doctest, which was not tested. @@ -1788,12 +1793,10 @@ cdef class NumberFieldElement(FieldElement): sage: K.<a> = NumberField(x^3 + x^2 - 2*x - 1, 'a') sage: P.<X> = K[] sage: L.<b> = NumberField(X^2 + a^2 + 2*a + 1, 'b') - sage: (xbar, q) = K(17)._rnfisnorm(L) - sage: q == 1 - 1 - sage: xbar.norm() + sage: y, q = K(17)._rnfisnorm(L) + sage: y.norm() 4913 - sage: xbar in ((a^2 - 2)*b - 4, (a^2 - 2)*b + 4) + sage: q == 1 and y in ((a^2 - 2)*b - 4, (a^2 - 2)*b + 4) True AUTHORS: @@ -3988,8 +3991,8 @@ cdef class NumberFieldElement(FieldElement): return ht def global_height_non_arch(self, prec=None): - """ - Returns the total non-archimedean component of the height of self. + r""" + Return the total non-archimedean component of the height of ``self``. INPUT: @@ -4446,7 +4449,7 @@ cdef class NumberFieldElement(FieldElement): sage: f = Qi.embeddings(K)[0] sage: a = f(2+3*i) * (2-zeta)^2 sage: a.descend_mod_power(Qi,2) - [-3*i - 2, -2*i + 3] + [-2*i + 3, 3*i + 2] An absolute example:: @@ -5124,7 +5127,7 @@ cdef class NumberFieldElement_relative(NumberFieldElement): EXAMPLES:: sage: K.<a, b, c> = NumberField([x^2 - 2, x^2 - 3, x^2 - 5]) - sage: P = K.prime_factors(5)[0] + sage: P = K.prime_factors(5)[1] sage: (2*a + b - c).valuation(P) 1 """ 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 24fc7db909e..ed662749e35 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Optimized Quadratic Number Field Elements This file defines a Cython class ``NumberFieldElement_quadratic`` to speed up @@ -23,16 +23,15 @@ AUTHORS: The ``_new()`` method should be overridden in this class to copy the ``D`` and ``standard_embedding`` attributes """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu> # # 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/ +# **************************************************************************** include "sage/libs/ntl/decl.pxi" from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT @@ -1634,7 +1633,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): ################################################################################# def __hash__(self): - """ + r""" Return hash of this number field element. For elements in `\ZZ` or `\QQ` the hash coincides with the one in the @@ -2019,10 +2018,11 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpq_canonicalize(res.value) return res - def norm(self, K=None): - """ - Return the norm of self. If the second argument is None, this is the + r""" + Return the norm of ``self``. + + If the second argument is ``None``, this is the norm down to `\QQ`. Otherwise, return the norm down to K (which had better be either `\QQ` or this number field). @@ -3074,4 +3074,3 @@ cpdef bint is_sqrt_disc(Rational ad, Rational bd): mpz_clear(denom) return ret - diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 5f587556a4c..d5f7157217f 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -42,7 +42,8 @@ import sage.rings.rational_field as rational_field import sage.rings.integer_ring as integer_ring -from sage.arith.all import kronecker_symbol, gcd +from sage.arith.misc import kronecker as kronecker_symbol +from sage.arith.misc import GCD as gcd import sage.misc.misc as misc from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -2073,7 +2074,7 @@ def reduce(self, f): Rbasis = R.basis() n = len(Rbasis) - from sage.matrix.all import MatrixSpace + from sage.matrix.matrix_space import MatrixSpace M = MatrixSpace(ZZ,n)([R.coordinates(y) for y in self.basis()]) D = M.hermite_form() @@ -2142,7 +2143,7 @@ def residues(self): R = self.number_field().maximal_order() Rbasis = R.basis() n = len(Rbasis) - from sage.matrix.all import MatrixSpace + from sage.matrix.matrix_space import MatrixSpace M = MatrixSpace(ZZ, n)([R.coordinates(_) for _ in self.basis()]) D = M.hermite_form() @@ -2283,7 +2284,8 @@ def invertible_residues_mod(self, subgp_gens=[], reduce=True): g = G.gens_values() n = G.ngens() - from sage.matrix.all import Matrix, diagonal_matrix + from sage.matrix.constructor import Matrix + from sage.matrix.special import diagonal_matrix M = diagonal_matrix(ZZ, invs) if subgp_gens: @@ -2711,7 +2713,11 @@ def ideallog(self, x, gens=None, check=True): G = self.idealstar(2) invs = G.invariants() - from sage.matrix.all import matrix, identity_matrix, zero_matrix, diagonal_matrix, block_matrix + from sage.matrix.constructor import Matrix as matrix + from sage.matrix.special import identity_matrix + from sage.matrix.special import zero_matrix + from sage.matrix.special import diagonal_matrix + from sage.matrix.special import block_matrix # We use Hermite normal form twice: once to express the standard # generators in terms of the new ones (independently of x) and once to @@ -3355,7 +3361,7 @@ def quotient_char_p(I, p): [] sage: I = K.factor(13)[0][0]; I - Fractional ideal (-3*i - 2) + Fractional ideal (-2*i + 3) sage: I.residue_class_degree() 1 sage: quotient_char_p(I, 13)[0] 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 bae36d4b9c4..192c8f15034 100644 --- a/src/sage/rings/number_field/number_field_ideal_rel.py +++ b/src/sage/rings/number_field/number_field_ideal_rel.py @@ -204,8 +204,6 @@ def _from_absolute_ideal(self, id): r""" Convert the absolute ideal id to a relative number field ideal. - Assumes id.number_field() == self.absolute_field('a'). - WARNING: This is an internal helper function. TESTS:: @@ -218,18 +216,18 @@ def _from_absolute_ideal(self, id): True sage: J.absolute_norm() 22584817 - sage: J.absolute_ideal() - Fractional ideal (22584817, -1473/812911*a^5 + 8695/4877466*a^4 - 1308209/4877466*a^3 + 117415/443406*a^2 - 22963264/2438733*a - 13721081784272/2438733) - sage: J.absolute_ideal().norm() + sage: Labs.<c> = L.absolute_field(); Labs # random (polynomial not unique) + Number Field in c with defining polynomial x^6 + 217*x^4 - 2*x^3 + 15127*x^2 + 422*x + 338032 + sage: Jabs = J.absolute_ideal(names='c') + sage: Jabs == Labs.ideal(22584817, -1473/812911*c^5 + 8695/4877466*c^4 - 1308209/4877466*c^3 + 117415/443406*c^2 - 22963264/2438733*c - 13721081784272/2438733) + True + sage: Jabs.norm() 22584817 - - sage: J._from_absolute_ideal(J.absolute_ideal()) == J + sage: J._from_absolute_ideal(Jabs) == J True """ - L = self.number_field() - K = L.absolute_field('a') - to_L = K.structure()[0] - return L.ideal([to_L(_) for _ in id.gens()]) + f, _ = id.number_field().structure() + return self.number_field().ideal([f(_) for _ in id.gens()]) def free_module(self): r""" @@ -272,7 +270,7 @@ def gens_reduced(self): sage: L.<b> = K.extension(5*x^2 + 1) sage: P = L.primes_above(2)[0] sage: P.gens_reduced() - (2, 15*a*b + 3*a + 1) + (2, -15*a*b + 3*a + 1) """ try: # Compute the single generator, if it exists @@ -401,7 +399,7 @@ def relative_norm(self): sage: L.<b> = K.extension(5*x^2 + 1) sage: P = L.primes_above(2)[0] sage: P.relative_norm() - Fractional ideal (-6*a + 2) + Fractional ideal (6*a + 2) """ L = self.number_field() K = L.base_field() @@ -518,7 +516,7 @@ def ideal_below(self): sage: L.<b> = K.extension(5*x^2 + 1) sage: P = L.primes_above(2)[0] sage: P.ideal_below() - Fractional ideal (-6*a + 2) + Fractional ideal (6*a + 2) """ L = self.number_field() K = L.base_field() diff --git a/src/sage/rings/number_field/number_field_morphisms.pyx b/src/sage/rings/number_field/number_field_morphisms.pyx index 605026be684..34a51a97c62 100644 --- a/src/sage/rings/number_field/number_field_morphisms.pyx +++ b/src/sage/rings/number_field/number_field_morphisms.pyx @@ -4,8 +4,7 @@ Embeddings into ambient fields This module provides classes to handle embeddings of number fields into ambient fields (generally `\RR` or `\CC`). """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu> # # Distributed under the terms of the GNU General Public License (GPL) @@ -17,8 +16,8 @@ fields (generally `\RR` or `\CC`). # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sage.rings.complex_double @@ -345,7 +344,7 @@ cpdef matching_root(poly, target, ambient_field=None, margin=1, max_prec=None): target best approximates as compared in ambient_field. If the parent of target is exact, the equality is required, otherwise - find closest root (with respect to the \code{abs} function) in the + find closest root (with respect to the ``abs`` function) in the ambient field to the target, and return the root of poly (if any) that approximates it best. @@ -405,7 +404,7 @@ cpdef matching_root(poly, target, ambient_field=None, margin=1, max_prec=None): cpdef closest(target, values, margin=1): """ This is a utility function that returns the item in values closest to - target (with respect to the \code{abs} function). If margin is greater + target (with respect to the ``abs`` function). If margin is greater than 1, and x and y are the first and second closest elements to target, then only return x if x is margin times closer to target than y, i.e. margin * abs(target-x) < abs(target-y). @@ -696,7 +695,7 @@ cdef class CyclotomicFieldEmbedding(NumberFieldEmbedding): def section(self): """ Return the section of ``self``. - + EXAMPLES:: sage: from sage.rings.number_field.number_field_morphisms import CyclotomicFieldEmbedding @@ -716,10 +715,10 @@ cdef class CyclotomicFieldConversion(Map): EXAMPLES:: sage: from sage.rings.number_field.number_field_morphisms import CyclotomicFieldConversion - sage: K1.<z1> = CyclotomicField(12) + sage: K1.<z1> = CyclotomicField(12) sage: K2.<z2> = CyclotomicField(18) - sage: f = CyclotomicFieldConversion(K1, K2) - sage: f(z1^2) + sage: f = CyclotomicFieldConversion(K1, K2) + sage: f(z1^2) z2^3 sage: f(z1) Traceback (most recent call last): @@ -741,7 +740,7 @@ cdef class CyclotomicFieldConversion(Map): def __init__(self, K, L): """ Construct a conversion map between cyclotomic fields. - + EXAMPLES:: sage: from sage.rings.number_field.number_field_morphisms import CyclotomicFieldEmbedding @@ -776,4 +775,3 @@ cdef class CyclotomicFieldConversion(Map): return self.phi.preimage(M(x)) except ValueError: raise ValueError('Element {} has no image in the codomain'.format(x)) - diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index d33980c4b10..8ecb87fce28 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -99,6 +99,12 @@ from sage.rings.number_field.morphism import RelativeNumberFieldHomomorphism_from_abs from sage.libs.pari.all import pari_gen +from sage.categories.homset import Hom +from sage.categories.sets_cat import Sets +from sage.modules.free_module import VectorSpace +from sage.modules.free_module_element import vector + +from sage.rings.real_mpfr import RR from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ @@ -213,14 +219,14 @@ def __init__(self, base, polynomial, name, sage: l.<b> = k.extension(5*x^2 + 3); l Number Field in b with defining polynomial 5*x^2 + 3 over its base field sage: l.pari_rnf() - [x^2 + (-1/2*y^2 + y - 3/2)*x + (-1/4*y^3 + 1/4*y^2 - 3/4*y - 13/4), ..., y^4 + 6*y^2 + 1, x^2 + (-1/2*y^2 + y - 3/2)*x + (-1/4*y^3 + 1/4*y^2 - 3/4*y - 13/4)], [0, 0]] + [x^2 + (-y^3 + 1/2*y^2 - 6*y + 3/2)*x + (-3/4*y^3 - 1/4*y^2 - 17/4*y - 19/4), ..., y^4 + 6*y^2 + 1, x^2 + (-y^3 + 1/2*y^2 - 6*y + 3/2)*x + (-3/4*y^3 - 1/4*y^2 - 17/4*y - 19/4)], [0, 0]] sage: b b sage: l.<b> = k.extension(x^2 + 3/5); l Number Field in b with defining polynomial x^2 + 3/5 over its base field sage: l.pari_rnf() - [x^2 + (-1/2*y^2 + y - 3/2)*x + (-1/4*y^3 + 1/4*y^2 - 3/4*y - 13/4), ..., y^4 + 6*y^2 + 1, x^2 + (-1/2*y^2 + y - 3/2)*x + (-1/4*y^3 + 1/4*y^2 - 3/4*y - 13/4)], [0, 0]] + [x^2 + (-y^3 + 1/2*y^2 - 6*y + 3/2)*x + (-3/4*y^3 - 1/4*y^2 - 17/4*y - 19/4), ..., y^4 + 6*y^2 + 1, x^2 + (-y^3 + 1/2*y^2 - 6*y + 3/2)*x + (-3/4*y^3 - 1/4*y^2 - 17/4*y - 19/4)], [0, 0]] sage: b b @@ -2062,6 +2068,75 @@ def automorphisms(self): check=False, universe=self.Hom(self)) return self.__automorphisms + def logarithmic_embedding(self, prec=53): + r""" + Return the morphism of ``self`` under the logarithmic embedding + in the category Set. + + The logarithmic embedding is defined as a map from the relative number field ``self`` to `\RR^n`. + + It is defined under Definition 4.9.6 in [Coh1993]_. + + INPUT: + + - ``prec`` -- desired floating point precision. + + OUTPUT: + + - the morphism of ``self`` under the logarithmic embedding in the category Set. + + EXAMPLES:: + + sage: K.<k> = CyclotomicField(3) + sage: R.<x> = K[] + sage: L.<l> = K.extension(x^5 + 5) + sage: f = L.logarithmic_embedding() + sage: f(0) + (-1, -1, -1, -1, -1) + sage: f(5) + (3.21887582486820, 3.21887582486820, 3.21887582486820, + 3.21887582486820, 3.21887582486820) + + :: + + sage: K.<i> = NumberField(x^2 + 1) + sage: t = K['t'].gen() + sage: L.<a> = K.extension(t^4 - i) + sage: f = L.logarithmic_embedding() + sage: f(0) + (-1, -1, -1, -1, -1, -1, -1, -1) + sage: f(3) + (2.19722457733622, 2.19722457733622, 2.19722457733622, 2.19722457733622, + 2.19722457733622, 2.19722457733622, 2.19722457733622, 2.19722457733622) + """ + def closure_map(x, prec=53): + """ + The function closure of the logarithmic embedding. + """ + K = self + K_embeddings = K.places(prec) + r1, r2 = K.signature() + r = r1 + r2 - 1 + + from sage.rings.real_mpfr import RealField + Reals = RealField(prec) + + if x == 0: + return vector([-1 for _ in range(r + 1)]) + + x_logs = [] + for i in range(r1): + sigma = K_embeddings[i] + x_logs.append(Reals(abs(sigma(x))).log()) + for i in range(r1, r + 1): + tau = K_embeddings[i] + x_logs.append(2 * Reals(abs(tau(x))).log()) + + return vector(x_logs) + + hom = Hom(self, VectorSpace(RR, len(closure_map(self(0), prec))), Sets()) + return hom(closure_map) + def places(self, all_complex=False, prec=None): """ Return the collection of all infinite places of self. @@ -2141,7 +2216,7 @@ def relative_different(self): """ I = self.absolute_different() J = self.ideal(self.base_field().absolute_different().gens()) - return I/J + return I/J def different(self): """ diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index 6eca89ed8dd..f0b0c0f9656 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -520,7 +520,7 @@ def __mul__(self, right): sage: k.<a> = NumberField(x^2 + 5077); G = k.class_group(); G Class group of order 22 with structure C22 of Number Field in a with defining polynomial x^2 + 5077 sage: G.0 ^ -9 - Fractional ideal class (11, a + 7) + Fractional ideal class (43, a + 13) sage: Ok = k.maximal_order(); Ok Maximal Order in Number Field in a with defining polynomial x^2 + 5077 sage: Ok * (11, a + 7) @@ -867,12 +867,12 @@ def zeta(self, n=2, all=False): sage: F.<alpha> = NumberField(x**2+3) sage: F.ring_of_integers().zeta(6) - 1/2*alpha + 1/2 + -1/2*alpha + 1/2 sage: O = F.order([3*alpha]) sage: O.zeta(3) Traceback (most recent call last): ... - ArithmeticError: There are no 3rd roots of unity in self. + ArithmeticError: there are no 3rd roots of unity in self """ roots_in_field = self.number_field().zeta(n, True) roots_in_self = [self(x) for x in roots_in_field if x in self] @@ -880,7 +880,7 @@ def zeta(self, n=2, all=False): if all: return [] else: - raise ArithmeticError("There are no %s roots of unity in self." % n.ordinal_str()) + raise ArithmeticError("there are no %s roots of unity in self" % n.ordinal_str()) if all: return roots_in_self else: diff --git a/src/sage/rings/number_field/selmer_group.py b/src/sage/rings/number_field/selmer_group.py index c534aaa9f66..487550ef4d5 100644 --- a/src/sage/rings/number_field/selmer_group.py +++ b/src/sage/rings/number_field/selmer_group.py @@ -128,7 +128,7 @@ def _coords_in_C_p(I, C, p): """ cyclic_orders = C.gens_orders() - non_p_indices = [i for i,n in enumerate(cyclic_orders) if not p.divides(n)] + non_p_indices = [i for i,n in enumerate(cyclic_orders) if not p.divides(n)] p_indices = [(i, n // p) for i,n in enumerate(cyclic_orders) if p.divides(n)] coords = C(I).exponents() @@ -283,7 +283,7 @@ def coords_in_U_mod_p(u, U, p): """ coords = U.log(u) start = 1 - int(p.divides(U.zeta_order())) # 0 or 1 - return [c%p for c in coords[start:]] + return [c%p for c in coords[start:]] def basis_for_p_cokernel(S, C, p): r""" @@ -491,7 +491,7 @@ def pSelmerGroup(K, S, p, proof=None, debug=False): sage: [K.ideal(g).factor() for g in gens] [(Fractional ideal (2, a + 1)) * (Fractional ideal (3, a + 1)), - Fractional ideal (-a), + Fractional ideal (a), (Fractional ideal (2, a + 1))^2, 1] @@ -646,7 +646,7 @@ def to_KSp(a): vals = [a.valuation(P) for P in supp] if debug: assert all(v % p == 0 for v in vals) - one = K(1) if K == QQ else K.ideal(1) + one = K(1) if K == QQ else K.ideal(1) aa = a.abs() if K == QQ else K.ideal(a) B = prod((P ** (v // p) for P, v in zip(supp,vals)), one) if debug: 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 93cb7d97f70..facd5207a1c 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 @@ -227,7 +227,7 @@ def __next__(self): [29, 41, 61, 89, 101, 109, 149, 181, 229, 241] sage: ids[9] == N.ideal(3*a + 1/2*b + 5/2) True - + We test that :trac:`23468` is fixed:: sage: R.<z> = QQ[] diff --git a/src/sage/rings/number_field/splitting_field.py b/src/sage/rings/number_field/splitting_field.py index 6dc15cc6bc9..14871fecc05 100644 --- a/src/sage/rings/number_field/splitting_field.py +++ b/src/sage/rings/number_field/splitting_field.py @@ -19,7 +19,7 @@ #***************************************************************************** from sage.rings.integer import Integer -from sage.arith.all import factorial +from sage.arith.misc import factorial from sage.rings.number_field.all import NumberField from sage.rings.polynomial.all import PolynomialRing from sage.rings.rational_field import RationalField, is_RationalField @@ -378,7 +378,7 @@ def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=No abort_rel_degree = abort_degree//absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) - + # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s"%[s._repr_tuple() for s in Lred]) t = cputime() diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 35786842573..adc52e6cfab 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -27,7 +27,8 @@ AUTHORS: from libc.math cimport sqrt from cysignals.memory cimport sig_malloc, sig_free -from sage.arith.all import binomial, gcd +from sage.arith.misc import binomial +from sage.arith.misc import GCD as gcd from sage.libs.gmp.mpz cimport * from sage.rings.rational_field import RationalField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 008edfd4e0b..07477c46f8f 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -90,7 +90,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.arith.all import binomial, gcd, divisors +from sage.arith.misc import binomial +from sage.arith.misc import GCD as gcd +from sage.arith.misc import divisors from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.rings.number_field.totallyreal_data import ZZx, lagrange_degree_3, int_has_small_square_divisor, hermite_constant diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 8f1c4c7795c..a0a66c19621 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -55,7 +55,7 @@ cdef class CAElement(pAdicTemplateElement): INPUT: - - ``x`` -- data defining a `p`-adic element: int, long, + - ``x`` -- data defining a `p`-adic element: int, Integer, Rational, other `p`-adic element... - ``val`` -- the valuation of the resulting element @@ -468,8 +468,7 @@ cdef class CAElement(pAdicTemplateElement): cdef Integer right cdef CAElement pright, ans cdef bint exact_exp - if isinstance(_right, Integer) or isinstance(_right, (int, long)) \ - or isinstance(_right, Rational): + if isinstance(_right, (Integer, int, Rational)): if _right < 0: base = ~self return base.__pow__(-_right, dummy) @@ -490,7 +489,7 @@ cdef class CAElement(pAdicTemplateElement): elif ciszero(self.value, self.prime_pow): # We may assume from above that right > 0 if exact. # So we return a zero of precision right * self.ordp. - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): right = <Integer>_right diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index 602a756a2a9..1b3134f3195 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -80,7 +80,7 @@ cdef class CRElement(pAdicTemplateElement): INPUT: - - ``x`` -- data defining a `p`-adic element: int, long, + - ``x`` -- data defining a `p`-adic element: int, Integer, Rational, other `p`-adic element... - ``val`` -- the valuation of the resulting element @@ -672,7 +672,7 @@ cdef class CRElement(pAdicTemplateElement): cdef Integer right cdef CRElement base, pright, ans cdef bint exact_exp - if (isinstance(_right, Integer) or isinstance(_right, (int, long)) or isinstance(_right, Rational)): + if isinstance(_right, (Integer, int, Rational)): if _right < 0: base = ~self return base.__pow__(-_right, dummy) @@ -702,7 +702,7 @@ cdef class CRElement(pAdicTemplateElement): ans = self._new_c() if self.relprec == 0: # If a positive integer exponent, return an inexact zero of valuation right * self.ordp. Otherwise raise an error. - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): right = <Integer>_right diff --git a/src/sage/rings/padics/FP_template.pxi b/src/sage/rings/padics/FP_template.pxi index 65485cdc0ad..befd8ee01fb 100644 --- a/src/sage/rings/padics/FP_template.pxi +++ b/src/sage/rings/padics/FP_template.pxi @@ -618,7 +618,7 @@ cdef class FPElement(pAdicTemplateElement): cdef Integer right cdef FPElement base, pright, ans cdef bint exact_exp - if isinstance(_right, (Integer, int, long, Rational)): + if isinstance(_right, (Integer, int, Rational)): if _right < 0: self = ~self _right = -_right diff --git a/src/sage/rings/padics/common_conversion.pyx b/src/sage/rings/padics/common_conversion.pyx index 7ad0554072d..4ed5570d306 100644 --- a/src/sage/rings/padics/common_conversion.pyx +++ b/src/sage/rings/padics/common_conversion.pyx @@ -42,7 +42,7 @@ from sage.structure.element cimport parent cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 cdef long minusmaxordp = -maxordp -# The following Integer (resp. Rational) is used so that +# The following Integer (resp. Rational) is used so that # the functions here don't need to initialize an mpz_t (resp. mpq_t) cdef Integer temp = PY_NEW(Integer) cdef Rational rat_temp = PY_NEW(Rational) diff --git a/src/sage/rings/padics/eisenstein_extension_generic.py b/src/sage/rings/padics/eisenstein_extension_generic.py index 6473ba4d2ca..4f68e63ffae 100644 --- a/src/sage/rings/padics/eisenstein_extension_generic.py +++ b/src/sage/rings/padics/eisenstein_extension_generic.py @@ -39,7 +39,7 @@ def __init__(self, poly, prec, print_mode, names, element_class): def _extension_type(self): """ - Return the type (``Unramified``, ``Eisenstein``) of this + Return the type (``Unramified``, ``Eisenstein``) of this extension as a string, if any. Used for printing. diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 72506764209..044dcae0bed 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -2949,12 +2949,12 @@ def ZpLF(p, prec=None, *args, **kwds): See documentation for :func:`Zp` for a description of the input parameters. - NOTE: + .. NOTE:: - The precision is tracked using automatic differentiation - techniques (see [CRV2018]_ and [CRV2014]_). - Floating point `p`-adic numbers are used for the computation - of the differential (which is then not exact). + The precision is tracked using automatic differentiation + techniques (see [CRV2018]_ and [CRV2014]_). + Floating point `p`-adic numbers are used for the computation + of the differential (which is then not exact). EXAMPLES:: diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 5923228bb69..cf7df2f2541 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -202,9 +202,9 @@ cdef class LocalGenericElement(CommutativeRingElement): def slice(self, i, j, k = 1, lift_mode='simple'): r""" Returns the sum of the `pi^{i + l \cdot k}` terms of the series - expansion of this element, where pi is the uniformizer, - for `i + l \cdot k` between ``i`` and ``j-1`` inclusive, and - nonnegative integers `l`. Behaves analogously to the slice + expansion of this element, where pi is the uniformizer, + for `i + l \cdot k` between ``i`` and ``j-1`` inclusive, and + nonnegative integers `l`. Behaves analogously to the slice function for lists. INPUT: @@ -300,7 +300,7 @@ cdef class LocalGenericElement(CommutativeRingElement): 5^2 + O(5^8) Test that slices also work over eisenstein extensions:: - + sage: F = Qp(5) sage: H.<x> = F[] sage: T.<t> = F.extension(x^2-5) @@ -313,7 +313,7 @@ cdef class LocalGenericElement(CommutativeRingElement): 3*t^-2 + 4*t + O(t^6) Test that slices also work over unramified extensions:: - + sage: F = Qp(5) sage: H.<x> = F[] sage: T.<t> = F.extension(x^2-2) @@ -326,7 +326,7 @@ cdef class LocalGenericElement(CommutativeRingElement): 3*5^-1 + (3*t + 4)*5^2 + O(5^6) Test that slices also work over 2-step extensions (unramified followed by eisenstein):: - + sage: F = Qp(5) sage: H.<x> = F[] sage: T.<t> = F.extension(x^2-3) @@ -337,7 +337,7 @@ cdef class LocalGenericElement(CommutativeRingElement): (2*t + 2)*w^-23 + O(w^2) sage: a.slice(0, 1) O(w) - + Verify that :trac:`14106` has been fixed:: sage: R = Zp(5,7) @@ -408,8 +408,8 @@ cdef class LocalGenericElement(CommutativeRingElement): return ans def _latex_(self): - """ - Returns a latex representation of self. + r""" + Return a latex representation of self. EXAMPLES:: @@ -737,10 +737,10 @@ cdef class LocalGenericElement(CommutativeRingElement): The square root or the list of all square roots of this element. - NOTE: + .. NOTE:: - The square root is chosen (resp. the square roots are ordered) in - a deterministic way, which is compatible with change of precision. + The square root is chosen (resp. the square roots are ordered) in + a deterministic way, which is compatible with change of precision. EXAMPLES:: @@ -1001,7 +1001,7 @@ cdef class LocalGenericElement(CommutativeRingElement): # so that this test doesn't take too long for large precision cap prec_cutoff = int(min((10000 / (1 + self.precision_relative())).ceil(), 100)) - from sage.categories.all import Fields + from sage.categories.fields import Fields if self.parent() in Fields(): v = self.valuation() from sage.rings.infinity import infinity diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 731d27ec33c..a159aa14c9b 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-adic ``ZZ_pX`` CA Element This file implements elements of Eisenstein and unramified extensions @@ -178,7 +178,7 @@ from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.libs.pari.all import pari_gen -from sage.interfaces.gp import GpElement +from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.padics.padic_ext_element cimport pAdicExtElement @@ -361,7 +361,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): tmp_Int = PY_NEW(Integer) ZZ_to_mpz(tmp_Int.value, &(<ntl_ZZ>x).x) x = tmp_Int - elif isinstance(x, (int, long)): + elif isinstance(x, int): x = Integer(x) elif x in parent.residue_field() and x.parent().is_finite(): # Should only reach here if x is not in F_p @@ -1253,7 +1253,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): cdef long i if self._is_inexact_zero(): # If an integer exponent, return an inexact zero of valuation right * self_ordp. Otherwise raise an error. - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): mpz_init_set_si(tmp, self_ordp) @@ -1270,7 +1270,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): raise ValueError("need more precision") else: raise TypeError("exponent must be an integer, rational or base p-adic with the same prime") - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) cdef pAdicZZpXCAElement unit if isinstance(_right, Integer): @@ -1546,7 +1546,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): return self.to_fraction_field() * (~right) def _integer_(self, Z=None): - """ + r""" Returns an integer congruent to this element modulo `\pi`^``self.absolute_precision()``, if possible. @@ -1951,8 +1951,8 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): return [zero] * ordp + ulist def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are @@ -2351,4 +2351,3 @@ def make_ZZpXCAElement(parent, value, absprec, version): return ans else: raise ValueError("unknown unpickling version") - diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 463cc371031..3d744acd62c 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-adic ``ZZ_pX`` CR Element This file implements elements of Eisenstein and unramified extensions @@ -204,7 +204,7 @@ from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.libs.pari.all import pari_gen -from sage.interfaces.gp import GpElement +from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.padics.padic_ext_element cimport pAdicExtElement from sage.rings.padics.precision_error import PrecisionError @@ -230,7 +230,7 @@ cdef inline int check_ordp(long a) except -1: cdef class pAdicZZpXCRElement(pAdicZZpXElement): def __init__(self, parent, x, absprec = infinity, relprec = infinity, empty = False): - """ + r""" Creates an element of a capped relative precision, unramified or Eisenstein extension of `\ZZ_p` or `\QQ_p`. @@ -388,7 +388,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): tmp_Int = PY_NEW(Integer) ZZ_to_mpz(tmp_Int.value, &(<ntl_ZZ>x).x) x = tmp_Int - elif isinstance(x, (int, long)): + elif isinstance(x, int): x = Integer(x) elif x in parent.residue_field() and x.parent().is_finite(): # Should only reach here if x is not in F_p @@ -1939,7 +1939,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): cdef long i if self._is_exact_zero(): # Return 0 except for 0^0 error or type error on the exponent. - if isinstance(_right, Integer) or isinstance(_right, Rational) or (isinstance(_right, pAdicGenericElement) and _right._is_base_elt(self.prime_pow.prime)) or isinstance(_right, (int, long)): + if isinstance(_right, (Integer, Rational, int)) or (isinstance(_right, pAdicGenericElement) and _right._is_base_elt(self.prime_pow.prime)): if _right == 0: return self.parent(1) return self @@ -1947,7 +1947,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): raise TypeError("exponent must be an integer, rational or base p-adic with the same prime") elif self._is_inexact_zero(): # If an integer exponent, return an inexact zero of valuation right * self.ordp. Otherwise raise an error. - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): ans = self._new_c(0) @@ -1962,7 +1962,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): raise ValueError("Need more precision") else: raise TypeError("exponent must be an integer, rational or base p-adic with the same prime") - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): right = <Integer> _right @@ -2833,7 +2833,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): return ulist def matrix_mod_pn(self): - """ + r""" Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the @@ -3284,4 +3284,3 @@ def make_ZZpXCRElement(parent, unit, ordp, relprec, version): return ans else: raise ValueError("unknown unpickling version") - diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 9e182de50d5..548a8e635d2 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -147,7 +147,7 @@ from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.rational cimport Rational from sage.libs.pari.all import pari_gen -from sage.interfaces.gp import GpElement +from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.padics.pow_computer_ext cimport PowComputer_ZZ_pX_FM_Eis @@ -260,7 +260,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): tmp_Int = Integer.__new__(Integer) ZZ_to_mpz(tmp_Int.value, &(<ntl_ZZ>x).x) x = tmp_Int - elif isinstance(x, (int, long)): + elif isinstance(x, int): x = Integer(x) if isinstance(x, Integer): self._set_from_mpz((<Integer>x).value) @@ -1721,6 +1721,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): """ return self.ext_p_list_precs(pos, self.prime_pow.ram_prec_cap) + def make_ZZpXFMElement(parent, f): """ Create a new ``pAdicZZpXFMElement`` out of an ``ntl_ZZ_pX`` ``f``, with @@ -1737,4 +1738,3 @@ def make_ZZpXFMElement(parent, f): True """ return pAdicZZpXFMElement(parent, f) - diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index a450f28de65..ec33070be49 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -483,12 +483,12 @@ cdef class pAdicZZpXElement(pAdicExtElement): 4*5 + 5^2 + 5^3 + 2*5^4 sage: (a+b).trace() 4*5 + 5^2 + 5^3 + 2*5^4 - + TESTS: We check that :trac:`32072` is resolved:: - sage: F = Qp(2) + sage: F = Qp(2) sage: S.<x> = F[] sage: L.<w> = F.ext(x^2 - 2) sage: L(0, 20).trace() @@ -658,7 +658,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): for i from 0 <= i < len(L): if isinstance(L[i], ntl_ZZ): L[i] = ntl_ZZ_p(L[i]*pshift_z, ctx) - elif isinstance(L[i], Integer) or isinstance(L[i], Rational) or isinstance(L[i], (int, long)): + elif isinstance(L[i], (Integer, Rational, int)): L[i] = ntl_ZZ_p(L[i]*pshift_m, ctx) elif isinstance(L[i], pAdicGenericElement) and L[i]._is_base_elt(elt.prime_pow.prime): L[i] = ntl_ZZ_p((L[i] >> min_val).lift(), ctx) @@ -676,7 +676,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): py_tmp = ntl_ZZ.__new__(ntl_ZZ) py_tmp.x = tmp L[i] = ntl_ZZ_p(py_tmp, ctx) - elif isinstance(L[i], Integer) or isinstance(L[i], Rational) or isinstance(L[i], (int, long)): + elif isinstance(L[i], (Integer, Rational, int)): L[i] = ntl_ZZ_p(L[i]//pshift_m, ctx) elif isinstance(L[i], pAdicGenericElement) and L[i]._is_base_elt(elt.prime_pow.prime): L[i] = ntl_ZZ_p((L[i] >> min_val).lift(), ctx) @@ -689,7 +689,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): L[i] = ntl_ZZ_p(py_tmp, ctx) else: for i from 0 <= i < len(L): - if isinstance(L[i], ntl_ZZ) or isinstance(L[i], Integer) or isinstance(L[i], Rational) or isinstance(L[i], (int, long)): + if isinstance(L[i], (ntl_ZZ, Integer, Rational, int)): L[i] = ntl_ZZ_p(L[i], ctx) elif (isinstance(L[i], pAdicGenericElement) and L[i]._is_base_elt(elt.prime_pow.prime)) or is_IntegerMod(L[i]) or (L[i].modulus_context() is not ctx): L[i] = ntl_ZZ_p(L[i].lift(), ctx) @@ -865,7 +865,7 @@ cdef get_val_prec(PowComputer_ext pp, a): py_tmp = ntl_ZZ.__new__(ntl_ZZ) py_tmp.x = pp.pow_ZZ_tmp(1)[0] return (Integer(a.valuation(py_tmp)), big, two) - if isinstance(a, (int, long)): + if isinstance(a, int): if a == 0: return (big, big, two) return (Integer(a).valuation(pp.prime), big, two) @@ -914,4 +914,3 @@ cdef get_val_prec(PowComputer_ext pp, a): print(py_tmp) raise TypeError("modulus must be a positive power of the appropriate prime") raise TypeError("unsupported type for list element: %s" % type(a)) - diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index b8f7598251f..69d5b474f20 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -9,8 +9,7 @@ AUTHORS: - Genya Zaytman: documentation - David Harvey: doctests """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe <roed.math@gmail.com> # William Stein <wstein@gmail.com> # @@ -18,9 +17,8 @@ AUTHORS: # 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/ +# **************************************************************************** include "sage/libs/linkages/padics/mpz.pxi" include "CA_template.pxi" @@ -89,6 +87,8 @@ cdef class pAdicCappedAbsoluteElement(CAElement): """ def lift(self): """ + EXAMPLES:: + sage: R = ZpCA(3) sage: R(10).lift() 10 @@ -320,7 +320,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled @@ -383,7 +383,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): return ans def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -393,7 +393,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -443,7 +443,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -458,7 +458,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index e7dce3c3c51..b5843d467a7 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -610,4 +610,3 @@ def base_p_list(Integer n, bint pos, PowComputer_class prime_pow): cdef ExpansionIter expansion = ExpansionIter(dummy, n.exact_log(p) + 2, mode) mpz_set(expansion.curvalue, n.value) return trim_zeros(list(expansion)) - diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index 7cd211e5fd8..39f58efcd0b 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -509,4 +509,3 @@ cdef class pAdicExtElement(pAdicGenericElement): return R.residue_field()(self.expansion(0)) else: raise NotImplementedError("residue() not implemented in extensions for absprec larger than one") - diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 8b45f6cea71..2e9e9a1ed3b 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -157,7 +157,7 @@ cdef class pAdicFixedModElement(FMElement): return self.lift_c() cdef lift_c(self): - """ + r""" Returns an integer congruent to this element modulo the precision. .. WARNING:: @@ -227,7 +227,7 @@ cdef class pAdicFixedModElement(FMElement): holder.value) def _integer_(self, Z=None): - """ + r""" Return an integer congruent to ``self`` modulo the precision. .. WARNING:: @@ -389,7 +389,7 @@ cdef class pAdicFixedModElement(FMElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled @@ -449,7 +449,7 @@ cdef class pAdicFixedModElement(FMElement): return ans def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -459,7 +459,7 @@ cdef class pAdicFixedModElement(FMElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -508,7 +508,7 @@ cdef class pAdicFixedModElement(FMElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -523,7 +523,7 @@ cdef class pAdicFixedModElement(FMElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -567,7 +567,6 @@ cdef class pAdicFixedModElement(FMElement): return ans - def make_pAdicFixedModElement(parent, value): """ Unpickles a fixed modulus element. @@ -580,4 +579,3 @@ def make_pAdicFixedModElement(parent, value): 2*5^2 + 3*5^3 """ return unpickle_fme_v2(pAdicFixedModElement, parent, value) - diff --git a/src/sage/rings/padics/padic_floating_point_element.pyx b/src/sage/rings/padics/padic_floating_point_element.pyx index 7cad653a214..13eb30a3abc 100644 --- a/src/sage/rings/padics/padic_floating_point_element.pyx +++ b/src/sage/rings/padics/padic_floating_point_element.pyx @@ -442,4 +442,3 @@ cdef class pAdicFloatingPointElement(FPElement): sig_off() return ans - diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 5cf7d8a04f3..3785ac7a3d6 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -544,7 +544,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return self._repr_(mode=mode) def _repr_(self, mode=None, do_latex=False): - """ + r""" Returns a string representation of this element. INPUT: @@ -565,7 +565,6 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: K.<pi> = Qp(2).extension(x^3 - 2) sage: latex(pi) \pi + O(\pi^{61}) - """ return self.parent()._printer.repr_gen(self, do_latex, mode=mode) @@ -1172,7 +1171,7 @@ cdef class pAdicGenericElement(LocalGenericElement): x^4 - x^3 + x^2 - x + 1 """ # TODO: figure out if this works for extension rings. If not, move this to padic_base_generic_element. - from sage.arith.all import algdep + from sage.arith.misc import algdep return algdep(self, n) def algebraic_dependency(self, n): @@ -2005,7 +2004,10 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``self`` -- a p-adic element - ``p`` -- a prime (default: None). If specified, will make sure that p==self.parent().prime() - NOTE: The optional argument p is used for consistency with the valuation methods on integer and rational. + .. NOTE:: + + The optional argument p is used for consistency with the valuation + methods on integer and rational. OUTPUT: @@ -2076,7 +2078,7 @@ cdef class pAdicGenericElement(LocalGenericElement): ... ValueError: Ring (5-adic Field with capped relative precision 4) residue field of the wrong characteristic. """ - if not p is None and p != self.parent().prime(): + if p is not None and p != self.parent().prime(): raise ValueError('Ring (%s) residue field of the wrong characteristic.' % self.parent()) cdef long v = self.valuation_c() if v == maxordp: @@ -2127,8 +2129,10 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``self`` -- a p-adic element - ``p`` -- a prime (default: ``None``). If specified, will make sure that ``p == self.parent().prime()`` - NOTE: The optional argument p is used for consistency with the valuation methods on integer and rational. + .. NOTE:: + The optional argument p is used for consistency with the valuation + methods on integer and rational. OUTPUT: @@ -2216,7 +2220,7 @@ cdef class pAdicGenericElement(LocalGenericElement): p = self.parent().prime() alpha = self.unit_part().lift() m = Integer(p**self.precision_relative()) - from sage.arith.all import rational_reconstruction + from sage.arith.misc import rational_reconstruction r = rational_reconstruction(alpha, m) return (Rational(p)**self.valuation())*r @@ -2457,7 +2461,7 @@ cdef class pAdicGenericElement(LocalGenericElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled @@ -3028,7 +3032,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return series_unit*nfactorial_unit.inverse_of_unit()<<(series_val-nfactorial_val) def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -3038,7 +3042,7 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -3074,7 +3078,7 @@ cdef class pAdicGenericElement(LocalGenericElement): raise NotImplementedError("the binary splitting algorithm is not implemented for the parent: %s" % self.parent()) def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -3089,7 +3093,7 @@ cdef class pAdicGenericElement(LocalGenericElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -3783,7 +3787,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return parent(root) def _inverse_pth_root(self, twist=None, hint=None): - """ + r""" In its simplest form, computes the inverse of ``p``-th root of this element. @@ -4409,8 +4413,9 @@ def _polylog_c(n, p): """ return p/(p-1) - (n-1)/p.log().n() + (n-1)*(n*(p-1)/p.log().n()).log(p).n() + (2*p*(p-1)*n/p.log().n()).log(p).n() + def _findprec(c_1, c_2, c_3, p): - """ + r""" Return an integer k such that c_1*k - c_2*log_p(k) > c_3. This is an internal function, used by :meth:`polylog`. @@ -4439,16 +4444,17 @@ def _findprec(c_1, c_2, c_3, p): return k k += 1 + def _compute_g(p, n, prec, terms): - """ + r""" Return the list of power series `g_i = \int(-g_{i-1}/(v-v^2))` used in the computation of polylogarithms. + This is an internal function, used by :meth:`polylog`. EXAMPLES:: sage: sage.rings.padics.padic_generic_element._compute_g(7, 3, 3, 3)[0] O(7^3)*v^2 + (1 + O(7^3))*v + O(7^3) - """ from sage.rings.power_series_ring import PowerSeriesRing from sage.functions.other import ceil diff --git a/src/sage/rings/padics/padic_relaxed_errors.pyx b/src/sage/rings/padics/padic_relaxed_errors.pyx index 9451c7df14d..ddadb111ca9 100644 --- a/src/sage/rings/padics/padic_relaxed_errors.pyx +++ b/src/sage/rings/padics/padic_relaxed_errors.pyx @@ -69,4 +69,3 @@ def raise_error(error, permissive=False): raise PrecisionError("not enough precision") if error & ERROR_ABANDON: raise PrecisionError("computation has been abandoned; try to increase precision") - diff --git a/src/sage/rings/padics/padic_template_element.pxi b/src/sage/rings/padics/padic_template_element.pxi index fed8ac89f81..712f7dc9eeb 100644 --- a/src/sage/rings/padics/padic_template_element.pxi +++ b/src/sage/rings/padics/padic_template_element.pxi @@ -11,8 +11,7 @@ AUTHORS: - David Roe -- initial version (2012-3-1) """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe <roed.math@gmail.com> # William Stein <wstein@gmail.com> # @@ -20,8 +19,8 @@ AUTHORS: # 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 cpython.int cimport * @@ -120,7 +119,7 @@ cdef class pAdicTemplateElement(pAdicGenericElement): pAdicGenericElement.__init__(self, parent) cdef long val, xprec cdef GEN pari_tmp - if isinstance(x, (int, long)): + if isinstance(x, int): x = Integer(x) elif isinstance(x, pari_gen): pari_tmp = (<pari_gen>x).g @@ -776,7 +775,7 @@ cdef Integer exact_pow_helper(long *ansrelprec, long relprec, _right, PowCompute cdef Integer right, p = prime_pow.prime cdef long exp_val cdef bint isbase - if isinstance(_right, (int, long)): + if isinstance(_right, int): _right = Integer(_right) if isinstance(_right, Integer): right = <Integer> _right diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 883e36f5c96..2d444d0e676 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -368,7 +368,7 @@ def _normalize_number_field_data(self, R): G = L.relative_polynomial() K = L.base_ring() elif is_PolynomialQuotientRing(R): - from sage.categories.all import NumberFields + from sage.categories.number_fields import NumberFields if R.base_ring().fraction_field() not in NumberFields(): raise NotImplementedError("cannot normalize quotients over %r"%(R.base_ring(),)) L = R.fraction_field() @@ -798,7 +798,7 @@ def extensions(self, ring): base_extensions = self._base_valuation.extensions(self._base_valuation.domain().change_ring(self._base_valuation.domain().base_ring().fraction_field())) return [pAdicValuation(ring, base._initial_approximation) for base in base_extensions] if ring.base_ring() is self.domain(): - from sage.categories.all import IntegralDomains + from sage.categories.integral_domains import IntegralDomains if ring in IntegralDomains(): return self._extensions_to_quotient(ring) elif self.domain().is_subring(ring.base_ring()): @@ -843,7 +843,7 @@ def value_semigroup(self): Additive Abelian Semigroup generated by 1/2 """ - from sage.categories.all import Fields + from sage.categories.fields import Fields v = self(self.uniformizer()) if self.domain() in Fields(): return DiscreteValueSemigroup([-v,v]) @@ -1265,7 +1265,7 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): # algorithm (based on the extended Euclidean algorithm) here. We do not # get the uniqueness properties but we do not need them actually. # This is certainly slower than the implementation in Cython. - from sage.categories.all import Fields + from sage.categories.fields import Fields m = self.p()**(QQ(error).floor() + 1 - v) if self.domain() in Fields(): r = (m, lift) @@ -1274,7 +1274,7 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): qq, rr = r[0].quo_rem(r[1]) r = r[1], rr s = s[1], s[0] - qq*s[1] - from sage.arith.all import gcd + from sage.arith.misc import GCD as gcd if s[1] != 0 and gcd(s[1], r[1]) == 1: rational = self.domain()(r[1]) / self.domain()(s[1]) * self.p()**v if self._relative_size(rational) < self._relative_size(best): @@ -1446,13 +1446,13 @@ def _fraction_field(ring): Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1 """ - from sage.categories.all import Fields + from sage.categories.fields import Fields if ring in Fields(): return ring from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if is_PolynomialQuotientRing(ring): - from sage.categories.all import IntegralDomains + from sage.categories.integral_domains import IntegralDomains if ring in IntegralDomains(): return ring.base().change_ring(ring.base_ring().fraction_field()).quo(ring.modulus()) return ring.fraction_field() diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 7dc2887337d..fe60e6bde33 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -1214,8 +1214,9 @@ cdef class PowComputer_ZZ_pX(PowComputer_ext): ZZ_pX_add(xnew_q, xnew_q, x[0]) return 0 + cdef class PowComputer_ZZ_pX_FM(PowComputer_ZZ_pX): - """ + r""" This class only caches a context and modulus for p^prec_cap. Designed for use with fixed modulus p-adic rings, in Eisenstein diff --git a/src/sage/rings/padics/pow_computer_relative.pyx b/src/sage/rings/padics/pow_computer_relative.pyx index f28a46f03b7..29cee93968c 100644 --- a/src/sage/rings/padics/pow_computer_relative.pyx +++ b/src/sage/rings/padics/pow_computer_relative.pyx @@ -34,7 +34,6 @@ from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport mpz_init, mpz_clear, mpz_pow_ui from cpython.object cimport Py_EQ, Py_NE -from sage.structure.richcmp cimport richcmp_not_equal from sage.rings.integer cimport Integer from sage.rings.integer_ring import ZZ from sage.misc.cachefunc import cached_method diff --git a/src/sage/rings/padics/qadic_flint_CA.pyx b/src/sage/rings/padics/qadic_flint_CA.pyx index d10e4b3eaff..3e4d01fcad1 100644 --- a/src/sage/rings/padics/qadic_flint_CA.pyx +++ b/src/sage/rings/padics/qadic_flint_CA.pyx @@ -27,8 +27,8 @@ cdef class qAdicCappedAbsoluteElement(CAElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_CR.pyx b/src/sage/rings/padics/qadic_flint_CR.pyx index dfadaa2f053..c20fa087424 100644 --- a/src/sage/rings/padics/qadic_flint_CR.pyx +++ b/src/sage/rings/padics/qadic_flint_CR.pyx @@ -27,8 +27,8 @@ cdef class qAdicCappedRelativeElement(CRElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_FM.pyx b/src/sage/rings/padics/qadic_flint_FM.pyx index 4f3afe77e81..0a5e0563101 100644 --- a/src/sage/rings/padics/qadic_flint_FM.pyx +++ b/src/sage/rings/padics/qadic_flint_FM.pyx @@ -27,8 +27,8 @@ cdef class qAdicFixedModElement(FMElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_FP.pyx b/src/sage/rings/padics/qadic_flint_FP.pyx index 1e250990c4e..020b03b722a 100644 --- a/src/sage/rings/padics/qadic_flint_FP.pyx +++ b/src/sage/rings/padics/qadic_flint_FP.pyx @@ -29,8 +29,8 @@ cdef class qAdicFloatingPointElement(FPElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/relative_ramified_CA.pyx b/src/sage/rings/padics/relative_ramified_CA.pyx index 3c5d95b02ff..0a2df891f87 100644 --- a/src/sage/rings/padics/relative_ramified_CA.pyx +++ b/src/sage/rings/padics/relative_ramified_CA.pyx @@ -4,7 +4,7 @@ include "CA_template.pxi" cdef class RelativeRamifiedCappedAbsoluteElement(CAElement): def _poly_rep(self): """ - Return the underlying polynomial representation of this element + Return the underlying polynomial representation of this element (which is used for computations). For debugging and printing purpose. @@ -22,7 +22,7 @@ cdef class RelativeRamifiedCappedAbsoluteElement(CAElement): The coefficients of P are floating point p-adics:: sage: P = W.random_element()._poly_rep() - sage: ring = P.parent().base_ring() + sage: ring = P.parent().base_ring() sage: ring 5-adic Unramified Extension Ring in a defined by x^3 + 3*x + 3 sage: ring._prec_type() diff --git a/src/sage/rings/padics/relaxed_template.pxi b/src/sage/rings/padics/relaxed_template.pxi index f87034e56e3..4de2b197ad1 100644 --- a/src/sage/rings/padics/relaxed_template.pxi +++ b/src/sage/rings/padics/relaxed_template.pxi @@ -702,7 +702,7 @@ cdef class RelaxedElement(pAdicGenericElement): use the default halting precision of the parent - ``secure`` -- a boolean (default: ``False`` if ``prec`` is given, - ``True`` otherwise); when the elements cannot be distingiushed + ``True`` otherwise); when the elements cannot be distinguished at the given precision, raise an error if ``secure`` is ``True``, return ``True`` otherwise. diff --git a/src/sage/rings/padics/unramified_extension_generic.py b/src/sage/rings/padics/unramified_extension_generic.py index d8eecc36f02..7973ed814c3 100644 --- a/src/sage/rings/padics/unramified_extension_generic.py +++ b/src/sage/rings/padics/unramified_extension_generic.py @@ -56,7 +56,7 @@ def __init__(self, poly, prec, print_mode, names, element_class): def _extension_type(self): """ - Return the type (``Unramified``, ``Eisenstein``) of this + Return the type (``Unramified``, ``Eisenstein``) of this extension as a string, if any. Used for printing. diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index a05a36ebca1..816db5efe2a 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -22,7 +22,7 @@ # Quotient of polynomial ring from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement - + # Univariate Polynomial Rings from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.polynomial_ring import polygen, polygens diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index cf63cba4bce..0d84a9c757b 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -40,7 +40,7 @@ from sage.rings.complex_mpfr import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.qqbar import AA, QQbar -from sage.arith.all import sort_complex_numbers_for_display +from sage.arith.misc import sort_complex_numbers_for_display from sage.rings.polynomial.refine_root import refine_root diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index cc588f0ec05..1053f471a7a 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -32,7 +32,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.structure.element cimport parent -from sage.arith.all import factor +from sage.arith.misc import factor from sage.rings.integer_ring import ZZ from sage.misc.misc_c import prod from sage.misc.misc import subsets diff --git a/src/sage/rings/polynomial/hilbert.pyx b/src/sage/rings/polynomial/hilbert.pyx index 9c5c5b1232c..0c49e1655c2 100644 --- a/src/sage/rings/polynomial/hilbert.pyx +++ b/src/sage/rings/polynomial/hilbert.pyx @@ -601,4 +601,3 @@ def hilbert_poincare_series(I, grading=None): if HP.leading_coefficient() >= 0: return HP / PR.prod([(1-t**d) for d in grading]) return (-HP) / (-PR.prod([(1-t**d) for d in grading])) - diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index ac21b71553f..b47b171973c 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -58,6 +58,18 @@ returns non-negative integers, then ``c^P`` means to apply ``P`` to the variable indices occurring in ``c``. +If you want to substitute variables you can use the standard polynomial +methods, such as +:meth:`~sage.rings.polynomial.infinite_polynomial_element.InfinitePolynomial_sparse.subs`:: + + sage: R.<x,y> = InfinitePolynomialRing(QQ) + sage: f = x[1] + x[1]*x[2]*x[3] + sage: f.subs({x[1]: x[0]}) + x_3*x_2*x_0 + x_0 + sage: g = x[0] + x[1] + y[0] + sage: g.subs({x[0]: y[0]}) + x_1 + 2*y_0 + TESTS: We test whether coercion works, even in complicated cases in which @@ -317,6 +329,10 @@ def __call__(self, *args, **kwargs): sage: a(x_1=x[100]) x_100 + x_0 + sage: M = matrix([[1,1],[2,0]]) + sage: a(x_1=M) + [x_0 + 1 1] + [ 2 x_0] """ # Replace any InfinitePolynomials by their underlying polynomials if hasattr(self._p, 'variables'): @@ -433,6 +449,88 @@ def __getattr__(self, s): except AttributeError: raise AttributeError('%s has no attribute %s' % (self.__class__, s)) + def subs(self, fixed=None, **kwargs): + """ + Substitute variables in ``self``. + + INPUT: + + - ``fixed`` -- (optional) ``dict`` with ``{variable:value}`` pairs + - ``**kwargs`` -- named parameters + + OUTPUT: + + the resulting substitution + + EXAMPLES:: + + sage: R.<x,y> = InfinitePolynomialRing(QQ) + sage: f = x[1] + x[1]*x[2]*x[3] + + Passing ``fixed={x[1]: x[0]}``. Note that the keys may be given + using the generators of the infinite polynomial ring + or as a string:: + + sage: f.subs({x[1]: x[0]}) + x_3*x_2*x_0 + x_0 + sage: f.subs({'x_1': x[0]}) + x_3*x_2*x_0 + x_0 + + Passing the variables as names parameters:: + + sage: f.subs(x_1=y[1]) + x_3*x_2*y_1 + y_1 + sage: f.subs(x_1=y[1], x_2=2) + 2*x_3*y_1 + y_1 + + The substitution returns the original polynomial if you try + to substitute a variable not present:: + + sage: g = x[0] + x[1] + sage: g.subs({y[0]: x[0]}) + x_1 + x_0 + + The substitution can also handle matrices:: + + sage: M = matrix([[1,0],[0,2]]) + sage: N = matrix([[0,3],[4,0]]) + sage: g = x[0]^2 + 3*x[1] + sage: g.subs({'x_0': M}) + [3*x_1 + 1 0] + [ 0 3*x_1 + 4] + sage: g.subs({x[0]: M, x[1]: N}) + [ 1 9] + [12 4] + + If you pass both ``fixed`` and ``kwargs``, any conflicts + will defer to ``fixed``:: + + sage: R.<x,y> = InfinitePolynomialRing(QQ) + sage: f = x[0] + sage: f.subs({x[0]:1}) + 1 + sage: f.subs(x_0=5) + 5 + sage: f.subs({x[0]:1}, x_0=5) + 1 + + TESTS:: + + sage: g.subs(fixed=x[0], x_1=N) + Traceback (most recent call last): + ... + ValueError: fixed must be a dict + """ + if fixed: + if not isinstance(fixed, dict): + raise ValueError('fixed must be a dict') + kwargs.update(fixed) + try: + return self(**kwargs) + except TypeError: + str_kwargs = {str(k): v for k, v in kwargs.items()} + return self(**str_kwargs) + def ring(self): """ The ring which ``self`` belongs to. @@ -779,7 +877,9 @@ def p(m): else: # Permutation group element p = n - def q(s): return s[0]+'_'+str(p(ZZ(s[1]))) + def q(s): + return s[0] + '_' + str(p(ZZ(s[1]))) + newVars = [q(X.split('_')) for X in self._p.parent().variable_names()] if not newVars: return self @@ -976,9 +1076,13 @@ def squeezed(self): x_2*y_4 + x_1*y_3 """ - Indices = set([0]+[Integer(str(Y).split('_')[1]) for Y in self.variables()]) + Indices = set([0] + [Integer(str(Y).split('_')[1]) + for Y in self.variables()]) Indices = sorted(Indices) - def P(n): return Indices.index(n) if n in Indices else n + + def P(n): + return Indices.index(n) if n in Indices else n + return self**P def footprint(self): @@ -1571,7 +1675,9 @@ def __pow__(self, n): if hasattr(n, 'to_cycles') and hasattr(n, '__len__'): # duck typing Permutation # auxiliary function, necessary since n(m) raises an error if m>len(n) l = len(n) - def p(m): return n(m) if 0 < m <= l else m + + def p(m): + return n(m) if 0 < m <= l else m else: # Permutation group element p = n diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index 68e20e1aa74..ff237743f4a 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -19,7 +19,7 @@ - ``R``, the base ring. It has to be a commutative ring, and in some applications it must even be a field -- ``names``, a list of generator names. Generator names must be alpha-numeric. +- ``names``, a finite list of generator names. Generator names must be alpha-numeric. - ``order`` (optional string). The default order is ``'lex'`` (lexicographic). ``'deglex'`` is degree lexicographic, and ``'degrevlex'`` (degree reverse lexicographic) is possible but discouraged. @@ -180,7 +180,7 @@ If the type of monomial orderings (e.g., 'degrevlex' versus 'lex') or -if the implementations don't match, there is no simplified +if the implementations do not match, there is no simplified construction available:: sage: X.<x,y> = InfinitePolynomialRing(ZZ) @@ -254,18 +254,24 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +import operator +import re +from functools import reduce from sage.rings.ring import CommutativeRing from sage.categories.rings import Rings from sage.structure.all import SageObject, parent from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method -import operator -import re -from functools import reduce + +################################################### +# The Construction Functor + +from sage.categories.pushout import InfinitePolynomialFunctor + ############################################################### -## Ring Factory framework +# Ring Factory framework class InfinitePolynomialRingFactory(UniqueFactory): """ @@ -326,23 +332,21 @@ def create_key(self, R, names=('x',), order='lex', implementation='dense'): Traceback (most recent call last): ... ValueError: Infinite Polynomial Rings must have at least one generator - """ if isinstance(names, list): names = tuple(names) if not names: raise ValueError("Infinite Polynomial Rings must have at least one generator") - if len(names)>len(set(names)): - raise ValueError("The variable names must be distinct") - F = InfinitePolynomialFunctor(names,order,implementation) - while hasattr(R,'construction'): + if len(names) > len(set(names)): + raise ValueError("the variable names must be distinct") + F = InfinitePolynomialFunctor(names, order, implementation) + while hasattr(R, 'construction'): C = R.construction() if C is None: break - F = F*C[0] + F = F * C[0] R = C[1] - return (F,R) - + return (F, R) def create_object(self, version, key): """ @@ -352,36 +356,32 @@ def create_object(self, version, key): sage: InfinitePolynomialRing.create_object('1.0', InfinitePolynomialRing.create_key(ZZ, ('x3',))) Infinite polynomial ring in x3 over Integer Ring - """ - if len(key)>2: + if len(key) > 2: # We got an old pickle. By calling the ring constructor, it will automatically # be transformed into the new scheme return InfinitePolynomialRing(*key) # By now, we have different unique keys, based on construction functors - C,R = key + C, R = key from sage.categories.pushout import CompositeConstructionFunctor, InfinitePolynomialFunctor - if isinstance(C,CompositeConstructionFunctor): + if isinstance(C, CompositeConstructionFunctor): F = C.all[-1] - if len(C.all)>1: + if len(C.all) > 1: R = CompositeConstructionFunctor(*C.all[:-1])(R) else: F = C if not isinstance(F, InfinitePolynomialFunctor): - raise TypeError("We expected an InfinitePolynomialFunctor, not %s"%type(F)) - if F._imple=='sparse': + raise TypeError("we expected an InfinitePolynomialFunctor, not %s" % type(F)) + if F._imple == 'sparse': return InfinitePolynomialRing_sparse(R, F._gens, order=F._order) return InfinitePolynomialRing_dense(R, F._gens, order=F._order) -InfinitePolynomialRing = InfinitePolynomialRingFactory('InfinitePolynomialRing') -################################################### -## The Construction Functor +InfinitePolynomialRing = InfinitePolynomialRingFactory('InfinitePolynomialRing') -from sage.categories.pushout import InfinitePolynomialFunctor ############################################################## -## An auxiliary dictionary-like class that returns variables +# An auxiliary dictionary-like class that returns variables class InfiniteGenDict: """ @@ -421,9 +421,8 @@ def __init__(self, Gens): [InfiniteGenDict defined by ['a', 'b'], {'1': 1}] sage: D._D == loads(dumps(D._D)) # indirect doctest True - """ - self._D = dict(zip([(hasattr(X,'_name') and X._name) or repr(X) for X in Gens],Gens)) + self._D = dict(zip(((hasattr(X, '_name') and X._name) or repr(X) for X in Gens), Gens)) def __eq__(self, other): """ @@ -481,16 +480,16 @@ def __getitem__(self, k): sage: type(_) <class 'sage.rings.polynomial.infinite_polynomial_element.InfinitePolynomial_dense'> """ - if not isinstance(k, str): - raise KeyError("String expected") + raise KeyError("string expected") L = k.split('_') try: - if len(L)==2: + if len(L) == 2: return self._D[L[0]][int(L[1])] except Exception: pass - raise KeyError("%s is not a variable name"%k) + raise KeyError("%s is not a variable name" % k) + class GenDictWithBasering: """ @@ -513,10 +512,8 @@ class GenDictWithBasering: <class 'sage.rings.polynomial.infinite_polynomial_element.InfinitePolynomial_dense'> sage: sage_eval('3*a_3*b_5-1/2*a_7', D) -1/2*a_7 + 3*a_3*b_5 - """ - - def __init__(self,parent, start): + def __init__(self, parent, start): """ INPUT: @@ -547,14 +544,13 @@ def __init__(self,parent, start): KeyError: 'a' sage: D['a'] a - """ P = self._P = parent - if isinstance(start,list): + if isinstance(start, list): self._D = start return self._D = [start] - while hasattr(P,'base_ring') and (P.base_ring() is not P): + while hasattr(P, 'base_ring') and (P.base_ring() is not P): P = P.base_ring() D = P.gens_dict() if isinstance(D, GenDictWithBasering): @@ -562,6 +558,7 @@ def __init__(self,parent, start): break else: self._D.append(D) + def __next__(self): """ Return a dictionary that can be used to interprete strings in the base ring of ``self``. @@ -576,10 +573,9 @@ def __next__(self): GenDict of Univariate Polynomial Ring in t over Rational Field sage: sage_eval('t^2', next(D)) t^2 - """ - if len(self._D)<=1: - raise ValueError("No next term for %s available"%self) + if len(self._D) <= 1: + raise ValueError("no next term for %s available" % self) return GenDictWithBasering(self._P.base_ring(), self._D[1:]) next = __next__ @@ -593,7 +589,7 @@ def __repr__(self): sage: D GenDict of Infinite polynomial ring in a, b over Integer Ring """ - return "GenDict of "+repr(self._P) + return "GenDict of " + repr(self._P) def __getitem__(self, k): """ @@ -613,10 +609,11 @@ def __getitem__(self, k): return D[k] except KeyError: pass - raise KeyError("%s is not a variable name of %s or its iterated base rings"%(k,self._P)) + raise KeyError("%s is not a variable name of %s or its iterated base rings" % (k, self._P)) + ############################################################## -## The sparse implementation +# The sparse implementation class InfinitePolynomialRing_sparse(CommutativeRing): r""" @@ -697,20 +694,19 @@ def __init__(self, R, names, order): True sage: X.gen(1)[2]*Y.gen(0)[1] alpha_1*beta_2 - """ if not names: 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 isn't"%n) + raise ValueError("generator names must be alpha-numeric 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) if not isinstance(order, str): - raise TypeError("The monomial order must be given as a string") + raise TypeError("the monomial order must be given as a string") if R not in Rings().Commutative(): - raise TypeError("The given 'base ring' (= %s) must be a commutative ring" % R) + raise TypeError("the given 'base ring' (= %s) must be a commutative ring" % R) # now, the input is accepted if hasattr(R, '_underlying_ring'): @@ -722,7 +718,7 @@ def __init__(self, R, names, order): self._identify_variable = lambda x, y: (-self._names.index(x), int(y)) self._find_maxshift = re.compile('_([0-9]+)') # findall yields stringrep of the shifts self._find_variables = re.compile('[a-zA-Z0-9]+_[0-9]+') - self._find_varpowers = re.compile(r'([a-zA-Z0-9]+)_([0-9]+)\^?([0-9]*)') # findall yields triple "generator_name", "index", "exponent" + self._find_varpowers = re.compile(r'([a-zA-Z0-9]+)_([0-9]+)\^?([0-9]*)') # findall yields triple "generator_name", "index", "exponent" # Create some small underlying polynomial ring. # It is used to ensure that the parent of the underlying @@ -753,9 +749,8 @@ def __repr__(self): sage: X.<alpha,beta> = InfinitePolynomialRing(ZZ, order='deglex'); X Infinite polynomial ring in alpha, beta over Integer Ring - """ - return "Infinite polynomial ring in %s over %s"%(", ".join(self._names), self._base) + return "Infinite polynomial ring in %s over %s" % (", ".join(self._names), self._base) def _latex_(self): r""" @@ -794,10 +789,10 @@ def one(self): 1 """ from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial - return InfinitePolynomial(self,self._base(1)) + return InfinitePolynomial(self, self._base(1)) ##################### - ## coercion + # coercion def construction(self): """ @@ -887,7 +882,7 @@ def _element_constructor_(self, x): sage: Y('1/3') Traceback (most recent call last): ... - ValueError: Can't convert 1/3 into an element of Infinite polynomial ring in x over Integer Ring + ValueError: cannot convert 1/3 into an element of Infinite polynomial ring in x over Integer Ring """ from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial # In many cases, the easiest solution is to "simply" evaluate @@ -897,20 +892,20 @@ def _element_constructor_(self, x): try: x = sage_eval(x, self.gens_dict()) except Exception: - raise ValueError("Can't convert %s into an element of %s" % (x, self)) + raise ValueError("cannot convert %s into an element of %s" % (x, self)) P = parent(x) if P is self: return x elif self._base.has_coerce_map_from(P): return InfinitePolynomial(self, self._base(x)) else: - raise ValueError("Can't convert %s into an element of %s" % (x, self)) + raise ValueError("cannot convert %s into an element of %s" % (x, self)) if isinstance(parent(x), InfinitePolynomialRing_sparse): # the easy case - parent == self - is already past - if x.parent() is self._base: # another easy case - return InfinitePolynomial(self,x) - xmaxind = x.max_index() # save for later + if x.parent() is self._base: # another easy case + return InfinitePolynomial(self, x) + xmaxind = x.max_index() # save for later x = x._p else: xmaxind = -1 @@ -932,26 +927,26 @@ def _element_constructor_(self, x): # By now, we can assume that x has a parent, because # types like int have already been done in the previous step; # and also it is not an InfinitePolynomial. - # If it isn't a polynomial (duck typing: we need + # If it is not a polynomial (duck typing: we need # the variables attribute), we fall back to using strings - if not hasattr(x,'variables'): + if not hasattr(x, 'variables'): try: return sage_eval(repr(x), self.gens_dict()) except Exception: - raise ValueError("Can't convert %s into an element of %s" % (x, self)) + raise ValueError("cannot convert %s into an element of %s" % (x, self)) # direct conversion will only be used if the underlying polynomials are libsingular. from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomial_libsingular, MPolynomialRing_libsingular # try interpretation in self._P, if we have a dense implementation - if hasattr(self,'_P'): + if hasattr(self, '_P'): if x.parent() is self._P: - return InfinitePolynomial(self,x) + return InfinitePolynomial(self, x) # It's a shame to use sage_eval. However, it's even more of a shame - # that MPolynomialRing_polydict doesn't work in complicated settings. + # that MPolynomialRing_polydict does not work in complicated settings. # So, if self._P is libsingular (and this will be the case in many # applications!), we do it "nicely". Otherwise, we have to use sage_eval. - if isinstance(x, MPolynomial_libsingular) and isinstance(self._P,MPolynomialRing_libsingular): - if xmaxind == -1: # Otherwise, x has been an InfinitePolynomial + if isinstance(x, MPolynomial_libsingular) and isinstance(self._P, MPolynomialRing_libsingular): + if xmaxind == -1: # Otherwise, x has been an InfinitePolynomial # We infer the correct variable shift. # Note: Since we are in the "libsingular" case, there are # no further "variables" hidden in the base ring of x.parent() @@ -963,7 +958,7 @@ def _element_constructor_(self, x): # This tests admissibility on the fly: VarList.sort(key=self.varname_key, reverse=True) except ValueError: - raise ValueError("Can't convert %s into an element of %s - variables aren't admissible"%(x,self)) + raise ValueError("cannot convert %s into an element of %s - variables are not admissible" % (x, self)) xmaxind = max([int(v.split('_')[1]) for v in VarList]) try: # Apparently, in libsingular, the polynomial conversion is not done by @@ -975,17 +970,17 @@ def _element_constructor_(self, x): if self._max < xmaxind: self.gen()[xmaxind] if self._P.ngens() == x.parent().ngens(): - self.gen()[self._max+1] + self.gen()[self._max + 1] # conversion to self._P will be done in InfinitePolynomial.__init__ return InfinitePolynomial(self, x) except (ValueError, TypeError, NameError): - raise ValueError("Can't convert %s (from %s, but variables %s) into an element of %s - no conversion into underlying polynomial ring %s"%(x,x.parent(),x.variables(),self,self._P)) + raise ValueError("cannot convert %s (from %s, but variables %s) into an element of %s - no conversion into underlying polynomial ring %s" % (x, x.parent(), x.variables(), self, self._P)) # By now, x or self._P are not libsingular. Since MPolynomialRing_polydict # is too buggy, we use string evaluation try: return sage_eval(repr(x), self.gens_dict()) except (ValueError, TypeError, NameError): - raise ValueError("Can't convert %s into an element of %s - no conversion into underlying polynomial ring"%(x,self)) + raise ValueError("cannot convert %s into an element of %s - no conversion into underlying polynomial ring" % (x, self)) # By now, we are in the sparse case. try: @@ -996,48 +991,48 @@ def _element_constructor_(self, x): # This tests admissibility on the fly: VarList.sort(key=self.varname_key, reverse=True) except ValueError: - raise ValueError("Can't convert %s into an element of %s - variables aren't admissible"%(x,self)) + raise ValueError("cannot convert %s into an element of %s - variables are not admissible" % (x, self)) - if len(VarList)==1: + if len(VarList) == 1: # univariate polynomial rings are crab. So, make up another variable. - if VarList[0]==self._names[0]+'_0': - VarList.append(self._names[0]+'_1') + if VarList[0] == self._names[0] + '_0': + VarList.append(self._names[0] + '_1') else: - VarList.append(self._names[0]+'_0') + VarList.append(self._names[0] + '_0') # We ensure that polynomial conversion is done by names; # the problem is that it is done by names if the number of variables coincides. - if len(VarList)==x.parent().ngens(): + if len(VarList) == x.parent().ngens(): BigList = x.parent().variable_names() ind = 2 - while self._names[0]+'_'+str(ind) in BigList: - ind+=1 - VarList.append(self._names[0]+'_'+str(ind)) + while self._names[0] + '_' + str(ind) in BigList: + ind += 1 + VarList.append(self._names[0] + '_' + str(ind)) try: VarList.sort(key=self.varname_key, reverse=True) except ValueError: - raise ValueError("Can't convert %s into an element of %s; the variables aren't admissible"%(x,self)) + raise ValueError("cannot convert %s into an element of %s; the variables are not admissible" % (x, self)) from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self._base, VarList, order=self._order) - if isinstance(R, MPolynomialRing_libsingular) and isinstance(x,MPolynomial_libsingular): # everything else is so buggy that it's even not worth to try. + if isinstance(R, MPolynomialRing_libsingular) and isinstance(x, MPolynomial_libsingular): # everything else is so buggy that it's even not worth to try. try: # Problem: If there is only a partial overlap in the variables # of x.parent() and R, then R(x) raises an error (which, I think, # is a bug, since we talk here about conversion, not coercion). # Hence, for being on the safe side, we coerce into a pushout ring: - x = R(1)*x - return InfinitePolynomial(self,x) + x = R(1) * x + return InfinitePolynomial(self, x) except Exception: # OK, last resort, to be on the safe side try: return sage_eval(repr(x), self.gens_dict()) - except (ValueError,TypeError,NameError): - raise ValueError("Can't convert %s into an element of %s; conversion of the underlying polynomial failed"%(x,self)) + except (ValueError, TypeError, NameError): + raise ValueError("cannot convert %s into an element of %s; conversion of the underlying polynomial failed" % (x, self)) else: try: return sage_eval(repr(x), self.gens_dict()) - except (ValueError,TypeError,NameError): - raise ValueError("Can't convert %s into an element of %s"%(x,self)) + except (ValueError, TypeError, NameError): + raise ValueError("cannot convert %s into an element of %s" % (x, self)) def tensor_with_ring(self, R): """ @@ -1073,20 +1068,20 @@ def tensor_with_ring(self, R): True """ if not R.has_coerce_map_from(self._underlying_ring): - raise TypeError("We can't tensor with "+repr(R)) + raise TypeError("we cannot tensor with " + repr(R)) B = self.base_ring() - if hasattr(B,'tensor_with_ring'): + if hasattr(B, 'tensor_with_ring'): return InfinitePolynomialRing(B.tensor_with_ring(R), self._names, self._order, implementation='sparse') - if hasattr(B,'change_ring'): # e.g., polynomial rings + if hasattr(B, 'change_ring'): # e.g., polynomial rings return InfinitePolynomialRing(B.change_ring(R), self._names, self._order, implementation='sparse') # try to find the correct base ring in other ways: try: - o = B.one()*R.one() + o = B.one() * R.one() except Exception: - raise TypeError("We can't tensor with "+repr(R)) + raise TypeError("we cannot tensor with " + repr(R)) return InfinitePolynomialRing(o.parent(), self._names, self._order, implementation='sparse') - ## Basic Ring Properties + # Basic Ring Properties # -- some stuff that is useful for quotient rings etc. def is_noetherian(self): """ @@ -1230,7 +1225,6 @@ def gen(self, i=None): sage: XX = InfinitePolynomialRing(GF(5)) sage: XX.gen(0) is XX.gen() True - """ if i is not None and i > len(self._names): raise ValueError @@ -1354,6 +1348,19 @@ def order(self): from sage.rings.infinity import Infinity return Infinity + # Other bases + def key_basis(self): + r""" + Return the basis of ``self`` given by key polynomials. + + EXAMPLES:: + + sage: R.<x> = InfinitePolynomialRing(GF(2)) + sage: R.key_basis() + Key polynomial basis over Finite Field of size 2 + """ + from sage.combinat.key_polynomial import KeyPolynomialBasis + return KeyPolynomialBasis(self) class InfinitePolynomialGen(SageObject): """ @@ -1449,48 +1456,49 @@ def __getitem__(self, i): alpha_1 """ if int(i) != i: - raise ValueError("The index (= %s) must be an integer" % i) + raise ValueError("the index (= %s) must be an integer" % i) i = int(i) if i < 0: - raise ValueError("The index (= %s) must be non-negative" % i) + raise ValueError("the index (= %s) must be non-negative" % i) P = self._parent from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense, InfinitePolynomial_sparse from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing OUT = self._output.get(i) - if hasattr(P,'_P'): + if hasattr(P, '_P'): if i <= P._max: - #return InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) + # return InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) if OUT is None: - self._output[i] = InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) + self._output[i] = InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name + '_' + str(i)))) else: if OUT._p.parent() is not P._P: OUT._p = P._P(OUT._p) return self._output[i] - #Calculate all of the new names needed + # Calculate all of the new names needed try: - names = [ [name+'_'+str(j) for name in P._names] for j in range(i+1)] + names = [[name + '_' + str(j) for name in P._names] + for j in range(i + 1)] except OverflowError: - raise IndexError("Variable index is too big - consider using the sparse implementation") + raise IndexError("variable index is too big - consider using the sparse implementation") names = reduce(operator.add, names) names.sort(key=P.varname_key, reverse=True) - #Create the new polynomial ring - P._P = PolynomialRing(P.base_ring(), names, order = P._order) - ##Get the generators + # Create the new polynomial ring + P._P = PolynomialRing(P.base_ring(), names, order=P._order) + # Get the generators P._max = i - #return InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) - self._output[i] = InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) + # return InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name+'_'+str(i)))) + self._output[i] = InfinitePolynomial_dense(P, P._P.gen(P._P.variable_names().index(self._name + '_' + str(i)))) return self._output[i] # Now, we are in the sparse implementation - if OUT is not None: # in the sparse implementation, this is ok + if OUT is not None: # in the sparse implementation, this is ok return OUT - if i==0: - names = [self._name+'_0',self._name+'_1'] + if i == 0: + names = [self._name + '_0', self._name + '_1'] else: - names = [self._name+'_0',self._name+'_'+str(i)] + names = [self._name + '_0', self._name + '_' + str(i)] names.sort(key=P.varname_key, reverse=True) Pol = PolynomialRing(P.base_ring(), names, order=P._order) - #return InfinitePolynomial_sparse(P, Pol.gen(names.index(self._name+'_'+str(i)))) - self._output[i] = InfinitePolynomial_sparse(P, Pol.gen(names.index(self._name+'_'+str(i)))) + # return InfinitePolynomial_sparse(P, Pol.gen(names.index(self._name+'_'+str(i)))) + self._output[i] = InfinitePolynomial_sparse(P, Pol.gen(names.index(self._name + '_' + str(i)))) return self._output[i] def _repr_(self): @@ -1500,9 +1508,8 @@ def _repr_(self): sage: X.<x,y> = InfinitePolynomialRing(QQ) sage: x # indirect doctest x_* - """ - return self._name+'_*' + return self._name + '_*' def __str__(self): """ @@ -1511,12 +1518,12 @@ def __str__(self): sage: X.<x,y> = InfinitePolynomialRing(QQ) sage: print(x) # indirect doctest Generator for the x's in Infinite polynomial ring in x, y over Rational Field - """ - return "Generator for the %s's in %s"%(self._name, self._parent) + return "Generator for the %s's in %s" % (self._name, self._parent) + ############################################################## -## The dense implementation +# The dense implementation class InfinitePolynomialRing_dense(InfinitePolynomialRing_sparse): """ @@ -1537,14 +1544,14 @@ def __init__(self, R, names, order): """ if not names: names = ['x'] - #Generate the initial polynomial ring + # Generate the initial polynomial ring self._max = 0 InfinitePolynomialRing_sparse.__init__(self, R, names, order) self._P = self._minP - #self._pgens = self._P.gens() + # self._pgens = self._P.gens() ##################### - ## Coercion + # Coercion def construction(self): """ @@ -1598,17 +1605,17 @@ def tensor_with_ring(self, R): """ if not R.has_coerce_map_from(self._underlying_ring): - raise TypeError("We can't tensor with "+repr(R)) + raise TypeError("we cannot tensor with " + repr(R)) B = self.base_ring() - if hasattr(B,'tensor_with_ring'): + if hasattr(B, 'tensor_with_ring'): return InfinitePolynomialRing(B.tensor_with_ring(R), self._names, self._order, implementation='dense') - if hasattr(B,'change_ring'): # e.g., polynomial rings + if hasattr(B, 'change_ring'): # e.g., polynomial rings return InfinitePolynomialRing(B.change_ring(R), self._names, self._order, implementation='dense') # try to find the correct base ring in other ways: try: - o = B.one()*R.one() + o = B.one() * R.one() except Exception: - raise TypeError("We can't tensor with "+repr(R)) + raise TypeError("we cannot tensor with " + repr(R)) return InfinitePolynomialRing(o.parent(), self._names, self._order, implementation='dense') def polynomial_ring(self): diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index c82cf65eb0f..dae87365a5c 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -294,10 +294,11 @@ 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.dict().items()])) + cdef class LaurentPolynomial_univariate(LaurentPolynomial): - """ + r""" A univariate Laurent polynomial in the form of `t^n \cdot f` where `f` is a polynomial in `t`. @@ -1118,12 +1119,27 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): x^3 + 3*x^4 + 3*x^5 + 10*x^6 + 18*x^7 + 9*x^8 + 27*x^9 + 27*x^10 + 27*x^12 sage: g^4 x^-40 - 4*x^-29 + 6*x^-18 - 4*x^-7 + x^4 + + sage: R.<x> = LaurentPolynomialRing(Zmod(6)) + sage: x^-2 + x^-2 + sage: (5*x^2)^-4 + x^-8 + sage: (5*x^-4)^-3 + 5*x^12 """ cdef LaurentPolynomial_univariate self = _self cdef long right = r if right != r: raise ValueError("exponent must be an integer") - return self._parent.element_class(self._parent, self.__u**right, self.__n*right) + try: + return self._parent.element_class(self._parent, self.__u**right, self.__n*right) + except TypeError as err: + # we need to handle the special case of negative powers and a unit + if not self.__u.is_constant() or not self.__u.leading_coefficient().is_unit(): + raise + c = self._parent._R(self.__u.leading_coefficient() ** right) + return self._parent.element_class(self._parent, c, self.__n*right) cpdef _floordiv_(self, rhs): """ @@ -3292,12 +3308,12 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): to variables supplied in args. Multiple variables and iteration counts may be supplied; see - documentation for the global derivative() function for more + documentation for the global :func:`derivative` function for more details. .. SEEALSO:: - :meth:`_derivative` + :meth:`_derivative` EXAMPLES:: @@ -3320,7 +3336,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): respect to the given variable. If var is among the generators of this ring, the derivative - is with respect to the generator. Otherwise, _derivative(var) is called + is with respect to the generator. Otherwise, ``_derivative(var)`` is called recursively for each coefficient of this polynomial. .. SEEALSO:: :meth:`derivative` @@ -3747,4 +3763,3 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if new_ring is not None: return new_ring(ans) return ans - diff --git a/src/sage/rings/polynomial/laurent_polynomial_ideal.py b/src/sage/rings/polynomial/laurent_polynomial_ideal.py index 24d8f05546b..76d1b495274 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ideal.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ideal.py @@ -2,7 +2,7 @@ r""" Ideals in Laurent polynomial rings. -For `R` a commutative ring, ideals in the Laurent polynomial ring +For `R` a commutative ring, ideals in the Laurent polynomial ring `R[x_1^{\pm 1}, x_2^{\pm 1}, \ldots, x_n^{\pm 1}]` are implemented as ideals in the ordinary polynomial ring `R[x_1, \ldots, x_n]` which are saturated with respect to the ideal `(x_1 \cdots x_n)`. @@ -35,7 +35,7 @@ def __init__(self, ring, gens, coerce=True, hint=None): associated ordinary polynomial ring `R[x_1,\ldots,x_n]` which is saturated with respect to the ideal `(x_1 \cdots x_n)`. Since computing the saturation can be expensive, we employ some strategies to reduce the need for it. - + - We only create the polynomial ideal as needed. - For some operations, we try some superficial tests first. E.g., for @@ -98,26 +98,26 @@ def set_hint(self, hint): """ Set the hint of this ideal. - The hint is an ideal of the associated polynomial ring, which is + The hint is an ideal of the associated polynomial ring, which is assumed to be contained in the associated ideal. It is used internally to speed up computation of the associated ideal in some cases; normally the end user will have no need to work with it directly. sage: P.<x,y,z> = LaurentPolynomialRing(QQ, 3) sage: I = P.ideal([x^2*y + 3*x*y^2]) - sage: I.hint() + sage: I.hint() Ideal (0) of Multivariate Polynomial Ring in x, y, z over Rational Field sage: I.set_hint(P.polynomial_ring().ideal([x + 3*y])) sage: I.hint() Ideal (x + 3*y) of Multivariate Polynomial Ring in x, y, z over Rational Field """ self._hint = hint - + def hint(self): """ Return the hint of this ideal. - The hint is an ideal of the associated polynomial ring, which is + The hint is an ideal of the associated polynomial ring, which is assumed to be contained in the associated ideal. It is used internally to speed up computation of the associated ideal in some cases; normally the end user will have no need to work with it directly. @@ -126,11 +126,11 @@ def hint(self): sage: P.<x,y,z> = LaurentPolynomialRing(QQ, 3) sage: I = P.ideal([x^2*y + 3*x*y^2]) - sage: I.hint() + sage: I.hint() Ideal (0) of Multivariate Polynomial Ring in x, y, z over Rational Field """ return self._hint - + # Comparisons, using the associated polynomial ideal. def _richcmp_(self, right_r, op): r""" @@ -192,14 +192,14 @@ def __contains__(self, f): f = self.ring()(f) g = f.__reduce__()[1][0] return (g in self.polynomial_ideal()) - + # Operations on ideals - + def change_ring(self, R, hint=None): """ Coerce an ideal into a new ring. - - This operation does not forward hints, but a new hint can be + + This operation does not forward hints, but a new hint can be specified manually. EXAMPLES:: @@ -313,13 +313,13 @@ def toric_coordinate_change(self, M, forward_hint=True): apply_to_hint = None return self.apply_map(lambda x, M=M: x.toric_coordinate_change(M), apply_to_hint=apply_to_hint) - + def __add__(self, other): """ Return the sum of two ideals in the same ring. Currently this operation does not support coercion. - + This operation forwards hints. EXAMPLES:: @@ -361,16 +361,16 @@ def normalize_gens(self): Ideal (x - 1, y + 1) of Multivariate Laurent Polynomial Ring in x, y over Rational Field """ return self.ring().ideal(self.groebner_basis(), hint=self._hint) - + # Structural queries and properties def polynomial_ideal(self, saturate=True): """ Return the associated polynomial ideal. - + By default, the ideal is saturated with respect to the product of the polynomial ring generators; this is necessary for testing equality and inclusion. - As saturation can be quite time-consuming, it can be disabled by setting + As saturation can be quite time-consuming, it can be disabled by setting ``saturate=False``; however, the result will then depend not just on the original ideal but also on the choice of generators. @@ -406,7 +406,7 @@ def polynomial_ideal(self, saturate=True): self._hint = I self._saturated = True return I - + def groebner_basis(self, saturate=True): """ Return the reduced Groebner basis for the specified term order. @@ -441,7 +441,7 @@ def is_binomial(self, groebner_basis=False): """ Determine whether every generator of ``self`` is a binomial. - If ``groebner_basis`` is True, this becomes intrinsic (for a choice of + If ``groebner_basis`` is True, this becomes intrinsic (for a choice of term order). EXAMPLES:: @@ -456,7 +456,7 @@ def is_binomial(self, groebner_basis=False): else: l = self.gens() return all(not f or f.number_of_terms() == 2 for f in l) - + def associated_primes(self): """ Return associated primes of this ideal. diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 9f9948bb84b..c10faf46568 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -730,7 +730,7 @@ def ideal(self, *args, **kwds): Ideal (1) of Multivariate Laurent Polynomial Ring in x0, x1 over Rational Field TESTS: - + check that :trac:`26421` is fixed: sage: R.<t> = LaurentPolynomialRing(ZZ) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 65ae859f751..0be0c676758 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -8,7 +8,7 @@ This module provide implementations of some operations on polynomial ideals based on msolve. -Note that msolve must be installed separately. +Note that the `optional package msolve <../spkg/msolve.html>`_ must be installed. .. SEEALSO:: @@ -46,13 +46,11 @@ def _run_msolve(ideal, options): # Run msolve - msolve().require() - drlpolring = ideal.ring().change_ring(order='degrevlex') polys = ideal.change_ring(drlpolring).gens() msolve_in = tempfile.NamedTemporaryFile(mode='w', encoding='ascii', delete=False) - command = ["msolve", "-f", msolve_in.name] + options + command = [msolve().absolute_filename(), "-f", msolve_in.name] + options try: print(",".join(drlpolring.variable_names()), file=msolve_in) print(base.characteristic(), file=msolve_in) @@ -290,7 +288,7 @@ def to_poly(p, d=1, *, upol=PolynomialRing(base, 't')): variety = [] for rt in elim_roots: den_of_rt = den(rt) - point = [-p(rt)/den_of_rt for p in param] + point = [-p(rt) / den_of_rt for p in param] if len(param) != len(vars): point.append(rt) assert len(point) == len(vars) @@ -313,4 +311,3 @@ def to_poly(p, d=1, *, upol=PolynomialRing(base, 't')): for point in l] return [KeyConvertingDict(out_ring, zip(vars, point)) for point in variety] - diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index ce14319f63a..7bb03631661 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -196,12 +196,12 @@ cdef class MPolynomial(CommutativeRingElement): var = R.variable_name() if var in self._parent.variable_names(): return R(self.polynomial(self._parent(var))) - else: - return R([self]) + return R([self]) def coefficients(self): - """ + r""" Return the nonzero coefficients of this polynomial in a list. + The returned list is decreasingly ordered by the term ordering of ``self.parent()``, i.e. the list of coefficients matches the list of monomials returned by @@ -1210,7 +1210,7 @@ cdef class MPolynomial(CommutativeRingElement): Rational Field """ - from sage.arith.all import gcd + from sage.arith.misc import GCD as gcd return gcd(self.coefficients()) def content_ideal(self): @@ -1315,11 +1315,12 @@ cdef class MPolynomial(CommutativeRingElement): return R(dict([(k,f(v)) for (k,v) in self.dict().items()])) def _norm_over_nonprime_finite_field(self): - """ + r""" Given a multivariate polynomial over a nonprime finite field - `\GF{p**e}`, compute the norm of the polynomial down to `\GF{p}`, which - is the product of the conjugates by the Frobenius action on - coefficients, where Frobenius acts by p-th power. + `\GF{p^e}`, compute the norm of the polynomial down to `\GF{p}`. + + This is the product of the conjugates by the Frobenius action + on coefficients, where Frobenius acts by p-th power. This is (currently) an internal function used in factoring over finite fields. @@ -2574,6 +2575,43 @@ cdef class MPolynomial(CommutativeRingElement): d = self.dict() return all(c.is_nilpotent() for c in d.values()) + def _test_subs(self, tester=None, **options): + r""" + Run some tests using the ``subs`` method. + + TESTS:: + + sage: R.<x,y> = QQbar[] + sage: (x + y)._test_subs() + """ + if tester is None: + tester = self._tester(**options) + + gens = self.parent().gens() + + if gens: + # substituting all variables (in a polynomial ring with variables) with 0 + d = {str(gen): 0 for gen in gens} + tester.assertEqual(self.subs(**d).parent(), self.parent().base_ring()) + + # substituting all variables (in a polynomial ring with variables) + # with elements of another ring + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + other = PolynomialRing(self.parent().base_ring(), 'other', len(gens)) + other_gens = other.gens() + d = {str(gen): other_gen for gen, other_gen in zip(gens, other_gens)} + tester.assertEqual(self.subs(**d).parent(), other) + + if len(gens) > 1: + # substituting one variable (in a polynomial ring with variables) with 0 + d = {str(gens[0]): 0} + tester.assertEqual(self.subs(**d).parent(), self.parent()) + + # test error checking: partial substitution by elements + # from another ring is not allowed + d = {str(gens[0]): other_gens[0]} + with tester.assertRaises((ValueError, TypeError)): + self.subs(**d) cdef remove_from_tuple(e, int ind): w = list(e) diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 9cb5f58f830..702bf9af7eb 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -2285,7 +2285,7 @@ def subresultants(self, other, variable=None): x = variable p = self.polynomial(x) q = other.polynomial(x) - return [R(f) for f in p.subresultants(q)] + return [R(f) for f in p.subresultants(q)] def reduce(self, I): """ diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 4e1937a4645..71494eddbc0 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -60,7 +60,7 @@ Note that the result of a computation is not necessarily reduced:: sage: (a+b)^17 - 256*a*b^16 + 256*b^17 + a*b^16 + b^17 sage: S(17) == 0 True @@ -546,6 +546,55 @@ def _groebner_basis_libsingular(self, algorithm="groebner", *args, **kwds): raise NameError("Algorithm '%s' unknown"%algorithm) return S + @libsingular_gb_standard_options + def _groebner_cover(self): + r""" + Compute the Gröbner cover of the ideal. + + The Gröbner cover is a partition of the space of parameters, + such that the Gröbner basis is constant for each of the parts. + + OUTPUT: + + A list of parts. Each element of this list contains: + + - The leading monomials of the Gröbner basis in the part + - The Gröbner basis in the part + - A list of components of the part. Each component is + two lists of equations. The parameters in the + part are those that satisfy the first list of + equations, but do not satisfy the second one. + + EXAMPLES:: + + sage: from sage.libs.singular.function import singular_function, lib + sage: F = PolynomialRing(QQ,'a').fraction_field() + sage: F.inject_variables() + Defining a + sage: R.<x,y,z> = F[] + sage: I = R.ideal([-x+3*y+z-5,2*x+a*z+4,4*x-3*z-a-1]) + sage: I._groebner_cover() + [[[z, y, x], + [(2*a + 3)*z + (a + 9), (12*a + 18)*y + (-a^2 - 23*a - 36), (4*a + 6)*x + (-a^2 - a + 12)], + [[[0], [[(2*a + 3)]]]]], + [[1], [1], [[[(2*a + 3)], [[1]]]]]] + """ + from sage.rings.fraction_field import FractionField_generic + from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + F = self.base_ring() + if (not isinstance(F, FractionField_generic) or + (not is_MPolynomialRing(F.ring()) and not is_PolynomialRing(F.ring()))): + raise TypeError("the base ring must be a field with parameters") + from sage.libs.singular.function import singular_function, lib + from sage.arith.functions import lcm + lib("grobcov.lib") + grobcov = singular_function("grobcov") + polynomials = [] + for f in self.gens(): + polynomials.append(f * lcm([c.denominator() for c in f.coefficients()])) + return grobcov(self.ring().ideal(polynomials)) + class MPolynomialIdeal_singular_repr( MPolynomialIdeal_singular_base_repr): @@ -2467,7 +2516,8 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True {y: 0.3611030805286474?, x: 2.769292354238632?}, {y: 1, x: 1}] - We can also use the external program msolve to compute the variety. + We can also use the `optional package msolve <../spkg/msolve.html>`_ + to compute the variety. See :mod:`~sage.rings.polynomial.msolve` for more information. :: sage: I.variety(RBF, algorithm='msolve', proof=False) # optional - msolve @@ -2543,9 +2593,9 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True uses triangular decomposition, via Singular if possible, falling back on a toy implementation otherwise. - - With ``algorithm`` = ``"msolve"``, calls the external program - `msolve <https://msolve.lip6.fr/>`_ (if available in the system - program search path). Note that msolve uses heuristics and therefore + - With ``algorithm`` = ``"msolve"``, uses the + `optional package msolve <../spkg/msolve.html>`_. + Note that msolve uses heuristics and therefore requires setting the ``proof`` flag to ``False``. See :mod:`~sage.rings.polynomial.msolve` for more information. """ @@ -2815,37 +2865,41 @@ def hilbert_polynomial(self, algorithm='sage'): sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) sage: I.hilbert_polynomial() 5*t - 5 + + Check for :trac:`33597`:: + + sage: R.<X, Y, Z> = QQ[] + sage: I = R.ideal([X^2*Y^3, X*Z]) + sage: I.hilbert_polynomial() + t + 5 """ if not self.is_homogeneous(): raise TypeError("ideal must be homogeneous") - if algorithm == 'sage': from sage.misc.misc_c import prod hilbert_poincare = self.hilbert_series() - denom = hilbert_poincare.denominator().factor() - second_hilbert = hilbert_poincare.numerator() - t = second_hilbert.parent().gen() - if denom: - s = denom[0][1] # this is the pole order of the Hilbert-Poincaré series at t=1 - else: - return t.parent().zero() - # we assume the denominator of the Hilbert series is of the form (1-t)^s, scale if needed - if hilbert_poincare.denominator().leading_coefficient() == 1: - second_hilbert = second_hilbert*(-1)**s - denom = ZZ(s-1).factorial() - out = sum(c / denom * prod(s - 1 - n - nu + t for nu in range(s-1)) - for n,c in enumerate(second_hilbert)) + t.parent().zero() - return out - elif algorithm == 'singular': + denom = hilbert_poincare.denominator() + if denom.degree() == 0: + return denom.parent().zero() + t = denom.parent().gen() + s = denom.valuation(t - 1) + numerator = hilbert_poincare.numerator() + # we assume the denominator of the Hilbert series is of + # the form (1 - t)^s, need to scale numerator + scalar = ~(denom[0] * (s - 1).factorial()) + st = s - 1 + t + out = scalar * sum(c * prod(st - n - nu for nu in range(s - 1)) + for n, c in enumerate(numerator)) + return t.parent().zero() + out + if algorithm == 'singular': from sage.libs.singular.function_factory import ff hilbPoly = ff.polylib__lib.hilbPoly hp = hilbPoly(self) t = ZZ['t'].gen() - fp = ZZ(len(hp)-1).factorial() - return sum(ZZ(coeff) * t**i for i,coeff in enumerate(hp)) / fp - else: - raise ValueError("'algorithm' must be 'sage' or 'singular'") + fp = ZZ(len(hp) - 1).factorial() + return sum(ZZ(coeff) * t**i for i, coeff in enumerate(hp)) / fp + raise ValueError("'algorithm' must be 'sage' or 'singular'") @require_field @handle_AA_and_QQbar @@ -3088,12 +3142,21 @@ def _normal_basis_libsingular(self, degree, weights=None): sage: I = R.ideal(x^2-2*x*z+5, x*y^2+y*z+1, 3*y^2-8*x*z) sage: I._normal_basis_libsingular(5) [] + + Check what happens with weights but no degree (:trac:`34789`):: + + sage: TO = TermOrder('wdegrevlex', (2,)) + sage: R = PolynomialRing(QQ, 1, ['k'], order=TO) + sage: k = R.gen() + sage: I = R.ideal([k**2]) + sage: I.normal_basis() + [k, 1] """ from sage.rings.polynomial.multi_polynomial_ideal_libsingular import kbase_libsingular from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence gb = self._groebner_basis_libsingular() J = self.ring().ideal(gb) - if weights is None: + if weights is None or degree is None: res = kbase_libsingular(J, degree) else: from sage.libs.singular.function_factory import ff @@ -3175,21 +3238,8 @@ def normal_basis(self, degree=None, algorithm='libsingular', sage: S.<x,y,z> = PolynomialRing(GF(2), order=T) sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') [x^4*y, x^2*y^2, y^3, x^3*z, x*y*z, z^2] - - Check the deprecation:: - - sage: R.<x,y> = PolynomialRing(QQ) - sage: _ = R.ideal(x^2+y^2, x*y+2*y).normal_basis('singular') - doctest:...: DeprecationWarning: "algorithm" should be used as keyword argument - See https://trac.sagemath.org/29543 for details. """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - if isinstance(degree, str): - from sage.misc.superseded import deprecation - deprecation(29543, - '"algorithm" should be used as keyword argument') - algorithm = degree - degree = None weights = tuple(x.degree() for x in self.ring().gens()) if all(w == 1 for w in weights): @@ -4089,7 +4139,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Macaulay2's ``GroebnerBasis`` command with the strategy "MGB" (if available) 'msolve' - `msolve <https://msolve.lip6.fr/>`_ (if available, degrevlex order, + `optional package msolve <../spkg/msolve.html>`_ (degrevlex order, prime fields) 'magma:GroebnerBasis' @@ -4215,9 +4265,8 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] - Over prime fields of small characteristic, we can also use - `msolve <https://msolve.lip6.fr/>`_ (if available in the system program - search path):: + Over prime fields of small characteristic, we can also use the + `optional package msolve <../spkg/msolve.html>`_:: sage: R.<a,b,c> = PolynomialRing(GF(101), 3) sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching @@ -4563,6 +4612,50 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal gb = PolynomialSequence(self.ring(), gb, immutable=True) return gb + def groebner_cover(self): + r""" + Compute the Gröbner cover of the ideal, over a field with parameters. + + The Groebner cover is a partition of the space of parameters, + such that the Groebner basis in each part is given by the same + expression. + + EXAMPLES:: + + sage: F = PolynomialRing(QQ,'a').fraction_field() + sage: F.inject_variables() + Defining a + sage: R.<x,y,z> = F[] + sage: I = R.ideal([-x+3*y+z-5,2*x+a*z+4,4*x-3*z-1/a]) + sage: I.groebner_cover() + {Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + 0 + and Y is defined by: + 2*a^2 + 3*a: [(2*a^2 + 3*a)*z + (8*a + 1), (12*a^2 + 18*a)*y + (-20*a^2 - 35*a - 2), (4*a + 6)*x + 11], + Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + ... + and Y is defined by: + 1: [1], + Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + ... + and Y is defined by: + 1: [1]} + """ + from sage.schemes.affine.affine_space import AffineSpace + gc = self._groebner_cover() + F = self.base_ring() + A = AffineSpace(F.base_ring(), F.ngens(), list(F.gens_dict())) + result = {} + ring = F.ring() + for segment in gc: + for piece in segment[2]: + X = A.subscheme([ring(c) for c in piece[0]]) + Y = A.subscheme([ring(c) for c in piece[1][0]]) + for pol in piece[1][1:]: + Y = Y.union(A.subscheme([ring(c) for c in pol])) + result[Y.complement(X)] = segment[1] + return result + def change_ring(self, P): r""" Return the ideal ``I`` in ``P`` spanned by @@ -5458,4 +5551,3 @@ def __richcmp__(self, other, op): return not (contained and contains) else: # remaining case < return contained and not contains - diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 2109c516a1c..81d266f499f 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -193,7 +193,7 @@ from sage.libs.singular.decl cimport ( p_IsUnit, p_IsOne, p_Series, p_Head, idInit, fast_map_common_subexp, id_Delete, p_IsHomogeneous, p_Homogen, p_Totaldegree,pLDeg1_Totaldegree, singclap_pdivide, singclap_factorize, idLift, IDELEMS, On, Off, SW_USE_CHINREM_GCD, SW_USE_EZGCD, - p_LmIsConstant, pTakeOutComp1, singclap_gcd, pp_Mult_qq, p_GetMaxExp, + p_LmIsConstant, pTakeOutComp, singclap_gcd, pp_Mult_qq, p_GetMaxExp, pLength, kNF, p_Neg, p_Minus_mm_Mult_qq, p_Plus_mm_Mult_qq, pDiff, singclap_resultant, p_Normalize, prCopyR, prCopyR_NoSort) @@ -247,10 +247,9 @@ from sage.structure.factorization import Factorization from sage.structure.sequence import Sequence from sage.rings.fraction_field import FractionField -from sage.rings.all import RealField +from sage.rings.real_mpfr import RealField -from sage.interfaces.singular import singular as singular_default, is_SingularElement, SingularElement -from sage.interfaces.macaulay2 import macaulay2 as macaulay2_default, is_Macaulay2Element +import sage.interfaces.abc from sage.misc.misc_c import prod as mul from sage.misc.sage_eval import sage_eval @@ -969,9 +968,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): gens_map = dict(zip(Q.variable_names(),self.gens()[:Q.ngens()])) return eval(str(element),gens_map) - if isinstance(element, (SingularElement, cypari2.gen.Gen)): + if isinstance(element, (sage.interfaces.abc.SingularElement, cypari2.gen.Gen)): element = str(element) - elif is_Macaulay2Element(element): + elif isinstance(element, sage.interfaces.abc.Macaulay2Element): element = element.external_string() if isinstance(element, str): @@ -1106,10 +1105,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if gens.ring() is self: return gens gens = gens.gens() - if is_SingularElement(gens): - gens = list(gens) - coerce = True - elif is_Macaulay2Element(gens): + if isinstance(gens, (sage.interfaces.abc.SingularElement, sage.interfaces.abc.Macaulay2Element)): gens = list(gens) coerce = True if not isinstance(gens, (list, tuple)): @@ -1118,7 +1114,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly! return MPolynomialIdeal(self, gens, coerce=False) - def _macaulay2_(self, macaulay2=macaulay2_default): + def _macaulay2_(self, macaulay2=None): """ Create an M2 representation of this polynomial ring if Macaulay2 is installed. @@ -1147,6 +1143,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): --[x...y] 17 """ + if macaulay2 is None: + from sage.interfaces.macaulay2 import macaulay2 try: R = self.__macaulay2 if R is None or not (R.parent() is macaulay2): @@ -1165,18 +1163,18 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): 'sage...[symbol x0,symbol x1, MonomialSize=>16, MonomialOrder=>GLex]' """ if macaulay2 is None: - macaulay2 = macaulay2_default + from sage.interfaces.macaulay2 import macaulay2 return macaulay2._macaulay2_input_ring(self.base_ring(), self.gens(), self.term_order().macaulay2_str()) - def _singular_(self, singular=singular_default): + def _singular_(self, singular=None): """ Create a SINGULAR (as in the computer algebra system) representation of this polynomial ring. The result is cached. INPUT: - - ``singular`` - SINGULAR interpreter (default: ``singular_default``) + - ``singular`` - SINGULAR interpreter (default: ``sage.interfaces.singular.singular``) EXAMPLES:: @@ -1222,6 +1220,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): // : names x // block 2 : ordering C """ + if singular is None: + from sage.interfaces.singular import singular try: R = self.__singular if R is None or not (R.parent() is singular): @@ -1241,7 +1241,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): except (AttributeError, ValueError): return self._singular_init_(singular) - def _singular_init_(self, singular=singular_default): + def _singular_init_(self, singular=None): """ Create a SINGULAR (as in the computer algebra system) representation of this polynomial ring. The result is NOT @@ -1249,7 +1249,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): INPUT: - - ``singular`` - SINGULAR interpreter (default: ``singular_default``) + - ``singular`` - SINGULAR interpreter (default: ``sage.interfaces.singular.singular``) EXAMPLES:: @@ -1375,6 +1375,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): """ from sage.functions.other import ceil + if singular is None: + from sage.interfaces.singular import singular + if self.ngens()==1: _vars = str(self.gen()) if "*" in _vars: # 1.000...000*x @@ -1587,7 +1590,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): 9/4 sage: P.monomial_quotient(x,y) # Note the wrong result - x*y^65535*z^65535 + x*y^65535*z^65535 # 32-bit + x*y^1048575*z^1048575 # 64-bit sage: P.monomial_quotient(x,P(1)) x @@ -1984,7 +1988,6 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: R.<x,y> = QQ[] sage: x._new_constant_poly(2/1,R) 2 - """ if not x: return new_MP(P, NULL) @@ -1993,7 +1996,7 @@ cdef class MPolynomial_libsingular(MPolynomial): return new_MP(P, _p) def __call__(self, *x, **kwds): - """ + r""" Evaluate this multi-variate polynomial at ``x``, where ``x`` is either the tuple of values to substitute in, or one can use functional notation ``f(a_0,a_1,a_2, \ldots)`` to evaluate @@ -2248,10 +2251,11 @@ cdef class MPolynomial_libsingular(MPolynomial): 9/4*x^2 - 1/4*y^2 - y - 1 sage: P.<x,y> = PolynomialRing(QQ,order='lex') - sage: (x^2^15) * x^2^15 + sage: (x^2^32) * x^2^32 Traceback (most recent call last): ... - OverflowError: exponent overflow (...) + OverflowError: Python int too large to convert to C unsigned long # 32-bit + OverflowError: exponent overflow (...) # 64-bit """ # all currently implemented rings are commutative cdef poly *_p @@ -2261,7 +2265,7 @@ cdef class MPolynomial_libsingular(MPolynomial): return new_MP((<MPolynomial_libsingular>left)._parent,_p) cpdef _div_(left, right_ringelement): - """ + r""" Divide left by right EXAMPLES:: @@ -2372,10 +2376,11 @@ cdef class MPolynomial_libsingular(MPolynomial): ValueError: not a 2nd power sage: P.<x,y> = PolynomialRing(QQ,order='lex') - sage: (x+y^2^15)^10 + sage: (x+y^2^32)^10 Traceback (most recent call last): .... - OverflowError: exponent overflow (...) + OverflowError: Python int too large to convert to C unsigned long # 32-bit + OverflowError: exponent overflow (...) # 64-bit Test fractional powers (:trac:`22329`):: @@ -2858,7 +2863,7 @@ cdef class MPolynomial_libsingular(MPolynomial): # Extract the monomials that match the specifications # this loop needs improvement - while(p): + while p: flag = 0 for i from 0<=i<gens: if exps[i] != -1 and p_GetExp(p,i+1,r)!=exps[i]: @@ -2924,10 +2929,10 @@ cdef class MPolynomial_libsingular(MPolynomial): cdef poly *m = mon._poly cdef ring *r = self._parent_ring - if not mon._parent is self._parent: + if mon._parent is not self._parent: raise TypeError("mon must have same parent as self.") - while(p): + while p: if p_ExpVectorEqual(p, m, r) == 1: return si2sa(p_GetCoeff(p, r), r, self._parent._base) p = pNext(p) @@ -3127,7 +3132,7 @@ cdef class MPolynomial_libsingular(MPolynomial): i += 1 p_Setm(m, r) - while(p): + while p: if p_ExpVectorEqual(p, m, r) == 1: p_Delete(&m,r) return si2sa(p_GetCoeff(p, r), r, self._parent._base) @@ -3462,7 +3467,7 @@ cdef class MPolynomial_libsingular(MPolynomial): x^10000 no overflow - sage: n = 1000 + sage: n = 100000 sage: try: ....: f = x^n ....: f.subs(x = x^n) @@ -4568,7 +4573,7 @@ cdef class MPolynomial_libsingular(MPolynomial): l = [] for i from 0 <= i < IDELEMS(res): for j from 1 <= j <= IDELEMS(_I): - l.append( new_MP(parent, pTakeOutComp1(&res.m[i], j)) ) + l.append( new_MP(parent, pTakeOutComp(&res.m[i], 1)) ) id_Delete(&fI, r) id_Delete(&_I, r) @@ -4576,7 +4581,7 @@ cdef class MPolynomial_libsingular(MPolynomial): return Sequence(l, check=False, immutable=True) def reduce(self,I): - """ + r""" Return a remainder of this polynomial modulo the polynomials in ``I``. @@ -4636,7 +4641,7 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: f = 3*x sage: f.reduce([2*x,y]) - 3*x + x The reduction is not canonical when ``I`` is not a Groebner basis:: @@ -5002,14 +5007,14 @@ cdef class MPolynomial_libsingular(MPolynomial): sig_off() return new_MP(parent, quo), new_MP(parent, rem) - def _singular_init_(self, singular=singular_default): + def _singular_init_(self, singular=None): """ Return a SINGULAR (as in the computer algebra system) string representation for this element. INPUT: - - ``singular`` - interpreter (default: ``singular_default``) + - ``singular`` - interpreter (default: ``sage.interfaces.singular.singular``) EXAMPLES:: @@ -5024,6 +5029,8 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: P(0)._singular_init_() '0' """ + if singular is None: + from sage.interfaces.singular import singular self._parent._singular_().set_ring() return self._repr_short_() @@ -5054,9 +5061,9 @@ cdef class MPolynomial_libsingular(MPolynomial): """ cdef ring *r = self._parent_ring - if not self._parent is m._parent: + if self._parent is not m._parent: m = self._parent.coerce(m) - if not self._parent is q._parent: + if self._parent is not q._parent: q = self._parent.coerce(q) if m._poly and m._poly.next: @@ -5072,7 +5079,7 @@ cdef class MPolynomial_libsingular(MPolynomial): return new_MP(self._parent, p_Minus_mm_Mult_qq(p_Copy(self._poly, r), m._poly, q._poly, r)) - def _macaulay2_(self, macaulay2=macaulay2_default): + def _macaulay2_(self, macaulay2=None): """ Return a Macaulay2 element corresponding to this polynomial. @@ -5111,6 +5118,8 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: macaulay2(R('4')).ring()._operator('===', R) # optional - macaulay2 true """ + if macaulay2 is None: + from sage.interfaces.macaulay2 import macaulay2 m2_parent = macaulay2(self.parent()) macaulay2.use(m2_parent) return macaulay2('substitute(%s,%s)' % (repr(self), m2_parent._name)) @@ -5142,9 +5151,9 @@ cdef class MPolynomial_libsingular(MPolynomial): """ cdef ring *r = self._parent_ring - if not self._parent is m._parent: + if self._parent is not m._parent: m = self._parent.coerce(m) - if not self._parent is q._parent: + if self._parent is not q._parent: q = self._parent.coerce(q) if m._poly and m._poly.next: @@ -5288,15 +5297,14 @@ cdef class MPolynomial_libsingular(MPolynomial): return new_MP(self._parent,p) def integral(self, MPolynomial_libsingular var): - """ - Integrates this polynomial with respect to the provided - variable. + r""" + Integrate this polynomial with respect to the provided variable. One requires that `\QQ` is contained in the ring. INPUT: - - ``variable`` - the integral is taken with respect to variable + - ``variable`` -- the integral is taken with respect to variable EXAMPLES:: @@ -5419,7 +5427,6 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: d = a.resultant(b,y); d 2*x^3 - sage: P.<x,y> = PolynomialRing(ZZ,2) sage: f = x+y sage: g=y^2+x @@ -5432,13 +5439,12 @@ cdef class MPolynomial_libsingular(MPolynomial): if variable is None: variable = self.parent().gen(0) - if not self._parent is other._parent: + if self._parent is not other._parent: raise TypeError("first parameter needs to be an element of self.parent()") if not variable.parent() is self.parent(): raise TypeError("second parameter needs to be an element of self.parent() or None") - if n_GetChar(_ring.cf) > 1<<29: raise NotImplementedError("Resultants of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.") diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index adc5c87387d..f0381318b30 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -70,8 +70,8 @@ from sage.rings.polynomial.polydict import PolyDict, ETuple from sage.rings.polynomial.term_order import TermOrder -from sage.interfaces.singular import is_SingularElement -from sage.interfaces.macaulay2 import is_Macaulay2Element +import sage.interfaces.abc + from sage.libs.pari.all import pari_gen from sage.structure.element import Element @@ -489,7 +489,7 @@ def __call__(self, x=0, check=True): else: raise TypeError("unable to coerce since the denominator is not 1") - elif is_SingularElement(x) and self._has_singular: + elif isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: self._singular_().set_ring() try: return x.sage_poly(self) @@ -507,7 +507,7 @@ def __call__(self, x=0, check=True): raise TypeError("unable to evaluate {!r} in {}".format(x, self)) return self(x) - elif is_Macaulay2Element(x): + elif isinstance(x, sage.interfaces.abc.Macaulay2Element): try: s = x.sage_polystring() if len(s) == 0: @@ -927,10 +927,7 @@ def ideal(self, *gens, **kwds): if not self._has_singular: # pass through MPolynomialRing_base.ideal(self, gens, **kwds) - if is_SingularElement(gens): - gens = list(gens) - do_coerce = True - if is_Macaulay2Element(gens): + if isinstance(gens, (sage.interfaces.abc.SingularElement, sage.interfaces.abc.Macaulay2Element)): gens = list(gens) do_coerce = True elif not isinstance(gens, (list, tuple)): diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 02eeb51bacf..6e8ca561e2a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -1,6 +1,12 @@ r""" Base class for multivariate polynomial rings """ +import itertools +import warnings +from collections.abc import Iterable +from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector + import sage.misc.latex from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod @@ -15,7 +21,7 @@ from sage.categories.morphism import IdentityMorphism from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() -from sage.arith.all import binomial +from sage.arith.misc import binomial from sage.combinat.integer_vector import IntegerVectors @@ -341,6 +347,164 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): """ return self.remove_var(x)[str(x)] + def interpolation(self, bound, *args): + """ + Create a polynomial with specified evaluations. + + CALL FORMATS: + + This function can be called in two ways: + + 1. interpolation(bound, points, values) + + 2. interpolation(bound, function) + + INPUT: + + * ``bound`` -- either an integer bounding the total degree or a + list/tuple of integers bounding the degree of the variables + + * ``points`` -- list/tuple containing the evaluation points + + * ``values`` -- list/tuple containing the desired values at ``points`` + + * ``function`` -- evaluable function in `n` variables, where `n` is the + number of variables of the polynomial ring + + OUTPUT: + + 1. A polynomial respecting the bounds and having ``values`` as values + when evaluated at ``points``. + + 2. A polynomial respecting the bounds and having the same values as + ``function`` at exactly so many points so that the polynomial is + unique. + + EXAMPLES:: + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R.<x,y,z> = PolynomialRing(QQ) + sage: R.interpolation(4, F) + x^3*y + z^2 + y + 25 + + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R.<x,y,z> = PolynomialRing(QQ) + sage: R.interpolation([3,1,2], F) + x^3*y + z^2 + y + 25 + + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R.<x,y,z> = PolynomialRing(QQ) + sage: points = [(5,1,1),(7,2,2),(8,5,-1),(2,5,3),(1,4,0),(5,9,0), + ....: (2,7,0),(1,10,13),(0,0,1),(-1,1,0),(2,5,3),(1,1,1),(7,4,11), + ....: (12,1,9),(1,1,3),(4,-1,2),(0,1,5),(5,1,3),(3,1,-2),(2,11,3), + ....: (4,12,19),(3,1,1),(5,2,-3),(12,1,1),(2,3,4)] + sage: R.interpolation([3,1,2], points, [F(*x) for x in points]) + x^3*y + z^2 + y + 25 + + ALGORITHM: + + Solves a linear system of equations with the linear algebra module. If + the points are not specified, it samples exactly as many points as + needed for a unique solution. + + .. NOTE:: + + It will only run if the base ring is a field, even though it might + work otherwise as well. If your base ring is an integral domain, + let it run over the fraction field. + + Also, if the solution is not unique, it spits out one solution, + without any notice that there are more. + + Lastly, the interpolation function for univariate polynomial rings + is called ``lagrange_polynomial()``. + + .. WARNING:: + + If you don't provide point/value pairs but just a function, it + will only use as many points as needed for a unique solution with + the given bounds. In particular it will *not* notice or check + whether the result yields the correct evaluation for other points + as well. So if you give wrong bounds, you will get a wrong answer + without any warning. + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R.<x,y,z> = PolynomialRing(QQ) + sage: R.interpolation(3,F) + 1/2*x^3 + x*y + z^2 - 1/2*x + y + 25 + + .. SEEALSO:: + + :meth:`lagrange_polynomial<sage.rings.polynomial.polynomial_ring.PolynomialRing_field.lagrange_polynomial>` + """ + # get ring and number of variables + R = self.base_ring() + n = self.ngens() + + # we only run the algorithm over fields + if not R.is_field(): + raise TypeError(f'The base ring {R} is not a field.') + + # helper function to sample "num_samples" elements from R + def sample_points(num_samples): + try: + samples = list(itertools.islice(R, num_samples)) + if len(samples) < num_samples: + raise ValueError(f'Could not sample {num_samples} different elements of {R}.') + except NotImplementedError: + if R.characteristic() == 0 or R.characteristic() >= num_samples: + samples = [R(k) for k in range(num_samples)] + else: + raise NotImplementedError(f'Could not sample {num_samples} different elements of {R}.') + + return samples + + # set points and values + if len(args) == 2: + points, values = args + else: + F, = args + + if isinstance(bound, Iterable): + R_points = sample_points(max(bound) + 1) + points = list(itertools.product(*[R_points[:bound[i] + 1] for i in range(n)])) + else: + points = list(itertools.combinations_with_replacement(sample_points(bound + 1), n)) + + values = [F(*x) for x in points] + + # find all possibly appearing exponents + if isinstance(bound, Iterable): + exponents_space = list(itertools.product(*(range(bound[i] + 1) for i in range(n)))) + else: + exponents_space = [] + for entry in itertools.combinations_with_replacement(range(bound + 1), n): + exponents_space.append([entry[0]] + [entry[i] - entry[i - 1] for i in range(1, n)]) + + # build matrix + M = matrix.zero(R, 0, len(points)) + for exponents in exponents_space: + M = M.stack(vector(R, [self.monomial(*exponents)(*x) for x in points])) + + # solve for coefficients and construct polynomial + try: + coeff = M.solve_left(vector(R, values)) + except ValueError: + raise ValueError('Could not find a solution.') + solution = sum(coeff[i] * self.monomial(*exponents_space[i]) for i in range(len(exponents_space))) + + return solution + def _coerce_map_from_base_ring(self): """ Return a coercion map from the base ring of ``self``. @@ -1135,6 +1299,27 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): return self(dict(zip(M, C))) + def some_elements(self): + r""" + Return a list of polynomials. + + This is typically used for running generic tests. + + EXAMPLES:: + + sage: R.<x,y> = QQ[] + sage: R.some_elements() + [x, y, x + y, x^2 + x*y, 0, 1] + + """ + R = self.base_ring() + L = list(self.gens()) + if L: + L.append(L[0] + L[-1]) + L.append(L[0] * L[-1]) + L.extend([self.zero(), self.one()]) + return L + def change_ring(self, base_ring=None, names=None, order=None): """ Return a new multivariate polynomial ring which isomorphic to @@ -1184,6 +1369,27 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): """ return self({exponents: self.base_ring().one()}) + def monomials_of_degree(self, degree): + r""" + Return a list of all monomials of the given total degree in this + multivariate polynomial ring. + + EXAMPLES:: + + sage: R.<x,y,z> = ZZ[] + sage: mons = R.monomials_of_degree(2) + sage: mons + [x^2, x*y, x*z, y^2, y*z, z^2] + + The number of such monomials equals `\binom{n+k-1}{k}` + where `n` is the number of variables and `k` the degree:: + + sage: len(mons) == binomial(3+2-1,2) + True + """ + from sage.combinat.integer_vector import IntegerVectors + return [self.monomial(*a) for a in IntegerVectors(degree, self.ngens())] + def _macaulay_resultant_getS(self, mon_deg_tuple, dlist): r""" In the Macaulay resultant algorithm the list of all monomials of the total degree is partitioned into sets `S_i`. diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index e7d6f7e0207..10c3582d79f 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -758,7 +758,7 @@ def coefficient_matrix(self, sparse=True): for y in poly.monomials(): A[ x , v[y] ] = poly.monomial_coefficient(y) - return A, Matrix(R,nm,1,m) + return A, Matrix(R,nm,1,m) def subs(self, *args, **kwargs): """ @@ -1466,13 +1466,7 @@ def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, ver S = PolynomialSequence( R_solving, [ R_solving(f) for f in T] ) if S != []: - if algorithm == "exhaustive_search": - from sage.features.fes import LibFES - LibFES().require() - from sage.libs.fes import exhaustive_search - solutions = exhaustive_search(S, max_sols=n, verbose=verbose, **kwds) - - elif algorithm == "polybori": + if algorithm == "polybori": I = S.ideal() if verbose: I.groebner_basis(full_prot=True, **kwds) diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index 97b97d4fe8c..f2ab5232b2e 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -358,8 +358,7 @@ def _simplify_(numerator, terms): OUTPUT: A pair of a Laurent polynomial and a tuple of Laurent polynomials - representing numerator and denominator as described in the - INPUT-section. + representing numerator and denominator as described in the INPUT-section. EXAMPLES:: @@ -557,7 +556,8 @@ def Omega_ge(a, exponents): logger = logging.getLogger(__name__) logger.info('Omega_ge: a=%s, exponents=%s', a, exponents) - from sage.arith.all import lcm, srange + from sage.arith.functions import lcm + from sage.arith.srange import srange from sage.rings.integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.number_field.number_field import CyclotomicField diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index 69f394a3e74..d21ed8d1ed4 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -1,12 +1,11 @@ r""" -An element in the fraction field of a Ore polynomial ring. +Fraction field elements of Ore polynomial rings AUTHOR: - Xavier Caruso (2020-05) """ - # *************************************************************************** # Copyright (C) 2020 Xavier Caruso <xavier.caruso@normalesup.org> # @@ -17,7 +16,6 @@ # https://www.gnu.org/licenses/ # *************************************************************************** - from sage.structure.richcmp import richcmp, op_EQ, op_NE from sage.misc.cachefunc import cached_method from sage.misc.latex import latex diff --git a/src/sage/rings/polynomial/ore_function_field.py b/src/sage/rings/polynomial/ore_function_field.py index 5cdaa302990..f223f9799dd 100644 --- a/src/sage/rings/polynomial/ore_function_field.py +++ b/src/sage/rings/polynomial/ore_function_field.py @@ -1,5 +1,5 @@ r""" -Fraction fields of Ore polynomial rings. +Fraction fields of Ore polynomial rings Sage provides support for building the fraction field of any Ore polynomial ring and performing basic operations in it. diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index d1492a35946..b9fa812bd7a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -1,5 +1,5 @@ r""" -Univariate Ore Polynomials +Univariate Ore polynomials This module provides the :class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial`, @@ -300,10 +300,8 @@ cdef class OrePolynomial(AlgebraElement): To: Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ if a: - n = self._new_c([a],P,check) - else: - n = self._new_c([],P) - return n + return self._new_c([a], P, check) + return self._new_c([], P) def __setitem__(self, n, value): r""" @@ -527,9 +525,7 @@ cdef class OrePolynomial(AlgebraElement): cdef Morphism m = self._parent.twisting_morphism(-self.degree()) if m is None: return self * a - else: - return self * m(a) - return r + return self * m(a) def right_monic(self): r""" @@ -578,8 +574,7 @@ cdef class OrePolynomial(AlgebraElement): a = self.base_ring()(~self.leading_coefficient()) except (ZeroDivisionError, TypeError): raise NotImplementedError("the leading coefficient is not a unit") - r = a * self - return r + return a * self cpdef _mod_(self, other): r""" @@ -625,9 +620,8 @@ cdef class OrePolynomial(AlgebraElement): Traceback (most recent call last): ... NotImplementedError: the leading coefficient of the divisor is not invertible - """ - q,_ = self.right_quo_rem(right) + q, _ = self.right_quo_rem(right) return q cpdef _div_(self, right): @@ -918,13 +912,13 @@ cdef class OrePolynomial(AlgebraElement): V1 = self._parent.zero() V3 = other while V3: - Q,R = G._left_quo_rem(V3) - T = U - V1*Q + Q, R = G._left_quo_rem(V3) + T = U - V1 * Q U = V1 G = V3 V1 = T V3 = R - V = G - self*U + V = G - self * U V, _ = V._left_quo_rem(other) if monic: lc = ~G.leading_coefficient() @@ -934,7 +928,7 @@ cdef class OrePolynomial(AlgebraElement): G = G * lc U = U * lc V = V * lc - return G,U,V + return G, U, V cdef _left_quo_rem(self, OrePolynomial other): r""" @@ -1137,19 +1131,19 @@ cdef class OrePolynomial(AlgebraElement): V3 = other while not V3.is_zero(): Q, R = G._right_quo_rem(V3) - T = U - Q*V1 + T = U - Q * V1 U = V1 G = V3 V1 = T V3 = R - V = G - U*self - V,_ = V._right_quo_rem(other) + V = G - U * self + V, _ = V._right_quo_rem(other) if monic: lc = ~G.leading_coefficient() G = lc * G U = lc * U V = lc * V - return G,U,V + return G, U, V @coerce_binop def right_gcd(self, other, monic=True): @@ -1330,7 +1324,7 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial V = <OrePolynomial>self._parent.zero() while other: Q, R = self._right_quo_rem(other) - T = U - Q*V + T = U - Q * V U = V V = T self = other @@ -1407,7 +1401,7 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial V = <OrePolynomial>self._parent.zero() while other: Q, R = self._left_quo_rem(other) - T = U - V*Q + T = U - V * Q U = V V = T self = other @@ -1460,7 +1454,6 @@ cdef class OrePolynomial(AlgebraElement): W1, _ = L._left_quo_rem(other) return L, V1, W1 - @coerce_binop def left_lcm(self, other, monic=True): r""" @@ -1648,14 +1641,14 @@ cdef class OrePolynomial(AlgebraElement): if y.find("-") == 0: y = y[1:] if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): - x = "(%s)"%x + x = "(%s)" % x if n > 1: - var = "*%s^%s"%(name,n) - elif n==1: - var = "*%s"%name + var = "*%s^%s" % (name, n) + elif n == 1: + var = "*%s" % name else: var = "" - s += "%s%s"%(x,var) + s += "%s%s" % (x, var) s = s.replace(" + -", " - ") s = re.sub(r' 1(\.0+)?\*', ' ', s) s = re.sub(r' -1(\.0+)?\*', ' -', s) @@ -1693,23 +1686,23 @@ cdef class OrePolynomial(AlgebraElement): x = self[n] x = y = x._latex_() if x != '0': - if n != m-1: + if n != m - 1: s += " + " if y.find("-") == 0: y = y[1:] if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): - x = "\\left(%s\\right)"%x + x = "\\left(%s\\right)" % x if n > 1: - var = "|%s^{%s}"%(name,n) - elif n==1: - var = "|%s"%name + var = "|%s^{%s}" % (name, n) + elif n == 1: + var = "|%s" % name else: var = "" - s += "%s %s"%(x,var) + s += "%s %s" % (x, var) s = s.replace(" + -", " - ") - s = re.sub(" 1(\.0+)? \|"," ", s) - s = re.sub(" -1(\.0+)? \|", " -", s) - s = s.replace("|","") + s = re.sub(r" 1(\.0+)? \|", " ", s) + s = re.sub(r" -1(\.0+)? \|", " -", s) + s = s.replace("|", "") if s == " ": return "0" return s[1:].lstrip().rstrip() @@ -1798,7 +1791,7 @@ cdef class OrePolynomial(AlgebraElement): if n == 0 or self.degree() < 0: return self if n > 0: - return self._parent(n*[self.base_ring().zero()] + self.list(), check=False) + return self._parent(n * [self.base_ring().zero()] + self.list(), check=False) if n < 0: if n > self.degree(): return self._parent([]) @@ -2097,7 +2090,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.exponents() [0, 2, 4] """ - return [i for i in range(self.degree()+1) if bool(self[i])] + return [i for i in range(self.degree() + 1) if bool(self[i])] def prec(self): r""" @@ -2157,9 +2150,8 @@ cdef class OrePolynomial(AlgebraElement): raise ValueError("n must be at least 0") if len(v) < n: z = self._parent.base_ring().zero() - return v + [z]*(n - len(v)) - else: - return v[:int(n)] + return v + [z] * (n - len(v)) + return v[:int(n)] def variable_name(self): r""" @@ -2195,14 +2187,15 @@ cdef void lmul_gen(list A, Morphism m, d): """ if m is None: A.append(A[-1]) - for j in range(len(A)-2, 0, -1): - A[j] = d(A[j]) + A[j-1] + for j in range(len(A) - 2, 0, -1): + A[j] = d(A[j]) + A[j - 1] else: A.append(m(A[-1])) - for j in range(len(A)-2, 0, -1): - A[j] = d(A[j]) + m(A[j-1]) + for j in range(len(A) - 2, 0, -1): + A[j] = d(A[j]) + m(A[j - 1]) A[0] = d(A[0]) + cdef class OrePolynomial_generic_dense(OrePolynomial): r""" Generic implementation of dense Ore polynomial supporting any valid base @@ -2317,8 +2310,8 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): This is an internal method. Use :meth:`__hash__` instead. """ - #todo - come up with a way to create hashes of zero that - # that do not incorrectly indicate that the element is 0. + # todo - come up with a way to create hashes of zero that + # that do not incorrectly indicate that the element is 0. cdef long result = 0 cdef long result_mon cdef long c_hash @@ -2577,11 +2570,11 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef Py_ssize_t dx = len(x), dy = len(y) cdef RingElement c if dx > dy: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dy] + x[dy:], self._parent, 0) + r = self._new_c([x[i] - y[i] for i in range(dy)] + x[dy:], self._parent, 0) elif dx < dy: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dx] + [ -c for c in y[dx:] ], self._parent, 0) + r = self._new_c([x[i] - y[i] for i in range(dx)] + [-c for c in y[dx:]], self._parent, 0) else: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dx], self._parent, 1) + r = self._new_c([x[i] - y[i] for i in range(dx)], self._parent, 1) return r cpdef _neg_(self): @@ -2654,7 +2647,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): This is an helper function. """ - cdef list BA = [ self.base_ring().zero() ] * (len(self._coeffs) + len(A) - 1) + cdef list BA = [self.base_ring().zero()] * (len(self._coeffs) + len(A) - 1) cdef Morphism m = self._parent._morphism cdef d = self._parent._derivation cdef coeff @@ -2713,7 +2706,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: f.degree() -1 """ - return self._new_c([ s*c for c in self._coeffs ], self._parent, 1) + return self._new_c([s * c for c in self._coeffs], self._parent, 1) cpdef _mul_(self, other): r""" @@ -2735,7 +2728,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: P * Q == Q * P False - TESTS:: + TESTS: We check associativity and distributivity:: @@ -2766,19 +2759,19 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef RingElement s, inv = other.leading_coefficient() cdef Py_ssize_t nb = len(A) - degB cdef list L - cdef list quo = [ ] + cdef list quo = [] if not inv.is_unit(): raise ValueError("the leading coefficient of the divisor in not a unit") inv = inv.inverse_of_unit() - for i in range(nb-1, -1, -1): - s = A[i+degB] + for i in range(nb - 1, -1, -1): + s = A[i + degB] if s: s = s * inv if m is not None: s = m(s) L = (<OrePolynomial_generic_dense>other)._mul_list([s]) - for j in range(len(L)-1): - A[i+j] -= L[j] + for j in range(len(L) - 1): + A[i + j] -= L[j] quo.append(s) quo.reverse() return self._new_c(quo, self._parent), self._new_c(A[:degB], self._parent, 1) @@ -2797,8 +2790,8 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef d = self._parent._derivation cdef RingElement s, inv = other.leading_coefficient() cdef list XnB = (<OrePolynomial_generic_dense>other)._coeffs - cdef list XnBs = [ ] - cdef list quo = [ ] + cdef list XnBs = [] + cdef list quo = [] if not inv.is_unit(): raise ValueError("the leading coefficient of the divisor in not a unit") inv = inv.inverse_of_unit() @@ -2807,21 +2800,20 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): XnB = list(XnB) lmul_gen(XnB, m, d) XnBs.append(XnB) - for i in range(nb-1, -1, -1): - s = A[i+degB] + for i in range(nb - 1, -1, -1): + s = A[i + degB] if s: if m: s *= self._parent.twisting_morphism(i)(inv) else: s *= inv XnB = XnBs[i] - for j in range(len(XnB)-1): + for j in range(len(XnB) - 1): A[j] -= s * XnB[j] quo.append(s) quo.reverse() return self._new_c(quo, self._parent), self._new_c(A[:degB], self._parent, 1) - cpdef list coefficients(self, sparse=True): r""" Return the coefficients of the monomials appearing in ``self``. @@ -2930,7 +2922,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): X = S.gen() + s Xi = S.one() ans = S(self[0]) - for i in range(1, self.degree()+1): + for i in range(1, self.degree() + 1): Xi = X * Xi ans += self[i] * Xi return ans @@ -3039,7 +3031,7 @@ cdef class OrePolynomialBaseringInjection(Morphism): """ assert codomain.base_ring() is domain, \ "the domain of the injection must be the base ring of the Ore polynomial ring" - Morphism.__init__(self, Hom(domain,codomain)) + Morphism.__init__(self, Hom(domain, codomain)) self._an_element = codomain.gen() self._repr_type_str = "Ore Polynomial base injection" self._new_constant_poly_ = self._an_element._new_constant_poly diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 0914fcb822b..f7eacf3112a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1,5 +1,5 @@ r""" -Univariate Ore Polynomial Rings +Univariate Ore polynomial rings This module provides the :class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`, diff --git a/src/sage/rings/polynomial/pbori/gbrefs.py b/src/sage/rings/polynomial/pbori/gbrefs.py index 76e3924715d..70dc795cbab 100644 --- a/src/sage/rings/polynomial/pbori/gbrefs.py +++ b/src/sage/rings/polynomial/pbori/gbrefs.py @@ -1,6 +1,6 @@ import gzip from io import StringIO -import uu +import base64 as uu import re from types import ModuleType from .PyPolyBoRi import Polynomial diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index e369473c3c4..dbd5defb793 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -217,8 +217,9 @@ from sage.categories.action cimport Action from sage.monoids.monoid import Monoid_class from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.interfaces.singular import singular as singular_default -from sage.interfaces.singular import SingularElement + +import sage.interfaces.abc + order_dict = {"lp": pblp, "dlex": pbdlex, @@ -307,7 +308,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): True sage: x2 > x3 True - sage: TestSuite(P).run() + sage: TestSuite(P).run(skip=["_test_zero_divisors", "_test_elements"]) Boolean polynomial rings are unique parent structures. We thus have:: @@ -328,8 +329,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: S.<x,y> = BooleanPolynomialRing(2, order='deglex') sage: P == S False - - """ def __init__(self, n=None, names=None, order='lex'): """ @@ -348,7 +347,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): cdef Py_ssize_t i, j, bstart, bsize if names is None: - raise TypeError("You must specify the names of the variables.") + raise TypeError("you must specify the names of the variables") if n is None: if isinstance(names, (tuple, list)): @@ -357,10 +356,10 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): try: n = int(n) except TypeError as msg: - raise TypeError("Number of variables must be an integer") + raise TypeError("number of variables must be an integer") if n < 1: - raise ValueError("Number of variables must be greater than 1.") + raise ValueError("number of variables must be greater than 1") self.pbind = <Py_ssize_t*>sig_malloc(n*sizeof(Py_ssize_t)) @@ -369,13 +368,13 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): try: pb_order_code = order_mapping[order[0].name()] except KeyError: - raise ValueError("Only order keys " + + raise ValueError("only order keys " + ', '.join(order_mapping.keys()) + - " are supported.") + " are supported") if order.is_block_order(): if pb_order_code is pblp: - raise ValueError("Only deglex and degneglex are supported for block orders.") + raise ValueError("only deglex and degneglex are supported for block orders") elif pb_order_code is pbdlex: pb_order_code = pbblock_dlex elif pb_order_code is pbdp_asc: @@ -384,8 +383,8 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): pb_order_code = pbblock_dp for i in range(1, len(order.blocks())): if order[0].name() != order[i].name(): - raise ValueError("Each block must have the same order type " - "(deglex and degneglex) for block orders.") + raise ValueError("each block must have the same order type " + "(deglex and degneglex) for block orders") if pb_order_code is pbdp: for i in range(n): @@ -506,8 +505,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): INPUT: - - ``i`` - an integer or a boolean monomial in one - variable + - ``i`` -- an integer or a boolean monomial in one variable EXAMPLES:: @@ -530,10 +528,10 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): if len(i) == 1: i = i.index() else: - raise TypeError("Boolean monomials must be in one variable only.") + raise TypeError("boolean monomials must be in one variable only") cdef idx = int(i) if idx < 0 or idx >= self._pbring.nVariables(): - raise ValueError("Generator not defined.") + raise ValueError("generator not defined") return new_BP_from_PBVar(self, self._pbring.variable(self.pbind[idx])) def gens(self): @@ -551,7 +549,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: P = BooleanPolynomialRing(10,'x') sage: P.gens() (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) - """ return tuple(new_BP_from_PBVar(self, self._pbring.variable(self.pbind[i])) @@ -559,14 +556,12 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): def change_ring(self, base_ring=None, names=None, order=None): """ - Return a new multivariate polynomial ring with base ring - ``base_ring``, variable names set to ``names``, and term - ordering given by ``order``. + Return a new multivariate polynomial ring with base ring ``base_ring``, + variable names set to ``names``, and term ordering given by ``order``. When ``base_ring`` is not specified, this function returns a ``BooleanPolynomialRing`` isomorphic to ``self``. Otherwise, - this returns a ``MPolynomialRing``. Each argument above is - optional. + this returns a ``MPolynomialRing``. Each argument above is optional. INPUT: @@ -590,7 +585,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: T.term_order() Lexicographic term order """ - if names is None: names = self.variable_names() if order is None: @@ -1004,7 +998,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): m *= var_mapping[j] p += m return p - elif isinstance(other, SingularElement): + elif isinstance(other, sage.interfaces.abc.SingularElement): other = str(other) if isinstance(other, str): @@ -1082,10 +1076,9 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: R.remove_var(x,y,z) Traceback (most recent call last): ... - ValueError: impossible to use the original term order (most likely because it was a block order). Please specify the term order for the subring + ValueError: impossible to use the original term order (most likely because it was a block order); please specify the term order for the subring sage: R.remove_var(x,y,z, order='deglex') Boolean PolynomialRing in u, v - """ vars = list(self.variable_names()) for v in var: @@ -1096,7 +1089,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): try: return BooleanPolynomialRing(names=vars, order=self.term_order()) except ValueError: - raise ValueError("impossible to use the original term order (most likely because it was a block order). Please specify the term order for the subring") + raise ValueError("impossible to use the original term order (most likely because it was a block order); please specify the term order for the subring") else: return BooleanPolynomialRing(names=vars, order=order) @@ -1205,7 +1198,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: P.random_element(degree=4) Traceback (most recent call last): ... - ValueError: Given degree should be less than or equal to number of variables (3) + ValueError: given degree should be less than or equal to number of variables (3) sage: f = P.random_element(degree=1, terms=5) sage: f.degree() <= 1 @@ -1228,7 +1221,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): sage: r = B.random_element(terms=(n/2)**2) """ from sage.rings.integer import Integer - from sage.arith.all import binomial + from sage.arith.misc import binomial if not vars_set: vars_set=range(self.ngens()) @@ -1251,7 +1244,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): degree = Integer(degree) if degree > nvars: - raise ValueError("Given degree should be less than or equal to number of variables (%s)" % nvars) + raise ValueError("given degree should be less than or equal to number of variables (%s)" % nvars) tot_terms = 0 monom_counts = [] @@ -1436,7 +1429,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): G = R.gens() return R.ideal([x**2 + x for x in G]) - def _singular_init_(self, singular=singular_default): + def _singular_init_(self, singular=None): r""" Return a newly created Singular quotient ring matching this boolean polynomial ring. @@ -1638,10 +1631,10 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): if len(i) == 1: i = i.index() else: - raise TypeError("Boolean monomials must be in one variable only.") + raise TypeError("boolean monomials must be in one variable only") i = int(i) if i < 0 or i >= self._pbring.nVariables(): - raise ValueError("Generator not defined.") + raise ValueError("generator not defined") return new_BM_from_PBVar(self._monom_monoid, self, self._pbring.variable(self.pbind[i])) @@ -1998,7 +1991,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): x50 """ if i < 0 or i >= self.ngens(): - raise ValueError("Generator not defined.") + raise ValueError("generator not defined") cdef PBVar newvar newvar = PBBooleVariable(i, (<BooleanPolynomialRing>self._ring)._pbring) @@ -2221,7 +2214,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): self.base_ring().has_coerce_map_from(other.parent()) and \ self.base_ring()(other).is_one(): return self._one_element - elif isinstance(other, (int, long)) and other % 2: + elif isinstance(other, int) and other % 2: return self._one_element elif isinstance(other, (list, set)): @@ -2382,11 +2375,11 @@ cdef class BooleanMonomial(MonoidElement): """ P = self.parent() if args and kwds: - raise ValueError("Using keywords and regular arguments not supported.") + raise ValueError("using keywords and regular arguments not supported") if args: - d = {} if len(args) > self._parent.ngens(): - raise ValueError("Number of arguments is greater than the number of variables of parent ring.") + raise ValueError("number of arguments is greater than the number of variables of parent ring") + d = {} for i in range(len(args)): d[i] = args[i] elif kwds: @@ -2522,7 +2515,7 @@ cdef class BooleanMonomial(MonoidElement): return self._pbmonom.deg() if x not in self._parent.gens(): - raise ValueError("x must be one of the generators of the parent.") + raise ValueError("x must be one of the generators of the parent") if self.reducible_by(x.lm()): return 1 @@ -3004,7 +2997,7 @@ cdef class BooleanPolynomial(MPolynomial): sage: a._repr_with_changed_varnames([1,'y','z']) Traceback (most recent call last): ... - TypeError: varnames has entries with wrong type. + TypeError: varnames has entries with wrong type :: @@ -3016,7 +3009,7 @@ cdef class BooleanPolynomial(MPolynomial): cdef int N = P._pbring.nVariables() if len(varnames) != N: - raise TypeError("len(varnames) doesn't equal self.parent().ngens()") + raise TypeError("len(varnames) is not equal to self.parent().ngens()") orig_varnames = P.variable_names() try: @@ -3025,7 +3018,7 @@ cdef class BooleanPolynomial(MPolynomial): except TypeError: for i in range(N): P._pbring.setVariableName(i, str_to_bytes(orig_varnames[i])) - raise TypeError("varnames has entries with wrong type.") + raise TypeError("varnames has entries with wrong type") s = ccrepr(self._pbpoly) for i in range(N): P._pbring.setVariableName(i, str_to_bytes(orig_varnames[i])) @@ -3256,7 +3249,7 @@ cdef class BooleanPolynomial(MPolynomial): sage: p^-1 Traceback (most recent call last): ... - NotImplementedError: Negative exponents for non constant boolean polynomials not implemented. + NotImplementedError: negative exponents for non constant boolean polynomials not implemented :: @@ -3278,7 +3271,7 @@ cdef class BooleanPolynomial(MPolynomial): elif self._pbpoly.isZero(): raise ZeroDivisionError else: - raise NotImplementedError("Negative exponents for non constant boolean polynomials not implemented.") + raise NotImplementedError("negative exponents for non constant boolean polynomials not implemented") def __neg__(BooleanPolynomial self): r""" @@ -3953,11 +3946,11 @@ cdef class BooleanPolynomial(MPolynomial): P = self._parent cdef int N = P.ngens() if args and kwds: - raise ValueError("Using keywords and regular arguments not supported.") + raise ValueError("using keywords and regular arguments not supported") if args: d = {} if len(args) != N: - raise ValueError("Number of arguments is different from the number of variables of parent ring.") + raise ValueError("number of arguments is different from the number of variables of parent ring") for i in range(N): arg = args[i] try: @@ -4565,7 +4558,7 @@ cdef class BooleanPolynomial(MPolynomial): L.append(tuple(l)) return tuple(L) else: - raise TypeError("Type '%s' of s not supported." % type(s)) + raise TypeError("type '%s' of s not supported" % type(s)) def spoly(self, BooleanPolynomial rhs): r""" @@ -4665,7 +4658,7 @@ cdef class BooleanPolynomial(MPolynomial): sage: a.reduce([None,None]) Traceback (most recent call last): ... - TypeError: argument must be a BooleanPolynomial. + TypeError: argument must be a BooleanPolynomial """ from sage.rings.polynomial.pbori.pbori import red_tail if not I: @@ -4674,7 +4667,7 @@ cdef class BooleanPolynomial(MPolynomial): I = I.gens() first = I[0] if first is None: - raise TypeError("argument must be a BooleanPolynomial.") + raise TypeError("argument must be a BooleanPolynomial") g = ReductionStrategy(first.ring()) g.opt_red_tail = True for p in I: @@ -4729,7 +4722,7 @@ cdef class PolynomialConstruct: # So, it is just a conversion. [Simon King] return (<BooleanPolynomialRing>ring)._element_constructor_(x) - raise TypeError("Cannot generate Boolean polynomial from %s , %s" % + raise TypeError("cannot generate Boolean polynomial from %s , %s" % (type(x), type(ring))) @@ -4768,7 +4761,7 @@ cdef class MonomialConstruct: return result.lm() return result except Exception: - raise TypeError("Cannot convert to Boolean Monomial %s" % + raise TypeError("cannot convert to Boolean Monomial %s" % type(x)) cdef class VariableConstruct: @@ -4790,11 +4783,10 @@ cdef class VariableConstruct: """ if isinstance(arg, BooleanPolynomialRing): return arg.variable(0) - elif isinstance(ring, BooleanPolynomialRing): + if isinstance(ring, BooleanPolynomialRing): return (<BooleanPolynomialRing>ring).variable(arg) - else: - raise TypeError("todo polynomial factory %s%s" % - (str(type(arg)), str(type(ring)))) + raise TypeError("todo polynomial factory %s%s" % + (str(type(arg)), str(type(ring)))) cdef class BooleanPolynomialIterator: @@ -4932,7 +4924,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): - ``lazy`` - (default: ``True``) - ``invert`` - setting ``invert=True`` input and output get a - transformation ``x+1`` for each variable ``x``, which shouldn't + transformation ``x+1`` for each variable ``x``, which should not effect the calculated GB, but the algorithm. - ``other_ordering_first`` - possible values are ``False`` or @@ -5526,7 +5518,7 @@ cdef class BooleSet: elif isinstance(rhs, BooleanPolynomial): s = (<BooleanPolynomial>rhs)._pbpoly.set() else: - raise TypeError("Argument 'rhs' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(rhs)) + raise TypeError("argument 'rhs' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(rhs)) return new_BS_from_PBSet(self._pbset.diff(s), self._ring) def union(self, rhs): @@ -5560,7 +5552,7 @@ cdef class BooleSet: elif isinstance(rhs, BooleanPolynomial): s = (<BooleanPolynomial>rhs)._pbpoly.set() else: - raise TypeError("Argument 'rhs' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(rhs)) + raise TypeError("argument 'rhs' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(rhs)) return new_BS_from_PBSet(self._pbset.unite(s), self._ring) def change(self, ind): @@ -6175,7 +6167,7 @@ cdef class BooleanPolynomialVector: elif isinstance(el, BooleanMonomial): p = PBBoolePolynomial((<BooleanMonomial>el)._pbmonom) else: - raise TypeError("Argument 'el' has incorrect type (expected BooleanPolynomial or BooleanMonomial, got %s)" % type(el)) + raise TypeError("argument 'el' has incorrect type (expected BooleanPolynomial or BooleanMonomial, got %s)" % type(el)) self._vec.push_back(<PBBoolePolynomial>p) cdef inline BooleanPolynomialVector new_BPV_from_PBPolyVector( @@ -6255,12 +6247,12 @@ cdef class ReductionStrategy: sage: red.add_generator(None) Traceback (most recent call last): ... - TypeError: argument must be a BooleanPolynomial. + TypeError: argument must be a BooleanPolynomial """ if p is None: - raise TypeError("argument must be a BooleanPolynomial.") + raise TypeError("argument must be a BooleanPolynomial") if p._pbpoly.isZero(): - raise ValueError("zero generators not allowed.") + raise ValueError("zero generators not allowed") deref(self._strat).addGenerator(p._pbpoly) def nf(self, BooleanPolynomial p): @@ -6525,7 +6517,6 @@ cdef class FGLMStrategy: Traceback (most recent call last): ... RuntimeError... - """ cdef BooleanPolynomialRing _from_ring, _to_ring @@ -6598,7 +6589,7 @@ cdef class GroebnerStrategy: self._strat = make_shared[PBGBStrategy]((<BooleanPolynomialRing>param)._pbring) self._parent = param else: - raise ValueError("Cannot generate GroebnerStrategy from %s." % + raise ValueError("cannot generate GroebnerStrategy from %s" % type(param)) self.reduction_strategy = ReductionStrategy(self._parent) @@ -6629,7 +6620,7 @@ cdef class GroebnerStrategy: [a + b, a + c] """ if p._pbpoly.isZero(): - raise ValueError("zero generators not allowed.") + raise ValueError("zero generators not allowed") deref(self._strat).addGeneratorDelayed(p._pbpoly) def add_generator(self, BooleanPolynomial p): @@ -6654,7 +6645,7 @@ cdef class GroebnerStrategy: ValueError: strategy already contains a polynomial with same lead """ if p._pbpoly.isZero(): - raise ValueError("zero generators not allowed.") + raise ValueError("zero generators not allowed") if deref(self._strat).generators.leadingTerms.owns(p._pbpoly.lead()): raise ValueError("strategy already contains a polynomial with same lead") deref(self._strat).generators.addGenerator(p._pbpoly) @@ -6689,7 +6680,7 @@ cdef class GroebnerStrategy: [a + c, b + c] """ if p._pbpoly.isZero(): - raise ValueError("zero generators not allowed.") + raise ValueError("zero generators not allowed") deref(self._strat).addAsYouWish(p._pbpoly) def implications(self, i): @@ -7207,7 +7198,7 @@ def zeros(pol, BooleSet s): elif isinstance(pol, BooleanMonomial): p = PBBoolePolynomial((<BooleanMonomial>pol)._pbmonom) else: - raise TypeError("Argument 'p' has incorrect type (expected BooleanPolynomial or BooleanMonomial, got %s)" % type(pol)) + raise TypeError("argument 'p' has incorrect type (expected BooleanPolynomial or BooleanMonomial, got %s)" % type(pol)) return new_BS_from_PBSet(pb_zeros(p, s._pbset), s._ring) @@ -7256,13 +7247,13 @@ def interpolate(zero, one): z = (<BooleanPolynomial>zero)._pbpoly.set() ring = (<BooleanPolynomial>zero)._parent else: - raise TypeError("Argument 'zero' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(zero)) + raise TypeError("argument 'zero' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(zero)) if isinstance(one, BooleSet): o = (<BooleSet>one)._pbset elif isinstance(one, BooleanPolynomial): o = (<BooleanPolynomial>one)._pbpoly.set() else: - raise TypeError("Argument 'one' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(one)) + raise TypeError("argument 'one' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(one)) return new_BP_from_PBPoly(ring, pb_interpolate(z, o)) @@ -7333,13 +7324,13 @@ def interpolate_smallest_lex(zero, one): elif isinstance(zero, BooleanPolynomial): z = (<BooleanPolynomial>zero)._pbpoly.set() else: - raise TypeError("Argument 'zero' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(zero)) + raise TypeError("argument 'zero' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(zero)) if isinstance(one, BooleSet): o = (<BooleSet>one)._pbset elif isinstance(one, BooleanPolynomial): o = (<BooleanPolynomial>one)._pbpoly.set() else: - raise TypeError("Argument 'one' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(one)) + raise TypeError("argument 'one' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(one)) return new_BP_from_PBPoly(zero.ring(), pb_interpolate_smallest_lex(z, o)) @@ -7400,7 +7391,7 @@ def ll_red_nf_redsb(p, BooleSet reductors): t = PBBoolePolynomial((<BooleanMonomial>p)._pbmonom) parent = (<BooleanMonomial>p)._ring else: - raise TypeError("Argument 'p' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(p)) + raise TypeError("argument 'p' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(p)) res = pb_ll_red_nf(t, reductors._pbset) @@ -7513,7 +7504,7 @@ def if_then_else(root, a, b): sage: if_then_else(x5, f0, f1) Traceback (most recent call last): ... - IndexError: index of root must be less than the values of roots of the branches. + IndexError: index of root must be less than the values of roots of the branches """ cdef PBSet a_set, b_set cdef PBSet res @@ -7525,14 +7516,14 @@ def if_then_else(root, a, b): b_set = (<BooleanPolynomial>b)._pbpoly.set() ring = (<BooleanPolynomial>b)._parent else: - raise TypeError("Argument 'b' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(b)) + raise TypeError("argument 'b' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(b)) if isinstance(a, BooleSet): a_set = (<BooleSet>a)._pbset elif isinstance(a, BooleanPolynomial): a_set = (<BooleanPolynomial>a)._pbpoly.set() else: - raise TypeError("Argument 'a' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(a)) + raise TypeError("argument 'a' has incorrect type (expected BooleSet or BooleanPolynomial, got %s)" % type(a)) try: root = int(root) @@ -7541,22 +7532,22 @@ def if_then_else(root, a, b): if len(root) == 1: root = root.lm() else: - raise TypeError("Only variables are acceptable as root.") + raise TypeError("only variables are acceptable as root") if isinstance(root, BooleanMonomial): if len(root) == 1: root = root.index() else: - raise TypeError("Only variables are acceptable as root.") + raise TypeError("only variables are acceptable as root") if not isinstance(root, int): - raise TypeError("Only variables are acceptable as root.") + raise TypeError("only variables are acceptable as root") cdef Py_ssize_t* pbind = ring.pbind root = ring.pbind[root] if (root >= a_set.navigation().value()) or (root >= b_set.navigation().value()): raise IndexError("index of root must be less than " - "the values of roots of the branches.") + "the values of roots of the branches") res = PBBooleSet(root, a_set.navigation(), b_set.navigation(), ring._pbring) @@ -7590,7 +7581,7 @@ def top_index(s): elif isinstance(s, BooleanPolynomial): idx = (<BooleanPolynomial>s)._pbpoly.navigation().value() else: - raise TypeError("Argument 's' has incorrect type (expected BooleSet, BooleanMonomial or BooleanPolynomial, got %s)" % type(s)) + raise TypeError("argument 's' has incorrect type (expected BooleSet, BooleanMonomial or BooleanPolynomial, got %s)" % type(s)) return (<BooleanPolynomialRing>s.ring()).pbind[idx] @@ -8010,19 +8001,16 @@ cdef class VariableFactory: a sage: VariableFactory(B)(0) a - """ if ring is None and self._ring is not None: return new_BM_from_PBVar(self._ring._monom_monoid, self._ring, self._factory(<long>arg)) - elif isinstance(arg, BooleanPolynomialRing): + if isinstance(arg, BooleanPolynomialRing): return arg.variable(0) - elif isinstance(ring, BooleanPolynomialRing): + if isinstance(ring, BooleanPolynomialRing): return (<BooleanPolynomialRing>ring).variable(arg) - else: - raise TypeError( - "Cannot convert (%s, %s) to Boolean Variable" % - (type(arg), type(ring))) + raise TypeError("cannot convert (%s, %s) to Boolean Variable" % + (type(arg), type(ring))) cdef class MonomialFactory: @@ -8097,7 +8085,7 @@ cdef class MonomialFactory: return result except Exception: raise TypeError( - "Cannot %s convert to Boolean Monomial" % type(arg)) + "cannot %s convert to Boolean Monomial" % type(arg)) cdef class PolynomialFactory: @@ -8174,5 +8162,5 @@ cdef class PolynomialFactory: return new_BP_from_PBPoly(self._ring, self._factory((<BooleanMonomial>arg)._pbmonom)) - raise TypeError("Cannot convert %s to BooleanPolynomial" % + raise TypeError("cannot convert %s to BooleanPolynomial" % type(arg)) diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 92e70f25343..d019a89d99e 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -1,5 +1,5 @@ r""" -Noncommutative Polynomials via libSINGULAR/Plural +Noncommutative polynomials via libSINGULAR/Plural This module provides specialized and optimized implementations for noncommutative multivariate polynomials over many coefficient rings, via the @@ -250,10 +250,9 @@ cdef class NCPolynomialRing_plural(Ring): sage: H.<x,y,z> = A.g_algebra({z*x:x*z+2*x, z*y:y*z-2*y}) sage: x*y == y*x True - """ def __init__(self, base_ring, names, c, d, order, category, check=True): - """ + r""" Construct a noncommutative polynomial G-algebra subject to the following conditions: INPUT: @@ -570,19 +569,10 @@ cdef class NCPolynomialRing_plural(Ring): _n = sa2si(base_ring(element), _ring) _p = p_NSet(_n, _ring) - # and longs - elif isinstance(element, long): - if isinstance(base_ring, FiniteField_prime_modn): - element = element % self.base_ring().characteristic() - _p = p_ISet(int(element),_ring) - else: - _n = sa2si(base_ring(element),_ring) - _p = p_NSet(_n, _ring) - else: - raise NotImplementedError("not able to interpret "+repr(element) + - " of type "+ repr(type(element)) + - " as noncommutative polynomial") ### ?????? + raise NotImplementedError(f"not able to interpret {element}" + f" of type {type(element)}" + " as noncommutative polynomial") # ??? return new_NCP(self, _p) cpdef _coerce_map_from_(self, S): @@ -1629,10 +1619,10 @@ cdef class NCPolynomial_plural(RingElement): sage: P = A.g_algebra(relations={y*x:-x*y + z}, order='lex') sage: P.inject_variables() Defining x, z, y - sage: (x^2^15) * x^2^15 + sage: (x^2^31) * x^2^31 Traceback (most recent call last): ... - OverflowError: exponent overflow (65536) + OverflowError: exponent overflow (2147483648) """ # all currently implemented rings are commutative cdef poly *_p @@ -1699,10 +1689,10 @@ cdef class NCPolynomial_plural(RingElement): sage: P = A.g_algebra(relations={y*x:-x*y + z}, order='lex') sage: P.inject_variables() Defining x, z, y - sage: (x+y^2^15)^10 + sage: (x+y^2^31)^10 Traceback (most recent call last): .... - OverflowError: exponent overflow (327680) + OverflowError: exponent overflow (2147483648) """ if type(exp) is not Integer: try: @@ -2190,8 +2180,8 @@ cdef class NCPolynomial_plural(RingElement): cdef poly *m = mon._poly cdef ring *r = (<NCPolynomialRing_plural>self._parent)._ring - if not mon._parent is self._parent: - raise TypeError("mon must have same parent as self.") + if mon._parent is not self._parent: + raise TypeError("mon must have same parent as self") while(p): if p_ExpVectorEqual(p, m, r) == 1: diff --git a/src/sage/rings/polynomial/polynomial_compiled.pyx b/src/sage/rings/polynomial/polynomial_compiled.pyx index 3f01b3c8731..83f33a3a4d2 100644 --- a/src/sage/rings/polynomial/polynomial_compiled.pyx +++ b/src/sage/rings/polynomial/polynomial_compiled.pyx @@ -509,4 +509,3 @@ cdef class abc_pd(binary_pd): self.value = self.left.value * self.right.value + coeffs[self.index] pd_clean(self.left) pd_clean(self.right) - diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx index ed4617dd951..5fc4ff1f6af 100644 --- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx +++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx @@ -717,6 +717,44 @@ cdef class Polynomial_complex_arb(Polynomial): sig_off() return res + def _lgamma_series(self, long n): + r""" + Return the series expansion of the log-gamma function composed + with this polynomial, truncated before degree ``n``. + + EXAMPLES:: + + sage: Pol.<x> = CBF[] + sage: (1000 + x)._lgamma_series(3) + ([0.00050025008333331...])*x^2 + ([6.9072551956488...])*x + [5905.2204232091...] + """ + cdef Polynomial_complex_arb res = self._new() + if n < 0: + n = 0 + sig_on() + acb_poly_lgamma_series(res.__poly, self.__poly, n, prec(self)) + sig_off() + return res + + def _rgamma_series(self, long n): + r""" + Return the series expansion of the reciprocal gamma function composed + with this polynomial, truncated before degree ``n``. + + EXAMPLES:: + + sage: Pol.<x> = CBF[] + sage: (-1 + x)._rgamma_series(4) + ([1.23309373642178...])*x^3 + ([0.422784335098467...])*x^2 - x + """ + cdef Polynomial_complex_arb res = self._new() + if n < 0: + n = 0 + sig_on() + acb_poly_rgamma_series(res.__poly, self.__poly, n, prec(self)) + sig_off() + return res + def _lambert_w_series(self, long n, branch=0): r""" Return the series expansion of the specified branch of the Lambert W diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7f781c65060..09807019d6c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1,45 +1,50 @@ # coding: utf-8 """ -Univariate Polynomial Base Class +Univariate polynomial base class + +TESTS:: + + sage: R.<x> = ZZ[] + sage: f = x^5 + 2*x^2 + (-1) + sage: f == loads(dumps(f)) + True + + sage: PolynomialRing(ZZ,'x').objgen() + (Univariate Polynomial Ring in x over Integer Ring, x) AUTHORS: -- William Stein: first version. +- William Stein: first version -- Martin Albrecht: Added singular coercion. +- Martin Albrecht: added singular coercion -- Robert Bradshaw: Move Polynomial_generic_dense to Cython. +- Robert Bradshaw: moved Polynomial_generic_dense to Cython -- Miguel Marco: Implemented resultant in the case where PARI fails. +- Miguel Marco: implemented resultant in the case where PARI fails -- Simon King: Use a faster way of conversion from the base ring. +- Simon King: used a faster way of conversion from the base ring -- Julian Rueth (2012-05-25,2014-05-09): Fixed is_squarefree() for imperfect - fields, fixed division without remainder over QQbar; added ``_cache_key`` - for polynomials with unhashable coefficients +- Kwankyu Lee (2013-06-02): enhanced :meth:`quo_rem` -- Simon King (2013-10): Implement copying of :class:`PolynomialBaseringInjection`. +- Julian Rueth (2012-05-25,2014-05-09): fixed is_squarefree() for imperfect + fields, fixed division without remainder over QQbar; added ``_cache_key`` + for polynomials with unhashable coefficients -- Kiran Kedlaya (2016-03): Added root counting. +- Simon King (2013-10): implemented copying of :class:`PolynomialBaseringInjection` -- Edgar Costa (2017-07): Added rational reconstruction. +- Bruno Grenet (2014-07-13): enhanced :meth:`quo_rem` -- Kiran Kedlaya (2017-09): Added reciprocal transform, trace polynomial. +- Kiran Kedlaya (2016-03): added root counting -- David Zureick-Brown (2017-09): Added is_weil_polynomial. +- Edgar Costa (2017-07): added rational reconstruction -- Sebastian Oehms (2018-10): made :meth:`roots` and :meth:`factor` work over more - cases of proper integral domains (see :trac:`26421`) +- Kiran Kedlaya (2017-09): added reciprocal transform, trace polynomial -TESTS:: +- David Zureick-Brown (2017-09): added is_weil_polynomial - sage: R.<x> = ZZ[] - sage: f = x^5 + 2*x^2 + (-1) - sage: f == loads(dumps(f)) - True +- Sebastian Oehms (2018-10): made :meth:`roots` and :meth:`factor` work over more + cases of proper integral domains (see :trac:`26421`) - sage: PolynomialRing(ZZ,'x').objgen() - (Univariate Polynomial Ring in x over Integer Ring, x) """ # **************************************************************************** @@ -81,7 +86,6 @@ from sage.structure.factorization import Factorization from sage.structure.richcmp cimport (richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn) -from sage.interfaces.singular import singular as singular_default, is_SingularElement from sage.libs.pari.all import pari, pari_gen, PariError cimport sage.rings.abc @@ -94,6 +98,8 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF import sage.rings.abc +import sage.interfaces.abc + from sage.structure.coerce cimport coercion_model from sage.structure.element import coerce_binop from sage.structure.element cimport (parent, have_same_parent, @@ -110,8 +116,8 @@ from sage.structure.category_object cimport normalize_names from sage.misc.derivative import multi_derivative -from sage.arith.all import (sort_complex_numbers_for_display, - power_mod, lcm, is_prime) +from sage.arith.misc import sort_complex_numbers_for_display, power_mod, is_prime +from sage.arith.functions import lcm from . import polynomial_fateman @@ -2744,9 +2750,9 @@ cdef class Polynomial(CommutativeAlgebraElement): var = "" s += "%s %s" % (x, var) s = s.replace(" + -", " - ") - s = re.sub(" 1(\.0+)? \|"," ", s) - s = re.sub(" -1(\.0+)? \|", " -", s) - s = s.replace("|","") + s = re.sub(r" 1(\.0+)? \|", " ", s) + s = re.sub(r" -1(\.0+)? \|", " -", s) + s = s.replace("|", "") if s == " ": return "0" return s[1:].lstrip().rstrip() @@ -2845,7 +2851,7 @@ cdef class Polynomial(CommutativeAlgebraElement): raise IndexError("polynomials are immutable") cpdef _floordiv_(self, right): - """ + r""" Quotient of division of self by other. This is denoted //. If self = quotient \* right + remainder, this function returns @@ -4954,9 +4960,10 @@ cdef class Polynomial(CommutativeAlgebraElement): return ~(q.leading_coefficient())*q # make monic (~ is inverse in python) def is_primitive(self, n=None, n_prime_divs=None): - """ - Return ``True`` if the polynomial is primitive. The semantics of - "primitive" depend on the polynomial coefficients. + r""" + Return ``True`` if the polynomial is primitive. + + The semantics of "primitive" depend on the polynomial coefficients. - (field theory) A polynomial of degree `m` over a finite field `\GF{q}` is primitive if it is irreducible and its root in @@ -7210,7 +7217,7 @@ cdef class Polynomial(CommutativeAlgebraElement): return x - c if k > n - k: # use (n-k)'th symmetric power g = self.symmetric_power(n - k, monic=monic) - from sage.arith.all import binomial + from sage.arith.misc import binomial g = ((-x)**binomial(n,k) * g(c/x) / c**binomial(n-1,k)).numerator() if monic: g = g.monic() @@ -11336,7 +11343,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: class BrokenRational(Rational): ....: def __bool__(self): ....: raise NotImplementedError("cannot check whether number is non-zero") - ....: + ....: sage: z = BrokenRational() sage: R.<x> = QQ[] sage: from sage.rings.polynomial.polynomial_element import Polynomial_generic_dense @@ -11636,19 +11643,13 @@ cdef class Polynomial_generic_dense(Polynomial): Raises a ``ZerodivisionError`` if ``other`` is zero. Raises an ``ArithmeticError`` if the division is not exact. - AUTHORS: - - - Kwankyu Lee (2013-06-02) - - - Bruno Grenet (2014-07-13) - EXAMPLES:: sage: P.<x> = QQ[] sage: R.<y> = P[] - sage: f = R.random_element(10) - sage: g = y^5+R.random_element(4) - sage: q,r = f.quo_rem(g) + sage: f = y^10 + R.random_element(9) + sage: g = y^5 + R.random_element(4) + sage: q, r = f.quo_rem(g) sage: f == q*g + r True sage: g = x*y^5 @@ -11662,6 +11663,17 @@ cdef class Polynomial_generic_dense(Polynomial): ... ZeroDivisionError: division by zero polynomial + Polynomials over noncommutative rings are also allowed + (after :trac:`34733`):: + + sage: HH = QuaternionAlgebra(QQ, -1, -1) + sage: P.<x> = HH[] + sage: f = P.random_element(5) + sage: g = P.random_element((0, 5)) + sage: q, r = f.quo_rem(g) + sage: f == q*g + r + True + TESTS: The following shows that :trac:`16649` is indeed fixed. :: @@ -11674,7 +11686,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: h.quo_rem(f) ((-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1, 0) sage: h += (2/3*x^2-3*x+1)*y + 7/17*x+6/5 - sage: q,r = h.quo_rem(f) + sage: q, r = h.quo_rem(f) sage: h == q*f + r and r.degree() < f.degree() True @@ -11709,7 +11721,7 @@ cdef class Polynomial_generic_dense(Polynomial): convert = True if convert: for k from m-n >= k >= 0: - q = inv * x[n+k-1] + q = x[n+k-1] * inv try: q = R(q) except TypeError: @@ -11719,7 +11731,7 @@ cdef class Polynomial_generic_dense(Polynomial): quo.append(q) else: for k from m-n >= k >= 0: - q = inv * x[n+k-1] + q = x[n+k-1] * inv for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] quo.append(q) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 38586320dcc..64fcf65b210 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -36,7 +36,7 @@ from sage.libs.pari.all import pari_gen from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn -from sage.structure.element import coerce_binop +from sage.structure.element import coerce_binop, parent from sage.rings.infinity import infinity, Infinity from sage.rings.integer_ring import ZZ @@ -543,6 +543,29 @@ def degree(self, gen=None): return -1 return max(self.__coeffs) + def __floordiv__(self, right): + """ + Return the quotient upon division (no remainder). + + EXAMPLES:: + + sage: R.<x> = PolynomialRing(QQbar, sparse=True) + sage: f = (1+2*x)^3 + 3*x; f + 8*x^3 + 12*x^2 + 9*x + 1 + sage: g = f // (1+2*x); g + 4*x^2 + 4*x + 5/2 + sage: f - g * (1+2*x) + -3/2 + sage: f.quo_rem(1+2*x) + (4*x^2 + 4*x + 5/2, -3/2) + + """ + P = self.parent() + if P is parent(right): + return self._floordiv_(right) + d = P.base_ring()(right) + return self.map_coefficients(lambda c: c // d) + def _add_(self, right): r""" EXAMPLES:: @@ -801,6 +824,16 @@ def quo_rem(self, other): sage: f.quo_rem(g) (-y^5 + 2*y^2, y^3 - 2*x^2*y^2 - y) + Polynomials over noncommutative rings are also allowed:: + + sage: HH = QuaternionAlgebra(QQ, -1, -1) + sage: P.<x> = PolynomialRing(HH, sparse=True) + sage: f = P.random_element(5) + sage: g = P.random_element((0, 5)) + sage: q, r = f.quo_rem(g) + sage: f == q*g + r + True + TESTS:: sage: P.<x> = PolynomialRing(ZZ, sparse=True) diff --git a/src/sage/rings/polynomial/polynomial_fateman.py b/src/sage/rings/polynomial/polynomial_fateman.py index 3b3246f2d47..8ccf13eba09 100644 --- a/src/sage/rings/polynomial/polynomial_fateman.py +++ b/src/sage/rings/polynomial/polynomial_fateman.py @@ -1,5 +1,4 @@ "Polynomial multiplication by Kronecker substitution" - ################################################################################ # Copyright (C) 2007 William Stein <wstein@gmail.com> # @@ -7,7 +6,6 @@ # # https://www.gnu.org/licenses/ ################################################################################ - from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ @@ -19,49 +17,51 @@ def _mul_fateman_to_int2(f_list, g_list): """ Convert a polynomial to an integer by evaluating it + INPUT: p, a list of integers + OUTPUT: padding """ max_coeff_f = max([abs(i) for i in f_list]) max_coeff_g = max([abs(i) for i in g_list]) - b = (1+min(len(f_list),len(g_list)))*max_coeff_f*max_coeff_g + b = (1 + min(len(f_list), len(g_list))) * max_coeff_f * max_coeff_g return int(pyceil(pylog(b, 2))) -def _mul_fateman_to_poly(number,padding): +def _mul_fateman_to_poly(number, padding): """ - Converts a number to a polynomial, according - to a padding - OUTPUT: a list containing the coefficient of - a polynomial of degree len(list) + Converts a number to a polynomial, according to a padding. + OUTPUT: + + a list containing the coefficient of a polynomial of degree len(list) """ coeffs = [] - flag=0 + flag = 0 append = coeffs.append if number < 0: number = -number - flag=1 + flag = 1 while number > 0: - r = number%(1<<padding) - number = (number-r) >> padding - if r > (1<<(padding-1)): - r -= 1<<padding - number+=1 + r = number % (1 << padding) + number = (number - r) >> padding + if r > (1 << (padding - 1)): + r -= 1 << padding + number += 1 append(r) - if flag==1: + if flag == 1: return [-c for c in coeffs] return coeffs -def _mul_fateman_mul(f,g): + +def _mul_fateman_mul(f, g): """ Multiply 2 polynomials """ - - f=f.change_ring(QQ) - g=g.change_ring(QQ) + f = f.change_ring(QQ) + g = g.change_ring(QQ) f_list = f.list() g_list = g.list() @@ -76,21 +76,20 @@ def _mul_fateman_mul(f,g): ggcd = g_list[0].content(g_list) # Need to change ring to ZZ - z_poly_f=(f*fgcd.denominator()).change_ring(ZZ) - z_poly_g=(g*ggcd.denominator()).change_ring(ZZ) + z_poly_f = (f * fgcd.denominator()).change_ring(ZZ) + z_poly_g = (g * ggcd.denominator()).change_ring(ZZ) - div = 1/(fgcd.denominator()*ggcd.denominator()) + div = 1 / (fgcd.denominator() * ggcd.denominator()) z_poly_f_list = z_poly_f.coefficients(sparse=False) z_poly_g_list = z_poly_g.coefficients(sparse=False) - padding = _mul_fateman_to_int2(z_poly_f_list,z_poly_g_list) + padding = _mul_fateman_to_int2(z_poly_f_list, z_poly_g_list) - n_f = z_poly_f(1<<padding) - n_g = z_poly_g(1<<padding) + n_f = z_poly_f(1 << padding) + n_g = z_poly_g(1 << padding) if div == 1: - return _mul_fateman_to_poly(n_f*n_g,padding) - #return to_poly(n_f*n_g,padding) + return _mul_fateman_to_poly(n_f * n_g, padding) else: - l=_mul_fateman_to_poly(n_f*n_g,padding) - return [QQ(i*div) for i in l] + l = _mul_fateman_to_poly(n_f * n_g, padding) + return [QQ(i * div) for i in l] diff --git a/src/sage/rings/polynomial/polynomial_gf2x.pyx b/src/sage/rings/polynomial/polynomial_gf2x.pyx index 3c90a42e033..284e319be99 100644 --- a/src/sage/rings/polynomial/polynomial_gf2x.pyx +++ b/src/sage/rings/polynomial/polynomial_gf2x.pyx @@ -100,14 +100,14 @@ cdef class Polynomial_GF2X(Polynomial_template): sage: pari(f) Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(1, 2) """ - #TODO: put this in a superclass + # TODO: put this in a superclass parent = self._parent if variable is None: variable = parent.variable_name() return pari(self.list()).Polrev(variable) * pari(1).Mod(2) def modular_composition(Polynomial_GF2X self, Polynomial_GF2X g, Polynomial_GF2X h, algorithm=None): - """ + r""" Compute `f(g) \pmod h`. Both implementations use Brent-Kung's Algorithm 2.1 (*Fast Algorithms diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 38a8324fde1..1d5a863e899 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Dense univariate polynomials over `\ZZ`, implemented using FLINT AUTHORS: @@ -59,7 +59,7 @@ from sage.libs.pari.all import pari, pari_gen from sage.structure.factorization import Factorization from sage.rings.fraction_field_element import FractionFieldElement -from sage.arith.all import lcm +from sage.arith.functions import lcm from sage.libs.arb.arb_fmpz_poly cimport arb_fmpz_poly_evaluate_arb, arb_fmpz_poly_evaluate_acb from sage.libs.flint.fmpz cimport * @@ -367,7 +367,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): interpreted as polynomial composition or evaluation by this method. - If the argument is not simply an integer (``int``, ``long`` or + If the argument is not simply an integer (``int`` or ``Integer``) a real number (``RealNumber``) a real interval (``RealIntervalFieldElement``) or a polynomial (of the same type as ``self``), the call is passed on to the generic implementation in the @@ -428,7 +428,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_clear(z_fmpz) sig_off() return z - if isinstance(x0, (int, long)): + if isinstance(x0, int): x0 = Integer(x0) if isinstance(x0, Integer): a = <Integer> x0 @@ -1394,7 +1394,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): return smallInteger(fmpz_poly_degree(self.__poly)) def pseudo_divrem(self, B): - """ + r""" Write ``A = self``. This function computes polynomials `Q` and `R` and an integer `d` such that diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 41ddaa92e51..c2db35e0bf7 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -67,7 +67,7 @@ from sage.structure.factorization import Factorization from sage.structure.element import coerce_binop from sage.rings.fraction_field_element import FractionFieldElement -from sage.arith.all import lcm +from sage.arith.functions import lcm import sage.rings.polynomial.polynomial_ring from sage.libs.ntl.ZZX cimport * diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 643ef81027a..2b55a681a32 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Dense univariate polynomials over `\ZZ/n\ZZ`, implemented using NTL This implementation is generally slower than the FLINT implementation in @@ -22,16 +22,15 @@ AUTHORS: - Robert Bradshaw: Split off from polynomial_element_generic.py (2007-09) - Robert Bradshaw: Major rewrite to use NTL directly (2007-09) """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein <wstein@gmail.com> # # 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 cysignals.memory cimport sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 93e4a741925..ab35434ca30 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -235,7 +235,8 @@ def create_object(self, version, key): ring, polynomial, names = key R = ring.base_ring() - from sage.categories.all import IntegralDomains, Fields + from sage.categories.integral_domains import IntegralDomains + from sage.categories.fields import Fields if R in IntegralDomains(): try: is_irreducible = polynomial.is_irreducible() @@ -256,7 +257,7 @@ def is_PolynomialQuotientRing(x): return isinstance(x, PolynomialQuotientRing_generic) -class PolynomialQuotientRing_generic(CommutativeRing): +class PolynomialQuotientRing_generic(QuotientRing_generic): """ Quotient of a univariate polynomial ring by an ideal. @@ -418,7 +419,9 @@ def __init__(self, ring, polynomial, name=None, category=None): # Note that is_finite() is cheap so it does not seem to do a lazy # _refine_category_() in is_finite() as we do for is_field() category = category.Finite() - CommutativeRing.__init__(self, ring, names=name, category=category) + + QuotientRing_generic.__init__(self, ring, ring.ideal(polynomial), names=name, category=category) + self._base = ring # backwards compatibility -- different from QuotientRing_generic _ideal_class_ = QuotientRing_generic._ideal_class_ @@ -699,7 +702,7 @@ def __ne__(self, other): sage: Qz != Qx False """ - return not (self == other) + return not (self == other) def __hash__(self): """ @@ -780,11 +783,11 @@ def construction(self): -- Simon King (2010-05) """ from sage.categories.pushout import QuotientFunctor - Cover = self.base() + Cover = self.__ring + kwds = {} if Cover in CommutativeRings(): - return QuotientFunctor([self.modulus()]*Cover, self.variable_names(), - domain=CommutativeRings(), codomain=CommutativeRings()), Cover - return QuotientFunctor([self.modulus()]*self.base(), self.variable_names()), Cover + kwds['domain'] = kwds['codomain'] = CommutativeRings() + return QuotientFunctor([self.modulus()]*Cover, self.variable_names(), **kwds), Cover @cached_method def base_ring(self): @@ -1054,7 +1057,7 @@ def is_field(self, proof = True): ret = False if ret: - from sage.categories.all import Fields + from sage.categories.fields import Fields self._refine_category_(Fields()) return ret @@ -1110,7 +1113,7 @@ def is_integral_domain(self, proof = True): Unfortunately, the program above is already unable to determine that the modulus is irreducible. """ - from sage.categories.all import IntegralDomains + from sage.categories.integral_domains import IntegralDomains if self.category().is_subcategory(IntegralDomains()): return True ret = self.base_ring().is_integral_domain(proof) @@ -1792,7 +1795,7 @@ def selmer_generators(self, S, m, proof=True): sage: D.selmer_generators([K.ideal(2, -a+1), K.ideal(3, a+1)], 3) [2, a + 1] sage: D.selmer_generators([K.ideal(2, -a+1), K.ideal(3, a+1), K.ideal(a)], 3) - [2, a + 1, a] + [2, a + 1, -a] """ fields, isos, iso_classes = self._S_decomposition(tuple(S)) @@ -1975,7 +1978,7 @@ def _isomorphic_ring(self): # interface which we cannot provide (e.g. NumberFields). # So we just check some important special cases here (note that # integral domains is already handled elsewhere.) - from sage.categories.all import Fields + from sage.categories.fields import Fields if isomorphic_ring in Fields(): self._refine_category_(Fields()) @@ -2021,7 +2024,7 @@ def _isomorphic_ring(self): return from_isomorphic_ring, to_isomorphic_ring, isomorphic_ring - from sage.categories.all import NumberFields + from sage.categories.number_fields import NumberFields if self.base_ring() in NumberFields(): try: isomorphic_ring = self.base_ring().extension(self.modulus(), names=self.variable_names()) @@ -2064,7 +2067,8 @@ def _test_isomorphic_ring(self, **options): tester.assertNotIsInstance(ring, PolynomialQuotientRing_generic) - from sage.categories.all import Fields, IntegralDomains + from sage.categories.fields import Fields + from sage.categories.integral_domains import IntegralDomains if ring.category().is_subcategory(IntegralDomains()): category = IntegralDomains() if ring.category().is_subcategory(Fields()): diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 803e78f6e13..be05a312725 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -683,8 +683,54 @@ def matrix(self): def minpoly(self): """ The minimal polynomial of this element, which is by definition the - minimal polynomial of right multiplication by this element. + minimal polynomial of the :meth:`matrix` of this element. + + ALGORITHM: Use + :meth:`~sage.rings.polynomial.polynomial_zz_pex.Polynomial_ZZ_pEX.minpoly_mod` + if possible, otherwise compute the minimal polynomial of the :meth:`matrix`. + + EXAMPLES:: + + sage: R.<x> = PolynomialRing(QQ) + sage: S.<a> = R.quotient(x^3 + 2*x - 5) + sage: (a+123).minpoly() + x^3 - 369*x^2 + 45389*x - 1861118 + sage: (a+123).matrix().minpoly() + x^3 - 369*x^2 + 45389*x - 1861118 + + One useful application of this function is to compute a minimal + polynomial of a finite-field element over an intermediate extension, + rather than the absolute minimal polynomial over the prime field:: + + sage: F2.<i> = GF((431,2), modulus=[1,0,1]) + sage: F6.<u> = F2.extension(3) + sage: (u+1).minpoly() + x^6 + 425*x^5 + 19*x^4 + 125*x^3 + 189*x^2 + 239*x + 302 + sage: ext = F6.over(F2) + sage: ext(u+1).minpoly() # indirect doctest + x^3 + (396*i + 428)*x^2 + (80*i + 39)*x + 9*i + 178 + + TESTS: + + We make sure that the previous example works on random examples:: + + sage: p = random_prime(50) + sage: K.<u> = GF((p, randrange(1,20))) + sage: L.<v> = K.extension(randrange(2,20)) + sage: LK = L.over(K) + sage: a = L.random_element() + sage: poly = LK(a).minpoly() # indirect doctest + sage: poly(a) + 0 + sage: abs_deg = a.minpoly().degree() + sage: poly.degree() == abs_deg // gcd(abs_deg, K.degree()) + True """ + poly = self.lift() + try: + return poly.minpoly_mod(self.parent().modulus()) + except AttributeError: + pass return self.matrix().minpoly() def norm(self): @@ -737,4 +783,3 @@ def rational_reconstruction(self, *args, **kwargs): R = m.parent() f = R(self._polynomial) return f.rational_reconstruction(m, *args, **kwargs) - diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 88cccdfb537..ea0469386dc 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2513,25 +2513,25 @@ cdef class Polynomial_rational_flint(Polynomial): def galois_group_davenport_smith_test(self, num_trials=50, assume_irreducible=False): """ Use the Davenport-Smith test to attempt to certify that `f` has Galois group A_n or S_n. - + Return 1 if the Galois group is certified as S_n, 2 if A_n, or 0 if no conclusion is reached. - + By default, we first check that `f` is irreducible. For extra efficiency, one can override this by specifying `assume_irreducible=True`; this yields undefined results if `f` is not irreducible. - + A corresponding function in Magma is `IsEasySnAn`. EXAMPLES:: sage: P.<x> = QQ[] sage: u = x^7 + x + 1 - sage: u.galois_group_davenport_smith_test() + sage: u.galois_group_davenport_smith_test() 1 - sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 - sage: u.galois_group_davenport_smith_test() + sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 + sage: u.galois_group_davenport_smith_test() 2 sage: u = x^7 - 2 - sage: u.galois_group_davenport_smith_test() + sage: u.galois_group_davenport_smith_test() 0 """ diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index d72a8076848..8fa744ff668 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -778,4 +778,3 @@ def make_PolynomialRealDense(parent, data): 3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000 """ return PolynomialRealDense(parent, data) - diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 34f25975963..bd34d113f5f 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -177,7 +177,7 @@ from . import cyclotomic -from sage.interfaces.singular import SingularElement +import sage.interfaces.abc def is_PolynomialRing(x): @@ -435,13 +435,13 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, elif P == self.base_ring(): return C(self, [x], check=True, is_gen=False, construct=construct) - if isinstance(x, SingularElement) and self._has_singular: + if isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: self._singular_().set_ring() try: return x.sage_poly(self) except Exception: raise TypeError("Unable to coerce singular object") - elif isinstance(x , str): + elif isinstance(x, str): try: from sage.misc.parser import Parser, LookupNameMaker R = self.base_ring() @@ -619,6 +619,9 @@ def flattening_morphism(self): return IdentityMorphism(self) def construction(self): + """ + Return the construction functor. + """ return categories.pushout.PolynomialFunctor(self.variable_name(), sparse=self.__is_sparse), self.base_ring() def completion(self, p, prec=20, extras=None): @@ -1951,6 +1954,38 @@ def _repr_(self): s = PolynomialRing_commutative._repr_(self) return s + self._implementation_repr + def construction(self): + """ + Return the construction functor. + + EXAMPLES:: + + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing + sage: R = PRing(ZZ, 'x'); R + Univariate Polynomial Ring in x over Integer Ring + sage: functor, arg = R.construction(); functor, arg + (Poly[x], Integer Ring) + sage: functor.implementation is None + True + + sage: R = PRing(ZZ, 'x', implementation='NTL'); R + Univariate Polynomial Ring in x over Integer Ring (using NTL) + sage: functor, arg = R.construction(); functor, arg + (Poly[x], Integer Ring) + sage: functor.implementation + 'NTL' + """ + implementation = None + # NOTE: This is obviously not a complete solution. The parents + # don't keep track in a clean way what the implementation is. + # Trac #31852 is the task of finding a general solution for + # construction functors of parents with multiple + # implementations, such as MatrixSpace, Polyhedron, and + # PolynomialRing. + if 'NTL' in self._implementation_repr: + implementation = 'NTL' + return categories.pushout.PolynomialFunctor(self.variable_name(), sparse=self.is_sparse(), + implementation=implementation), self.base_ring() class PolynomialRing_field(PolynomialRing_integral_domain, ring.PrincipalIdealDomain): diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index c07c93adb38..65e67cc3527 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -11,18 +11,15 @@ rings but rather quotients of them (see module :mod:`sage.rings.polynomial.pbori` for more details). """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # # 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.category_object import normalize_names import sage.rings.ring as ring import sage.rings.padics.padic_base_leaves as padic_base_leaves @@ -32,16 +29,19 @@ from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.misc.cachefunc import weak_cached_function +import sage.misc.weak_dict from sage.categories.fields import Fields -_Fields = Fields() from sage.categories.commutative_rings import CommutativeRings -_CommutativeRings = CommutativeRings() +from sage.categories.domains import Domains from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields + +_CommutativeRings = CommutativeRings() +_Fields = Fields() +_Domains = Domains() _CompleteDiscreteValuationRings = CompleteDiscreteValuationRings() _CompleteDiscreteValuationFields = CompleteDiscreteValuationFields() -import sage.misc.weak_dict _cache = sage.misc.weak_dict.WeakValueDictionary() @@ -418,22 +418,22 @@ def PolynomialRing(base_ring, *args, **kwds): The generic implementation is different in some cases:: - sage: R = PolynomialRing(GF(2), 'j', implementation="generic"); type(R) + sage: R = PolynomialRing(GF(2), 'j', implementation="generic"); TestSuite(R).run(skip=['_test_construction', '_test_pickling']); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> - sage: S = PolynomialRing(GF(2), 'j'); type(S) + sage: S = PolynomialRing(GF(2), 'j'); TestSuite(S).run(); type(S) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_dense_mod_p_with_category'> - sage: R = PolynomialRing(ZZ, 'x,y', implementation="generic"); type(R) + sage: R = PolynomialRing(ZZ, 'x,y', implementation="generic"); TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']); type(R) <class 'sage.rings.polynomial.multi_polynomial_ring.MPolynomialRing_polydict_domain_with_category'> - sage: S = PolynomialRing(ZZ, 'x,y'); type(S) + sage: S = PolynomialRing(ZZ, 'x,y'); TestSuite(S).run(skip='_test_elements'); type(S) <class 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'> Sparse univariate polynomials only support a generic implementation:: - sage: R = PolynomialRing(ZZ, 'j', sparse=True); type(R) + sage: R = PolynomialRing(ZZ, 'j', sparse=True); TestSuite(R).run(); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_integral_domain_with_category'> - sage: R = PolynomialRing(GF(49), 'j', sparse=True); type(R) + sage: R = PolynomialRing(GF(49), 'j', sparse=True); TestSuite(R).run(); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> If the requested implementation is not known or not supported for @@ -487,11 +487,14 @@ def PolynomialRing(base_ring, *args, **kwds): :trac:`7712` and :trac:`13760` are fixed:: sage: P.<y,z> = PolynomialRing(RealIntervalField(2)) + sage: TestSuite(P).run(skip=['_test_elements', '_test_elements_eq_transitive']) sage: Q.<x> = PolynomialRing(P) + sage: TestSuite(Q).run(skip=['_test_additive_associativity', '_test_associativity', '_test_distributivity', '_test_prod']) sage: C = (y-x)^3 sage: C(y/2) 1.?*y^3 sage: R.<x,y> = PolynomialRing(RIF,2) + sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) sage: RIF(-2,1)*x 0.?e1*x @@ -549,6 +552,49 @@ def PolynomialRing(base_ring, *args, **kwds): Traceback (most recent call last): ... TypeError: unable to convert 'x' to an integer + + We run the testsuite for various polynomial rings, skipping tests that currently fail:: + + sage: R.<w> = PolynomialRing(PolynomialRing(GF(7),'k')); TestSuite(R).run(); R + Univariate Polynomial Ring in w over Univariate Polynomial Ring in k over Finite Field of size 7 + sage: ZxNTL = PolynomialRing(ZZ, 'x', implementation='NTL'); TestSuite(ZxNTL).run(skip='_test_pickling'); ZxNTL + Univariate Polynomial Ring in x over Integer Ring (using NTL) + sage: ZxFLINT = PolynomialRing(ZZ, 'x', implementation='FLINT'); TestSuite(ZxFLINT).run(); ZxFLINT + Univariate Polynomial Ring in x over Integer Ring + sage: Zx = PolynomialRing(ZZ, 'x', implementation='generic'); TestSuite(Zx).run(skip=['_test_construction', '_test_pickling']); Zx + Univariate Polynomial Ring in x over Integer Ring + sage: R = PolynomialRing(QQ, 'a,b,c'); TestSuite(R).run(skip='_test_elements'); R + Multivariate Polynomial Ring in a, b, c over Rational Field + sage: R = PolynomialRing(QQ, 'x,y,z', order='degrevlex'); TestSuite(R).run(skip='_test_elements'); R + Multivariate Polynomial Ring in x, y, z over Rational Field + sage: S = PolynomialRing(QQ, 'x,y,z', order='invlex'); TestSuite(S).run(skip=['_test_construction', '_test_elements']); S + Multivariate Polynomial Ring in x, y, z over Rational Field + sage: Q0 = PolynomialRing(QQ,[]); TestSuite(Q0).run(skip=['_test_elements', '_test_elements_eq_transitive', '_test_gcd_vs_xgcd', '_test_quo_rem']); Q0 + Multivariate Polynomial Ring in no variables over Rational Field + sage: P.<x> = PolynomialRing(QQ, implementation="singular"); TestSuite(P).run(skip=['_test_construction', '_test_elements', '_test_euclidean_degree', '_test_quo_rem']); P + Multivariate Polynomial Ring in x over Rational Field + sage: Q1 = PolynomialRing(QQ,"x",1); TestSuite(Q1).run(skip=['_test_construction', '_test_elements', '_test_euclidean_degree', '_test_quo_rem']); Q1 + Multivariate Polynomial Ring in x over Rational Field + sage: Q0 = PolynomialRing(QQ,"x",0); TestSuite(Q0).run(skip=['_test_elements', '_test_elements_eq_transitive', '_test_gcd_vs_xgcd', '_test_quo_rem']); Q0 + Multivariate Polynomial Ring in no variables over Rational Field + sage: R = PolynomialRing(GF(2), 'j', implementation="generic"); TestSuite(R).run(skip=['_test_construction', '_test_pickling']); type(R) + <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> + sage: S = PolynomialRing(GF(2), 'j'); TestSuite(S).run(); type(S) + <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_dense_mod_p_with_category'> + sage: R = PolynomialRing(ZZ, 'x,y', implementation="generic"); TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']); type(R) + <class 'sage.rings.polynomial.multi_polynomial_ring.MPolynomialRing_polydict_domain_with_category'> + sage: S = PolynomialRing(ZZ, 'x,y'); TestSuite(S).run(skip='_test_elements'); type(S) + <class 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'> + sage: R = PolynomialRing(ZZ, 'j', sparse=True); TestSuite(R).run(); type(R) + <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_integral_domain_with_category'> + sage: R = PolynomialRing(GF(49), 'j', sparse=True); TestSuite(R).run(); type(R) + <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> + sage: P.<y,z> = PolynomialRing(RealIntervalField(2)) + sage: TestSuite(P).run(skip=['_test_elements', '_test_elements_eq_transitive']) + sage: Q.<x> = PolynomialRing(P) + sage: TestSuite(Q).run(skip=['_test_additive_associativity', '_test_associativity', '_test_distributivity', '_test_prod']) + sage: R.<x,y> = PolynomialRing(RIF,2) + sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) """ if not ring.is_Ring(base_ring): raise TypeError("base_ring {!r} must be a ring".format(base_ring)) @@ -659,6 +705,7 @@ def unpickle_PolynomialRing(base_ring, arg1=None, arg2=None, sparse=False): args = [arg for arg in (arg1, arg2) if arg is not None] return PolynomialRing(base_ring, *args, sparse=sparse) + from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.rings.polynomial.polynomial_ring_constructor', 'PolynomialRing', unpickle_PolynomialRing) @@ -720,15 +767,15 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non # Generic implementations if constructor is None: - if not isinstance(base_ring, ring.CommutativeRing): + if base_ring not in _CommutativeRings: constructor = polynomial_ring.PolynomialRing_general elif base_ring in _CompleteDiscreteValuationRings: constructor = polynomial_ring.PolynomialRing_cdvr elif base_ring in _CompleteDiscreteValuationFields: constructor = polynomial_ring.PolynomialRing_cdvf - elif base_ring.is_field(proof=False): + elif base_ring in _Fields: constructor = polynomial_ring.PolynomialRing_field - elif base_ring.is_integral_domain(proof=False): + elif base_ring in _Domains: constructor = polynomial_ring.PolynomialRing_integral_domain else: constructor = polynomial_ring.PolynomialRing_commutative @@ -737,8 +784,8 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non # Only use names which are not supported by the specialized class. if specialized is not None: - implementation_names = [n for n in implementation_names if - specialized._implementation_names_impl(n, base_ring, sparse) is NotImplemented] + implementation_names = [n for n in implementation_names + if specialized._implementation_names_impl(n, base_ring, sparse) is NotImplemented] if implementation is not None: kwds["implementation"] = implementation @@ -928,7 +975,6 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): sage: x2 > x3 True """ - if isinstance(n, str): names = n n = -1 diff --git a/src/sage/rings/polynomial/polynomial_singular_interface.py b/src/sage/rings/polynomial/polynomial_singular_interface.py index 40b77534d19..2b5bfd4bf94 100644 --- a/src/sage/rings/polynomial/polynomial_singular_interface.py +++ b/src/sage/rings/polynomial/polynomial_singular_interface.py @@ -115,7 +115,7 @@ def _do_singular_init_(singular, base_ring, char, _vars, order): R = make_ring(f"({char},{gen})") minpoly = str(base_ring.modulus()).replace("x",gen).replace(" ","") - if singular.eval('minpoly') != f"({minpoly})": + if singular.eval('minpoly') != f"({minpoly})": singular.eval(f"minpoly={minpoly}") minpoly = singular.eval('minpoly')[1:-1] @@ -130,7 +130,7 @@ def _do_singular_init_(singular, base_ring, char, _vars, order): R = make_ring(f"({char},{gen})") minpoly = poly_str.replace(" ","") - if singular.eval('minpoly') != f"({minpoly})": + if singular.eval('minpoly') != f"({minpoly})": singular.eval(f"minpoly={minpoly}") minpoly = singular.eval('minpoly')[1:-1] @@ -336,7 +336,7 @@ def _singular_(self, singular=singular): if sage.rings.finite_rings.finite_field_constructor.is_FiniteField(self.base_ring()) or \ (number_field.number_field_base.is_NumberField(self.base_ring()) and self.base_ring().is_absolute()): R.set_ring() # sorry for that, but needed for minpoly - if singular.eval('minpoly') != f"({self.__minpoly})": + if singular.eval('minpoly') != f"({self.__minpoly})": singular.eval(f"minpoly={self.__minpoly}") self.__minpoly = singular.eval('minpoly')[1:-1] return R diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index 001ace2194c..15958758d18 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -1,10 +1,10 @@ -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: include_dirs = NTL_INCDIR # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" Dense univariate polynomials over `\ZZ/n\ZZ`, implemented using FLINT This module gives a fast implementation of `(\ZZ/n\ZZ)[x]` whenever `n` is at @@ -63,13 +63,6 @@ include "sage/libs/flint/nmod_poly_linkage.pxi" # and then the interface include "polynomial_template.pxi" -cdef extern from "zn_poly/zn_poly.h": - ctypedef struct zn_mod_struct: - pass - cdef void zn_mod_init(zn_mod_struct *mod, unsigned long m) - cdef void zn_mod_clear(zn_mod_struct *mod) - cdef void zn_array_mul(unsigned long* res, unsigned long* op1, size_t n1, unsigned long* op2, size_t n2, zn_mod_struct *mod) - from sage.libs.flint.fmpz_poly cimport * from sage.libs.flint.nmod_poly cimport * @@ -420,66 +413,6 @@ cdef class Polynomial_zmod_flint(Polynomial_template): else: raise IndexError("Polynomial coefficient index must be nonnegative.") - def _mul_zn_poly(self, other): - r""" - Returns the product of two polynomials using the zn_poly library. - - See http://www.math.harvard.edu/~dmharvey/zn_poly/ for details - on zn_poly. - - INPUT: - - - self: Polynomial - - right: Polynomial (over same base ring as self) - - OUTPUT: (Polynomial) the product self*right. - - - EXAMPLES:: - - sage: P.<x> = PolynomialRing(GF(next_prime(2^30))) - sage: f = P.random_element(1000) - sage: g = P.random_element(1000) - sage: f*g == f._mul_zn_poly(g) - True - - sage: P.<x> = PolynomialRing(Integers(100)) - sage: P - Univariate Polynomial Ring in x over Ring of integers modulo 100 - sage: r = (10*x)._mul_zn_poly(10*x); r - 0 - sage: r.degree() - -1 - - ALGORITHM: - - uses David Harvey's zn_poly library. - - NOTE: This function is a technology preview. It might - disappear or be replaced without a deprecation warning. - """ - cdef Polynomial_zmod_flint _other = <Polynomial_zmod_flint>self._parent.coerce(other) - - cdef type t = type(self) - cdef Polynomial_zmod_flint r = <Polynomial_zmod_flint>t.__new__(t) - r._parent = (<Polynomial_zmod_flint>self)._parent - r._cparent = (<Polynomial_zmod_flint>self)._cparent - - cdef unsigned long p = self._parent.modulus() - cdef unsigned long n1 = self.x.length - cdef unsigned long n2 = _other.x.length - - cdef zn_mod_struct zn_mod - - nmod_poly_init2(&r.x, p, n1 + n2 -1 ) - - zn_mod_init(&zn_mod, p) - zn_array_mul(<unsigned long *> r.x.coeffs, <unsigned long *> self.x.coeffs, n1, <unsigned long*> _other.x.coeffs, n2, &zn_mod) - r.x.length = n1 + n2 -1 - _nmod_poly_normalise(&r.x) - zn_mod_clear(&zn_mod) - return r - cpdef Polynomial _mul_trunc_(self, Polynomial right, long n): """ Return the product of this polynomial and other truncated to the diff --git a/src/sage/rings/polynomial/polynomial_zz_pex.pyx b/src/sage/rings/polynomial/polynomial_zz_pex.pyx index 853ca0629c3..0d885e522ef 100644 --- a/src/sage/rings/polynomial/polynomial_zz_pex.pyx +++ b/src/sage/rings/polynomial/polynomial_zz_pex.pyx @@ -10,6 +10,7 @@ Univariate Polynomials over GF(p^e) via NTL's ZZ_pEX AUTHOR: - Yann Laigle-Chapuy (2010-01) initial implementation +- Lorenz Panny (2023-01): :meth:`minpoly_mod` """ from sage.rings.integer_ring import ZZ @@ -367,6 +368,50 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): raise ValueError("unknown algorithm") return res != 0 + def minpoly_mod(self, other): + r""" + Compute the minimal polynomial of this polynomial modulo another + polynomial in the same ring. + + ALGORITHM: + + NTL's ``MinPolyMod()``, which uses Shoup's algorithm [Sho1999]_. + + EXAMPLES:: + + sage: R.<x> = GF(101^2)[] + sage: f = x^17 + x^2 - 1 + sage: (x^2).minpoly_mod(f) + x^17 + 100*x^2 + 2*x + 100 + + TESTS: + + Random testing:: + + sage: p = random_prime(50) + sage: e = randrange(2,10) + sage: R.<x> = GF((p,e),'a')[] + sage: d = randrange(1,50) + sage: f = R.random_element(d) + sage: g = R.random_element((-1,5*d)) + sage: poly = g.minpoly_mod(f) + sage: poly(R.quotient(f)(g)) + 0 + """ + self._parent._modulus.restore() + + if other.parent() is not self._parent: + other = self._parent.coerce(other) + + cdef Polynomial_ZZ_pEX r + r = Polynomial_ZZ_pEX.__new__(Polynomial_ZZ_pEX) + celement_construct(&r.x, (<Polynomial_template>self)._cparent) + r._parent = (<Polynomial_template>self)._parent + r._cparent = (<Polynomial_template>self)._cparent + + ZZ_pEX_MinPolyMod(r.x, (<Polynomial_ZZ_pEX>(self % other)).x, (<Polynomial_ZZ_pEX>other).x) + return r + cpdef _richcmp_(self, other, int op): """ EXAMPLES:: @@ -443,4 +488,3 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): x^4 + x^3 + x """ return self.shift(-n) - diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 1315d68c4a9..b939908ef92 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -1258,8 +1258,9 @@ def de_casteljau_intvec(Vector_integer_dense c, int c_bitsize, Rational x, int u # double-rounding on x86 PCs. cdef double half_ulp = ldexp(1.0 * 65/64, -54) + def intvec_to_doublevec(Vector_integer_dense b, long err): - """ + r""" Given a vector of integers A = [a1, ..., an], and an integer error bound E, returns a vector of floating-point numbers B = [b1, ..., bn], lower and upper error bounds F1 and F2, and @@ -2139,19 +2140,22 @@ cdef int subsample_vec(int a, int slen, int llen): sage: [subsample_vec_doctest(a, 3, 4) for a in range(3)] [1, 2, 3] """ - # round((a + 0.5) * (llen - 1) / slen) # round((2*a + 1) * (llen - 1) / (2 * slen) # floor(((2*a + 1) * (llen - 1) + slen) / (2 * slen)) return ((2*a + 1) * (llen - 1) + slen) // (2 * slen) + def subsample_vec_doctest(a, slen, llen): return subsample_vec(a, slen, llen) + def maximum_root_first_lambda(p): - """ + r""" Given a polynomial with real coefficients, computes an upper bound - on its largest real root, using the first-\lambda algorithm from + on its largest real root. + + This is using the first-\lambda algorithm from "Implementations of a New Theorem for Computing Bounds for Positive Roots of Polynomials", by Akritas, Strzebo\'nski, and Vigklas. diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index c47515141fd..215db4e78c7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -1,5 +1,5 @@ r""" -Univariate Skew Polynomials +Univariate skew polynomials This module provides the :class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomial`. @@ -690,4 +690,3 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) - diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 220c9b87c1d..107077623d6 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -1,7 +1,7 @@ r""" -Univariate Dense Skew Polynomials over Finite Fields +Univariate dense skew polynomials over finite fields -This module provides the +This module provides the class:`~sage.rings.polynomial.skew_polynomial_finite_field.SkewPolynomial_finite_field_dense`, which constructs a single univariate skew polynomial over a finite field equipped with the Frobenius endomorphism. Among other things, it implements @@ -336,7 +336,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: N = R._reduced_norm_factor_uniform() ....: counts[N] += 1 sage: counts # random - {z + 1: 969, z + 2: 31} + {z + 1: 969, z + 2: 31} """ skew_ring = self._parent F = self._reduced_norm_factored() @@ -390,7 +390,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: for D in rightdiv: ....: assert P.is_right_divisible_by(D), "not right divisible" ....: assert D.is_irreducible(), "not irreducible" - + sage: P = S.random_element(degree=10) sage: leftdiv = [ f for f in P.left_irreducible_divisors() ] # indirect doctest sage: len(leftdiv) == P.count_irreducible_divisors() @@ -401,7 +401,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: assert P.is_left_divisible_by(D), "not left divisible" ....: assert D.is_irreducible(), "not irreducible" """ - cdef SkewPolynomial_finite_field_dense NS, P, Q, R, P1, Q1, L, V, g, d + cdef SkewPolynomial_finite_field_dense NS, P, Q, R, P1, Q1, L, V, g, d cdef Py_ssize_t i, m, degrandom if not self: return @@ -697,8 +697,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): .. NOTE:: - One can prove that there are always as many left - irreducible monic divisors as right irreducible + One can prove that there are always as many left + irreducible monic divisors as right irreducible monic divisors. EXAMPLES:: @@ -822,10 +822,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): factors.reverse() return Factorization(factors, sort=False, unit=unit) - cdef _factor_uniform_c(self): r""" - Compute a uniformly distrbuted factorization of ``self``. + Compute a uniformly distributed factorization of ``self``. This is the low level implementation of :meth:`factor`. """ @@ -937,7 +936,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): INPUT: - - ``uniform`` -- a boolean (default: ``False``); whether the + - ``uniform`` -- a boolean (default: ``False``); whether the output irreducible divisor should be uniformly distributed among all possibilities @@ -967,7 +966,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: F.value() == a True - There is a priori no guarantee on the distribution of the + There is a priori no guarantee on the distribution of the factorizations we get. Passing in the keyword ``uniform=True`` ensures the output is uniformly distributed among all factorizations:: @@ -1078,9 +1077,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: assert F.value() == a, "factorization has a different value" ....: for d,_ in F: ....: assert d.is_irreducible(), "a factor is not irreducible" - - Note that the algorithm used in this method is probabilistic. - As a consequence, if we call it two times with the same input, + + Note that the algorithm used in this method is probabilistic. + As a consequence, if we call it two times with the same input, we can get different orderings:: sage: factorizations2 = [ F for F in a.factorizations() ] diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_order.pxd index ab22926414f..438773a39ef 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pxd @@ -2,6 +2,7 @@ from sage.rings.polynomial.skew_polynomial_element cimport SkewPolynomial_generi cdef class SkewPolynomial_finite_order_dense (SkewPolynomial_generic_dense): cdef _norm + cdef _charpoly cdef _optbound cdef _matphir_c(self) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index e61f20e17f3..f3ff0617484 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -1,5 +1,5 @@ r""" -Univariate Dense Skew Polynomials over a field equipped with a finite order automorphism +Univariate dense skew polynomials over a field with a finite order automorphism AUTHOR:: @@ -71,6 +71,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): """ SkewPolynomial_generic_dense.__init__ (self, parent, x, check, construct, **kwds) self._norm = None + self._charpoly = None self._optbound = None @@ -87,7 +88,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): cdef k = parent.base_ring() cdef RingElement zero = k(0) cdef RingElement one = k(1) - cdef list line, phir = [ ] + cdef list line, phir = [] if r < d: for i from 0 <= i < d-r: line = d * [zero] @@ -130,10 +131,6 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): Return the matrix of the multiplication by ``self`` on `K[X,\sigma]` considered as a free module over `K[X^r]` (here `r` is the order of `\sigma`). - - .. WARNING:: - - Does not work if self is not monic. """ from sage.matrix.constructor import matrix cdef Py_ssize_t i, j, deb, k, r = self.parent()._order @@ -142,15 +139,15 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): cdef RingElement minusone = <RingElement?>base_ring(-1) cdef RingElement zero = <RingElement?>base_ring(0) cdef Polk = PolynomialRing (base_ring, 'xr') - cdef list M = [ ] + cdef list M = [] cdef list l = self.list() - for j from 0 <= j < r: - for i from 0 <= i < r: + for j in range(r): + for i in range(r): if i < j: - pol = [ zero ] + pol = [zero] deb = i-j+r else: - pol = [ ] + pol = [] deb = i-j for k from deb <= k <= d by r: pol.append(l[k]) @@ -193,13 +190,13 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): By default, the name of the central variable is usually ``z`` (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` - for more details about this). - However, the user can specify a different variable name if desired:: + for more details about this). + However, the user can specify a different variable name if desired:: sage: a.reduced_trace(var='u') 3*u + 4 - When passing in ``var=False``, a tuple of coefficients (instead of + When passing in ``var=False``, a tuple of coefficients (instead of an actual polynomial) is returned:: sage: a.reduced_trace(var=False) @@ -213,10 +210,14 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): sage: b = S.random_element(degree=7) sage: a.reduced_trace() + b.reduced_trace() == (a+b).reduced_trace() True + + .. SEEALSO:: + + :meth:`reduced_norm`, :meth:`reduced_charpoly` """ order = self.parent()._order twisting_morphism = self.parent().twisting_morphism() - coeffs = [ ] + coeffs = [] for i in range(0, self.degree()+1, order): tr = c = self._coeffs[i] for _ in range(order-1): @@ -264,20 +265,20 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): By default, the name of the central variable is usually ``z`` (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` - for more details about this). - However, the user can speciify a different variable name if desired:: + for more details about this). + However, the user can specify a different variable name if desired:: sage: a.reduced_norm(var='u') u^3 + 4*u^2 + 4 - When passing in ``var=False``, a tuple of coefficients (instead of + When passing in ``var=False``, a tuple of coefficients (instead of an actual polynomial) is returned:: sage: a.reduced_norm(var=False) (4, 0, 4, 1) TESTS: - + We check that `N` is a multiple of `a`:: sage: S(N).is_right_divisible_by(a) @@ -312,10 +313,14 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): polynomial of the left multiplication by `X` on the quotient `K[X,\sigma] / K[X,\sigma] P` (which is a `K`-vector space of dimension `d`). + + .. SEEALSO:: + + :meth:`reduced_trace`, :meth:`reduced_charpoly` """ if self._norm is None: if self.is_zero(): - self._norm = 0 + self._norm = 0 else: parent = self._parent section = parent._embed_constants.section() @@ -335,6 +340,84 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): center = self.parent().center(name=var) return center(self._norm) + def reduced_charpoly(self, var=None): + r""" + Return the reduced characteristic polynomial of this + skew polynomial. + + INPUT: + + - ``var`` -- a string, a pair of strings or ``None`` + (default: ``None``); the variable names used for the + characteristic polynomial and the center + + .. NOTE:: + + The result is cached. + + EXAMPLES:: + + sage: k.<t> = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S.<u> = k['u', Frob] + sage: a = u^3 + (2*t^2 + 3)*u^2 + (4*t^2 + t + 4)*u + 2*t^2 + 2 + sage: chi = a.reduced_charpoly() + sage: chi + x^3 + (2*z + 1)*x^2 + (3*z^2 + 4*z)*x + 4*z^3 + z^2 + 1 + + The reduced characteristic polynomial has coefficients in the center + of `S`, which is itself a univariate polynomial ring in the variable + `z = u^3` over `\GF{5}`. Hence it appears as a bivariate polynomial:: + + sage: chi.parent() + Univariate Polynomial Ring in x over Univariate Polynomial Ring in z over Finite Field of size 5 + + The constant coefficient of the reduced characteristic polynomial is + the reduced norm, up to a sign:: + + sage: chi[0] == -a.reduced_norm() + True + + Its coefficient of degree `\deg(a) - 1` is the opposite of the reduced + trace:: + + sage: chi[2] == -a.reduced_trace() + True + + By default, the name of the variable of the reduced characteristic + polynomial is ``x`` and the name of central variable is usually ``z`` + (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` + for more details about this). + The user can speciify different names if desired:: + + sage: a.reduced_charpoly(var='T') # variable name for the caracteristic polynomial + T^3 + (2*z + 1)*T^2 + (3*z^2 + 4*z)*T + 4*z^3 + z^2 + 1 + + sage: a.reduced_charpoly(var=('T', 'c')) + T^3 + (2*c + 1)*T^2 + (3*c^2 + 4*c)*T + 4*c^3 + c^2 + 1 + + .. SEEALSO:: + + :meth:`reduced_trace`, :meth:`reduced_norm` + """ + if self._charpoly is None: + parent = self._parent + section = parent._embed_constants.section() + M = self._matmul_c() + chi = M.charpoly() + self._charpoly = [tuple(c.list()) for c in chi.list()] + if self._norm is not None: + self._norm = self._charpoly[-1] + varcenter = None + if var is None: + varcharpoly = 'x' + elif isinstance(var, (tuple, list)) and len(var) == 2: + (varcharpoly, varcenter) = var + else: + varcharpoly = var + center = self.parent().center(name=varcenter) + coeffs = [center(c) for c in self._charpoly] + return PolynomialRing(center, name=varcharpoly)(coeffs) def is_central(self): r""" @@ -438,7 +521,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): return center(self) except ValueError: pass - if not self._optbound is None: + if self._optbound is not None: return center(self._optbound) return self.reduced_norm() @@ -486,11 +569,9 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): except ValueError: bound = self._matphir_c().minimal_polynomial() section = self._parent._embed_constants.section() - self._optbound = [ section(x) for x in bound.list() ] + self._optbound = [section(x) for x in bound.list()] return center(self._optbound) - # TODO: # fast multiplication # reduced characteristic polynomial - diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index c31a386c226..7d0c12538dc 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,5 +1,5 @@ r""" -Skew Univariate Polynomial Rings +Univariate skew polynomial rings This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing`. diff --git a/src/sage/rings/polynomial/symmetric_ideal.py b/src/sage/rings/polynomial/symmetric_ideal.py index f96a7b57e39..dfe66e9f2f5 100644 --- a/src/sage/rings/polynomial/symmetric_ideal.py +++ b/src/sage/rings/polynomial/symmetric_ideal.py @@ -942,7 +942,7 @@ def groebner_basis(self, tailreduce=False, reduced=True, algorithm=None, report= CommonR = PolynomialRing(PARENT._base, VarList, order=PARENT._order) try: # working around one libsingular bug and one libsingular oddity - DenseIdeal = [CommonR(P._p) if ((CommonR is P._p.parent()) or CommonR.ngens()!=P._p.parent().ngens()) else CommonR(repr(P._p)) for P in OUT.gens()]*CommonR + DenseIdeal = [CommonR(P._p) if ((CommonR is P._p.parent()) or CommonR.ngens()!=P._p.parent().ngens()) else CommonR(repr(P._p)) for P in OUT.gens()]*CommonR except Exception: if report is not None: print("working around a libsingular bug") diff --git a/src/sage/rings/polynomial/symmetric_reduction.pyx b/src/sage/rings/polynomial/symmetric_reduction.pyx index c8c08e1c61f..25213e1318a 100644 --- a/src/sage/rings/polynomial/symmetric_reduction.pyx +++ b/src/sage/rings/polynomial/symmetric_reduction.pyx @@ -1,4 +1,4 @@ -""" +r""" Symmetric Reduction of Infinite Polynomials :class:`~sage.rings.polynomial.symmetric_reduction.SymmetricReductionStrategy` @@ -102,7 +102,7 @@ Symmetric Reduction Strategy is created:: """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Simon King <king@mathematik.nuigalway.ie> # # Distributed under the terms of the GNU General Public License (GPL) @@ -114,9 +114,8 @@ Symmetric Reduction Strategy is created:: # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** import copy import operator import sys diff --git a/src/sage/rings/polynomial/toy_buchberger.py b/src/sage/rings/polynomial/toy_buchberger.py index 6d6475dbf24..2f02ca65e05 100644 --- a/src/sage/rings/polynomial/toy_buchberger.py +++ b/src/sage/rings/polynomial/toy_buchberger.py @@ -399,7 +399,9 @@ def inter_reduction(Q): - ``Q`` -- a set of polynomials - OUTPUT: if ``Q`` is the set `(f_1, ..., f_n)`, this method returns `(g_1, + OUTPUT: + + if ``Q`` is the set `(f_1, ..., f_n)`, this method returns `(g_1, ..., g_s)` such that: - `<f_1,...,f_n> = <g_1,...,g_s>` diff --git a/src/sage/rings/polynomial/toy_d_basis.py b/src/sage/rings/polynomial/toy_d_basis.py index 15cfd4eea52..07f83100df7 100644 --- a/src/sage/rings/polynomial/toy_d_basis.py +++ b/src/sage/rings/polynomial/toy_d_basis.py @@ -116,7 +116,8 @@ - Martin Albrecht (2008-08): initial version """ from sage.rings.integer_ring import ZZ -from sage.arith.all import xgcd, lcm, gcd +from sage.arith.functions import lcm +from sage.arith.misc import XGCD as xgcd, GCD as gcd from sage.rings.polynomial.toy_buchberger import inter_reduction from sage.structure.sequence import Sequence diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx index 2fd4ad9c84d..2e2d6974fb7 100755 --- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx +++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx @@ -83,7 +83,7 @@ cdef class dfs_manager: """ Data structure to manage depth-first search. - Such a structure is created and managed by an instance of `WeilPolynomials_iter`. + Such a structure is created and managed by an instance of `WeilPolynomials_iter`. There is generally no need for a user to manipulate it directly. """ cdef int d @@ -424,15 +424,15 @@ class WeilPolynomials(): - ``node_limit`` -- integer (default ``None``) - If set, imposes an upper bound on the number of terminal nodes during the search + If set, imposes an upper bound on the number of terminal nodes during the search (will raise a ``RuntimeError`` if exceeded). - ``parallel`` -- boolean (default ``False``), whether to use multiple processes - If set, will raise an error unless this file was compiled with OpenMP support + If set, will raise an error unless this file was compiled with OpenMP support (see instructions at the top of :mod:`sage.rings.polynomial.weil.weil_polynomials`). - - ``squarefree`` -- boolean (default ``False``), + - ``squarefree`` -- boolean (default ``False``), If set, only squarefree polynomials will be returned. @@ -526,10 +526,10 @@ class WeilPolynomials(): Test that :trac:`31809` is resolved:: - sage: from sage.rings.polynomial.weil.weil_polynomials import WeilPolynomials - sage: foo = list(WeilPolynomials(12, 3, lead=(1,0,9,2,46), squarefree=False)) + sage: from sage.rings.polynomial.weil.weil_polynomials import WeilPolynomials + sage: foo = list(WeilPolynomials(12, 3, lead=(1,0,9,2,46), squarefree=False)) sage: bar = list(WeilPolynomials(12, 3, lead=(1,0,9,2,46), squarefree=True)) - sage: bar == [f for f in foo if f.is_squarefree()] + sage: bar == [f for f in foo if f.is_squarefree()] True Test that :trac:`32348` is resolved:: @@ -585,4 +585,3 @@ class WeilPolynomials(): 158 """ return self.w.node_count() - diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index d4488574bab..7b9c2b5c783 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -953,4 +953,3 @@ cdef class PowerSeries_pari(PowerSeries): precision = self._prec f = self return PowerSeries_pari(self._parent, f.g.serreverse(), precision) - diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index affd674afda..a45943a4391 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -1293,4 +1293,3 @@ cdef class BaseRingFloorDivAction(Action): P = self.US() g = P.base_ring()(g) return type(x)(P, elt.__f // g, prec=prec, check=False) - diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 711c5fe3c6a..01617870b4b 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -149,7 +149,7 @@ import sage.misc.latex as latex from sage.structure.nonexact import Nonexact -from sage.interfaces.magma import MagmaElement +from sage.interfaces.abc import MagmaElement from sage.rings.fraction_field_element import FractionFieldElement from sage.misc.sage_eval import sage_eval diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 7cf46f7f170..2422521fba8 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1119,7 +1119,7 @@ cdef class PowerSeries(AlgebraElement): T^2 + O(T^3) """ from sage.rings.power_series_ring import PowerSeriesRing - if isinstance(other, (int, Integer, long)): + if isinstance(other, (int, Integer)): return PowerSeriesRing(IntegerModRing(other), self.variable())(self) raise NotImplementedError("Mod on power series ring elements not defined except modulo an integer.") diff --git a/src/sage/rings/puiseux_series_ring_element.pyx b/src/sage/rings/puiseux_series_ring_element.pyx index aff90ee1f89..7fe08d1b46d 100644 --- a/src/sage/rings/puiseux_series_ring_element.pyx +++ b/src/sage/rings/puiseux_series_ring_element.pyx @@ -1052,4 +1052,3 @@ cdef class PuiseuxSeries(AlgebraElement): 25*x^(23/2) + O(x^(27/2)) """ return self.__invert__() - diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 7b7c957fdf4..6db4b9cd6fd 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -312,8 +312,8 @@ True sage: sage_input(n) R.<y> = QQ[] - v = AA.polynomial_root(AA.common_polynomial(y^4 - 4*y^2 + 1), RIF(RR(0.51763809020504148), RR(0.51763809020504159))) - -109*v^3 - 89*v^2 + 327*v + 178 + v = AA.polynomial_root(AA.common_polynomial(y^4 - 4*y^2 + 1), RIF(-RR(1.9318516525781366), -RR(1.9318516525781364))) + -109*v^3 + 89*v^2 + 327*v - 178 We can also see that some computations (basically, those which are easy to perform exactly) are performed directly, instead of storing @@ -362,7 +362,7 @@ # Verified R1.<x> = QQbar[] R2.<y> = QQ[] - v = AA.polynomial_root(AA.common_polynomial(y^4 - 4*y^2 + 1), RIF(RR(0.51763809020504148), RR(0.51763809020504159))) + v = AA.polynomial_root(AA.common_polynomial(y^4 - 4*y^2 + 1), RIF(-RR(1.9318516525781366), -RR(1.9318516525781364))) AA.polynomial_root(AA.common_polynomial(x^4 + QQbar(v^3 - 3*v - 1)*x^3 + QQbar(-v^3 + 3*v - 3)*x^2 + QQbar(-3*v^3 + 9*v + 3)*x + QQbar(3*v^3 - 9*v)), RIF(RR(0.99999999999999989), RR(1.0000000000000002))) sage: one 1 @@ -578,7 +578,7 @@ from sage.rings.rational_field import QQ from sage.rings.number_field.number_field import NumberField, GaussianField, CyclotomicField from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian -from sage.arith.all import factor +from sage.arith.misc import factor from . import infinity from sage.categories.action import Action @@ -2310,7 +2310,7 @@ def do_polred(poly, threshold=32): cost = 2 * bitsize.nbits() + 5 * poly.degree().nbits() if cost > threshold: return parent.gen(), parent.gen(), poly - new_poly, elt_back = poly.__pari__().polredbest(flag=1) + new_poly, elt_back = poly.numerator().__pari__().polredbest(flag=1) elt_fwd = elt_back.modreverse() return parent(elt_fwd.lift()), parent(elt_back.lift()), parent(new_poly) @@ -2542,10 +2542,10 @@ def number_field_elements_from_algebraics(numbers, minimal=False, same_field=Fal Defn: a |--> 1.414213562373095?) sage: number_field_elements_from_algebraics((rt2,rt3)) - (Number Field in a with defining polynomial y^4 - 4*y^2 + 1, [-a^3 + 3*a, -a^2 + 2], Ring morphism: + (Number Field in a with defining polynomial y^4 - 4*y^2 + 1, [-a^3 + 3*a, a^2 - 2], Ring morphism: From: Number Field in a with defining polynomial y^4 - 4*y^2 + 1 To: Algebraic Real Field - Defn: a |--> 0.5176380902050415?) + Defn: a |--> -1.931851652578137?) ``rt3a`` is a real number in ``QQbar``. Ordinarily, we'd get a homomorphism to ``AA`` (because all elements are real), but if we specify ``same_field=True``, @@ -2570,7 +2570,7 @@ def number_field_elements_from_algebraics(numbers, minimal=False, same_field=Fal (Number Field in a with defining polynomial y^4 - 4*y^2 + 1, -a^3 + 3*a, Ring morphism: From: Number Field in a with defining polynomial y^4 - 4*y^2 + 1 To: Algebraic Real Field - Defn: a |--> 0.5176380902050415?) + Defn: a |--> -1.931851652578137?) We can specify ``minimal=True`` if we want the smallest number field:: @@ -2618,7 +2618,7 @@ def number_field_elements_from_algebraics(numbers, minimal=False, same_field=Fal sage: nfI^2 -1 sage: sum = nfrt2 + nfrt3 + nfI + nfz3; sum - 2*a^6 + a^5 - a^4 - a^3 - 2*a^2 - a + a^5 + a^4 - a^3 + 2*a^2 - a - 1 sage: hom(sum) 2.646264369941973? + 1.866025403784439?*I sage: hom(sum) == rt2 + rt3 + qqI + z3 @@ -2656,9 +2656,11 @@ def number_field_elements_from_algebraics(numbers, minimal=False, same_field=Fal sage: elems = [sqrt(5), 2^(1/3)+sqrt(3)*I, 3/4] sage: nf, nums, hom = number_field_elements_from_algebraics(elems, embedded=True) - sage: nf + sage: nf # random (polynomial and root not unique) Number Field in a with defining polynomial y^24 - 6*y^23 ...- 9*y^2 + 1 - with a = 0.2598678911433438? + 0.0572892247058457?*I + with a = 0.2598679? + 0.0572892?*I + sage: nf.is_isomorphic(NumberField(x^24 - 9*x^22 + 135*x^20 - 720*x^18 + 1821*x^16 - 3015*x^14 + 3974*x^12 - 3015*x^10 + 1821*x^8 - 720*x^6 + 135*x^4 - 9*x^2 + 1, 'a')) + True sage: list(map(QQbar, nums)) == elems == list(map(hom, nums)) True @@ -2725,7 +2727,7 @@ def number_field_elements_from_algebraics(numbers, minimal=False, same_field=Fal sqrt(2), AA.polynomial_root(x^3-3, RIF(0,3)), 11/9, 1] sage: res = number_field_elements_from_algebraics(my_nums, embedded=True) sage: res[0] - Number Field in a with defining polynomial y^24 - 107010*y^22 - 24*y^21 + ... + 250678447193040618624307096815048024318853254384 with a = -95.5053039433554? + Number Field in a with defining polynomial y^24 - 107010*y^22 - 24*y^21 + ... + 250678447193040618624307096815048024318853254384 with a = 93.32530798172420? """ gen = qq_generator @@ -3129,7 +3131,7 @@ def pari_field(self): sage: root = ANRoot(x^2 - x - 1, RIF(1, 2)) sage: gen = AlgebraicGenerator(nf, root) sage: gen.pari_field() - [y^2 - y - 1, [2, 0], ...] + [[y^2 - y - 1, [2, 0], ...] """ if self.is_trivial(): raise ValueError("No PARI field attached to trivial generator") @@ -3213,7 +3215,7 @@ def union(self, other): sage: qq_generator.union(gen3) is gen3 True sage: gen2.union(gen3) - Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 0.5176380902050415? + Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in -1.931851652578137? """ if self._trivial: return other @@ -3306,13 +3308,13 @@ def super_poly(self, super, checked=None): Number Field in a with defining polynomial y^2 - 3 with a in 1.732050807568878? sage: gen2_3 = gen2.union(gen3) sage: gen2_3 - Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 0.5176380902050415? + Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in -1.931851652578137? sage: qq_generator.super_poly(gen2) is None True sage: gen2.super_poly(gen2_3) -a^3 + 3*a sage: gen3.super_poly(gen2_3) - -a^2 + 2 + a^2 - 2 """ if checked is None: @@ -3360,13 +3362,13 @@ def __call__(self, elt): sage: sqrt3 = ANExtensionElement(gen3, nf3.gen()) sage: gen2_3 = gen2.union(gen3) sage: gen2_3 - Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 0.5176380902050415? + Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in -1.931851652578137? sage: gen2_3(sqrt2) -a^3 + 3*a sage: gen2_3(ANRational(1/7)) 1/7 sage: gen2_3(sqrt3) - -a^2 + 2 + a^2 - 2 """ if self._trivial: return elt._value @@ -4059,7 +4061,7 @@ def __bool__(self): self.exactify() return bool(self) - + def is_square(self): """ @@ -4336,10 +4338,10 @@ def as_number_field_element(self, minimal=False, embedded=False, prec=53): sage: rt3 = AA(sqrt(3)) sage: rt3b = rt2 + rt3 - rt2 sage: rt3b.as_number_field_element() - (Number Field in a with defining polynomial y^4 - 4*y^2 + 1, -a^2 + 2, Ring morphism: + (Number Field in a with defining polynomial y^4 - 4*y^2 + 1, a^2 - 2, Ring morphism: From: Number Field in a with defining polynomial y^4 - 4*y^2 + 1 To: Algebraic Real Field - Defn: a |--> 0.5176380902050415?) + Defn: a |--> -1.931851652578137?) sage: rt3b.as_number_field_element(minimal=True) (Number Field in a with defining polynomial y^2 - 3, a, Ring morphism: From: Number Field in a with defining polynomial y^2 - 3 @@ -4401,7 +4403,7 @@ def simplify(self): sage: rt2b = rt3 + rt2 - rt3 sage: rt2b.exactify() sage: rt2b._exact_value() - a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in 1.931851652578137? + a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in -0.5176380902050415? sage: rt2b.simplify() sage: rt2b._exact_value() a where a^2 - 2 = 0 and a in 1.414213562373095? @@ -4422,7 +4424,7 @@ def _exact_field(self): sage: QQbar(2)._exact_field() Trivial generator sage: (sqrt(QQbar(2)) + sqrt(QQbar(19)))._exact_field() - Number Field in a with defining polynomial y^4 - 20*y^2 + 81 with a in 2.375100220297941? + Number Field in a with defining polynomial y^4 - 20*y^2 + 81 with a in -3.789313782671036? sage: (QQbar(7)^(3/5))._exact_field() Number Field in a with defining polynomial y^5 - 2*y^4 - 18*y^3 + 38*y^2 + 82*y - 181 with a in 2.554256611698490? """ @@ -4442,7 +4444,7 @@ def _exact_value(self): sage: QQbar(2)._exact_value() 2 sage: (sqrt(QQbar(2)) + sqrt(QQbar(19)))._exact_value() - -1/9*a^3 - a^2 + 11/9*a + 10 where a^4 - 20*a^2 + 81 = 0 and a in 2.375100220297941? + -1/9*a^3 + a^2 + 11/9*a - 10 where a^4 - 20*a^2 + 81 = 0 and a in -3.789313782671036? sage: (QQbar(7)^(3/5))._exact_value() 2*a^4 + 2*a^3 - 34*a^2 - 17*a + 150 where a^5 - 2*a^4 - 18*a^3 + 38*a^2 + 82*a - 181 = 0 and a in 2.554256611698490? """ @@ -6885,7 +6887,7 @@ def generator(self): sage: p = sqrt(AA(2)) * x^2 - sqrt(AA(3)) sage: cp = AA.common_polynomial(p) sage: cp.generator() - Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 1.931851652578137? + Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in -0.5176380902050415? """ self.exactify() return self._gen @@ -7734,7 +7736,7 @@ def is_simple(self): sage: rt2b.exactify() sage: rt2b._descr - a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in 1.931851652578137? + a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in -0.5176380902050415? sage: rt2b._descr.is_simple() False """ @@ -7819,7 +7821,7 @@ def simplify(self, n): sage: rt2b = rt3 + rt2 - rt3 sage: rt2b.exactify() sage: rt2b._descr - a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in 1.931851652578137? + a^3 - 3*a where a^4 - 4*a^2 + 1 = 0 and a in -0.5176380902050415? sage: rt2b._descr.simplify(rt2b) a where a^2 - 2 = 0 and a in 1.414213562373095? """ @@ -7857,16 +7859,21 @@ def neg(self, n): sage: b = a._descr sage: type(b) <class 'sage.rings.qqbar.ANExtensionElement'> - sage: b.neg(a) - 1/3*a^3 - 2/3*a^2 + 4/3*a - 2 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? - 1.573132184970987?*I - sage: b.neg("ham spam and eggs") - 1/3*a^3 - 2/3*a^2 + 4/3*a - 2 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? - 1.573132184970987?*I + sage: c = b.neg(None); c # random (not uniquely represented) + -1/3*a^3 + 1/3*a^2 - a - 1 where a^4 - 2*a^3 + a^2 + 6*a + 3 = 0 and a in 1.724744871391589? + 1.573132184970987?*I + sage: c.generator() == b.generator() and c.field_element_value() + b.field_element_value() == 0 + True + + The parameter is ignored:: + + sage: b.neg("random").generator() == c.generator() and b.neg("random").field_element_value() == c.field_element_value() + True """ return ANExtensionElement(self._generator, -self._value) def invert(self, n): r""" - 1/self. + Reciprocal of self. EXAMPLES:: @@ -7875,16 +7882,20 @@ def invert(self, n): sage: b = a._descr sage: type(b) <class 'sage.rings.qqbar.ANExtensionElement'> - sage: b.invert(a) - 7/3*a^3 - 2/3*a^2 + 4/3*a - 12 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? - 1.573132184970987?*I - sage: b.invert("ham spam and eggs") - 7/3*a^3 - 2/3*a^2 + 4/3*a - 12 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? - 1.573132184970987?*I + sage: c = b.invert(None); c # random (not uniquely represented) + -7/3*a^3 + 19/3*a^2 - 7*a - 9 where a^4 - 2*a^3 + a^2 + 6*a + 3 = 0 and a in 1.724744871391589? + 1.573132184970987?*I + sage: c.generator() == b.generator() and c.field_element_value() * b.field_element_value() == 1 + True + + The parameter is ignored:: + + sage: b.invert("random").generator() == c.generator() and b.invert("random").field_element_value() == c.field_element_value() + True """ return ANExtensionElement(self._generator, ~self._value) def conjugate(self, n): - r""" - Negation of self. + r"""Complex conjugate of self. EXAMPLES:: @@ -7893,10 +7904,23 @@ def conjugate(self, n): sage: b = a._descr sage: type(b) <class 'sage.rings.qqbar.ANExtensionElement'> - sage: b.conjugate(a) - -1/3*a^3 + 2/3*a^2 - 4/3*a + 2 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? + 1.573132184970987?*I - sage: b.conjugate("ham spam and eggs") - -1/3*a^3 + 2/3*a^2 - 4/3*a + 2 where a^4 - 2*a^3 + a^2 - 6*a + 9 = 0 and a in -0.7247448713915890? + 1.573132184970987?*I + sage: c = b.conjugate(None); c # random (not uniquely represented) + 1/3*a^3 - 1/3*a^2 + a + 1 where a^4 - 2*a^3 + a^2 + 6*a + 3 = 0 and a in 1.724744871391589? - 1.573132184970987?*I + + Internally, complex conjugation is implemented by taking the + same abstract field element but conjugating the complex embedding of + the field:: + + sage: c.generator() == b.generator().conjugate() + True + sage: c.field_element_value() == b.field_element_value() + True + + The parameter is ignored:: + + sage: b.conjugate("random").generator() == c.generator() and b.conjugate("random").field_element_value() == c.field_element_value() + True + """ if self._exactly_real: return self @@ -8529,7 +8553,7 @@ def an_binop_expr(a, b, op): sage: x = an_binop_expr(a, b, operator.add); x <sage.rings.qqbar.ANBinaryExpr object at ...> sage: x.exactify() - -6/7*a^7 + 2/7*a^6 + 71/7*a^5 - 26/7*a^4 - 125/7*a^3 + 72/7*a^2 + 43/7*a - 47/7 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.12580...? + 6/7*a^7 - 2/7*a^6 - 71/7*a^5 + 26/7*a^4 + 125/7*a^3 - 72/7*a^2 - 43/7*a + 47/7 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in -0.3199179336182997? sage: a = QQbar(sqrt(2)) + QQbar(sqrt(3)) sage: b = QQbar(sqrt(3)) + QQbar(sqrt(5)) @@ -8538,7 +8562,7 @@ def an_binop_expr(a, b, op): sage: x = an_binop_expr(a, b, operator.mul); x <sage.rings.qqbar.ANBinaryExpr object at ...> sage: x.exactify() - 2*a^7 - a^6 - 24*a^5 + 12*a^4 + 46*a^3 - 22*a^2 - 22*a + 9 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.1258...? + 2*a^7 - a^6 - 24*a^5 + 12*a^4 + 46*a^3 - 22*a^2 - 22*a + 9 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in -0.3199179336182997? """ return ANBinaryExpr(a, b, op) diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index cae440d543d..8f9bf928cc5 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -97,7 +97,7 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein <wstein@gmail.com> # # This program is free software: you can redistribute it and/or modify @@ -105,9 +105,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** - - +# **************************************************************************** import sage.misc.latex as latex from . import ring, ideal, quotient_ring_element from sage.structure.category_object import normalize_names @@ -117,13 +115,13 @@ from sage.categories.rings import Rings from sage.categories.commutative_rings import CommutativeRings +import sage.interfaces.abc + +_Rings = Rings() +_CommRings = CommutativeRings() + MPolynomialIdeal_quotient = None -try: - from sage.interfaces.singular import singular as singular_default, is_SingularElement -except ImportError: - is_singularElement = lambda x : False - singular_default = None def QuotientRing(R, I, names=None, **kwds): @@ -276,20 +274,15 @@ def QuotientRing(R, I, names=None, **kwds): """ # 1. Not all rings inherit from the base class of rings. # 2. We want to support quotients of free algebras by homogeneous two-sided ideals. - #if not isinstance(R, commutative_ring.CommutativeRing): - # raise TypeError, "R must be a commutative ring." from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.integer_ring import ZZ - if R not in Rings(): - raise TypeError("R must be a ring.") - try: - is_commutative = R.is_commutative() - except (AttributeError, NotImplementedError): - is_commutative = False + if R not in _Rings: + raise TypeError("R must be a ring") + is_commutative = R in _CommRings if names is None: try: names = tuple([x + 'bar' for x in R.variable_names()]) - except ValueError: # no names are assigned + except ValueError: # no names are assigned pass else: names = normalize_names(R.ngens(), names) @@ -321,10 +314,11 @@ def QuotientRing(R, I, names=None, **kwds): if S == ZZ: return Integers((I_lift+J).gen(), **kwds) return R.__class__(S, I_lift + J, names=names) - if isinstance(R, ring.CommutativeRing): + if R in _CommRings: return QuotientRing_generic(R, I, names, **kwds) return QuotientRing_nc(R, I, names, **kwds) + def is_QuotientRing(x): """ Tests whether or not ``x`` inherits from :class:`QuotientRing_nc`. @@ -349,14 +343,12 @@ def is_QuotientRing(x): True sage: is_QuotientRing(F) False - """ return isinstance(x, QuotientRing_nc) -_Rings = Rings() -_RingsQuotients = Rings().Quotients() -_CommutativeRingsQuotients = CommutativeRings().Quotients() +_RingsQuotients = _Rings.Quotients() +_CommutativeRingsQuotients = _CommRings.Quotients() from sage.structure.category_object import check_default_category @@ -512,10 +504,13 @@ def construction(self): names = self.cover_ring().variable_names() except ValueError: names = None - if self in CommutativeRings(): - return QuotientFunctor(self.__I, names=names, domain=CommutativeRings(), codomain=CommutativeRings(), as_field=isinstance(self, Field)), self.__R + if self in _CommRings: + return QuotientFunctor(self.__I, names=names, domain=_CommRings, + codomain=_CommRings, + as_field=isinstance(self, Field)), self.__R else: - return QuotientFunctor(self.__I, names=names, as_field=isinstance(self, Field)), self.__R + return QuotientFunctor(self.__I, names=names, + as_field=isinstance(self, Field)), self.__R def _repr_(self): """ @@ -956,12 +951,11 @@ def ideal(self, *gens, **kwds): """ if len(gens) == 1: gens = gens[0] - from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular - if not isinstance(self.__R, MPolynomialRing_libsingular) and \ - (not hasattr(self.__R, '_has_singular') or not self.__R._has_singular): + from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base + if not (isinstance(self.__R, MPolynomialRing_base) and self.__R._has_singular): # pass through return super().ideal(gens, **kwds) - if is_SingularElement(gens): + if isinstance(gens, sage.interfaces.abc.SingularElement): gens = list(gens) elif not isinstance(gens, (list, tuple)): gens = [gens] @@ -1017,7 +1011,7 @@ def _element_constructor_(self, x, coerce=True): if x.parent() is self: return x x = x.lift() - if is_SingularElement(x): + if isinstance(x, sage.interfaces.abc.SingularElement): # self._singular_().set_ring() x = self.element_class(self, x.sage_poly(self.cover_ring())) return x @@ -1183,7 +1177,7 @@ def gen(self, i=0): """ return self(self.__R.gen(i)) - def _singular_(self, singular=singular_default): + def _singular_(self, singular=None): """ Returns the Singular quotient ring of ``self`` if the base ring is coercible to Singular. @@ -1215,7 +1209,7 @@ def _singular_(self, singular=singular_default): _[1]=x2+y2 """ if singular is None: - raise ImportError("could not import singular") + from sage.interfaces.singular import singular try: Q = self.__singular @@ -1224,7 +1218,8 @@ def _singular_(self, singular=singular_default): Q._check_valid() return Q except (AttributeError, ValueError): - return self._singular_init_(singular) + self.__singular = self._singular_init_(singular) + return self.__singular def _singular_init_(self, singular=None): """ @@ -1290,6 +1285,7 @@ def term_order(self): """ return self.__R.term_order() + class QuotientRing_generic(QuotientRing_nc, ring.CommutativeRing): r""" Creates a quotient ring of a *commutative* ring `R` by the ideal `I`. @@ -1316,13 +1312,14 @@ def __init__(self, R, I, names, category=None): TESTS:: - sage: isinstance(ZZ.quo(2), sage.rings.ring.CommutativeRing) # indirect doctest + sage: ZZ.quo(2) in Rings().Commutative() # indirect doctest True """ - if not isinstance(R, ring.CommutativeRing): + if R not in _CommRings: raise TypeError("This class is for quotients of commutative rings only.\n For non-commutative rings, use <sage.rings.quotient_ring.QuotientRing_nc>") if not self._is_category_initialized(): - category = check_default_category(_CommutativeRingsQuotients,category) + category = check_default_category(_CommutativeRingsQuotients, + category) QuotientRing_nc.__init__(self, R, I, names, category=category) def _macaulay2_init_(self, macaulay2=None): diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index 69df8d3319e..e2f190a4b1c 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -153,7 +153,7 @@ def __bool__(self): """ return self.__rep not in self.parent().defining_ideal() - + def is_unit(self): """ diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index f215dff7e67..2263d549aef 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -576,12 +576,8 @@ cdef class Rational(sage.structure.element.FieldElement): if isinstance(x, Rational): set_from_Rational(self, x) - elif isinstance(x, long): - mpz_set_pylong(mpq_numref(self.value), x) - elif isinstance(x, int): - i = x - mpq_set_si(self.value, i, 1) + mpz_set_pylong(mpq_numref(self.value), x) elif isinstance(x, integer.Integer): set_from_Integer(self, x) @@ -629,19 +625,15 @@ cdef class Rational(sage.structure.element.FieldElement): num = x[0] denom = x[1] - if isinstance(num, long): + if isinstance(num, int): mpz_set_pylong(mpq_numref(self.value), num) - elif isinstance(num, int): # Python 2 only - mpz_set_si(mpq_numref(self.value), num) else: if not isinstance(num, integer.Integer): num = integer.Integer(num, base) mpz_set(mpq_numref(self.value), (<integer.Integer>num).value) - if isinstance(denom, long): + if isinstance(denom, int): mpz_set_pylong(mpq_denref(self.value), denom) - elif isinstance(denom, int): # Python 2 only - mpz_set_si(mpq_denref(self.value), denom) else: if not isinstance(denom, integer.Integer): denom = integer.Integer(denom, base) @@ -1143,7 +1135,8 @@ cdef class Rational(sage.structure.element.FieldElement): seq.append(self) nums = [x.numerator() for x in seq] denoms = [x.denominator() for x in seq] - from sage.arith.all import gcd, lcm + from sage.arith.misc import GCD as gcd + from sage.arith.functions import lcm return gcd(nums) / lcm(denoms) def valuation(self, p): @@ -1767,7 +1760,7 @@ cdef class Rational(sage.structure.element.FieldElement): if p == 2: return ((m % 8) == 1) - from sage.arith.all import kronecker_symbol + from sage.arith.misc import kronecker as kronecker_symbol return (kronecker_symbol(m, p) == 1) def val_unit(self, p): @@ -3695,7 +3688,7 @@ cdef class Rational(sage.structure.element.FieldElement): 32/3 """ if isinstance(x, Rational): - if isinstance(y, (int, long, integer.Integer)): + if isinstance(y, (int, integer.Integer)): return (<Rational>x)._lshift(y) if isinstance(y, Rational): if mpz_cmp_si(mpq_denref((<Rational>y).value), 1) != 0: @@ -3743,7 +3736,7 @@ cdef class Rational(sage.structure.element.FieldElement): 1/8 """ if isinstance(x, Rational): - if isinstance(y, (int, long, integer.Integer)): + if isinstance(y, (int, integer.Integer)): return (<Rational>x)._rshift(y) if isinstance(y, Rational): if mpz_cmp_si(mpq_denref((<Rational>y).value), 1) != 0: diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 55e7a46319d..28a4d3b65c0 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -911,7 +911,7 @@ def phi(x): a = self.prod([Pq[i]**ZZ(l[i]) for i in range(l.degree())]) if check: assert phi(a) == v, "oops" - return a + return a def gens(self): r""" diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 4fc1de6cce8..23a41c2bf6a 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -238,7 +238,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.cpython.string cimport char_to_str, str_to_bytes cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision): - """ + r""" Convert an MPFI interval to an Arb ball. INPUT: @@ -282,7 +282,7 @@ cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision): mpfr_clear(right) cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except -1: - """ + r""" Convert an Arb ball to an MPFI interval. INPUT: @@ -798,7 +798,7 @@ class RealBallField(UniqueRepresentation, sage.rings.abc.RealBallField): # Ball functions of non-ball arguments def sinpi(self, x): - """ + r""" Return a ball enclosing `\sin(\pi x)`. This works even if ``x`` itself is not a ball, and may be faster or @@ -844,7 +844,7 @@ class RealBallField(UniqueRepresentation, sage.rings.abc.RealBallField): return res def cospi(self, x): - """ + r""" Return a ball enclosing `\cos(\pi x)`. This works even if ``x`` itself is not a ball, and may be faster or @@ -2986,7 +2986,7 @@ cdef class RealBall(RingElement): return res def sqrt1pm1(self): - """ + r""" Return `\sqrt{1+\mathrm{self}}-1`, computed accurately when ``self`` is close to zero. @@ -3728,7 +3728,7 @@ cdef class RealBall(RingElement): return res def gamma(self, a=None): - """ + r""" Image of this ball by the (upper incomplete) Euler Gamma function For `a` real, return the upper incomplete Gamma function @@ -3770,7 +3770,7 @@ cdef class RealBall(RingElement): gamma_inc = gamma def gamma_inc_lower(self, a): - """ + r""" Image of this ball by the lower incomplete Euler Gamma function For `a` real, return the lower incomplete Gamma function @@ -3828,7 +3828,7 @@ cdef class RealBall(RingElement): return res def rising_factorial(self, n): - """ + r""" Return the ``n``-th rising factorial of this ball. The `n`-th rising factorial of `x` is equal to `x (x+1) \cdots (x+n-1)`. @@ -3934,7 +3934,7 @@ cdef class RealBall(RingElement): return res def polylog(self, s): - """ + r""" Return the polylogarithm `\operatorname{Li}_s(\mathrm{self})`. EXAMPLES:: diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 02909654037..3d779b359ce 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -1748,4 +1748,3 @@ cdef class LazyWrapperMorphism(Morphism): else: e._value = x return e - diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index a9e4b1eed16..5f1494000f7 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -2912,7 +2912,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(1.0) << 32 4294967296 """ - if isinstance(x, RealIntervalFieldElement) and isinstance(y, (int,long, Integer)): + if isinstance(x, RealIntervalFieldElement) and isinstance(y, (int, Integer)): return x._lshift_(y) return sage.structure.element.bin_op(x, y, operator.lshift) @@ -2944,7 +2944,7 @@ cdef class RealIntervalFieldElement(RingElement): 0.062500000000000000? """ if isinstance(x, RealIntervalFieldElement) and \ - isinstance(y, (int,long,Integer)): + isinstance(y, (int, Integer)): return x._rshift_(y) return sage.structure.element.bin_op(x, y, operator.rshift) @@ -4411,7 +4411,7 @@ cdef class RealIntervalFieldElement(RingElement): """ if exponent == 2: return self.square() - if isinstance(exponent, (int, long, Integer)): + if isinstance(exponent, (int, Integer)): q, r = divmod (exponent, 2) if r == 0: # x^(2q) = (x^q)^2 xq = RingElement.__pow__(self, q) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index c9214785c3c..096cba738c6 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -678,7 +678,7 @@ cdef class RealField_class(sage.rings.abc.RealField): - Any MPFR real field with precision that is as large as this one - - int, long, integer, and rational rings. + - int, integer, and rational rings. - the field of algebraic reals @@ -1472,11 +1472,9 @@ cdef class RealNumber(sage.structure.element.RingElement): elif isinstance(x, Gen) and typ((<Gen>x).g) == t_REAL: _gen = x self._set_from_GEN_REAL(_gen.g) - elif isinstance(x, long): + elif isinstance(x, int): x = Integer(x) mpfr_set_z(self.value, (<Integer>x).value, parent.rnd) - elif isinstance(x, int): - mpfr_set_si(self.value, x, parent.rnd) elif isinstance(x, float): mpfr_set_d(self.value, x, parent.rnd) elif isinstance(x, complex) and x.imag == 0: @@ -5678,11 +5676,13 @@ cdef class RealLiteral(RealNumber): 1.3000000000000000000000000000000000000000000000000000000000 sage: 1.3 + 1.2 2.50000000000000 + sage: RR(1_0000.000000000000000000000000000000000000) + 10000.0000000000 """ RealNumber.__init__(self, parent, x, base) if isinstance(x, str): self.base = base - self.literal = x + self.literal = x.replace('_', '') def __neg__(self): """ @@ -6074,7 +6074,7 @@ cdef class int_toRR(Map): cdef long x_long cdef mpz_t x_mpz - if not isinstance(x, (int, long)): + if not isinstance(x, int): x = int(x) integer_check_long_py(x, &x_long, &err) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 75b0854d630..e806637c38a 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -132,8 +132,8 @@ cdef class Ring(ParentWithGens): running ._test_some_elements() . . . pass running ._test_zero() . . . pass running ._test_zero_divisors() . . . pass - sage: TestSuite(QQ['x','y']).run() - sage: TestSuite(ZZ['x','y']).run() + sage: TestSuite(QQ['x','y']).run(skip='_test_elements') + sage: TestSuite(ZZ['x','y']).run(skip='_test_elements') sage: TestSuite(ZZ['x','y']['t']).run() Test against another bug fixed in :trac:`9944`:: @@ -1383,7 +1383,7 @@ cdef class CommutativeRing(Ring): poly = poly.polynomial(self) except (AttributeError, TypeError): raise TypeError("polynomial (=%s) must be a polynomial." % repr(poly)) - if not names is None: + if names is not None: name = names if isinstance(name, tuple): name = name[0] diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 2b649359ad4..9c24c55d535 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1801,7 +1801,7 @@ cdef class RingExtension_generic(CommutativeAlgebra): sage: L = GF(5^12).over(F) sage: K.Hom(L) # indirect doctest - Set of Homomorphisms from Field in z4 with defining polynomial x^2 + (4*z2 + 3)*x + z2 over its base + Set of Homomorphisms from Field in z4 with defining polynomial x^2 + (4*z2 + 3)*x + z2 over its base to Field in z12 with defining polynomial x^6 + (4*z2 + 3)*x^5 + x^4 + (3*z2 + 1)*x^3 + x^2 + (4*z2 + 1)*x + z2 over its base sage: K.Hom(L, category=Sets()) @@ -1885,6 +1885,59 @@ cdef class RingExtension_generic(CommutativeAlgebra): parent = self.Hom(codomain, category=category) return RingExtensionHomomorphism(parent, im_gens, base_map, check) + def characteristic(self): + r""" + Return the characteristic of the extension as a ring. + + OUTPUT: + + A prime number or zero. + + EXAMPLES:: + + sage: F = GF(5^2).over() # over GF(5) + sage: K = GF(5^4).over(F) + sage: L = GF(5^12).over(K) + sage: F.characteristic() + 5 + sage: K.characteristic() + 5 + sage: L.characteristic() + 5 + + :: + + sage: F = RR.over(ZZ) + sage: F.characteristic() + 0 + + :: + + sage: F = GF(11) + sage: A.<x> = F[] + sage: K = Frac(F).over(F) + sage: K.characteristic() + 11 + + :: + + sage: E = GF(7).over(ZZ) + sage: E.characteristic() + 7 + + TESTS: + + Ensure ticket :trac:`34692` is fixed:: + + sage: Fq = GF(11) + sage: FqX.<X> = Fq[] + sage: k = Frac(FqX) + sage: K = k.over(FqX) + sage: K.frobenius_endomorphism() + Frobenius endomorphism x |--> x^11 of Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 over its base + """ + return self._backend.characteristic() + # Fraction fields ################# diff --git a/src/sage/rings/ring_extension_conversion.pyx b/src/sage/rings/ring_extension_conversion.pyx index 36e12b8fcac..9d94fb7567d 100644 --- a/src/sage/rings/ring_extension_conversion.pyx +++ b/src/sage/rings/ring_extension_conversion.pyx @@ -306,7 +306,7 @@ cpdef from_backend_morphism(f, RingExtension_generic E): - ``x`` -- a morphism - - ``E`` -- a ring extension + - ``E`` -- a ring extension EXAMPLES:: @@ -392,14 +392,14 @@ cpdef to_backend(arg): cpdef from_backend(arg, E): r""" - Try to reconstruct something (somehow related to ``E``) + Try to reconstruct something (somehow related to ``E``) whose backend is ``arg``. INPUT: - ``arg`` -- any argument - - ``E`` -- a ring extension + - ``E`` -- a ring extension EXAMPLES:: diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index 04d0f1033a9..134cd1a1174 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -1458,8 +1458,17 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): sage: L(u).minpoly(F).degree() in [ 1, 3 ] True """ - from sage.modules.free_module import FreeModule cdef RingExtensionWithBasis parent = self._parent + + if base is None: + mod = parent.modulus() + S = mod.parent().quotient(mod) + try: + return S(list(self.vector())).minpoly() + except NotImplementedError: + pass # fall back to generic code below + + from sage.modules.free_module import FreeModule cdef MapRelativeRingToFreeModule j base = parent._check_base(base) diff --git a/src/sage/rings/semirings/all.py b/src/sage/rings/semirings/all.py index 91594074088..b14da38328a 100644 --- a/src/sage/rings/semirings/all.py +++ b/src/sage/rings/semirings/all.py @@ -1,6 +1,2 @@ - -from sage.misc.lazy_import import lazy_import -lazy_import('sage.rings.semirings.non_negative_integer_semiring', - ['NonNegativeIntegerSemiring', 'NN']) - +from .non_negative_integer_semiring import NonNegativeIntegerSemiring, NN from .tropical_semiring import TropicalSemiring diff --git a/src/sage/rings/semirings/tropical_semiring.pyx b/src/sage/rings/semirings/tropical_semiring.pyx index 5ae1ea93bf7..73c3b8820ec 100644 --- a/src/sage/rings/semirings/tropical_semiring.pyx +++ b/src/sage/rings/semirings/tropical_semiring.pyx @@ -99,7 +99,7 @@ cdef class TropicalSemiringElement(Element): return repr(self._val) def _latex_(self): - """ + r""" Return a latex representation of ``self``. EXAMPLES:: @@ -135,7 +135,7 @@ cdef class TropicalSemiringElement(Element): # Comparisons cpdef _richcmp_(left, right, int op): - """ + r""" Return the standard comparison of ``left`` and ``right``. EXAMPLES:: @@ -189,7 +189,7 @@ cdef class TropicalSemiringElement(Element): cdef TropicalSemiringElement self, x self = left x = right - + if self._val is None: if x._val is None: return rich_to_bool(op, 0) @@ -259,7 +259,7 @@ cdef class TropicalSemiringElement(Element): return x def __neg__(self): - """ + r""" Return the additive inverse, which only exists for `\infty`. EXAMPLES:: @@ -610,7 +610,7 @@ class TropicalSemiring(Parent, UniqueRepresentation): @cached_method def zero(self): - """ + r""" Return the (tropical) additive identity element `+\infty`. EXAMPLES:: @@ -671,4 +671,3 @@ cdef class TropicalToTropical(Map): +infinity """ return self.codomain()((<TropicalSemiringElement>x)._val) - diff --git a/src/sage/rings/tate_algebra.py b/src/sage/rings/tate_algebra.py index 9097b2a4e07..877f2a2703e 100644 --- a/src/sage/rings/tate_algebra.py +++ b/src/sage/rings/tate_algebra.py @@ -239,7 +239,6 @@ class TateAlgebraFactory(UniqueFactory): AUTHORS: - Xavier Caruso, Thibaut Verron (2018-09) - """ def create_key(self, base, prec=None, log_radii=ZZ(0), names=None, order='degrevlex'): """ @@ -251,7 +250,7 @@ def create_key(self, base, prec=None, log_radii=ZZ(0), names=None, order='degrev - ``prec`` -- an integer or ``None`` (default: ``None``) - - ``log_radii`` -- an integer or a list or a tuple of integers + - ``log_radii`` -- an integer or a list or a tuple of integers (default: ``0``) - ``names`` -- names of the indeterminates @@ -1148,7 +1147,7 @@ def precision_cap(self): """ Return the precision cap of this Tate algebra. - NOTE:: + .. NOTE:: The precision cap is the truncation precision used for arithmetic operations computed by @@ -1295,4 +1294,3 @@ def is_integral_domain(self, proof=True): True """ return True - diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 3f09e3722d3..0ef1f8d86fe 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -1715,9 +1715,9 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): the precision at which the result is computed, if ``None``, the result is truncated according to the cap of the parent - NOTE: + .. NOTE:: - The ``n``-th root is computed as `\exp(\frac 1 n \log(f))`. + The ``n``-th root is computed as `\exp(\frac 1 n \log(f))`. EXAMPLES:: @@ -3318,7 +3318,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): - ``divisors`` -- a series, or a list of series - NOTE:: + NOTE: The condition on the remainder is that it has @@ -3502,7 +3502,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): - ``other`` -- a Tate series - TESTS:: + TESTS: We check that the S-polynomial of two monomials vanishes:: diff --git a/src/sage/rings/tate_algebra_ideal.pyx b/src/sage/rings/tate_algebra_ideal.pyx index 8826f67d000..17031adcec2 100644 --- a/src/sage/rings/tate_algebra_ideal.pyx +++ b/src/sage/rings/tate_algebra_ideal.pyx @@ -82,20 +82,20 @@ class TateAlgebraIdeal(Ideal_generic): of these algorithms. - ``options`` -- extra arguments that are passed in to the - algorithm; this notably include the keyword ``verbose`` (only + algorithm; this notably include the keyword ``verbose`` (only available for ``PoTe`` and ``VaPoTe``) which is an integer defining the verbosity level: - ``0``: no verbosity (quiet) - - ``1``: print each new generator and a notification each time a + - ``1``: print each new generator and a notification each time a J-pair is popped - ``2``: in addition, print the outcome of the treatment of a J-pair - ``3``: in addition, print all added J-pairs - - ``4``: print entire series instead of only their leading terms + - ``4``: print entire series instead of only their leading terms OUTPUT: @@ -116,9 +116,9 @@ class TateAlgebraIdeal(Ideal_generic): - it is sorted, in the sense that the leading term of `g_i` is greater than the leading of `g_{i+1}` for all `i`. - NOTE: + .. NOTE:: - The result of this method is cached. + The result of this method is cached. EXAMPLES:: @@ -142,7 +142,7 @@ class TateAlgebraIdeal(Ideal_generic): sage: h = 2*x^6*y^4 + 2*x^4 + 4*x^5*y^2 + 8*x^8*y^2 + 8*x^7*y^3 + 8*x^6*y sage: I = A.ideal([f,g,h]) sage: I.groebner_basis(algorithm="buchberger-integral") - [...0001*x^4 + O(2^4 * <x, y>), + [...0001*x^4 + O(2^4 * <x, y>), ...0001*x^2*y + O(2^4 * <x, y>), ...0001*y^2 + O(2^4 * <x, y>)] sage: I.groebner_basis(algorithm='buchberger') @@ -292,7 +292,7 @@ class TateAlgebraIdeal(Ideal_generic): All ideals are saturated when `\pi` is invertible. - EXAMPLES:: + EXAMPLES: Over classical Tate algebras (where `\pi` is invertible), this method always returns ``True``:: @@ -350,7 +350,7 @@ class TateAlgebraIdeal(Ideal_generic): When `\pi` is invertible in `A`, all ideals are saturated. - EXAMPLES:: + EXAMPLES: Over classical Tate algebras (where `\pi` is invertible), this method always returns the same ideal:: @@ -422,10 +422,10 @@ def groebner_basis_buchberger(I, prec, py_integral): Grobner basis of the ideal generated by the same generators over the ring over the ring of integers - NOTE:: + .. NOTE:: - This function is not meant to be called directly, but through the - ``groebner_basis`` method of Tate algebra ideals. + This function is not meant to be called directly, but through the + ``groebner_basis`` method of Tate algebra ideals. EXAMPLES:: @@ -487,7 +487,7 @@ def groebner_basis_buchberger(I, prec, py_integral): # Main loop of Buchberger algorithm # Loop invariant: - # the S-polynomials of pairs of elements in rgb + # the S-polynomials of pairs of elements in rgb # all reduce to zero modulo (rgb,S) while S: sig_check() @@ -599,7 +599,7 @@ cdef Jpair(p1, p2): t2 = t // sv2 su1 = t1*s1 if s2 is None: - return su1, t1*v1 + return su1, t1*v1 su2 = t2*s2 if su1 > su2: return su1, t1*v1 @@ -773,7 +773,7 @@ def print_pair(p, verbose): sage: s = v.leading_term() sage: p = (s,v) - When ``verbose`` is less than 4, only the leading term of + When ``verbose`` is less than 4, only the leading term of the series is printed:: sage: print_pair(p, 0) @@ -874,7 +874,7 @@ def groebner_basis_pote(I, prec, verbose=0): # TODO: this should probably be a single function call f = f.monic() << f.valuation() - + if verbose > 0: print("---") print("new generator: %s + ..." % f.leading_term()) @@ -924,7 +924,7 @@ def groebner_basis_pote(I, prec, verbose=0): print("| skip: sygyzy criterium; signature = %s" % syzygy) continue - # We check if (s,v) is covered by + # We check if (s,v) is covered by # the current strong Grobner basis cover = None for S, V in sgb: @@ -1019,7 +1019,7 @@ def groebner_basis_pote(I, prec, verbose=0): gb.sort(reverse=True) return gb - + def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, interrupt_interred_with_val=False): r""" Run the VaPoTe algorithm to compute the Groebner basis of ``I`` @@ -1158,7 +1158,7 @@ def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, inte continue f = f.monic() << f.valuation() - + if f and f.valuation() > val: if verbose > 0: print("reduction increases the valuation") @@ -1210,7 +1210,7 @@ def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, inte print("| skip: sygyzy criterium; signature = %s" % syzygy) continue - # We check if (s,v) is covered by + # We check if (s,v) is covered by # the current strong Grobner basis cover = None for S, V in sgb: diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index 56cb05ba256..c6f1a9ecbbf 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -93,7 +93,8 @@ def integer_mod_ring(): sage: R.cardinality() <= 50000 True """ - from sage.all import ZZ, IntegerModRing + from sage.rings.integer_ring import ZZ + from sage.rings.finite_rings.integer_mod_ring import IntegerModRing n = ZZ.random_element(x=2, y=50000) return IntegerModRing(n) @@ -109,7 +110,8 @@ def padic_field(): sage: sage.rings.tests.padic_field() ...-adic Field with capped relative precision ... """ - from sage.all import ZZ, Qp + from sage.rings.integer_ring import ZZ + from sage.rings.padics.factory import Qp prec = ZZ.random_element(x=10, y=100) p = ZZ.random_element(x=2, y=10**4 - 30).next_prime() return Qp(p, prec) @@ -125,7 +127,8 @@ def quadratic_number_field(): sage: K = sage.rings.tests.quadratic_number_field(); K Number Field in a with defining polynomial x^2 ... with a = ... """ - from sage.all import ZZ, QuadraticField + from sage.rings.integer_ring import ZZ + from sage.rings.number_field.number_field import QuadraticField while True: d = ZZ.random_element(x=-10**5, y=10**5) if not d.is_square(): @@ -144,7 +147,8 @@ def absolute_number_field(maxdeg=10): sage: K.degree() <= 10 True """ - from sage.all import ZZ, NumberField + from sage.rings.integer_ring import ZZ + from sage.rings.number_field.number_field import NumberField R = ZZ['x'] while True: f = R.random_element(degree=ZZ.random_element(x=1, y=maxdeg), @@ -231,7 +235,8 @@ def rings0(): sage: type(sage.rings.tests.rings0()) <... 'list'> """ - from sage.all import IntegerRing, RationalField + from sage.rings.integer_ring import IntegerRing + from sage.rings.rational_field import RationalField v = [(IntegerRing, 'ring of integers'), (RationalField, 'field of rational numbers'), (integer_mod_ring, 'integers modulo n for n at most 50000'), @@ -270,8 +275,10 @@ def rings1(): """ v = rings0() X = random_rings(level=0) - from sage.all import (PolynomialRing, PowerSeriesRing, - LaurentPolynomialRing, ZZ) + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.power_series_ring import PowerSeriesRing + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + from sage.rings.integer_ring import ZZ v = [(lambda: PolynomialRing(next(X), names='x'), 'univariate polynomial ring over level 0 ring'), (lambda: PowerSeriesRing(next(X), names='x'), @@ -448,7 +455,8 @@ def test_karatsuba_multiplication(base_ring, maxdeg1, maxdeg2, sage: sage.rings.tests.test_karatsuba_multiplication(ZZ, 10000, 10000, ref_mul=lambda f,g: f*g, base_ring_random_elt_args=[100000]) """ - from sage.all import randint, PolynomialRing + from sage.misc.prandom import randint + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing threshold = randint(0, min(maxdeg1, maxdeg2)) R = PolynomialRing(base_ring, 'x') if verbose: diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 2291f0ed393..d238bce3450 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -332,7 +332,7 @@ def __bool__(self): """ return bool(self._obj) - + def __reduce__(self): r""" diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index ed142b15485..ffb13d82917 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -115,7 +115,7 @@ def __init__(self, domain): from .value_group import DiscreteValuationCodomain # A valuation is a map from an additive semigroup to an additive semigroup, however, it # does not preserve that structure. It is therefore only a morphism in the category of sets. - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets UniqueRepresentation.__init__(self) Homset.__init__(self, domain, DiscreteValuationCodomain(), category=Sets()) @@ -343,7 +343,7 @@ def is_negative_pseudo_valuation(self): False """ - from sage.categories.all import Fields + from sage.categories.fields import Fields if self.is_discrete_valuation(): return False elif self.domain() in Fields(): @@ -1175,7 +1175,7 @@ def _test_shift(self, **options): y = self.shift(x, s) if s >= 0: tester.assertGreaterEqual(self(y),self(x)) - from sage.categories.all import Fields + from sage.categories.fields import Fields if self.domain().is_exact() and self.domain() in Fields(): # the shift here sometimes fails if elements implement # __floordiv__ incorrectly, see #23971 diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index f7265ef52d7..159bb675664 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -486,7 +486,7 @@ def __init__(self, generators): True """ - from sage.categories.all import AdditiveMagmas + from sage.categories.additive_magmas import AdditiveMagmas self._generators = generators category = AdditiveMagmas().AdditiveAssociative().AdditiveUnital() diff --git a/src/sage/sandpiles/examples.py b/src/sage/sandpiles/examples.py index b34129f3ed8..9ccc08d314f 100644 --- a/src/sage/sandpiles/examples.py +++ b/src/sage/sandpiles/examples.py @@ -11,7 +11,7 @@ The examples are accessible by typing ``sandpiles.NAME``, where ``NAME`` is the name of the example. You can get a list by typing -``sandpiles.`` and hitting the TAB key:: +``sandpiles.`` and hitting the :kbd:`Tab` key:: sandpiles.Complete sandpiles.Cycle diff --git a/src/sage/sat/solvers/__init__.py b/src/sage/sat/solvers/__init__.py index d30e2bea5fa..27b47db2d51 100644 --- a/src/sage/sat/solvers/__init__.py +++ b/src/sage/sat/solvers/__init__.py @@ -1,4 +1,4 @@ from .satsolver import SatSolver from .cryptominisat import CryptoMiniSat -from .dimacs import Glucose, GlucoseSyrup, RSat +from .dimacs import Glucose, GlucoseSyrup, RSat, Kissat diff --git a/src/sage/sat/solvers/cryptominisat.py b/src/sage/sat/solvers/cryptominisat.py index 6a090604438..d2a8ae9e5a6 100644 --- a/src/sage/sat/solvers/cryptominisat.py +++ b/src/sage/sat/solvers/cryptominisat.py @@ -94,10 +94,11 @@ def var(self, decision=None): def nvars(self): r""" - Return the number of variables. Note that for compatibility with DIMACS - convention, the number of variables corresponds to the maximal index of - the variables used. - + Return the number of variables. + + Note that for compatibility with DIMACS convention, the number + of variables corresponds to the maximal index of the variables used. + EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat @@ -276,11 +277,9 @@ def clauses(self, filename=None): 1 2 -4 0 x1 2 3 0 x1 2 -5 0 - <BLANKLINE> + <BLANKLINE> """ if filename is None: return self._clauses - else: - from sage.sat.solvers.dimacs import DIMACS - DIMACS.render_dimacs(self._clauses, filename, self.nvars()) - + from sage.sat.solvers.dimacs import DIMACS + DIMACS.render_dimacs(self._clauses, filename, self.nvars()) diff --git a/src/sage/sat/solvers/dimacs.py b/src/sage/sat/solvers/dimacs.py index ca40b2d6fe7..c1ad5409f55 100644 --- a/src/sage/sat/solvers/dimacs.py +++ b/src/sage/sat/solvers/dimacs.py @@ -15,6 +15,8 @@ AUTHORS: - Martin Albrecht (2012): first version +- Sébastien Labbé (2018): adding Glucose SAT solver +- Sébastien Labbé (2023): adding Kissat SAT solver Classes and Methods ------------------- @@ -354,15 +356,10 @@ def render_dimacs(clauses, filename, nlits): fh.write(" ".join(map(str, lits)) + " 0\n") fh.close() - def __call__(self, assumptions=None): - """ + def _run(self): + r""" Run 'command' and collect output. - INPUT: - - - ``assumptions`` - ignored, accepted for compatibility with - other solvers (default: ``None``) - TESTS: This class is not meant to be called directly:: @@ -371,14 +368,25 @@ def __call__(self, assumptions=None): sage: fn = tmp_filename() sage: solver = DIMACS(filename=fn) sage: solver.add_clause( (1, -2 , 3) ) - sage: solver() + sage: solver._run() Traceback (most recent call last): ... ValueError: no SAT solver command selected + + It is used by subclasses:: + + sage: from sage.sat.solvers import Glucose + sage: solver = Glucose() + sage: solver.add_clause( (1, 2, 3) ) + sage: solver.add_clause( (-1,) ) + sage: solver.add_clause( (-2,) ) + sage: solver._run() # optional - glucose + sage: solver._output # optional - glucose + [... + 's SATISFIABLE\n', + 'v -1 -2 3 0\n'] """ from sage.misc.verbose import get_verbose - if assumptions is not None: - raise NotImplementedError("Assumptions are not supported for DIMACS based solvers.") self.write() output_filename = None @@ -414,19 +422,9 @@ def __call__(self, assumptions=None): process.kill() raise - -class RSat(DIMACS): - """ - An instance of the RSat solver. - - For information on RSat see: http://reasoning.cs.ucla.edu/rsat/ - """ - - command = "rsat {input} -v -s" - def __call__(self, assumptions=None): """ - Solve this instance. + Solve this instance and return the parsed output. INPUT: @@ -441,7 +439,55 @@ def __call__(self, assumptions=None): - If this instance is UNSAT: ``False`` - EXAMPLES:: + EXAMPLES: + + When the problem is SAT:: + + sage: from sage.sat.solvers import RSat + sage: solver = RSat() + sage: solver.add_clause( (1, 2, 3) ) + sage: solver.add_clause( (-1,) ) + sage: solver.add_clause( (-2,) ) + sage: solver() # optional - rsat + (None, False, False, True) + + When the problem is UNSAT:: + + sage: solver = RSat() + sage: solver.add_clause((1,2)) + sage: solver.add_clause((-1,2)) + sage: solver.add_clause((1,-2)) + sage: solver.add_clause((-1,-2)) + sage: solver() # optional - rsat + False + + With Glucose:: + + sage: from sage.sat.solvers.dimacs import Glucose + sage: solver = Glucose() + sage: solver.add_clause((1,2)) + sage: solver.add_clause((-1,2)) + sage: solver.add_clause((1,-2)) + sage: solver() # optional - glucose + (None, True, True) + sage: solver.add_clause((-1,-2)) + sage: solver() # optional - glucose + False + + With GlucoseSyrup:: + + sage: from sage.sat.solvers.dimacs import GlucoseSyrup + sage: solver = GlucoseSyrup() + sage: solver.add_clause((1,2)) + sage: solver.add_clause((-1,2)) + sage: solver.add_clause((1,-2)) + sage: solver() # optional - glucose + (None, True, True) + sage: solver.add_clause((-1,-2)) + sage: solver() # optional - glucose + False + + TESTS:: sage: from sage.sat.boolean_polynomials import solve as solve_sat sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True) @@ -452,10 +498,14 @@ def __call__(self, assumptions=None): ....: except ZeroDivisionError: ....: pass sage: solve_sat(F, solver=sage.sat.solvers.RSat) # optional - RSat + """ - DIMACS.__call__(self) + if assumptions is not None: + raise NotImplementedError("Assumptions are not supported for DIMACS based solvers.") + + self._run() - s = [None] + [False for _ in range(self.nvars())] + v_lines = [] for line in self._output: if line.startswith("c"): continue @@ -463,165 +513,242 @@ def __call__(self, assumptions=None): if "UNSAT" in line: return False if line.startswith("v"): - lits = map(int, line[2:-2].strip().split(" ")) - for e in lits: - s[abs(e)] = e>0 - return tuple(s) + v_lines.append(line[1:].strip()) -class Glucose(DIMACS): + if v_lines: + L = " ".join(v_lines).split(" ") + assert L[-1] == "0", "last digit of solution line must be zero (not {})".format(L[-1]) + return (None,) + tuple(int(e)>0 for e in L[:-1]) + else: + raise ValueError("When parsing the output, no line starts with letter v or s") + +class RSat(DIMACS): """ - An instance of the Glucose solver. + An instance of the RSat solver. - For information on Glucose see: http://www.labri.fr/perso/lsimon/glucose/ + For information on RSat see: http://reasoning.cs.ucla.edu/rsat/ EXAMPLES:: - sage: from sage.sat.solvers import Glucose - sage: solver = Glucose() + sage: from sage.sat.solvers import RSat + sage: solver = RSat() sage: solver - DIMACS Solver: 'glucose -verb=2 {input} {output}' + DIMACS Solver: 'rsat {input} -v -s' + + When the problem is SAT:: + + sage: from sage.sat.solvers import RSat + sage: solver = RSat() sage: solver.add_clause( (1, 2, 3) ) sage: solver.add_clause( (-1,) ) sage: solver.add_clause( (-2,) ) - sage: solver() # optional - glucose + sage: solver() # optional - rsat (None, False, False, True) + When the problem is UNSAT:: + + sage: solver = RSat() + sage: solver.add_clause((1,2)) + sage: solver.add_clause((-1,2)) + sage: solver.add_clause((1,-2)) + sage: solver.add_clause((-1,-2)) + sage: solver() # optional - rsat + False + """ + command = "rsat {input} -v -s" - command = "glucose -verb=2 {input} {output}" - def __call__(self, **kwds): - """ - Solve this instance. +class Glucose(DIMACS): + """ + An instance of the Glucose solver. - INPUT: + For information on Glucose see: http://www.labri.fr/perso/lsimon/glucose/ - - ``assumptions`` - ignored, accepted for compatibility with - other solvers (default: ``None``) + EXAMPLES:: - OUTPUT: + sage: from sage.sat.solvers import Glucose + sage: solver = Glucose() + sage: solver + DIMACS Solver: 'glucose -verb=0 -model {input}' - - If this instance is SAT: A tuple of length ``nvars()+1`` - where the ``i``-th entry holds an assignment for the - ``i``-th variables (the ``0``-th entry is always ``None``). + When the problem is SAT:: - - If this instance is UNSAT: ``False`` + sage: from sage.sat.solvers import Glucose + sage: solver1 = Glucose() + sage: solver1.add_clause( (1, 2, 3) ) + sage: solver1.add_clause( (-1,) ) + sage: solver1.add_clause( (-2,) ) + sage: solver1() # optional - glucose + (None, False, False, True) - EXAMPLES:: + When the problem is UNSAT:: - sage: from sage.sat.boolean_polynomials import solve as solve_sat - sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True) - sage: while True: # workaround (see :trac:`31891`) - ....: try: - ....: F, s = sr.polynomial_system() - ....: break - ....: except ZeroDivisionError: - ....: pass - sage: [sol] = solve_sat(F, solver=sage.sat.solvers.Glucose) # optional - glucose - sage: Fsol = F.subs(sol) # optional - glucose - sage: Fsol # optional - glucose - Polynomial Sequence with 36 Polynomials in 0 Variables - sage: Fsol.reduced() # optional - glucose - [] + sage: solver2 = Glucose() + sage: solver2.add_clause((1,2)) + sage: solver2.add_clause((-1,2)) + sage: solver2.add_clause((1,-2)) + sage: solver2.add_clause((-1,-2)) + sage: solver2() # optional - glucose + False - :: + With one hundred variables:: - sage: from sage.sat.solvers.dimacs import Glucose - sage: solver = Glucose() - sage: solver.add_clause((1,2)) - sage: solver.add_clause((-1,2)) - sage: solver.add_clause((1,-2)) - sage: solver() # optional - glucose - (None, True, True) - sage: solver.add_clause((-1,-2)) - sage: solver() # optional - glucose - False - """ - DIMACS.__call__(self) + sage: solver3 = Glucose() + sage: solver3.add_clause( (1, 2, 100) ) + sage: solver3.add_clause( (-1,) ) + sage: solver3.add_clause( (-2,) ) + sage: solver3() # optional - glucose + (None, False, False, ..., True) - for line in self._output: - if line.startswith("c"): - continue - if line.startswith("s"): - if "UNSAT" in line: - return False - try: - s = map(int, line[:-2].strip().split(" ")) - s = (None,) + tuple(e>0 for e in s) - return s - except ValueError: - pass - return False + TESTS:: + + sage: print(''.join(solver1._output)) # optional - glucose + c... + s SATISFIABLE + v -1 -2 3 0 + + :: + + sage: print(''.join(solver2._output)) # optional - glucose + c... + s UNSATISFIABLE + + Glucose gives large solution on one single line:: + + sage: print(''.join(solver3._output)) # optional - glucose + c... + s SATISFIABLE + v -1 -2 ... 100 0 + + """ + command = "glucose -verb=0 -model {input}" class GlucoseSyrup(DIMACS): """ An instance of the Glucose-syrup parallel solver. For information on Glucose see: http://www.labri.fr/perso/lsimon/glucose/ + + EXAMPLES:: + + sage: from sage.sat.solvers import GlucoseSyrup + sage: solver = GlucoseSyrup() + sage: solver + DIMACS Solver: 'glucose-syrup -model -verb=0 {input}' + + When the problem is SAT:: + + sage: solver1 = GlucoseSyrup() + sage: solver1.add_clause( (1, 2, 3) ) + sage: solver1.add_clause( (-1,) ) + sage: solver1.add_clause( (-2,) ) + sage: solver1() # optional - glucose + (None, False, False, True) + + When the problem is UNSAT:: + + sage: solver2 = GlucoseSyrup() + sage: solver2.add_clause((1,2)) + sage: solver2.add_clause((-1,2)) + sage: solver2.add_clause((1,-2)) + sage: solver2.add_clause((-1,-2)) + sage: solver2() # optional - glucose + False + + With one hundred variables:: + + sage: solver3 = GlucoseSyrup() + sage: solver3.add_clause( (1, 2, 100) ) + sage: solver3.add_clause( (-1,) ) + sage: solver3.add_clause( (-2,) ) + sage: solver3() # optional - glucose + (None, False, False, ..., True) + + TESTS:: + + sage: print(''.join(solver1._output)) # optional - glucose + c... + s SATISFIABLE + v -1 -2 3 0 + + :: + + sage: print(''.join(solver2._output)) # optional - glucose + c... + s UNSATISFIABLE + + GlucoseSyrup gives large solution on one single line:: + + sage: print(''.join(solver3._output)) # optional - glucose + c... + s SATISFIABLE + v -1 -2 ... 100 0 + """ + command = "glucose-syrup -model -verb=0 {input}" - command = "glucose-syrup -model -verb=2 {input}" +class Kissat(DIMACS): + """ + An instance of the Kissat SAT solver - def __call__(self, **kwds): - """ - Solve this instance. + For information on Kissat see: http://fmv.jku.at/kissat/ - INPUT: + EXAMPLES:: - - ``assumptions`` - ignored, accepted for compatibility with - other solvers (default: ``None``) + sage: from sage.sat.solvers import Kissat + sage: solver = Kissat() + sage: solver + DIMACS Solver: 'kissat -q {input}' - OUTPUT: + When the problem is SAT:: - - If this instance is SAT: A tuple of length ``nvars()+1`` - where the ``i``-th entry holds an assignment for the - ``i``-th variables (the ``0``-th entry is always ``None``). + sage: solver1 = Kissat() + sage: solver1.add_clause( (1, 2, 3) ) + sage: solver1.add_clause( (-1,) ) + sage: solver1.add_clause( (-2,) ) + sage: solver1() # optional - kissat + (None, False, False, True) - - If this instance is UNSAT: ``False`` + When the problem is UNSAT:: - EXAMPLES:: + sage: solver2 = Kissat() + sage: solver2.add_clause((1,2)) + sage: solver2.add_clause((-1,2)) + sage: solver2.add_clause((1,-2)) + sage: solver2.add_clause((-1,-2)) + sage: solver2() # optional - kissat + False - sage: from sage.sat.boolean_polynomials import solve as solve_sat - sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True) - sage: while True: # workaround (see :trac:`31891`) - ....: try: - ....: F, s = sr.polynomial_system() - ....: break - ....: except ZeroDivisionError: - ....: pass - sage: [sol] = solve_sat(F, solver=sage.sat.solvers.GlucoseSyrup) # optional - glucose - sage: Fsol = F.subs(sol) # optional - glucose - sage: Fsol # optional - glucose - Polynomial Sequence with 36 Polynomials in 0 Variables - sage: Fsol.reduced() # optional - glucose - [] + With one hundred variables:: - :: + sage: solver3 = Kissat() + sage: solver3.add_clause( (1, 2, 100) ) + sage: solver3.add_clause( (-1,) ) + sage: solver3.add_clause( (-2,) ) + sage: solver3() # optional - kissat + (None, False, False, ..., True) - sage: from sage.sat.solvers.dimacs import GlucoseSyrup - sage: solver = GlucoseSyrup() - sage: solver.add_clause((1,2)) - sage: solver.add_clause((-1,2)) - sage: solver.add_clause((1,-2)) - sage: solver() # optional - glucose - (None, True, True) - sage: solver.add_clause((-1,-2)) - sage: solver() # optional - glucose - False - """ - DIMACS.__call__(self) + TESTS:: - full_line = '' + sage: print(''.join(solver1._output)) # optional - kissat + s SATISFIABLE + v -1 -2 3 0 - for line in self._output: - if line.startswith("c"): - continue - if line.startswith("s"): - if "UNSAT" in line: - return False - if line.startswith("v"): - full_line += line[2:] + ' ' - s = map(int, full_line[:-3].strip().split(" ")) - s = (None,) + tuple(e>0 for e in s) - return s + :: + + sage: print(''.join(solver2._output)) # optional - kissat + s UNSATISFIABLE + + Here the output contains many lines starting with letter "v":: + + sage: print(''.join(solver3._output)) # optional - kissat + s SATISFIABLE + v -1 -2 ... + v ... + v ... + v ... 100 0 + + """ + command = "kissat -q {input}" diff --git a/src/sage/sat/solvers/picosat.py b/src/sage/sat/solvers/picosat.py index f27c70a92bf..a1a80e71794 100644 --- a/src/sage/sat/solvers/picosat.py +++ b/src/sage/sat/solvers/picosat.py @@ -85,10 +85,11 @@ def var(self, decision=None): def nvars(self): r""" - Return the number of variables. Note that for compatibility with DIMACS - convention, the number of variables corresponds to the maximal index of - the variables used. - + Return the number of variables. + + Note that for compatibility with DIMACS convention, the number + of variables corresponds to the maximal index of the variables used. + EXAMPLES:: sage: from sage.sat.solvers.picosat import PicoSAT @@ -220,7 +221,5 @@ def clauses(self, filename=None): """ if filename is None: return self._clauses - else: - from sage.sat.solvers.dimacs import DIMACS - DIMACS.render_dimacs(self._clauses, filename, self.nvars()) - + from sage.sat.solvers.dimacs import DIMACS + DIMACS.render_dimacs(self._clauses, filename, self.nvars()) diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index d93181c142f..30f092a3582 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -328,7 +328,7 @@ def SAT(solver=None, *args, **kwds): - ``"picosat"`` -- note that the pycosat package must be installed. - ``"glucose"`` -- note that the glucose package must be installed. - + - ``"glucose-syrup"`` -- note that the glucose package must be installed. - ``"LP"`` -- use :class:`~sage.sat.solvers.sat_lp.SatLP` to solve the @@ -362,12 +362,17 @@ def SAT(solver=None, *args, **kwds): Forcing Glucose:: sage: SAT(solver="glucose") - DIMACS Solver: 'glucose -verb=2 {input} {output}' + DIMACS Solver: 'glucose -verb=0 -model {input}' Forcing Glucose Syrup:: sage: SAT(solver="glucose-syrup") - DIMACS Solver: 'glucose-syrup -model -verb=2 {input}' + DIMACS Solver: 'glucose-syrup -model -verb=0 {input}' + + Forcing Kissat:: + + sage: SAT(solver="kissat") + DIMACS Solver: 'kissat -q {input}' """ if solver is None: import pkgutil @@ -393,6 +398,8 @@ def SAT(solver=None, *args, **kwds): elif solver == 'glucose-syrup': from .dimacs import GlucoseSyrup return GlucoseSyrup(*args, **kwds) + elif solver == 'kissat': + from .dimacs import Kissat + return Kissat(*args, **kwds) else: raise ValueError("Solver '{}' is not available".format(solver)) - diff --git a/src/sage/schemes/affine/affine_homset.py b/src/sage/schemes/affine/affine_homset.py index b9260996204..98b3b3a8994 100644 --- a/src/sage/schemes/affine/affine_homset.py +++ b/src/sage/schemes/affine/affine_homset.py @@ -23,15 +23,16 @@ - Ben Hutz (2018): add numerical point support """ - -#***************************************************************************** -# Copyright (C) 2006 William Stein <wstein@gmail.com> +# ***************************************************************************** +# Copyright (C) 2006 William Stein <wstein@gmail.com> # -# 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/ -#***************************************************************************** +# 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/ +# ***************************************************************************** + +from copy import copy from sage.misc.verbose import verbose from sage.rings.integer_ring import ZZ @@ -42,13 +43,13 @@ from sage.categories.number_fields import NumberFields from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -import sage.schemes.generic.homset -from copy import copy +from sage.schemes.generic.homset import SchemeHomset_points, SchemeHomset_generic + +# ******************************************************************* +# Affine varieties +# ******************************************************************* -#******************************************************************* -# Affine varieties -#******************************************************************* -class SchemeHomset_points_spec(sage.schemes.generic.homset.SchemeHomset_generic): +class SchemeHomset_points_spec(SchemeHomset_generic): """ Set of rational points of an affine variety. @@ -62,7 +63,6 @@ class SchemeHomset_points_spec(sage.schemes.generic.homset.SchemeHomset_generic) sage: SchemeHomset_points_spec(Spec(QQ), Spec(QQ)) Set of rational points of Spectrum of Rational Field """ - def _element_constructor_(self, *args, **kwds): """ The element constructor. @@ -86,7 +86,7 @@ def _element_constructor_(self, *args, **kwds): Defn: Ring endomorphism of Rational Field Defn: 1 |--> 1 """ - return sage.schemes.generic.homset.SchemeHomset_generic._element_constructor_(self, *args, **kwds) + return super()._element_constructor_(*args, **kwds) def _repr_(self): """ @@ -101,14 +101,47 @@ def _repr_(self): sage: S._repr_() 'Set of rational points of Spectrum of Rational Field' """ - return 'Set of rational points of '+str(self.codomain()) + return 'Set of rational points of {}'.format(self.codomain()) + + +class SchemeHomset_polynomial_affine_space(SchemeHomset_generic): + """ + Set of morphisms between affine spaces defined by polynomials. + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(2, QQ) + sage: Hom(A, A) + Set of morphisms + From: Affine Space of dimension 2 over Rational Field + To: Affine Space of dimension 2 over Rational Field + """ + def identity(self): + """ + The identity morphism of this homset. + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(2, QQ) + sage: I = A.identity_morphism() + sage: I.parent() + Set of morphisms + From: Affine Space of dimension 2 over Rational Field + To: Affine Space of dimension 2 over Rational Field + sage: _.identity() == I + True + """ + if self.is_endomorphism_set(): + from sage.schemes.generic.morphism import SchemeMorphism_polynomial_id + return SchemeMorphism_polynomial_id(self.domain()) + raise TypeError("identity map is only defined for endomorphisms") +# ******************************************************************* +# Affine varieties +# ******************************************************************* -#******************************************************************* -# Affine varieties -#******************************************************************* -class SchemeHomset_points_affine(sage.schemes.generic.homset.SchemeHomset_points): +class SchemeHomset_points_affine(SchemeHomset_points): """ Set of rational points of an affine variety. diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index 1c4f2dff188..32c2e47e494 100644 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -1148,9 +1148,9 @@ def reduce_base_field(self): sage: H = End(A) sage: f = H([(QQbar(sqrt(2))*x^2 + 1/QQbar(sqrt(3))) / (5*x)]) sage: f.reduce_base_field() - Scheme endomorphism of Affine Space of dimension 1 over Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a = 1.931851652578137? + Scheme endomorphism of Affine Space of dimension 1 over Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a = ...? Defn: Defined on coordinates by sending (x) to - (((a^3 - 3*a)*x^2 + (1/3*a^2 - 2/3))/(5*x)) + (((a^3 - 3*a)*x^2 + (-1/3*a^2 + 2/3))/(5*x)) :: diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py index bf86bc225c6..d7d50b2d064 100644 --- a/src/sage/schemes/affine/affine_point.py +++ b/src/sage/schemes/affine/affine_point.py @@ -76,12 +76,12 @@ def __init__(self, X, v, check=True): """ SchemeMorphism.__init__(self, X) if check: - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings if is_SchemeMorphism(v): v = list(v) else: try: - if isinstance(v.parent(), CommutativeRing): + if v.parent() in CommutativeRings(): v = [v] except AttributeError: pass @@ -105,7 +105,7 @@ def _matrix_times_point_(self, mat, dom): - ``mat`` -- a matrix - - ``dom`` -- (unused) needed for consistent function call with projective + - ``dom`` -- (unused) needed for consistent function call with projective OUTPUT: a scheme point given by ``mat*self`` diff --git a/src/sage/schemes/affine/affine_space.py b/src/sage/schemes/affine/affine_space.py index a4c48b334c3..ba56ef9fc8d 100644 --- a/src/sage/schemes/affine/affine_space.py +++ b/src/sage/schemes/affine/affine_space.py @@ -20,21 +20,24 @@ from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.map import Map from sage.categories.fields import Fields -_Fields = Fields() +from sage.categories.homset import Hom from sage.categories.number_fields import NumberFields from sage.misc.latex import latex from sage.misc.mrange import cartesian_product_iterator +from sage.matrix.constructor import matrix from sage.structure.category_object import normalize_names from sage.schemes.generic.scheme import AffineScheme from sage.schemes.generic.ambient_space import AmbientSpace -from sage.schemes.affine.affine_homset import SchemeHomset_points_affine +from sage.schemes.affine.affine_homset import (SchemeHomset_points_affine, + SchemeHomset_polynomial_affine_space) from sage.schemes.affine.affine_morphism import (SchemeMorphism_polynomial_affine_space, SchemeMorphism_polynomial_affine_space_field, SchemeMorphism_polynomial_affine_space_finite_field) from sage.schemes.affine.affine_point import (SchemeMorphism_point_affine, SchemeMorphism_point_affine_field, SchemeMorphism_point_affine_finite_field) -from sage.matrix.constructor import matrix + +_Fields = Fields() def is_AffineSpace(x): r""" @@ -225,7 +228,6 @@ def __iter__(self): for v in cartesian_product_iterator([R for _ in range(n)]): yield C._point(AHom, v, check=False) - def ngens(self): """ Return the number of generators of self, i.e. the number of @@ -363,6 +365,20 @@ def _morphism(self, *args, **kwds): """ return SchemeMorphism_polynomial_affine_space(*args, **kwds) + def _homset(self, *args, **kwds): + """ + Construct the Hom-set. + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(2, QQ) + sage: Hom(A, A) + Set of morphisms + From: Affine Space of dimension 2 over Rational Field + To: Affine Space of dimension 2 over Rational Field + """ + return SchemeHomset_polynomial_affine_space(*args, **kwds) + def _point_homset(self, *args, **kwds): """ Construct a Hom-set for this affine space. diff --git a/src/sage/schemes/affine/affine_subscheme.py b/src/sage/schemes/affine/affine_subscheme.py index 930e494a2a8..0ba77fd2b6d 100644 --- a/src/sage/schemes/affine/affine_subscheme.py +++ b/src/sage/schemes/affine/affine_subscheme.py @@ -65,10 +65,11 @@ def __init__(self, A, polynomials, embedding_center=None, """ AlgebraicScheme_subscheme.__init__(self, A, polynomials) if embedding_images is not None: - self._embedding_morphism = self.hom(embedding_images, embedding_codomain) + self._embedding_morphism = self.hom(embedding_images, + embedding_codomain) elif A._ambient_projective_space is not None: - self._embedding_morphism = self.projective_embedding \ - (A._default_embedding_index, A._ambient_projective_space) + self._embedding_morphism = self.projective_embedding( + A._default_embedding_index, A._ambient_projective_space) if embedding_center is not None: self._embedding_center = self.point(embedding_center) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 9587901c476..b8cc1887957 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -314,7 +314,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= center = (self._base_space)(center) except (TypeError, ValueError): raise ValueError('could not convert %s to %s' % (center, self._base_space)) - if not(center.scheme().ambient_space() is center.scheme()): + if center.scheme().ambient_space() is not center.scheme(): raise ValueError("the center of a point of projective Berkovich space cannot be " + "a point of %s" % (center.scheme())) # since we are over a field, we normalize coordinates @@ -334,7 +334,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise ValueError("center in %s, should be in %s") % (center.parent(), self._base_space) else: # make sure the center is in the appropriate number field - if not(center.parent() == self._base_space): + if not (center.parent() == self._base_space): try: center = (self._base_space)(center) except (TypeError, ValueError): diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 73369fda8d4..17eee57dd0a 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -258,7 +258,7 @@ def __ne__(self,right): sage: B != C False """ - return not(self == right) + return not (self == right) def __hash__(self): """ @@ -474,7 +474,7 @@ def _repr_(self): return "Affine Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().precision_cap()) else: - return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ + return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ self.base()) def _latex_(self): @@ -704,7 +704,7 @@ def _repr_(self): return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().base_ring().precision_cap()) else: - return "Projective Berkovich line over Cp(%s), with base %s" %(self.prime(),\ + return "Projective Berkovich line over Cp(%s), with base %s" %(self.prime(),\ self.base().base_ring()) def _latex_(self): diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index 8a85dd9737c..fc3d174de59 100644 --- a/src/sage/schemes/curves/constructor.py +++ b/src/sage/schemes/curves/constructor.py @@ -283,7 +283,7 @@ def Curve(F, A=None): raise TypeError("ambient space must be either an affine or projective space") if not isinstance(F, (list, tuple)): F = [F] - if not all(f.parent() == A.coordinate_ring() for f in F): + if not all(f.parent() == A.coordinate_ring() for f in F): raise TypeError("need a list of polynomials of the coordinate ring of {}".format(A)) n = A.dimension_relative() diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index f9ff5944a90..e5a6ba01be1 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -434,9 +434,9 @@ def projection(self, P=None, PS=None): l = list(PP.gens()) for i in range(n + 1): l[i] = 0 - while(F(l) == 0): - l[i] = l[i] + 1 - Q = PP(l) # will be a point not on the curve + while F(l) == 0: + l[i] += 1 + Q = PP(l) # will be a point not on the curve else: # if the base ring is a finite field, iterate over all points in the ambient space and check which # are on this curve @@ -1893,20 +1893,20 @@ def rational_points_iterator(self): g10 = R(g(X,one,zero)) if g10.is_zero(): for x in K: - yield(self.point([x,one,zero])) + yield self.point([x, one, zero]) else: for x in g10.roots(multiplicities=False): - yield(self.point([x,one,zero])) + yield self.point([x, one, zero]) # points with Z = 1 for y in K: gy1 = R(g(X,y,one)) if gy1.is_zero(): for x in K: - yield(self.point([x,y,one])) + yield self.point([x, y, one]) else: for x in gy1.roots(multiplicities=False): - yield(self.point([x,y,one])) + yield self.point([x, y, one]) def _points_via_singular(self, sort=True): r""" diff --git a/src/sage/schemes/cyclic_covers/charpoly_frobenius.py b/src/sage/schemes/cyclic_covers/charpoly_frobenius.py index 3e892e31fde..4c63fe44783 100644 --- a/src/sage/schemes/cyclic_covers/charpoly_frobenius.py +++ b/src/sage/schemes/cyclic_covers/charpoly_frobenius.py @@ -1,16 +1,11 @@ r""" - Computation of the Frobenius polynomial using Newton's identities - """ - - # ***************************************************************************** # Copyright (C) 2018 Edgar Costa <edgarc@mit.edu> # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # ***************************************************************************** - from sage.rings.integer_ring import ZZ from sage.functions.log import log @@ -23,15 +18,15 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= - ``frob_matrix`` -- a matrix representing the Frobenius matrix up to some precision - - ``charpoly_prec`` -- a vector ai, such that, `frob_matrix.change_ring(ZZ).charpoly()[i]` - will be correct mod `p^ai`, this can be easily deduced from the Hodge numbers and - knowing the q-adic precision of ``frob_matrix`` + - ``charpoly_prec`` -- a vector ai, such that, ``frob_matrix.change_ring(ZZ).charpoly()[i]`` + will be correct mod `p^ai`, this can be easily deduced from the + Hodge numbers and knowing the q-adic precision of ``frob_matrix`` - ``p`` -- prime `p` - ``weight`` -- weight of the motive - - ``a`` -- `q = q^a` + - ``a`` -- `p = q^a` - ``known_factor`` -- the list of coefficients of the known factor @@ -195,14 +190,12 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= sage: F+= F.base_ring()(0).add_bigoh(6)*ones_matrix(*F.dimensions()) sage: charpoly_frobenius(F, [6, 5, 4, 4], 17, 2) [-4913, -221, 13, 1] - - """ assert known_factor[-1] == 1 try: cp = frob_matrix.change_ring(ZZ).charpoly().list() except ValueError: - # the given matrix wasn't integral + # the given matrix was not integral cp = frob_matrix.charpoly().change_ring(ZZ).list() assert len(charpoly_prec) == len(cp) - (len(known_factor) - 1) assert cp[-1] == 1 @@ -216,7 +209,7 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= # figure out the sign # i.e., if it is a reciprocal or an antireciprocal polynomial - if weight % 2 == 1: + if weight % 2: # for odd weight the sign is always 1 # it's the charpoly of a USp matrix # and charpoly of a symplectic matrix is reciprocal @@ -227,7 +220,7 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= raise NotImplementedError() # we compare ith coefficient and (degree - i)th coefficient to deduce the sign # note, if degree is even, the middle coefficient will not help us determine the sign - for i in range((degree + 1)//2): + for i in range((degree + 1) // 2): # Note: degree*weight is even p_power = p**min( charpoly_prec[i], @@ -248,19 +241,21 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= # note, this includes the middle coefficient if degree is even halfdegree = degree // 2 + 1 - cp[0] = sign * p**((a * degree * weight) // 2) # Note: degree*weight is even + cp[0] = sign * p**((a * degree * weight) // 2) + # Note: degree*weight is even + # calculate the i-th power sum of the roots and correct cp along the way e = cp[-halfdegree:] e.reverse() for k in range(halfdegree): - if k % 2 != 0: + if k % 2: e[k] = -e[k] % mod[degree - k] # e[k] = cp[degree - k] if (k%2 ==0) else -cp[degree - k] if k > 0: # verify if p^charpoly_prec[degree - k] > 2*degree/k * q^(w*k/2) assert ( - log(k) / log(p) + charpoly_prec[degree - k] - > log(2 * degree) / log(p) + a * 0.5 * weight * k + log(k, p) + charpoly_prec[degree - k] + > log(2 * degree, p) + a * 0.5 * weight * k ), ( "log(k)/log(p) + charpoly_prec[degree - k] <= log(2*degree)/log(p) + a*0.5*weight*k, k = %d" % k @@ -271,7 +266,7 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= if len(fix_e) < halfdegree: fix_e.extend([0] * (halfdegree - len(fix_e))) for i in range(halfdegree): - if i % 2 != 0: + if i % 2: fix_e[i] *= -1 # e[k] = \sum x_{i_1} x_{i_2} ... x_{i_k} # where x_* are eigenvalues @@ -280,7 +275,9 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= # s[k] = \sum x_i ^k for k>0 s = [None] * (halfdegree) res = [None] * len(charpoly_prec) - res[0] = sign * p**((a * degree * weight) // 2) # Note: degree*weight is even + res[0] = sign * p**((a * degree * weight) // 2) + # Note: degree*weight is even + res[-1] = 1 e[1] -= fix_e[1] e[1] = e[1] % mod[degree - 1] @@ -306,8 +303,9 @@ def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a=1, known_factor= # (-1)^(k-1) s[k] - S = k*e[k] e[k] = (-S + (-1)**(k - 1) * s[k]) // k assert (-S + (-1)**(k - 1) * s[k]) % k == 0 - res[degree - k] = e[k] if k % 2 == 0 else -e[k] + res[degree - k] = e[k] if not k % 2 else -e[k] # Note: degree*weight is even + res[k] = sign * res[degree - k] * p**((a * (degree - 2 * k) * weight) // 2) # fix e[k + 1] if k + 1 < halfdegree: diff --git a/src/sage/schemes/elliptic_curves/Qcurves.py b/src/sage/schemes/elliptic_curves/Qcurves.py index bf6b7b0d36d..4c9e8f3718a 100644 --- a/src/sage/schemes/elliptic_curves/Qcurves.py +++ b/src/sage/schemes/elliptic_curves/Qcurves.py @@ -9,7 +9,6 @@ The code here implements the algorithm of Cremona and Najman presented in [CrNa2020]_. """ - ############################################################################## # Copyright (C) 2020-2021 John Cremona <john.cremona@gmail.com> # @@ -24,10 +23,10 @@ # # https://www.gnu.org/licenses/ ############################################################################## - from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring import polygen + def is_Q_curve(E, maxp=100, certificate=False, verbose=False): r""" Return whether ``E`` is a `\QQ`-curve, with optional certificate. @@ -195,16 +194,25 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): 'core_poly': x^2 - 840064*x + 1593413632, 'r': 1, 'rho': 1} + + TESTS:: + + sage: E = EllipticCurve([GF(5)(t) for t in [2,3,5,7,11]]) + sage: is_Q_curve(E) + Traceback (most recent call last): + ... + TypeError: Elliptic Curve defined by ... must be an elliptic curve + defined over a number field """ from sage.rings.number_field.number_field_base import is_NumberField if verbose: - print("Checking whether {} is a Q-curve".format(E)) + print(f"Checking whether {E} is a Q-curve") try: assert is_NumberField(E.base_field()) except (AttributeError, AssertionError): - raise TypeError("{} must be an elliptic curve defined over a number field in is_Q_curve()") + raise TypeError(f"{E} must be an elliptic curve defined over a number field") from sage.rings.integer_ring import ZZ from sage.arith.functions import lcm @@ -224,7 +232,7 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): # test for CM for d, f, j in cm_j_invariants_and_orders(QQ): if jE == j: - return True, {'CM': d*f**2} + return True, {'CM': d * f**2} # else not CM return True, {'CM': ZZ(0), 'r': ZZ(0), 'rho': ZZ(0), 'N': ZZ(1), 'core_poly': polygen(QQ)} else: @@ -234,7 +242,7 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): flag, df = is_cm_j_invariant(jE) if flag: d, f = df - D = d*f**2 + D = d * f**2 if verbose: print("Yes: E is CM (discriminant {})".format(D)) if certificate: @@ -246,15 +254,15 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): K = E.base_field() jpoly = jE.minpoly() - if jpoly.degree()<K.degree(): + if jpoly.degree() < K.degree(): if verbose: print("switching to smaller base field: j's minpoly is {}".format(jpoly)) - f = pari(jpoly).polredbest().sage({'x':jpoly.parent().gen()}) + f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()}) K2 = NumberField(f, 'b') jE = jpoly.roots(K2)[0][0] if verbose: print("New j is {} over {}, with minpoly {}".format(jE, K2, jE.minpoly())) - #assert jE.minpoly()==jpoly + # assert jE.minpoly() == jpoly E = EllipticCurve(j=jE) K = K2 if verbose: @@ -265,14 +273,14 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): NN = E.conductor().norm() for p in NN.support(): Plist = K.primes_above(p) - if len(Plist)<2: + if len(Plist) < 2: continue # pot_mult = potential multiplicative reduction pot_mult = [jE.valuation(P) < 0 for P in Plist] consistent = all(pot_mult) or not any(pot_mult) if not consistent: if verbose: - print("No: inconsistency at the {} primes dividing {}".format(len(Plist),p)) + print(f"No: inconsistency at the {len(Plist)} primes dividing {p}") print(" - potentially multiplicative: {}".format(pot_mult)) if certificate: return False, p @@ -308,13 +316,14 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): if certificate: for f in centrejpols: rho = f.degree().valuation(2) - centre_indices = [i for i,j in enumerate(jC) if f(j) == 0] + centre_indices = [i for i, j in enumerate(jC) if f(j) == 0] M = C.matrix() core_degs = [M[centre_indices[0], i] for i in centre_indices] level = lcm(core_degs) if level.is_squarefree(): r = len(level.prime_divisors()) - cert = {'CM': ZZ(0), 'core_poly':f, 'rho':rho, 'r':r, 'N':level, 'core_degs':core_degs} + cert = {'CM': ZZ(0), 'core_poly': f, 'rho': rho, + 'r': r, 'N': level, 'core_degs': core_degs} return True, cert print("No central curve found") else: @@ -380,6 +389,7 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): else: return False + def Step4Test(E, B, oldB=0, verbose=False): r""" Apply local Q-curve test to E at all primes up to B. @@ -450,7 +460,7 @@ def Step4Test(E, B, oldB=0, verbose=False): consistent = all(ordinary) or not any(ordinary) if not consistent: if verbose: - print("No: inconsistency at the {} primes dividing {} ".format(len(Plist),p)) + print(f"No: inconsistency at the {len(Plist)} primes dividing {p} ") print(" - ordinary: {}".format(ordinary)) return False, p @@ -469,6 +479,7 @@ def Step4Test(E, B, oldB=0, verbose=False): # Now we have failed to prove that E is not a Q-curve return True, 0 + def conjugacy_test(jlist, verbose=False): r""" Test whether a list of algebraic numbers contains a complete @@ -524,7 +535,7 @@ def conjugacy_test(jlist, verbose=False): if verbose: print("Yes: an isogenous curve has rational j-invariant {}".format(jQ)) x = polygen(QQ) - return [x-jQ] + return [x - jQ] # If the degree d is odd then we know that none of the # j-invariants in the class have 2-power degree, so we can exit. @@ -538,13 +549,13 @@ def conjugacy_test(jlist, verbose=False): # If K has no quadratic subfields we can similarly conclude right # away. This is one way of determining this. - if K(1).descend_mod_power(QQ,2) == [1]: + if K(1).descend_mod_power(QQ, 2) == [1]: if verbose: print("No-quadratic-subfield case: no rational j-invariant in the class {}".format(jlist)) return [] # compute the minimum polynomials of the j-invariants in the class - pols = [j.minpoly() for j in jlist] + pols = (j.minpoly() for j in jlist) # pick out those of 2-power degree pols = [f for f in pols if f.degree().prime_to_m_part(2) == 1] @@ -558,7 +569,7 @@ def conjugacy_test(jlist, verbose=False): # classes defined over the core field but not central, so we # return all those with the minimal degree. - mindeg = min([f.degree() for f in pols]) + mindeg = min(f.degree() for f in pols) minpols = [f for f in pols if f.degree() == mindeg] centrepols = list(Set([f for f in pols if f.degree() == minpols.count(f)])) if centrepols: diff --git a/src/sage/schemes/elliptic_curves/cardinality.py b/src/sage/schemes/elliptic_curves/cardinality.py index ea12ffa0965..cc93b9ecf74 100644 --- a/src/sage/schemes/elliptic_curves/cardinality.py +++ b/src/sage/schemes/elliptic_curves/cardinality.py @@ -2,28 +2,23 @@ Specific algorithms to compute cardinality of elliptic curves over a finite field Since point counting now uses PARI/GP, this code is only used when a -specific algorithm was specified or when the j-invariant lies in a -subfield. +specific algorithm was specified or when the j-invariant lies in a subfield. AUTHORS: - John Cremona (2008-2009): Original point counting code -- Jeroen Demeyer (2017-2018): Refactored and moved to - ``cardinality.py``. +- Jeroen Demeyer (2017-2018): Refactored and moved to ``cardinality.py``. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 John Cremona # # 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 .constructor import EllipticCurve, EllipticCurve_from_j from sage.schemes.curves.projective_curve import Hasse_bounds from sage.rings.all import Integer, ZZ, GF, polygen @@ -40,19 +35,19 @@ def _cardinality_with_j_invariant_1728(self): An example with q=p=1 (mod 4):: - sage: F=GF(10009) + sage: F = GF(10009) sage: [_cardinality_with_j_invariant_1728(EllipticCurve(F,[0,0,0,11^i,0])) for i in range(4)] [10016, 10210, 10004, 9810] An example with q=p=3 (mod 4):: - sage: F=GF(10007) + sage: F = GF(10007) sage: [_cardinality_with_j_invariant_1728(EllipticCurve(F,[0,0,0,5^i,0])) for i in range(4)] [10008, 10008, 10008, 10008] An example with `q=p^2`, p=3 (mod 4):: - sage: F.<a>=GF(10007^2,'a') + sage: F.<a> = GF(10007^2,'a') sage: [_cardinality_with_j_invariant_1728(EllipticCurve(F,[0,0,0,a^i,0])) for i in range(4)] [100160064, 100140050, 100120036, 100140050] @@ -90,11 +85,11 @@ def _cardinality_with_j_invariant_1728(self): Examples with `q=3^d`, d even (6 isomorphism classes):: - sage: F.<g>=GF(3^18,'g') - sage: i=F(-1).sqrt() - sage: a=g^8 # has trace 1 - sage: ais= [[0,0,0,1,0],[0,0,0,1,i*a],[0,0,0,g,0],[0,0,0,g^3,0],[0,0,0,g^2,0], [0,0,0,g^2,i*a*g^3]] - sage: curves=[EllipticCurve(F,ai) for ai in ais] + sage: F.<g> = GF(3^18,'g') + sage: i = F(-1).sqrt() + sage: a = g^8 # has trace 1 + sage: ais = [[0,0,0,1,0],[0,0,0,1,i*a],[0,0,0,g,0],[0,0,0,g^3,0],[0,0,0,g^2,0], [0,0,0,g^2,i*a*g^3]] + sage: curves = [EllipticCurve(F,ai) for ai in ais] sage: all((e1 == e2 or not e1.is_isomorphic(e2)) for e1 in curves for e2 in curves) True sage: [_cardinality_with_j_invariant_1728(e) for e in curves] @@ -104,7 +99,7 @@ def _cardinality_with_j_invariant_1728(self): Check that a bug noted at :trac:`15667` is fixed:: - sage: F.<a>=GF(3^6,'a') + sage: F.<a> = GF(3^6,'a') sage: EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1]).cardinality() 784 sage: EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1]).cardinality_exhaustive() @@ -116,27 +111,27 @@ def _cardinality_with_j_invariant_1728(self): pass k = self.base_ring() - assert self.j_invariant()==k(1728) + assert self.j_invariant() == k(1728) q = k.cardinality() p = k.characteristic() d = k.degree() - x=polygen(ZZ) + x = polygen(ZZ) # p=2, j=0=1728 # # Number of isomorphism classes is 3 (in odd degree) or 7 (in even degree) # - if p==2: - if d%2==1: + if p == 2: + if d % 2: # The 3 classes are represented, independently of d, # by [0,0,1,0,0], [0,0,1,1,0], [0,0,1,1,1] - E=EllipticCurve(k,[0,0,1,0,0]) + E = EllipticCurve(k, [0, 0, 1, 0, 0]) if self.is_isomorphic(E): t = 0 else: - n = (d+1)//2 + n = (d + 1) // 2 t = 2**n - n = n%4 + n = n % 4 if n == 0 or n == 1: t = -t E = EllipticCurve(k, [0, 0, 1, 1, 1]) @@ -153,31 +148,31 @@ def _cardinality_with_j_invariant_1728(self): # unity, so the traces are 2*pi, -2*pi, 0, -pi, +pi; # -pi, +pi. delta = self.discriminant() - discube = (delta**((q-1)//3) == k(1)) - pi = (-2)**(d//2) + discube = (delta**((q - 1) // 3) == k(1)) + pi = (-2)**(d // 2) if discube: a = k.gen() b = a while b.trace() == 0: b *= a - if self.is_isomorphic(EllipticCurve(k,[0,0,1,b,0])): + if self.is_isomorphic(EllipticCurve(k, [0, 0, 1, b, 0])): t = 0 else: - t = 2*pi - if not self.is_isomorphic(EllipticCurve(k,[0,0,1,0,0])): + t = 2 * pi + if not self.is_isomorphic(EllipticCurve(k, [0, 0, 1, 0, 0])): t = -t else: t = pi - if self.is_isomorphic(EllipticCurve(k,[0,0,delta,0,0])): + if self.is_isomorphic(EllipticCurve(k, [0, 0, delta, 0, 0])): t = -t # p=3, j=0=1728 # # Number of isomorphism classes is 4 (odd degree) or 6 (even degree) # - elif p==3: - if d%2==1: + elif p == 3: + if d % 2: # The 4 classes are represented by [0,0,0,1,0], # [0,0,0,-1,0], [0,0,0,-1,a], [0,0,0,-1,-a] where a # has trace 1 @@ -188,13 +183,13 @@ def _cardinality_with_j_invariant_1728(self): u = delta.sqrt() if not u.is_square(): u = -u - tr = ((self.a3()**2+self.a6())/u).trace() - if tr==0: + tr = ((self.a3()**2 + self.a6()) / u).trace() + if tr == 0: t = 0 else: - d2 = (d+1)//2 + d2 = (d + 1) // 2 t = 3**d2 - if d2%2 == 1: + if d2 % 2: t = -t if tr == -1: t = -t @@ -207,13 +202,13 @@ def _cardinality_with_j_invariant_1728(self): # The curve is isomorphic to [0,0,0,A4,A6] - A4 = self.a4() - self.a1()*self.a3() # = -b4 = 2*b4 + A4 = self.a4() - self.a1() * self.a3() # = -b4 = 2*b4 if A4.is_square(): u = A4.sqrt() - t = (-3)**(d//2) + t = (-3)**(d // 2) i = k(-1).sqrt() A6 = self.a3()**2 + self.a6() # = b6 - if (A6/(i*u*A4)).trace()==0: + if (A6 / (i * u * A4)).trace() == 0: t *= 2 else: t *= -1 @@ -226,16 +221,16 @@ def _cardinality_with_j_invariant_1728(self): # # Number of isomorphism classes is 4 if q=1 (mod 4), else 2 # - elif p%4==3: - if d%2==1: + elif p % 4 == 3: + if d % 2: t = 0 else: - t = (-p)**(d//2) - w = (self.c4()/k(48))**((q-1)//4) + t = (-p)**(d // 2) + w = (self.c4() / k(48))**((q - 1) // 4) if w == 1: - t = 2*t + t = 2 * t elif w == -1: - t = -2*t + t = -2 * t else: t = 0 @@ -243,31 +238,31 @@ def _cardinality_with_j_invariant_1728(self): # N(pi)=p and N(pi-1)=0 (mod 8). # else: - R = ZZ.extension(x**2+1,'i') + R = ZZ.extension(x**2 + 1, 'i') i = R.gen(1) pi = R.fraction_field().factor(p)[0][0].gens_reduced()[0] - a,b = pi.list() - if a%2==0: - a,b = -b,a - if (a+b+1)%4==0: - a,b = -a,-b - pi = a+b*i # Now pi=a+b*i with (a,b)=(1,0),(3,2) mod 4 + a, b = pi.list() + if a % 2 == 0: + a, b = -b, a + if (a + b + 1) % 4 == 0: + a, b = -a, -b + pi = a + b * i # Now pi=a+b*i with (a,b)=(1,0),(3,2) mod 4 # Lift to Frobenius for [0,0,0,-1,0] over GF(p^d): - if d>1: + if d > 1: pi = pi**d - a,b = pi.list() + a, b = pi.list() # Compute appropriate quartic twist: - w = (self.c4()/k(48))**((q-1)//4) - if w==1: - t = 2*a - elif w==-1: - t = -2*a - elif k(b)==w*k(a): - t = 2*b + w = (self.c4() / k(48))**((q - 1) // 4) + if w == 1: + t = 2 * a + elif w == -1: + t = -2 * a + elif k(b) == w * k(a): + t = 2 * b else: - t = -2*b + t = -2 * b return Integer(q + 1 - t) @@ -282,19 +277,19 @@ def _cardinality_with_j_invariant_0(self): An example with q=p=1 (mod 6):: - sage: F=GF(1009) + sage: F = GF(1009) sage: [_cardinality_with_j_invariant_0(EllipticCurve(F,[0,0,0,0,11^i])) for i in range(6)] [948, 967, 1029, 1072, 1053, 991] An example with q=p=5 (mod 6):: - sage: F=GF(1013) + sage: F = GF(1013) sage: [_cardinality_with_j_invariant_0(EllipticCurve(F,[0,0,0,0,3^i])) for i in range(6)] [1014, 1014, 1014, 1014, 1014, 1014] An example with `q=p^2`, p=5 (mod 6):: - sage: F.<a>=GF(1013^2,'a') + sage: F.<a> = GF(1013^2,'a') sage: [_cardinality_with_j_invariant_0(EllipticCurve(F,[0,0,0,0,a^i])) for i in range(6)] [1028196, 1027183, 1025157, 1024144, 1025157, 1027183] @@ -302,29 +297,29 @@ def _cardinality_with_j_invariant_0(self): :func:`_cardinality_with_j_invariant_1728`. """ k = self.base_ring() - assert self.j_invariant()==k(0) + assert self.j_invariant() == k.zero() p = k.characteristic() - if p==2 or p==3: # then 0==1728 + if p == 2 or p == 3: # then 0==1728 return _cardinality_with_j_invariant_1728(self) q = k.cardinality() d = k.degree() - x=polygen(ZZ) + x = polygen(ZZ) # p>3, j=0 # # Number of isomorphism classes is 4 if q=1 (mod 4), else 2 # - if p%6==5: - if d%2==1: + if p % 6 == 5: + if d % 2: t = 0 else: - t = (-p)**(d//2) - w = (self.c6()/k(-864))**((q-1)//6) + t = (-p)**(d // 2) + w = (self.c6() / k(-864))**((q - 1) // 6) if w == 1: - t = 2*t + t = 2 * t elif w == -1: - t = -2*t + t = -2 * t elif w**3 == 1: t = -t @@ -332,45 +327,46 @@ def _cardinality_with_j_invariant_0(self): # N(pi)=p and N(pi-1)=0 (mod 12). # else: - R = ZZ.extension(x**2-x+1,'zeta6') + R = ZZ.extension(x**2 - x + 1, 'zeta6') zeta6 = R.gen(1) pi = R.fraction_field().factor(p)[0][0].gens_reduced()[0] while (pi - 1).norm() % 12: pi *= zeta6 - a,b = pi.list() - z = k(-b)/k(a) # a *specific* 6th root of unity in k + a, b = pi.list() + z = k(-b) / k(a) # a *specific* 6th root of unity in k # Now pi=a+b*zeta6 with N(pi-1)=0 (mod 12) # Lift to Frobenius for [0,0,0,0,1] over GF(p^d): - if d>1: + if d > 1: pi = pi**d - a,b = pi.list() + a, b = pi.list() # Compute appropriate sextic twist: - w = (self.c6()/k(-864))**((q-1)//6) + w = (self.c6() / k(-864))**((q - 1) // 6) if w == 1: - t = 2*a+b # = Trace(pi) + t = 2 * a + b # = Trace(pi) elif w == -1: - t = -2*a-b # = Trace(-pi) + t = -2 * a - b # = Trace(-pi) elif w == z: - t = a-b # = Trace(pi*zeta6) + t = a - b # = Trace(pi*zeta6) elif w == z**2: - t = -a-2*b # = Trace(pi*zeta6**2) + t = -a - 2 * b # = Trace(pi*zeta6**2) elif w == z**4: - t = b-a # = Trace(pi*zeta6**4) + t = b - a # = Trace(pi*zeta6**4) elif w == z**5: - t = a+2*b # = Trace(pi*zeta6**5) + t = a + 2 * b # = Trace(pi*zeta6**5) return Integer(q + 1 - t) def cardinality_exhaustive(self): r""" - Return the cardinality of self over the base field. Simply adds up - the number of points with each x-coordinate: only used for small - field sizes! + Return the cardinality of ``self`` over the base field. + + This simply adds up the number of points with each x-coordinate: + only used for small field sizes! EXAMPLES:: @@ -382,13 +378,14 @@ def cardinality_exhaustive(self): sage: E.cardinality_exhaustive() 64 """ - self._order = Integer(1+sum([len(self.lift_x(x,all=True)) for x in self.base_field()])) + self._order = Integer(1 + sum(len(self.lift_x(x, all=True)) + for x in self.base_field())) return self._order def cardinality_bsgs(self, verbose=False): r""" - Return the cardinality of self over the base field. + Return the cardinality of ``self`` over the base field. ALGORITHM: A variant of "Mestre's trick" extended to all finite fields by Cremona and Sutherland, 2008. @@ -404,15 +401,15 @@ def cardinality_bsgs(self, verbose=False): EXAMPLES:: - sage: p=next_prime(10^3) - sage: E=EllipticCurve(GF(p),[3,4]) + sage: p = next_prime(10^3) + sage: E = EllipticCurve(GF(p),[3,4]) sage: E.cardinality_bsgs() 1020 - sage: E=EllipticCurve(GF(3^4,'a'),[1,1]) + sage: E = EllipticCurve(GF(3^4,'a'),[1,1]) sage: E.cardinality_bsgs() 64 - sage: F.<a>=GF(101^3,'a') - sage: E=EllipticCurve([2*a^2 + 48*a + 27, 89*a^2 + 76*a + 24]) + sage: F.<a> = GF(101^3,'a') + sage: E = EllipticCurve([2*a^2 + 48*a + 27, 89*a^2 + 76*a + 24]) sage: E.cardinality_bsgs() 1031352 """ @@ -431,12 +428,12 @@ def cardinality_bsgs(self, verbose=False): bounds = Hasse_bounds(q) lower, upper = bounds - B = upper-q-1 # = floor(2*sqrt(q)) + B = upper - q - 1 # = floor(2*sqrt(q)) a = ZZ(0) N1 = N2 = M = ZZ(1) kmin = -B kmax = B - q1 = q+1 + q1 = q + 1 # Throughout, we have #E=q+1-t where |t|<=B and t=a+k*M = a # (mod M) where kmin <= k <= kmax. @@ -445,28 +442,28 @@ def cardinality_bsgs(self, verbose=False): # kmin=kmax. if q > 2**10: - N1 = ZZ(2)**sum([e for P,e in E1._p_primary_torsion_basis(2)]) - N2 = ZZ(2)**sum([e for P,e in E2._p_primary_torsion_basis(2)]) + N1 = ZZ(2)**sum([e for P, e in E1._p_primary_torsion_basis(2)]) + N2 = ZZ(2)**sum([e for P, e in E2._p_primary_torsion_basis(2)]) if q > 2**20: - N1 *= ZZ(3)**sum([e for P,e in E1._p_primary_torsion_basis(3)]) - N2 *= ZZ(3)**sum([e for P,e in E2._p_primary_torsion_basis(3)]) + N1 *= ZZ(3)**sum([e for P, e in E1._p_primary_torsion_basis(3)]) + N2 *= ZZ(3)**sum([e for P, e in E2._p_primary_torsion_basis(3)]) if q > 2**40: - N1 *= ZZ(5)**sum([e for P,e in E1._p_primary_torsion_basis(5)]) - N2 *= ZZ(5)**sum([e for P,e in E2._p_primary_torsion_basis(5)]) + N1 *= ZZ(5)**sum([e for P, e in E1._p_primary_torsion_basis(5)]) + N2 *= ZZ(5)**sum([e for P, e in E2._p_primary_torsion_basis(5)]) # We now know that t=q+1 (mod N1) and t=-(q+1) (mod N2) a = q1 M = N1 - g,u,v = M.xgcd(N2) # g==u*M+v*N2 - if N2>g: - a = (a*v*N2-q1*u*M)//g - M *= (N2//g) # = lcm(M,N2) - a = a%M + g, u, v = M.xgcd(N2) # g==u*M+v*N2 + if N2 > g: + a = (a * v * N2 - q1 * u * M) // g + M *= (N2 // g) # = lcm(M,N2) + a = a % M if verbose: print("(a,M)=", (a, M)) - kmin = ((-B-a)/M).ceil() - kmax = ((B-a)/M).floor() - if kmin==kmax: - self._order = q1-a-kmin*M + kmin = ((-B - a) / M).ceil() + kmax = ((B - a) / M).floor() + if kmin == kmax: + self._order = q1 - a - kmin * M if verbose: print("no random points were needed") return self._order @@ -478,51 +475,51 @@ def cardinality_bsgs(self, verbose=False): # points on each curve. For large q it is worth initializing # these with the full order of the (2,3,5)-torsion which are # often non-trivial. - while kmax!=kmin: + while kmax != kmin: # Get a random point on E1 and find its order, using the # Hasse bounds and the fact that we know that the group # order is a multiple of N1: - n = order_from_bounds(E1.random_point(),bounds,N1,operation='+') + n = order_from_bounds(E1.random_point(), bounds, N1, operation='+') if verbose: print("New point on E has order ", n) # update N1 and M N1 = N1.lcm(n) - g,u,v = M.xgcd(n) # g==u*M+v*n - if n>g: + g, u, v = M.xgcd(n) # g==u*M+v*n + if n > g: # update congruence a (mod M) with q+1 (mod n) - a = (a*v*n+q1*u*M)//g - M *= (n//g) # = lcm(M,n) - a = a%M + a = (a * v * n + q1 * u * M) // g + M *= (n // g) # = lcm(M,n) + a = a % M if verbose: print("(a,M)=", (a, M)) - kmin = ((-B-a)/M).ceil() - kmax = ((B-a)/M).floor() - if kmin==kmax: - self._order = q1-a-kmin*M + kmin = ((-B - a) / M).ceil() + kmax = ((B - a) / M).floor() + if kmin == kmax: + self._order = q1 - a - kmin * M return self._order if verbose: - print("number of possibilities is now ",kmax-kmin+1) + print("number of possibilities is now ", kmax - kmin + 1) # Get a random point on E2 and find its order, using the # Hasse bounds and the fact that we know that the group # order is a multiple of N2: - n = order_from_bounds(E2.random_point(),bounds,N2,operation='+') + n = order_from_bounds(E2.random_point(), bounds, N2, operation='+') if verbose: print("New point on E' has order ", n) # update N2 and M N2 = N2.lcm(n) - g,u,v = M.xgcd(n) # g==u*M+v*n - if n>g: + g, u, v = M.xgcd(n) # g==u*M+v*n + if n > g: # update congruence a (mod M) with -(q+1) (mod n) - a = (a*v*n-q1*u*M)//g - M *= (n//g) # = lcm(M,n) - a = a%M + a = (a * v * n - q1 * u * M) // g + M *= (n // g) # = lcm(M,n) + a = a % M if verbose: print("(a,M)=", (a, M)) - kmin = ((-B-a)/M).ceil() - kmax = ((B-a)/M).floor() - if kmin==kmax: - self._order = q1-a-kmin*M + kmin = ((-B - a) / M).ceil() + kmax = ((B - a) / M).floor() + if kmin == kmax: + self._order = q1 - a - kmin * M return self._order if verbose: print("number of possibilities is now ", kmax - kmin + 1) @@ -598,4 +595,4 @@ def _cardinality_subfield(self, jpol): return N else: q = k.cardinality() - return 2*(q+1) - N + return 2 * (q + 1) - N diff --git a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx index 0c3a7a72385..5a265b40c85 100644 --- a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx +++ b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx @@ -2,15 +2,15 @@ r""" Descent on elliptic curves over `\QQ` with a 2-isogeny """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2009 Robert L. Miller <rlmillster@gmail.com> # # 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 cysignals.memory cimport sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off @@ -316,7 +316,7 @@ cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, result = 0 (<nmod_poly_factor_struct *>f_factzn)[0].num = 0 # reset data struct qq = nmod_poly_factor(f_factzn, f) - for i from 0 <= i < f_factzn.num: + for i in range(f_factzn.num): if f_factzn.exp[i]&1: result = 1 break @@ -327,15 +327,15 @@ cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, nmod_poly_zero(f) nmod_poly_set_coeff_ui(f, 0, 1) - for i from 0 <= i < f_factzn.num: - for j from 0 <= j < (f_factzn.exp[i]>>1): + for i in range(f_factzn.num): + for j in range(f_factzn.exp[i]>>1): nmod_poly_mul(f, f, &f_factzn.p[i]) (<nmod_poly_factor_struct *>f_factzn)[0].num = 0 # reset data struct nmod_poly_factor(f_factzn, f) has_roots = 0 j = 0 - for i from 0 <= i < f_factzn.num: + for i in range(f_factzn.num): if nmod_poly_degree(&f_factzn.p[i]) == 1 and 0 != nmod_poly_get_coeff_ui(&f_factzn.p[i], 1): has_roots = 1 roots[j] = pp_ui - nmod_poly_get_coeff_ui(&f_factzn.p[i], 0) @@ -393,7 +393,7 @@ cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, result = 0 mpz_init(tt) - for i from 0 <= i < j: + for i in range(j): mpz_mul_ui(tt, aaa, roots[i]) mpz_add(tt, tt, bbb) mpz_mul_ui(tt, tt, roots[i]) @@ -454,7 +454,7 @@ cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, has_roots = 0 has_single_roots = 0 j = 0 - for i from 0 <= i < f_factzn.num: + for i in range(f_factzn.num): if nmod_poly_degree(&f_factzn.p[i]) == 1 and 0 != nmod_poly_get_coeff_ui(&f_factzn.p[i], 1): has_roots = 1 if f_factzn.exp[i] == 1: @@ -473,7 +473,7 @@ cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, mpz_init(cc) mpz_init(dd) mpz_init(ee) - for i from 0 <= i < j: + for i in range(j): fmpz_poly_zero(f1) fmpz_poly_zero(linear) fmpz_poly_set_coeff_mpz(f1, 0, e) @@ -558,7 +558,7 @@ cdef bint Zp_soluble_siksek_large_p(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, f = ntl.ZZ_pX([1], P) for factor, exponent in f_factzn: - for j from 0 <= j < (exponent/2): + for j in range(exponent // 2): f *= factor f /= f.leading_coefficient() @@ -631,7 +631,7 @@ cdef bint Zp_soluble_siksek_large_p(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, result = 0 mpz_init(tt) - for i from 0 <= i < j: + for i in range(j): mpz_mul(tt, aaa, roots[i]) mpz_add(tt, tt, bbb) mpz_mul(tt, tt, roots[i]) @@ -712,7 +712,7 @@ cdef bint Zp_soluble_siksek_large_p(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, mpz_init(cc) mpz_init(dd) mpz_init(ee) - for i from 0 <= i < j: + for i in range(j): fmpz_poly_zero(f1) fmpz_poly_zero(linear) fmpz_poly_set_coeff_mpz(f1, 0, e) @@ -958,7 +958,7 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len # Set up coefficient array, and static variables cdef mpz_t *coeffs = <mpz_t *> sig_malloc(5 * sizeof(mpz_t)) - for i from 0 <= i <= 4: + for i in range(5): mpz_init(coeffs[i]) mpz_set_ui(coeffs[1], 0) # mpz_set(coeffs[2], c_mpz) # These never change @@ -969,7 +969,7 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len # local solubility over RR) cdef mpz_t *p_div_d_mpz = <mpz_t *> sig_malloc((p_list_len+1) * sizeof(mpz_t)) n_primes = 0 - for i from 0 <= i < p_list_len: + for i in range(p_list_len): if mpz_divisible_p(d_mpz, p_list[i]): mpz_init(p_div_d_mpz[n_primes]) mpz_set(p_div_d_mpz[n_primes], p_list[i]) @@ -987,7 +987,7 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len mpz_set_ui(n2, 0) while mpz_cmp(j, n_divisors) < 0: mpz_set_ui(coeffs[4], 1) - for i from 0 <= i < n_primes: + for i in range(n_primes): if mpz_tstbit(j, i): mpz_mul(coeffs[4], coeffs[4], p_div_d_mpz[i]) if verbosity > 3: @@ -1012,7 +1012,7 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len if not found_global_points: # Test whether the quartic is everywhere locally soluble: els = 1 - for i from 0 <= i < p_list_len: + for i in range(p_list_len): if not Qp_soluble(coeffs[4], coeffs[3], coeffs[2], coeffs[1], coeffs[0], p_list[i]): els = 0 break @@ -1035,11 +1035,11 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len print("\nDone calling ratpoints for large point search") mpz_add_ui(j, j, 1) mpz_clear(j) - for i from 0 <= i < n_primes: + for i in range(n_primes): mpz_clear(p_div_d_mpz[i]) sig_free(p_div_d_mpz) mpz_clear(n_divisors) - for i from 0 <= i <= 4: + for i in range(5): mpz_clear(coeffs[i]) sig_free(coeffs) return 0 @@ -1202,16 +1202,16 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, p_list_len = 1 n_factor_init(&fact) n_factor(&fact, mpz_get_ui(d_mpz), proof) - for i from 0 <= i < fact.num: + for i in range(fact.num): p = fact.p[i] if p != 2: mpz_init_set_ui(p_list_mpz[p_list_len], p) p_list_len += 1 n_factor(&fact, mpz_get_ui(d_prime_mpz), proof) - for i from 0 <= i < fact.num: + for i in range(fact.num): p = fact.p[i] found = 0 - for j from 0 <= j < p_list_len: + for j in range(p_list_len): if mpz_cmp_ui(p_list_mpz[j], p)==0: found = 1 break @@ -1233,7 +1233,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, if P not in primes: primes.append(P) p_list_len = len(primes) p_list_mpz = <mpz_t *> sig_malloc(p_list_len * sizeof(mpz_t)) - for i from 0 <= i < p_list_len: + for i in range(p_list_len): P = Integer(primes[i]) mpz_init_set(p_list_mpz[i], P.value) if d_neg: @@ -1257,7 +1257,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, global_limit_small, global_limit_large, verbosity, selmer_only, n1_prime.value, n2_prime.value) - for i from 0 <= i < p_list_len: + for i in range(p_list_len): mpz_clear(p_list_mpz[i]) sig_free(p_list_mpz) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 9c3cb195eff..68194f600c2 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -18,14 +18,18 @@ sage: Q = E(6,5) sage: phi = E.isogeny(Q) sage: phi - Isogeny of degree 7 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 + Isogeny of degree 7 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: P = E(4,5) sage: phi(P) (10 : 0 : 1) sage: phi.codomain() Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11 sage: phi.rational_maps() - ((x^7 + 4*x^6 - 3*x^5 - 2*x^4 - 3*x^3 + 3*x^2 + x - 2)/(x^6 + 4*x^5 - 4*x^4 - 5*x^3 + 5*x^2), (x^9*y - 5*x^8*y - x^7*y + x^5*y - x^4*y - 5*x^3*y - 5*x^2*y - 2*x*y - 5*y)/(x^9 - 5*x^8 + 4*x^6 - 3*x^4 + 2*x^3)) + ((x^7 + 4*x^6 - 3*x^5 - 2*x^4 - 3*x^3 + 3*x^2 + x - 2)/(x^6 + 4*x^5 - 4*x^4 + - 5*x^3 + 5*x^2), (x^9*y - 5*x^8*y - x^7*y + x^5*y - x^4*y - 5*x^3*y - + 5*x^2*y - 2*x*y - 5*y)/(x^9 - 5*x^8 + 4*x^6 - 3*x^4 + 2*x^3)) The methods directly accessible from an elliptic curve ``E`` over a field are @@ -63,6 +67,7 @@ use of univariate vs. bivariate polynomials and rational functions. - Lorenz Panny (2022-04): major cleanup of code and documentation +- Lorenz Panny (2022): inseparable duals """ # **************************************************************************** @@ -88,7 +93,7 @@ from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve from sage.schemes.elliptic_curves.weierstrass_morphism \ - import WeierstrassIsomorphism, isomorphisms, baseWI + import WeierstrassIsomorphism, _isomorphisms, baseWI, negation_morphism # # Private function for parsing input to determine the type of @@ -337,7 +342,6 @@ 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 @@ -374,7 +378,6 @@ 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 @@ -481,7 +484,6 @@ 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 @@ -516,6 +518,7 @@ def two_torsion_part(E, psi): psi_2 = E.two_division_polynomial(x) return psi.gcd(psi_2) + class EllipticCurveIsogeny(EllipticCurveHom): r""" This class implements separable isogenies of elliptic curves. @@ -813,18 +816,15 @@ class EllipticCurveIsogeny(EllipticCurveHom): sage: phi.codomain() Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31 - Composition tests (see :trac:`16245`):: + Composition tests (see :trac:`16245`, cf. :trac:`34410`):: sage: E = EllipticCurve(j=GF(7)(0)) sage: phi = E.isogeny([E(0), E((0,1)), E((0,-1))]); phi Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 sage: phi2 = phi * phi; phi2 - Composite map: + Composite morphism of degree 9 = 3^2: From: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 To: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 - Defn: Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 - then - Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 Examples over relative number fields used not to work (see :trac:`16779`):: @@ -1019,12 +1019,6 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True # Inheritance house keeping self.__perform_inheritance_housekeeping() - # over finite fields, isogenous curves have the same number - # of rational points, hence we copy over cached curve orders - if self.__base_field.is_finite(): - self._codomain._fetch_cached_order(self._domain) - self._domain._fetch_cached_order(self._codomain) - def _eval(self, P): r""" Less strict evaluation method for internal use. @@ -1158,6 +1152,16 @@ def _call_(self, P): Traceback (most recent call last): ... TypeError: (20 : 90 : 1) fails to convert into the map's domain Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3, but a `pushforward` method is not properly implemented + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P) + sage: phi(Q)._order + 27 """ if P.is_zero(): return self._codomain(0) @@ -1188,7 +1192,16 @@ def _call_(self, P): yP = self.__posti_ratl_maps[1](xP, yP) xP = self.__posti_ratl_maps[0](xP) - return self._codomain(xP, yP) + Q = self._codomain(xP, yP) + if hasattr(P, '_order') and P._order.gcd(self._degree).is_one(): + # TODO: For non-coprime degree, the order of the point + # gets reduced by a divisor of the degree when passing + # through the isogeny. We could run something along the + # lines of order_from_multiple() to determine the new + # order, but this probably shouldn't happen by default + # as it'll be detrimental to performance in some cases. + Q._order = P._order + return Q def __getitem__(self, i): r""" @@ -1276,11 +1289,55 @@ def __neg__(self): sage: P = E((7,13)) sage: phi(P) + negphi(P) == 0 True + + sage: E = EllipticCurve(GF(23), [0,0,0,1,0]) + sage: f = E.torsion_polynomial(3)/3 + sage: phi = EllipticCurveIsogeny(E, f, E) + sage: phi.rational_maps() == E.multiplication_by_m(3) + False + sage: negphi = -phi + sage: negphi.rational_maps() == E.multiplication_by_m(3) + True + + sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11]) + sage: R.<x> = GF(17)[] + sage: f = x+6 + sage: phi = EllipticCurveIsogeny(E, f) + sage: phi + Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17 + sage: phi.rational_maps() + ((x^2 + 6*x + 4)/(x + 6), (x^2*y - 5*x*y + 8*x - 2*y)/(x^2 - 5*x + 2)) + sage: negphi = -phi + sage: negphi + Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17 + sage: negphi.rational_maps() + ((x^2 + 6*x + 4)/(x + 6), + (2*x^3 - x^2*y - 5*x^2 + 5*x*y - 4*x + 2*y + 7)/(x^2 - 5*x + 2)) + + sage: E = EllipticCurve('11a1') + sage: R.<x> = QQ[] + sage: f = x^2 - 21*x + 80 + sage: phi = EllipticCurveIsogeny(E, f) + sage: (xmap1, ymap1) = phi.rational_maps() + sage: negphi = -phi + sage: (xmap2, ymap2) = negphi.rational_maps() + sage: xmap1 == xmap2 + True + sage: ymap1 == -ymap2 - E.a1()*xmap2 - E.a3() + True + + sage: K.<a> = NumberField(x^2 + 1) + sage: E = EllipticCurve(K, [0,0,0,1,0]) + sage: R.<x> = K[] + sage: phi = EllipticCurveIsogeny(E, x-a) + sage: phi.rational_maps() + ((x^2 + (-a)*x - 2)/(x + (-a)), (x^2*y + (-2*a)*x*y + y)/(x^2 + (-2*a)*x - 1)) + sage: negphi = -phi + sage: negphi.rational_maps() + ((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1)) """ output = copy(self) - E2 = output._codomain - iso = WeierstrassIsomorphism(E2, (-1, 0, -E2.a1(), -E2.a3())) - output._set_post_isomorphism(iso) + output._set_post_isomorphism(negation_morphism(output._codomain)) return output # @@ -1344,9 +1401,8 @@ def __clear_cached_values(self): sage: E = EllipticCurve(QQ, [0,0,0,1,0]) sage: phi = EllipticCurveIsogeny(E, x) sage: old_ratl_maps = phi.rational_maps() - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,0,0,0))) - ... + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism + sage: phi._set_post_isomorphism(negation_morphism(phi.codomain())) sage: old_ratl_maps == phi.rational_maps() False sage: old_ratl_maps[1] == -phi.rational_maps()[1] @@ -1358,8 +1414,7 @@ def __clear_cached_values(self): sage: phi = EllipticCurveIsogeny(E, f) sage: old_ratl_maps = phi.rational_maps() sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-13,13,-13,13))) - ... + sage: phi._set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-13,13,-13,13))) sage: old_ratl_maps == phi.rational_maps() False sage: phi._EllipticCurveIsogeny__clear_cached_values() @@ -1384,12 +1439,10 @@ def __perform_inheritance_housekeeping(self): sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism sage: E2 = phi.codomain() sage: post_isom = WeierstrassIsomorphism(E2, (41, 37, 31, 29)) - sage: phi.set_post_isomorphism(post_isom) - ... + sage: phi._set_post_isomorphism(post_isom) sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain() sage: pre_isom = E1pr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(pre_isom) - ... + sage: phi._set_pre_isomorphism(pre_isom) """ EllipticCurveHom.__init__(self, self._domain, self._codomain) @@ -1582,8 +1635,7 @@ def __set_pre_isomorphism(self, domain, isomorphism): sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain() sage: pre_isom = E1pr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(pre_isom) - ... + sage: phi._set_pre_isomorphism(pre_isom) sage: phi._EllipticCurveIsogeny__set_pre_isomorphism(E, WeierstrassIsomorphism(E, (-1, 3, -3, 4))) sage: E == phi.domain() True @@ -1625,8 +1677,7 @@ def __set_post_isomorphism(self, codomain, isomorphism): sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism sage: E2 = phi.codomain() sage: isom = WeierstrassIsomorphism(E2, (-1,2,-3,4)) - sage: phi.set_post_isomorphism(isom) - ... + sage: phi._set_post_isomorphism(isom) sage: phi._EllipticCurveIsogeny__set_post_isomorphism(E2, WeierstrassIsomorphism(phi.codomain(), (1,-2,3,-4))) sage: E2 == phi.codomain() True @@ -1702,7 +1753,6 @@ def __setup_post_isomorphism(self, codomain, model): post_isom = oldE2.isomorphism_to(codomain) self.__set_post_isomorphism(codomain, post_isom) - ########################### # Velu's Formula Functions ########################### @@ -1836,7 +1886,6 @@ def __compute_codomain_via_velu(self): """ return compute_codomain_formula(self._domain, self.__v, self.__w) - @staticmethod def __velu_sum_helper(xQ, Qvalues, a1, a3, x, y): r""" @@ -1885,7 +1934,6 @@ def __velu_sum_helper(xQ, Qvalues, a1, a3, x, y): return tX, tY - def __compute_via_velu_numeric(self, xP, yP): r""" Private function that sorts the list of points in the kernel @@ -1915,7 +1963,6 @@ def __compute_via_velu_numeric(self, xP, yP): return self.__compute_via_velu(xP,yP) - def __compute_via_velu(self, xP, yP): r""" Private function for Vélu's formulas, to perform the summation. @@ -1981,7 +2028,6 @@ def __compute_via_velu(self, xP, yP): return X, Y - def __initialize_rational_maps_via_velu(self): r""" Private function for Vélu's formulas, helper function to @@ -2003,7 +2049,6 @@ def __initialize_rational_maps_via_velu(self): y = self.__mpoly_ring.gen(1) return self.__compute_via_velu(x,y) - def __init_kernel_polynomial_velu(self): r""" Private function for Vélu's formulas, helper function to @@ -2033,7 +2078,6 @@ def __init_kernel_polynomial_velu(self): self.__kernel_polynomial = psi - ################################### # Kohel's Variant of Velu's Formula ################################### @@ -2175,7 +2219,7 @@ def __init_even_kernel_polynomial(self, E, psi_G): (x^7 + 5*x^6 + 2*x^5 + 6*x^4 + 3*x^3 + 5*x^2 + 6*x + 3, x^9*y - 3*x^8*y + 2*x^7*y - 3*x^3*y + 2*x^2*y + x*y - y, 1, 6, 3, 4) """ # check if the polynomial really divides the two_torsion_polynomial - if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0 : + if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0 : raise ValueError(f"the polynomial {psi_G} does not define a finite subgroup of {E}") n = psi_G.degree() # 1 or 3 @@ -2225,7 +2269,6 @@ def __init_even_kernel_polynomial(self, E, psi_G): return phi, omega, v, w, n, d - def __init_odd_kernel_polynomial(self, E, psi): r""" Return the isogeny parameters for a cyclic isogeny of odd degree. @@ -2326,7 +2369,6 @@ def __init_odd_kernel_polynomial(self, E, psi): return phi, omega, v, w, n, d - # # This is the fast omega computation that works when characteristic is not 2 # @@ -2655,36 +2697,6 @@ def scaling_factor(self): sc *= self.__post_isomorphism.scaling_factor() return sc - def as_morphism(self): - r""" - Return this isogeny as a morphism of projective schemes. - - EXAMPLES:: - - sage: k = GF(11) - sage: E = EllipticCurve(k, [1,1]) - sage: Q = E(6,5) - sage: phi = E.isogeny(Q) - sage: mor = phi.as_morphism() - sage: mor.domain() == E - True - sage: mor.codomain() == phi.codomain() - True - sage: mor(Q) == phi(Q) - True - - TESTS:: - - sage: mor(0*Q) - (0 : 1 : 0) - sage: mor(1*Q) - (0 : 1 : 0) - """ - from sage.schemes.curves.constructor import Curve - X_affine = Curve(self.domain()).affine_patch(2) - Y_affine = Curve(self.codomain()).affine_patch(2) - return X_affine.hom(self.rational_maps(), Y_affine).homogenize(2) - def kernel_polynomial(self): r""" Return the kernel polynomial of this isogeny. @@ -2716,75 +2728,29 @@ def kernel_polynomial(self): return self.__kernel_polynomial - def set_pre_isomorphism(self, preWI): + def is_separable(self): r""" - Modify this isogeny by precomposing with a Weierstrass isomorphism. - - .. WARNING:: + Determine whether or not this isogeny is separable. - Isogenies will be immutable in a future release of Sage. - This method is deprecated in favor of using the ``*`` operator - to compose elliptic-curve morphisms. + Since :class:`EllipticCurveIsogeny` only implements + separable isogenies, this method always returns ``True``. EXAMPLES:: - sage: E = EllipticCurve(GF(31), [1,1,0,1,-1]) - sage: R.<x> = GF(31)[] - sage: f = x^3 + 9*x^2 + x + 30 - sage: phi = EllipticCurveIsogeny(E, f) - sage: Epr = E.short_weierstrass_model() - sage: isom = Epr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(isom) - ... - sage: phi.rational_maps() - ((-6*x^4 - 3*x^3 + 12*x^2 + 10*x - 1)/(x^3 + x - 12), (3*x^7 + x^6*y - 14*x^6 - 3*x^5 + 5*x^4*y + 7*x^4 + 8*x^3*y - 8*x^3 - 5*x^2*y + 5*x^2 - 14*x*y + 14*x - 6*y - 6)/(x^6 + 2*x^4 + 7*x^3 + x^2 + 7*x - 11)) - sage: phi(Epr((0,22))) - (13 : 21 : 1) - sage: phi(Epr((3,7))) - (14 : 17 : 1) + sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) + sage: phi = EllipticCurveIsogeny(E, E((0,0))) + sage: phi.is_separable() + True - sage: E = EllipticCurve(GF(29), [0,0,0,1,0]) - sage: R.<x> = GF(29)[] - sage: f = x^2 + 5 - sage: phi = EllipticCurveIsogeny(E, f) - sage: phi - Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29 - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: inv_isom = WeierstrassIsomorphism(E, (1,-2,5,10)) - sage: Epr = inv_isom.codomain() - sage: isom = Epr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(isom) - ... - sage: phi - Isogeny of degree 5 from Elliptic Curve defined by y^2 + 10*x*y + 20*y = x^3 + 27*x^2 + 6 over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29 - sage: phi(Epr((12,1))) - (26 : 0 : 1) - sage: phi(Epr((2,9))) - (0 : 0 : 1) - sage: phi(Epr((21,12))) - (3 : 0 : 1) - sage: phi.rational_maps()[0] - (x^5 - 10*x^4 - 6*x^3 - 7*x^2 - x + 3)/(x^4 - 8*x^3 + 5*x^2 - 14*x - 6) + :: sage: E = EllipticCurve('11a1') - sage: R.<x> = QQ[] - sage: f = x^2 - 21*x + 80 - sage: phi = EllipticCurveIsogeny(E, f); phi - Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: Epr = E.short_weierstrass_model() - sage: isom = Epr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(isom) - ... - sage: phi - Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 - 13392*x - 1080432 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field - sage: phi(Epr((168,1188))) - (0 : 1 : 0) + sage: phi = EllipticCurveIsogeny(E, E.torsion_points()) + sage: phi.is_separable() + True """ - from sage.misc.superseded import deprecation - deprecation(32388, 'Elliptic-curve isogenies will be immutable in a future release of Sage.' - ' Use phi*psi instead of phi.set_pre_isomorphism(psi) to obtain the composite isogeny.') - return self._set_pre_isomorphism(preWI) + return True + def _set_pre_isomorphism(self, preWI): """ @@ -2793,9 +2759,7 @@ def _set_pre_isomorphism(self, preWI): For internal use only. - TESTS: - - These tests were copied from :meth:`set_pre_isomorphism`:: + TESTS:: sage: E = EllipticCurve(GF(31), [1,1,0,1,-1]) sage: R.<x> = GF(31)[] @@ -2868,58 +2832,6 @@ def _set_pre_isomorphism(self, preWI): self.__set_pre_isomorphism(domain, isom) - def set_post_isomorphism(self, postWI): - r""" - Modify this isogeny by postcomposing with a Weierstrass isomorphism. - - .. WARNING:: - - Isogenies will be immutable in a future release of Sage. - This method is deprecated in favor of using the ``*`` operator - to compose elliptic-curve morphisms. - - EXAMPLES:: - - sage: E = EllipticCurve(j=GF(31)(0)) - sage: R.<x> = GF(31)[] - sage: phi = EllipticCurveIsogeny(E, x+18) - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (6,8,10,12))) - ... - sage: phi - Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 31 to Elliptic Curve defined by y^2 + 24*x*y + 7*y = x^3 + 22*x^2 + 16*x + 20 over Finite Field of size 31 - - sage: E = EllipticCurve(j=GF(47)(0)) - sage: f = E.torsion_polynomial(3)/3 - sage: phi = EllipticCurveIsogeny(E, f) - sage: E2 = phi.codomain() - sage: post_isom = E2.isomorphism_to(E) - sage: phi.set_post_isomorphism(post_isom) - ... - sage: phi.rational_maps() == E.multiplication_by_m(3) - False - sage: phi.switch_sign() - ... - sage: phi.rational_maps() == E.multiplication_by_m(3) - True - - Example over a number field:: - - sage: R.<x> = QQ[] - sage: K.<a> = NumberField(x^2 + 2) - sage: E = EllipticCurve(j=K(1728)) - sage: ker_list = E.torsion_points() - sage: phi = EllipticCurveIsogeny(E, ker_list) - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: post_isom = WeierstrassIsomorphism(phi.codomain(), (a,2,3,5)) - sage: phi - Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^2 + 2 to Elliptic Curve defined by y^2 = x^3 + (-44)*x + 112 over Number Field in a with defining polynomial x^2 + 2 - """ - from sage.misc.superseded import deprecation - deprecation(32388, 'Elliptic-curve isogenies will be immutable in a future release of Sage.' - ' Use psi*phi instead of phi.set_post_isomorphism(psi) to obtain the composite isogeny.') - return self._set_post_isomorphism(postWI) - def _set_post_isomorphism(self, postWI): """ Modify this isogeny by post-composing with a @@ -2927,9 +2839,7 @@ def _set_post_isomorphism(self, postWI): For internal use only. - TESTS: - - These tests were copied from :meth:`set_post_isomorphism`:: + TESTS:: sage: E = EllipticCurve(j=GF(31)(0)) sage: R.<x> = GF(31)[] @@ -2982,150 +2892,6 @@ def _set_post_isomorphism(self, postWI): self.__set_post_isomorphism(codomain, isom) - def get_pre_isomorphism(self): - r""" - Return the pre-isomorphism of this isogeny, or ``None``. - - .. NOTE:: - - Pre- and post-isomorphisms are an implementation detail of - how isogenies are currently represented in Sage. They have - limited inherent mathematical meaning and this method may - disappear in a future release. - - EXAMPLES:: - - sage: E = EllipticCurve(GF(31), [1,1,0,1,-1]) - sage: R.<x> = GF(31)[] - sage: f = x^3 + 9*x^2 + x + 30 - sage: phi = EllipticCurveIsogeny(E, f) - sage: phi.get_post_isomorphism() - sage: Epr = E.short_weierstrass_model() - sage: isom = Epr.isomorphism_to(E) - sage: phi.set_pre_isomorphism(isom) - ... - sage: isom == phi.get_pre_isomorphism() - True - - sage: E = EllipticCurve(GF(83), [1,0,1,1,0]) - sage: R.<x> = GF(83)[]; f = x+24 - sage: phi = EllipticCurveIsogeny(E, f) - sage: E2 = phi.codomain() - sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2) - sage: phi2.get_pre_isomorphism() - Elliptic-curve morphism: - From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 83 - To: Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83 - Via: (u,r,s,t) = (1, 76, 41, 3) - """ - return self.__pre_isomorphism - - def get_post_isomorphism(self): - r""" - Return the post-isomorphism of this isogeny, or ``None``. - - .. NOTE:: - - Pre- and post-isomorphisms are an implementation detail of - how isogenies are currently represented in Sage. They have - limited inherent mathematical meaning and this method may - disappear in a future release. - - EXAMPLES:: - - sage: E = EllipticCurve(j=GF(31)(0)) - sage: R.<x> = GF(31)[] - sage: phi = EllipticCurveIsogeny(E, x+18) - sage: phi.get_post_isomorphism() - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: isom = WeierstrassIsomorphism(phi.codomain(), (6,8,10,12)) - sage: phi.set_post_isomorphism(isom) - ... - sage: isom == phi.get_post_isomorphism() - True - - sage: E = EllipticCurve(GF(83), [1,0,1,1,0]) - sage: R.<x> = GF(83)[]; f = x+24 - sage: phi = EllipticCurveIsogeny(E, f) - sage: E2 = phi.codomain() - sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2) - sage: phi2.get_post_isomorphism() - Elliptic-curve morphism: - From: Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83 - To: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 16 over Finite Field of size 83 - Via: (u,r,s,t) = (1, 7, 42, 42) - """ - return self.__post_isomorphism - - - def switch_sign(self): - r""" - Compose this isogeny with `[-1]` (negation). - - .. WARNING:: - - Isogenies will be immutable in a future version of Sage. - This method is deprecated in favor of using the unary ``-`` - operator to negate elliptic-curve morphisms. - - EXAMPLES:: - - sage: E = EllipticCurve(GF(23), [0,0,0,1,0]) - sage: f = E.torsion_polynomial(3)/3 - sage: phi = EllipticCurveIsogeny(E, f, E) - sage: phi.rational_maps() == E.multiplication_by_m(3) - False - sage: phi.switch_sign() - ... - sage: phi.rational_maps() == E.multiplication_by_m(3) - True - - sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11]) - sage: R.<x> = GF(17)[] - sage: f = x+6 - sage: phi = EllipticCurveIsogeny(E, f) - sage: phi - Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17 - sage: phi.rational_maps() - ((x^2 + 6*x + 4)/(x + 6), (x^2*y - 5*x*y + 8*x - 2*y)/(x^2 - 5*x + 2)) - sage: phi.switch_sign() - ... - sage: phi - Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17 - sage: phi.rational_maps() - ((x^2 + 6*x + 4)/(x + 6), - (2*x^3 - x^2*y - 5*x^2 + 5*x*y - 4*x + 2*y + 7)/(x^2 - 5*x + 2)) - - sage: E = EllipticCurve('11a1') - sage: R.<x> = QQ[] - sage: f = x^2 - 21*x + 80 - sage: phi = EllipticCurveIsogeny(E, f) - sage: (xmap1, ymap1) = phi.rational_maps() - sage: phi.switch_sign() - ... - sage: (xmap2, ymap2) = phi.rational_maps() - sage: xmap1 == xmap2 - True - sage: ymap1 == -ymap2 - E.a1()*xmap2 - E.a3() - True - - sage: K.<a> = NumberField(x^2 + 1) - sage: E = EllipticCurve(K, [0,0,0,1,0]) - sage: R.<x> = K[] - sage: phi = EllipticCurveIsogeny(E, x-a) - sage: phi.rational_maps() - ((x^2 + (-a)*x - 2)/(x + (-a)), (x^2*y + (-2*a)*x*y + y)/(x^2 + (-2*a)*x - 1)) - sage: phi.switch_sign() - ... - sage: phi.rational_maps() - ((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1)) - """ - from sage.misc.superseded import deprecation - deprecation(32388, 'Elliptic-curve isogenies will be immutable in a future release of Sage.' - ' Use -phi instead of phi.switch_sign() to obtain the negated isogeny.') - E2 = self._codomain - self._set_post_isomorphism(WeierstrassIsomorphism(E2, (-1, 0, -E2.a1(), -E2.a3()))) - def dual(self): r""" Return the isogeny dual to this isogeny. @@ -3189,6 +2955,31 @@ def dual(self): sage: (Xm, Ym) == E.multiplication_by_m(5) True + Inseparable duals should be computed correctly:: + + sage: z2 = GF(71^2).gen() + sage: E = EllipticCurve(j=57*z2+51) + sage: E.isogeny(3*E.lift_x(0)).dual() + Composite morphism of degree 71 = 71*1^2: + From: Elliptic Curve defined by y^2 = x^3 + (32*z2+67)*x + (24*z2+37) over Finite Field in z2 of size 71^2 + To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42) over Finite Field in z2 of size 71^2 + sage: E.isogeny(E.lift_x(0)).dual() + Composite morphism of degree 213 = 71*3: + From: Elliptic Curve defined by y^2 = x^3 + (58*z2+31)*x + (34*z2+58) over Finite Field in z2 of size 71^2 + To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42) over Finite Field in z2 of size 71^2 + + ...even if pre- or post-isomorphisms are present:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: phi = E.isogeny(E.lift_x(0)) + sage: pre = ~WeierstrassIsomorphism(phi.domain(), (z2,2,3,4)) + sage: post = WeierstrassIsomorphism(phi.codomain(), (5,6,7,8)) + sage: phi = post * phi * pre + sage: phi.dual() + Composite morphism of degree 213 = 71*3: + From: Elliptic Curve defined by y^2 + 17*x*y + 45*y = x^3 + 30*x^2 + (6*z2+64)*x + (48*z2+65) over Finite Field in z2 of size 71^2 + To: Elliptic Curve defined by y^2 + (60*z2+22)*x*y + (69*z2+37)*y = x^3 + (32*z2+48)*x^2 + (19*z2+58)*x + (56*z2+22) over Finite Field in z2 of size 71^2 + TESTS: Test for :trac:`23928`:: @@ -3238,55 +3029,77 @@ def dual(self): return self.__dual # trac 7096 - E1, E2pr, pre_isom, post_isom = compute_intermediate_curves(self.codomain(), self.domain()) + E1, E2pr, _, _ = compute_intermediate_curves(self.codomain(), self.domain()) F = self.__base_field d = self._degree - # trac 7096 - if F(d) == 0: - raise NotImplementedError("the dual isogeny is not separable: only separable isogenies are currently implemented") + from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite - # trac 7096 - # this should take care of the case when the isogeny is not normalized. - u = self.scaling_factor() - isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0)) + if F(d) == 0: # inseparable dual! + p = F.characteristic() + k = d.valuation(p) + + from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + frob = EllipticCurveHom_frobenius(self._codomain, k) - E2 = isom.codomain() + dsep = d // p**k + if dsep > 1: + #TODO: We could also use resultants here; this is much + # faster in some cases (but seems worse in general). + # Presumably there should be a wrapper function that + # decides on the fly which method to use. + # Eventually this should become a .separable_part() method. - pre_isom = self._codomain.isomorphism_to(E1) - post_isom = E2.isomorphism_to(self._domain) + f = self.kernel_polynomial() - phi_hat = EllipticCurveIsogeny(E1, None, E2, d) + psi = self._domain.division_polynomial(p) + mu_num = self._domain._multiple_x_numerator(p) + mu_den = self._domain._multiple_x_denominator(p) - phi_hat._set_pre_isomorphism(pre_isom) - phi_hat._set_post_isomorphism(post_isom) - phi_hat.__perform_inheritance_housekeeping() + for _ in range(k): + f //= f.gcd(psi) + S = f.parent().quotient_ring(f) + mu = S(mu_num) / S(mu_den) + f = mu.minpoly() - assert phi_hat.codomain() == self.domain() + sep = self._domain.isogeny(f, codomain=frob.codomain()).dual() - # trac 7096 : this adjusts a posteriori the automorphism on - # the codomain of the dual isogeny. we used _a_ Weierstrass - # isomorphism to get to the original curve, but we may have to - # change it by an automorphism. We impose the condition that - # the composition has the degree as a leading coefficient in - # the formal expansion. + else: + sep = frob.codomain().isomorphism_to(self._domain) - phihat_sc = phi_hat.scaling_factor() + phi_hat = EllipticCurveHom_composite.from_factors([frob, sep]) - sc = u * phihat_sc/F(d) + from sage.schemes.elliptic_curves.hom import find_post_isomorphism + mult = self._domain.scalar_multiplication(d) + rhs = phi_hat * self + corr = find_post_isomorphism(mult, rhs) + self.__dual = corr * phi_hat + return self.__dual - assert sc != 0, "bug in dual()" + else: + # trac 7096 + # this should take care of the case when the isogeny is not normalized. + u = self.scaling_factor() + E2 = E2pr.change_weierstrass_model(u/F(d), 0, 0, 0) - if sc != 1: - auts = self._domain.automorphisms() - aut = [a for a in auts if a.u == sc] - assert len(aut) == 1, "bug in dual()" - phi_hat._set_post_isomorphism(aut[0]) + phi_hat = EllipticCurveIsogeny(E1, None, E2, d) - self.__dual = phi_hat + pre_iso = self._codomain.isomorphism_to(E1) + post_iso = E2.isomorphism_to(self._domain) - return phi_hat +# assert phi_hat.scaling_factor() == 1 + sc = u * pre_iso.scaling_factor() * post_iso.scaling_factor() / F(d) + if not sc.is_one(): + auts = self._codomain.automorphisms() + aut = [a for a in auts if a.u == sc] + assert len(aut) == 1, "bug in dual()" + pre_iso *= aut[0] + + phi_hat._set_pre_isomorphism(pre_iso) + phi_hat._set_post_isomorphism(post_iso) + phi_hat.__perform_inheritance_housekeeping() + return phi_hat @staticmethod @@ -3332,7 +3145,7 @@ def _composition_impl(left, right): return NotImplemented -def compute_isogeny_starks(E1, E2, ell): +def compute_isogeny_stark(E1, E2, ell): r""" Return the kernel polynomial of an isogeny of degree ``ell`` from ``E1`` to ``E2``. @@ -3357,7 +3170,7 @@ def compute_isogeny_starks(E1, E2, ell): ALGORITHM: - This function uses Starks' algorithm as presented in Section 6.2 + This function uses Stark's algorithm as presented in Section 6.2 of [BMSS2006]_. .. NOTE:: @@ -3368,14 +3181,14 @@ def compute_isogeny_starks(E1, E2, ell): EXAMPLES:: - sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks, compute_sequence_of_maps + sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark, compute_sequence_of_maps sage: E = EllipticCurve(GF(97), [1,0,1,1,0]) sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21 sage: phi = EllipticCurveIsogeny(E, f) sage: E2 = phi.codomain() sage: (isom1, isom2, E1pr, E2pr, ker_poly) = compute_sequence_of_maps(E, E2, 11) - sage: compute_isogeny_starks(E1pr, E2pr, 11) + sage: compute_isogeny_stark(E1pr, E2pr, 11) x^10 + 37*x^9 + 53*x^8 + 66*x^7 + 66*x^6 + 17*x^5 + 57*x^4 + 6*x^3 + 89*x^2 + 53*x + 8 sage: E = EllipticCurve(GF(37), [0,0,0,1,8]) @@ -3383,7 +3196,7 @@ def compute_isogeny_starks(E1, E2, ell): sage: f = (x + 14) * (x + 30) sage: phi = EllipticCurveIsogeny(E, f) sage: E2 = phi.codomain() - sage: compute_isogeny_starks(E, E2, 5) + sage: compute_isogeny_stark(E, E2, 5) x^4 + 14*x^3 + x^2 + 34*x + 21 sage: f**2 x^4 + 14*x^3 + x^2 + 34*x + 21 @@ -3393,7 +3206,7 @@ def compute_isogeny_starks(E1, E2, ell): sage: f = x sage: phi = EllipticCurveIsogeny(E, f) sage: E2 = phi.codomain() - sage: compute_isogeny_starks(E, E2, 2) + sage: compute_isogeny_stark(E, E2, 2) x """ K = E1.base_field() @@ -3439,6 +3252,9 @@ def compute_isogeny_starks(E1, E2, ell): qn /= qn.leading_coefficient() return qn +from sage.misc.superseded import deprecated_function_alias +compute_isogeny_starks = deprecated_function_alias(34871, compute_isogeny_stark) + def split_kernel_polynomial(poly): r""" Obsolete internal helper function formerly used by @@ -3479,8 +3295,7 @@ def split_kernel_polynomial(poly): from sage.misc.misc_c import prod return prod([p for p,e in poly.squarefree_decomposition()]) - -def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"): +def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="stark"): r""" Return the kernel polynomial of an isogeny of degree ``ell`` from ``E1`` to ``E2``. @@ -3493,7 +3308,7 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"): - ``ell`` -- the degree of an isogeny from ``E1`` to ``E2`` - - ``algorithm`` -- currently only ``"starks"`` (default) is implemented + - ``algorithm`` -- currently only ``"stark"`` (default) is implemented OUTPUT: @@ -3535,8 +3350,8 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"): sage: f = (x + 10) * (x + 12) * (x + 16) sage: phi = EllipticCurveIsogeny(E, f) sage: E2 = phi.codomain() - sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks - sage: ker_poly = compute_isogeny_starks(E, E2, 7); ker_poly + sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark + sage: ker_poly = compute_isogeny_stark(E, E2, 7); ker_poly x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16 sage: ker_poly.factor() (x + 10)^2 * (x + 12)^2 * (x + 16)^2 @@ -3545,9 +3360,13 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"): sage: poly.factor() (x + 10) * (x + 12) * (x + 16) """ - if algorithm != "starks": - raise NotImplementedError - return compute_isogeny_starks(E1, E2, ell).radical() + if algorithm == 'starks': + from sage.misc.superseded import deprecation + deprecation(34871, 'The "starks" algorithm is being renamed to "stark".') + algorithm = 'stark' + if algorithm != "stark": + raise NotImplementedError(f'unknown algorithm {algorithm}') + return compute_isogeny_stark(E1, E2, ell).radical() def compute_intermediate_curves(E1, E2): r""" @@ -3630,9 +3449,9 @@ def compute_intermediate_curves(E1, E2): # We cannot even just use pre_iso = E1.isomorphism_to(E1w) since # it may have u=-1; similarly for E2 - urst = [w for w in isomorphisms(E1, E1w) if w[0] == 1][0] + urst = [w for w in _isomorphisms(E1, E1w) if w[0] == 1][0] pre_iso = WeierstrassIsomorphism(E1, urst, E1w) - urst = [w for w in isomorphisms(E2w, E2) if w[0] == 1][0] + urst = [w for w in _isomorphisms(E2w, E2) if w[0] == 1][0] post_iso = WeierstrassIsomorphism(E2w, urst, E2) return E1w, E2w, pre_iso, post_iso @@ -3720,7 +3539,6 @@ def compute_sequence_of_maps(E1, E2, ell): return pre_isom, post_isom, E1pr, E2pr, ker_poly - # Utility functions for manipulating isogeny degree matrices def fill_isogeny_matrix(M): diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 68b8375daee..0aef44177e5 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -414,7 +414,7 @@ def is_quadratic_twist(self, other): if not K == F.base_ring(): return zero j=E.j_invariant() - if j != F.j_invariant(): + if j != F.j_invariant(): return zero if E.is_isomorphic(F): @@ -440,7 +440,7 @@ def is_quadratic_twist(self, other): um = c6E/c6F x=rings.polygen(K) ulist=(x**3-um).roots(multiplicities=False) - if not ulist: + if not ulist: D = zero else: D = ulist[0] @@ -513,7 +513,7 @@ def is_quartic_twist(self, other): if not K == F.base_ring(): return zero j=E.j_invariant() - if j != F.j_invariant() or j!=K(1728): + if j != F.j_invariant() or j!=K(1728): return zero if E.is_isomorphic(F): @@ -582,7 +582,7 @@ def is_sextic_twist(self, other): if not K == F.base_ring(): return zero j=E.j_invariant() - if j != F.j_invariant() or not j.is_zero(): + if j != F.j_invariant() or not j.is_zero(): return zero if E.is_isomorphic(F): @@ -845,7 +845,7 @@ def division_field(self, l, names='t', map=False, **kwds): sage: E = E.base_extend(G).quadratic_twist(c); E Elliptic Curve defined by y^2 = x^3 + 5*a0*x^2 + (-200*a0^2)*x + (-42000*a0^2+42000*a0+126000) over Number Field in a0 with defining polynomial x^3 - 3*x^2 + 3*x + 9 sage: K.<b> = E.division_field(3, simplify_all=True); K - Number Field in b with defining polynomial x^12 - 10*x^10 + 55*x^8 - 60*x^6 + 75*x^4 + 1350*x^2 + 2025 + Number Field in b with defining polynomial x^12 + 5*x^10 + 40*x^8 + 315*x^6 + 750*x^4 + 675*x^2 + 2025 Some higher-degree examples:: @@ -1024,53 +1024,6 @@ def division_field(self, l, names='t', map=False, **kwds): else: return L - def _fetch_cached_order(self, other): - r""" - This method copies the ``_order`` member from ``other`` - to ``self`` if their base field is the same and finite. - - This is used in :class:`EllipticCurveIsogeny` to keep track of - an already computed curve order: According to Tate's theorem - [Tate1966b]_, isogenous elliptic curves over a finite field - have the same number of rational points. - - EXAMPLES:: - - sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) - sage: E1.set_order(170141183460469231746191640949390434666) - sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) - sage: E2._fetch_cached_order(E1) - sage: E2._order - 170141183460469231746191640949390434666 - - TESTS:: - - sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) - sage: hasattr(E3, '_order') - False - sage: E3._fetch_cached_order(E1) - Traceback (most recent call last): - ... - ValueError: curves have distinct base fields - - :: - - sage: E4 = EllipticCurve([1,2,3,4,5]) - sage: E4._fetch_cached_order(E1.change_ring(QQ)) - sage: hasattr(E4, '_order') - False - """ - if hasattr(self, '_order') or not hasattr(other, '_order'): - return - F = self.base_field() - if F != other.base_field(): - raise ValueError('curves have distinct base fields') - if not F.is_finite(): - raise ValueError('base field must be finite') - n = getattr(other, '_order', None) - if n is not None: - self._order = n - def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, algorithm=None): r""" Return an elliptic-curve isogeny from this elliptic curve. @@ -1098,7 +1051,7 @@ def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, al kernel point of odd order `\geq 5`. This algorithm is selected using ``algorithm="velusqrt"``. - - Factored Isogenies (*experimental* --- see + - Factored Isogenies (see :mod:`~sage.schemes.elliptic_curves.hom_composite`): Given a list of points which generate a composite-order subgroup, decomposes the isogeny into prime-degree steps. @@ -1200,9 +1153,7 @@ def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, al sage: E = EllipticCurve(GF(2^32-5), [170246996, 2036646110]) sage: P = E.lift_x(2) - sage: E.isogeny(P, algorithm="factored") # experimental - doctest:warning - ... + sage: E.isogeny(P, algorithm="factored") Composite morphism of degree 1073721825 = 3^4*5^2*11*19*43*59: From: Elliptic Curve defined by y^2 = x^3 + 170246996*x + 2036646110 over Finite Field of size 4294967291 To: Elliptic Curve defined by y^2 = x^3 + 272790262*x + 1903695400 over Finite Field of size 4294967291 diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 31e193a64b6..419c08cf9ca 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -36,8 +36,6 @@ from sage.misc.cachefunc import cached_method from sage.groups.additive_abelian.additive_abelian_wrapper import AdditiveAbelianGroupWrapper -import sage.plot.all as plot - class EllipticCurve_finite_field(EllipticCurve_field, HyperellipticCurve_finite_field): r""" @@ -98,10 +96,9 @@ def plot(self, *args, **kwds): if not R.is_prime_field(): raise NotImplementedError - G = plot.Graphics() - G += plot.points([P[0:2] for P in self.points() if not P.is_zero()], *args, **kwds) + from sage.plot.point import points - return G + return points([P[0:2] for P in self.points() if not P.is_zero()], *args, **kwds) def _points_via_group_structure(self): """ @@ -675,6 +672,28 @@ def frobenius(self): else: return R.gen(1) + def frobenius_endomorphism(self): + r""" + Return the `q`-power Frobenius endomorphism of this elliptic + curve, where `q` is the cardinality of the (finite) base field. + + EXAMPLES:: + + sage: F.<t> = GF(11^4) + sage: E = EllipticCurve([t,t]) + sage: E.frobenius_endomorphism() + Frobenius endomorphism of degree 14641 = 11^4: + From: Elliptic Curve defined by y^2 = x^3 + t*x + t over Finite Field in t of size 11^4 + To: Elliptic Curve defined by y^2 = x^3 + t*x + t over Finite Field in t of size 11^4 + sage: E.frobenius_endomorphism() == E.frobenius_isogeny(4) + True + + .. SEEALSO:: + + :meth:`~sage.schemes.elliptic_curves.ell_generic.EllipticCurve_generic.frobenius_isogeny` + """ + return self.frobenius_isogeny(self.base_field().degree()) + def cardinality_pari(self): r""" Return the cardinality of ``self`` using PARI. @@ -958,25 +977,27 @@ def abelian_group(self): if len(gens) == 2: P, Q = gens - n = self.cardinality() # cached - n1 = P.order() # cached + n = self.cardinality() # cached + n1 = P.order() # cached n2 = n//n1 - assert not n1 * Q # PARI should guarantee this + assert not n1 * Q # PARI should guarantee this k = n1.prime_to_m_part(n2) - Q *= k # don't need; kill that part + Q *= k # don't need; kill that part nQ = n2 * generic.order_from_multiple(n2*Q, n1//k//n2) S = n//nQ * P T = n2 * Q - S.set_order(nQ//n2) # for .discrete_log() + S.set_order(nQ//n2, check=False) # for .discrete_log() x = S.discrete_log(T) Q -= x * n1//nQ * P - Q.set_order(n2) # verifies n2*Q == 0 + assert not n2 * Q # by construction + Q.set_order(n2, check=False) + gens = P, Q - orders = [T.order() for T in gens] # cached + orders = [T.order() for T in gens] # cached self.gens.set_cache(gens) return AdditiveAbelianGroupWrapper(self.point_homset(), gens, orders) @@ -1158,7 +1179,7 @@ def is_ordinary(self, proof=True): """ return not is_j_supersingular(self.j_invariant(), proof=proof) - def set_order(self, value, num_checks=8): + def set_order(self, value, *, check=True, num_checks=8): r""" Set the value of self._order to value. @@ -1170,9 +1191,12 @@ def set_order(self, value, num_checks=8): - ``value`` -- integer in the Hasse-Weil range for this curve. - - ``num_checks`` (integer, default: 8) -- number of times to - check whether value*(a random point on this curve) is - equal to the identity. + - ``check`` (boolean, default: ``True``) -- whether or + not to run sanity checks on the input. + + - ``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. OUTPUT: @@ -1270,16 +1294,17 @@ def set_order(self, value, num_checks=8): """ value = Integer(value) - # Is value in the Hasse range? - q = self.base_field().order() - a,b = Hasse_bounds(q,1) - if not a <= value <= b: - raise ValueError('Value %s illegal (not an integer in the Hasse range)' % value) - # Is value*random == identity? - for i in range(num_checks): - G = self.random_point() - if value * G != self(0): - raise ValueError('Value %s illegal (multiple of random point not the identity)' % 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('Value %s illegal (not an integer in the Hasse range)' % value) + # Is value*random == identity? + for i in range(num_checks): + G = self.random_point() + if value * G != self(0): + raise ValueError('Value %s illegal (multiple of random point not the identity)' % value) # TODO: It might help some of PARI's algorithms if we # could copy this over to the .pari_curve() as well. @@ -1289,6 +1314,46 @@ def set_order(self, value, num_checks=8): self._order = value + def _fetch_cached_order(self, other): + r""" + This method copies the ``_order`` member from ``other`` to + ``self``. Both curves must have the same finite base field. + + This is used in + :class:`~sage.schemes.elliptic_curves.hom.EllipticCurveHom` + to keep track of an already computed curve order: According + to Tate's theorem [Tate1966b]_, isogenous elliptic curves + over a finite field have the same number of rational points. + + EXAMPLES:: + + sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) + sage: E1.set_order(170141183460469231746191640949390434666) + sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) + sage: E2._fetch_cached_order(E1) + sage: E2._order + 170141183460469231746191640949390434666 + + TESTS:: + + sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) + sage: hasattr(E3, '_order') + False + sage: E3._fetch_cached_order(E1) + Traceback (most recent call last): + ... + ValueError: curves have distinct base fields + """ + if hasattr(self, '_order') or not hasattr(other, '_order'): + return + F = self.base_field() + if F != other.base_field(): + raise ValueError('curves have distinct base fields') + n = getattr(other, '_order', None) + if n is not None: + self._order = n + + # dict to hold precomputed coefficient vectors of supersingular j values (excluding 0, 1728): supersingular_j_polynomials = {} diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 926ae310ea8..d3ca4896b01 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -61,9 +61,6 @@ from sage.rings.finite_rings.finite_field_base import FiniteField import sage.groups.additive_abelian.additive_abelian_group as groups import sage.groups.generic as generic -import sage.plot.all as plot -from sage.misc.lazy_import import lazy_import -lazy_import("sage.plot.plot", "generate_plot_points") from sage.arith.all import lcm import sage.rings.all as rings @@ -559,6 +556,8 @@ def __call__(self, *args, **kwds): (ell_point.EllipticCurvePoint_field, ell_point.EllipticCurvePoint_number_field, ell_point.EllipticCurvePoint)): + if P.curve() is self: + return P # check if denominator of the point contains a factor of the # characteristic of the base ring. if so, coerce the point to # infinity. @@ -1918,9 +1917,9 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2, force_evalu and x.lift().base_ring() is self.base_ring(): d = x.parent().modulus().degree() evaluate = m < 220 or \ - (d < 10 and m < 420) or (d < 15 and m < 340) or \ - (d < 30 and m < 280) or (d < 100 and m < 250) or \ - m <= min(250, d) + (d < 10 and m < 420) or (d < 15 and m < 340) or \ + (d < 30 and m < 280) or (d < 100 and m < 250) or \ + m <= min(250, d) # Check if we should (attempt to) compute the result by simply # evaluating a cached polynomial at the given input. @@ -2325,6 +2324,7 @@ def multiplication_by_m_isogeny(self, m): sage: E = EllipticCurve('11a1') sage: E.multiplication_by_m_isogeny(7) + doctest:warning ... DeprecationWarning: ... Isogeny of degree 49 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field TESTS: @@ -2350,10 +2350,26 @@ def multiplication_by_m_isogeny(self, m): sage: E.multiplication_by_m_isogeny(2).rational_maps() ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) + + Test for :trac:`34727`:: + + sage: E = EllipticCurve([5,5]) + sage: E.multiplication_by_m_isogeny(-1) + Isogeny of degree 1 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-2) + Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-3) + Isogeny of degree 9 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: mu = E.multiplication_by_m_isogeny + sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7)) + True """ + from sage.misc.superseded import deprecation + deprecation(32826, 'The .multiplication_by_m_isogeny() method is superseded by .scalar_multiplication().') + mx, my = self.multiplication_by_m(m) - torsion_poly = self.torsion_polynomial(m).monic() + torsion_poly = self.torsion_polynomial(abs(m)).monic() phi = self.isogeny(torsion_poly, codomain=self) phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) @@ -2365,6 +2381,60 @@ def multiplication_by_m_isogeny(self, m): assert False, 'bug in multiplication_by_m_isogeny()' + def scalar_multiplication(self, m): + r""" + Return the scalar-multiplication map `[m]` on this elliptic + curve as a + :class:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar` + object. + + EXAMPLES:: + + sage: E = EllipticCurve('77a1') + sage: m = E.scalar_multiplication(-7); m + Scalar-multiplication endomorphism [-7] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: m.degree() + 49 + sage: P = E(2,3) + sage: m(P) + (-26/225 : -2132/3375 : 1) + sage: m.rational_maps() == E.multiplication_by_m(-7) + True + """ + from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + return EllipticCurveHom_scalar(self, m) + + def frobenius_isogeny(self, n=1): + r""" + Return the `n`-power Frobenius isogeny from this curve to + its Galois conjugate. + + The Frobenius *endo*\morphism is the special case where `n` + is divisible by the degree of the base ring of the curve. + + .. SEEALSO:: + + :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.frobenius_endomorphism` + + EXAMPLES:: + + sage: z3, = GF(13^3).gens() + sage: E = EllipticCurve([z3,z3^2]) + sage: E.frobenius_isogeny() + Frobenius isogeny of degree 13: + From: Elliptic Curve defined by y^2 = x^3 + z3*x + z3^2 over Finite Field in z3 of size 13^3 + To: Elliptic Curve defined by y^2 = x^3 + (5*z3^2+7*z3+11)*x + (5*z3^2+12*z3+1) over Finite Field in z3 of size 13^3 + sage: E.frobenius_isogeny(3) + Frobenius endomorphism of degree 2197 = 13^3: + From: Elliptic Curve defined by y^2 = x^3 + z3*x + z3^2 over Finite Field in z3 of size 13^3 + To: Elliptic Curve defined by y^2 = x^3 + z3*x + z3^2 over Finite Field in z3 of size 13^3 + """ + p = self.base_ring().characteristic() + if not p: + raise ValueError('Frobenius isogeny only exists in positive characteristic') + from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + return EllipticCurveHom_frobenius(self, n) + def isomorphism_to(self, other): """ Given another weierstrass model ``other`` of self, return an @@ -2417,6 +2487,9 @@ def automorphisms(self, field=None): """ Return the set of isomorphisms from self to itself (as a list). + The identity and negation morphisms are guaranteed to appear + as the first and second entry of the returned list. + INPUT: - ``field`` (default ``None``) -- a field into which the @@ -2425,39 +2498,65 @@ def automorphisms(self, field=None): OUTPUT: - (list) A list of ``WeierstrassIsomorphism`` objects + (list) A list of :class:`~wm.WeierstrassIsomorphism` objects consisting of all the isomorphisms from the curve ``self`` to itself defined over ``field``. EXAMPLES:: - sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ - sage: E.automorphisms(); + sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ + sage: E.automorphisms() [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (-1, 0, 0, -1), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (1, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field + Via: (u,r,s,t) = (-1, 0, 0, -1)] We can also find automorphisms defined over extension fields:: - sage: K.<a> = NumberField(x^2+3) # adjoin roots of unity + sage: K.<a> = NumberField(x^2+3) # adjoin roots of unity sage: E.automorphisms(K) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 - Via: (u,r,s,t) = (-1, 0, 0, -1), - ... - Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (1, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1, 0, 0, -1), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1/2*a - 1/2, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (1/2*a + 1/2, 0, 0, -1), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (1/2*a - 1/2, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1/2*a + 1/2, 0, 0, -1)] :: - sage: [ len(EllipticCurve_from_j(GF(q,'a')(0)).automorphisms()) for q in [2,4,3,9,5,25,7,49]] + sage: [len(EllipticCurve_from_j(GF(q,'a')(0)).automorphisms()) for q in [2,4,3,9,5,25,7,49]] [2, 24, 2, 12, 2, 6, 6, 6] + + TESTS: + + Random testing:: + + sage: p = random_prime(100) + sage: k = randrange(1,30) + sage: F.<t> = GF((p,k)) + sage: while True: + ....: try: + ....: E = EllipticCurve(list((F^5).random_element())) + ....: except ArithmeticError: + ....: continue + ....: break + sage: Aut = E.automorphisms() + sage: Aut[0] == E.scalar_multiplication(1) + True + sage: Aut[1] == E.scalar_multiplication(-1) + True + sage: sorted(Aut) == Aut + True """ - if field is None: - return [wm.WeierstrassIsomorphism(self, urst, self) - for urst in wm.isomorphisms(self, self)] - E = self.change_ring(field) - return [wm.WeierstrassIsomorphism(E, urst, E) - for urst in wm.isomorphisms(E, E)] + if field is not None: + self = self.change_ring(field) + return self.isomorphisms(self) def isomorphisms(self, other, field=None): """ @@ -2473,7 +2572,7 @@ def isomorphisms(self, other, field=None): OUTPUT: - (list) A list of ``WeierstrassIsomorphism`` objects consisting of all + (list) A list of :class:`~wm.WeierstrassIsomorphism` objects consisting of all the isomorphisms from the curve ``self`` to the curve ``other`` defined over ``field``. @@ -2481,11 +2580,11 @@ def isomorphisms(self, other, field=None): sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ sage: F = EllipticCurve('27a3') # should be the same one - sage: E.isomorphisms(F); + sage: E.isomorphisms(F) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (-1, 0, 0, -1), + Via: (u,r,s,t) = (1, 0, 0, 0), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (-1, 0, 0, -1)] We can also find isomorphisms defined over extension fields:: @@ -2502,13 +2601,11 @@ def isomorphisms(self, other, field=None): To: Elliptic Curve defined by y^2 = x^3 + x + 6 over Finite Field in a of size 7^2 Via: (u,r,s,t) = (6*a + 4, 0, 0, 0)] """ - if field is None: - return [wm.WeierstrassIsomorphism(self, urst, other) - for urst in wm.isomorphisms(self, other)] - E = self.change_ring(field) - F = other.change_ring(field) - return [wm.WeierstrassIsomorphism(E, urst, F) - for urst in wm.isomorphisms(E, F)] + if field is not None: + self = self.change_ring(field) + other = other.change_ring(field) + return sorted(wm.WeierstrassIsomorphism(self, urst, other) + for urst in wm._isomorphisms(self, other)) def is_isomorphic(self, other, field=None): """ @@ -2542,17 +2639,16 @@ def is_isomorphic(self, other, field=None): if field is None: if self.base_ring() != other.base_ring(): return False - elif self.j_invariant() != other.j_invariant(): # easy check - return False - else: - return wm.isomorphisms(self, other, True) is not None else: - E = self.base_extend(field) - F = other.base_extend(field) - if E.j_invariant() != F.j_invariant(): # easy check - return False - else: - return wm.isomorphisms(E, other, F) is not None + self = self.base_extend(field) + other = other.base_extend(field) + if self.j_invariant() != other.j_invariant(): # easy check + return False + try: + next(wm._isomorphisms(self, other)) + except StopIteration: + return False + return True def change_weierstrass_model(self, *urst): r""" @@ -2849,7 +2945,7 @@ def montgomery_model(self, twisted=False, morphism=False): return E, self.isomorphism_to(E) return E - P2, (x,y,z) = self.ambient_space().objgens() + P2, (x, y, z) = self.ambient_space().objgens() f = B * y**2*z - x * (x * (x + A*z) + z**2) C = plane_curve.ProjectivePlaneCurve(P2, f) @@ -2857,8 +2953,8 @@ def montgomery_model(self, twisted=False, morphism=False): return C t = ~(B * s).sqrt() - iso_maps = (x - r * z, t * y , s * z) - inv_maps = (x * s + r * z, s * y/t, z) + iso_maps = (x - r * z, t * y, s * z) + inv_maps = (x * s + r * z, s * y / t, z) w = self.isomorphism_to(Ew) wmap, winv = w.rational_maps(), (~w).rational_maps() @@ -2868,7 +2964,7 @@ def montgomery_model(self, twisted=False, morphism=False): inv = [f(*inv_maps) for f in winv] from sage.schemes.elliptic_curves.weierstrass_transform \ - import WeierstrassTransformationWithInverse as WTI + import WeierstrassTransformationWithInverse as WTI iso = WTI(self, C, iso, 1, inv, s**-3) return C, iso @@ -3052,24 +3148,28 @@ def f2(z): else: I.append((xmin, xmax, '=')) - g = plot.Graphics() + from sage.plot.graphics import Graphics + from sage.plot.line import line + from sage.plot.plot import generate_plot_points + + g = Graphics() plot_points = int(args.pop('plot_points',200)) adaptive_tolerance = args.pop('adaptive_tolerance',0.01) adaptive_recursion = args.pop('adaptive_recursion',5) randomize = args.pop('randomize',True) for j in range(len(I)): - a,b,shape = I[j] + a, b, shape = I[j] v = generate_plot_points(f1, (a, b), plot_points, adaptive_tolerance, adaptive_recursion, randomize) w = generate_plot_points(f2, (a, b), plot_points, adaptive_tolerance, adaptive_recursion, randomize) if shape == 'o': - g += plot.line(v + list(reversed(w)) + [v[0]], **args) + g += line(v + list(reversed(w)) + [v[0]], **args) elif shape == '<': - g += plot.line(list(reversed(v)) + w, **args) + g += line(list(reversed(v)) + w, **args) elif shape == '>': - g += plot.line(v + list(reversed(w)), **args) + g += line(v + list(reversed(w)), **args) else: - g += plot.line(v, **args) - g += plot.line(w, **args) + g += line(v, **args) + g += line(w, **args) return g @cached_method @@ -3194,7 +3294,7 @@ def _p_primary_torsion_basis(self, p, m=None): k = 1 log_order = 2 if m <= log_order: - return [[P1,1],[P2,1]] + return [[P1, 1], [P2, 1]] pts1 = P1.division_points(p) pts2 = P2.division_points(p) @@ -3255,7 +3355,7 @@ def _p_primary_torsion_basis(self, p, m=None): return [[P1, n], [P2, k]] pts = P1.division_points(p) if not pts: - for Q in generic.multiples(P2,p-1,P1+P2,operation='+'): + for Q in generic.multiples(P2, p-1, P1+P2, operation='+'): # Q runs through P1+a*P2 for a=1,2,...,p-1 pts = Q.division_points(p) if pts: @@ -3324,8 +3424,8 @@ def pari_curve(self): sage: K.<a> = QuadraticField(2) sage: E = EllipticCurve([1,a]) sage: E.pari_curve() - [Mod(0, y^2 - 2), Mod(0, y^2 - 2), Mod(0, y^2 - 2), Mod(1, y^2 - 2), - Mod(y, y^2 - 2), Mod(0, y^2 - 2), Mod(2, y^2 - 2), Mod(4*y, y^2 - 2), + [0, 0, 0, Mod(1, y^2 - 2), + Mod(y, y^2 - 2), 0, Mod(2, y^2 - 2), Mod(4*y, y^2 - 2), Mod(-1, y^2 - 2), Mod(-48, y^2 - 2), Mod(-864*y, y^2 - 2), Mod(-928, y^2 - 2), Mod(3456/29, y^2 - 2), Vecsmall([5]), [[y^2 - 2, [2, 0], 8, 1, [[1, -1.41421356237310; diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 72489b121f9..c91a54fdc27 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -8,7 +8,7 @@ subspace on which complex conjugation acts as multiplication by `+1` and one on which it acts by `-1`. -There are three implementations of modular symbols, two within +There are three implementations of modular symbols, two within ``Sage`` and one in Cremona's ``eclib`` library. One can choose here which one is used. @@ -95,7 +95,7 @@ from sage.rings.infinity import unsigned_infinity as infinity from sage.rings.integer import Integer from sage.modular.cusps import Cusps -from sage.rings.integer_ring import ZZ +from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.misc.verbose import verbose diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index edbd1960907..09dbf9232d3 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -218,7 +218,7 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, sage: E == loads(dumps(E)) True sage: E.simon_two_descent() - (2, 2, [(0 : 0 : 1)]) + (2, 2, [(0 : 0 : 1), (1/18*a + 7/18 : -5/54*a - 17/54 : 1)]) sage: E.simon_two_descent(lim1=5, lim3=5, limtriv=10, maxprob=7, limbigprime=10) (2, 2, [(-1 : 0 : 1), (-2 : -1/2*a - 1/2 : 1)]) @@ -274,7 +274,7 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, sage: E.simon_two_descent() # long time (4s on sage.math, 2013) (3, 3, - [(5/8*zeta43_0^2 + 17/8*zeta43_0 - 9/4 : -27/16*zeta43_0^2 - 103/16*zeta43_0 + 39/8 : 1), + [(1/8*zeta43_0^2 - 3/8*zeta43_0 - 1/4 : -5/16*zeta43_0^2 + 7/16*zeta43_0 + 1/8 : 1), (0 : 0 : 1)]) """ verbose = int(verbose) @@ -865,7 +865,7 @@ def local_data(self, P=None, proof=None, algorithm="pari", globally=False): Conductor exponent: 1 Kodaira Symbol: I1 Tamagawa Number: 1, - Local data at Fractional ideal (-3*i - 2): + Local data at Fractional ideal (-2*i + 3): Reduction type: bad split multiplicative Local minimal model: Elliptic Curve defined by y^2 + (i+1)*x*y + y = x^3 over Number Field in i with defining polynomial x^2 + 1 Minimal discriminant valuation: 2 @@ -2290,13 +2290,21 @@ def gens(self, **kwds): It can happen that no points are found if the height bounds used in the search are too small (see :trac:`10745`):: - sage: K.<y> = NumberField(x^4 + x^2 - 7) - sage: E = EllipticCurve(K, [1, 0, 5*y^2 + 16, 0, 0]) + sage: K.<t> = NumberField(x^4 + x^2 - 7) + sage: E = EllipticCurve(K, [1, 0, 5*t^2 + 16, 0, 0]) sage: E.gens(lim1=1, lim3=1) [] - sage: E.rank(), E.gens(lim3=12) # long time (about 4s) - (1, - [(369/25*y^3 + 539/25*y^2 + 1178/25*y + 1718/25 : -29038/125*y^3 - 43003/125*y^2 - 92706/125*y - 137286/125 : 1)]) + sage: E.rank() + 1 + sage: gg=E.gens(lim3=13); gg # long time (about 4s) + [(... : 1)] + + Check that the the point found has infinite order, and that it is on the curve:: + + sage: P=gg[0]; P.order() # long time + +Infinity + sage: E.defining_polynomial()(*P) # long time + 0 Here is a curve of rank 2:: @@ -2644,13 +2652,13 @@ class number is only `3` is that the class also contains three sage: [phi.codomain().cm_discriminant() for phi in E.isogenies_prime_degree()] # long time [-92, -23, -23] - sage: C.matrix() # long time - [1 2 2 4 2 4] - [2 1 2 2 4 4] - [2 2 1 4 4 2] - [4 2 4 1 3 3] - [2 4 4 3 1 3] - [4 4 2 3 3 1] + sage: C.matrix() # long time # random + [1 2 2 4 4 2] + [2 1 2 4 2 4] + [2 2 1 2 4 4] + [4 4 2 1 3 3] + [4 2 4 3 1 3] + [2 4 4 3 3 1] The graph of this isogeny class has a shape which does not occur over `\QQ`: a triangular prism. Note that for curves @@ -2676,13 +2684,15 @@ class number is only `3` is that the class also contains three determined:: sage: G = C.graph() # long time - sage: G.adjacency_matrix() # long time - [0 1 1 0 1 0] - [1 0 1 1 0 0] - [1 1 0 0 0 1] - [0 1 0 0 1 1] - [1 0 0 1 0 1] - [0 0 1 1 1 0] + sage: G.adjacency_matrix() # long time # random + [0 1 1 0 0 1] + [1 0 1 0 1 0] + [1 1 0 1 0 0] + [0 0 1 0 1 1] + [0 1 0 1 0 1] + [1 0 0 1 1 0] + sage: Graph(polytopes.simplex(2).prism().adjacency_matrix()).is_isomorphic(G) # long time + True To display the graph without any edge labels:: @@ -3316,7 +3326,7 @@ def lll_reduce(self, points, height_matrix=None, precision=None): sage: points = [E.lift_x(x) for x in xi] sage: newpoints, U = E.lll_reduce(points) # long time (35s on sage.math, 2011) sage: [P[0] for P in newpoints] # long time - [6823803569166584943, 5949539878899294213, 2005024558054813068, 5864879778877955778, 23955263915878682727/4, 5922188321411938518, 5286988283823825378, 175620639884534615751/25, -11451575907286171572, 3502708072571012181, 1500143935183238709184/225, 27180522378120223419/4, -5811874164190604461581/625, 26807786527159569093, 7404442636649562303, 475656155255883588, 265757454726766017891/49, 7272142121019825303, 50628679173833693415/4, 6951643522366348968, 6842515151518070703, 111593750389650846885/16, 2607467890531740394315/9, -1829928525835506297] + [6823803569166584943, 5949539878899294213, 2005024558054813068, 5864879778877955778, 23955263915878682727/4, 5922188321411938518, 5286988283823825378, 11465667352242779838, -11451575907286171572, 3502708072571012181, 1500143935183238709184/225, 27180522378120223419/4, -5811874164190604461581/625, 26807786527159569093, 7041412654828066743, 475656155255883588, 265757454726766017891/49, 7272142121019825303, 50628679173833693415/4, 6951643522366348968, 6842515151518070703, 111593750389650846885/16, 2607467890531740394315/9, -1829928525835506297] An example to show the explicit use of the height pairing matrix:: diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 9c9038e0eb5..9f0c3799f36 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -85,7 +85,7 @@ sage: LCM([2..60])*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 1520944668 does not exist (characteristic = 1715761513 = 26927*63719) + ZeroDivisionError: Inverse of 26927 does not exist (characteristic = 1715761513 = 26927*63719) AUTHORS: @@ -118,8 +118,6 @@ import math -import sage.plot.all as plot - from sage.rings.padics.factory import Qp from sage.rings.padics.precision_error import PrecisionError @@ -256,14 +254,11 @@ def __init__(self, curve, v, check=True): (1 : -2 : 1) """ point_homset = curve.point_homset() + R = point_homset.value_ring() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) elif v == 0: - # some of the code assumes that E(0) has integral entries - # regardless of the base ring... - # R = self.base_ring() - # v = (R.zero(),R.one(),R.zero()) - v = (0, 1, 0) + v = (R.zero(), R.one(), R.zero()) if check: # mostly from SchemeMorphism_point_projective_field d = point_homset.codomain().ambient_space().ngens() @@ -271,7 +266,7 @@ def __init__(self, curve, v, check=True): raise TypeError("Argument v (= %s) must be a scheme point, list, or tuple." % str(v)) if len(v) != d and len(v) != d-1: raise TypeError("v (=%s) must have %s components" % (v, d)) - v = Sequence(v, point_homset.value_ring()) + v = Sequence(v, R) if len(v) == d-1: # very common special case v.append(v.universe()(1)) @@ -486,8 +481,13 @@ def order(self): sage: E(0).order() == 1 True """ + try: + return self._order + except AttributeError: + pass if self.is_zero(): - return Integer(1) + self._order = Integer(1) + return self._order raise NotImplementedError("Computation of order of a point " "not implemented over general fields.") @@ -523,7 +523,7 @@ def __bool__(self): """ return bool(self[2]) - + def has_finite_order(self): """ @@ -597,10 +597,13 @@ def plot(self, **args): sage: P.plot(pointsize=30, rgbcolor=(1,0,0)) Graphics object consisting of 1 graphics primitive """ + from sage.plot.point import point + from sage.plot.text import text + if self.is_zero(): - return plot.text("$\\infty$", (-3, 3), **args) + return text("$\\infty$", (-3, 3), **args) else: - return plot.point((self[0], self[1]), **args) + return point((self[0], self[1]), **args) def _add_(self, right): """ @@ -630,7 +633,7 @@ def _add_(self, right): sage: LCM([2..60])*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 1520944668 does not exist + ZeroDivisionError: Inverse of 26927 does not exist (characteristic = 1715761513 = 26927*63719) sage: N = 35 @@ -639,8 +642,15 @@ def _add_(self, right): sage: 4*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 28 does not exist + ZeroDivisionError: Inverse of 7 does not exist (characteristic = 35 = 7*5) + + Checks that :trac:`34681` is fixed:: + + sage: P+P + (15 : 14 : 1) + sage: 2*P + (15 : 14 : 1) """ # Use Prop 7.1.7 of Cohen "A Course in Computational Algebraic # Number Theory" @@ -1165,7 +1175,7 @@ def _divide_out(self, p): pts = Q.division_points(p) return (Q, k) - def set_order(self, value): + def set_order(self, value, *, check=True): r""" Set the value of self._order to value. @@ -1186,7 +1196,7 @@ def set_order(self, value): :: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) sage: G.set_order(2) sage: 2*G @@ -1214,7 +1224,7 @@ def set_order(self, value): It is an error to pass a `value` equal to `0`:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E.random_point() sage: G.set_order(0) Traceback (most recent call last): @@ -1229,7 +1239,7 @@ def set_order(self, value): order of this point. How unlikely is determined by the factorization of the actual order, and the actual group structure:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) # G has order 2 sage: G.set_order(11) Traceback (most recent call last): @@ -1240,7 +1250,7 @@ def set_order(self, value): of interest". For instance, the order can be set to a multiple the actual order:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) # G has order 2 sage: G.set_order(8) sage: G.order() @@ -1252,15 +1262,16 @@ def set_order(self, value): """ value = Integer(value) - E = self.curve() - q = E.base_ring().order() - if value <= 0: - raise ValueError('Value %s illegal for point order' % value) - low, hi = Hasse_bounds(q) - if value > hi: - raise ValueError('Value %s illegal: outside max Hasse bound' % value) - if value * self != E(0): - raise ValueError('Value %s illegal: %s * %s is not the identity' % (value, value, self)) + if check: + if value <= 0: + raise ValueError('Value %s illegal for point order' % value) + E = self.curve() + q = E.base_ring().cardinality() + low, hi = Hasse_bounds(q) + if value > hi: + raise ValueError('Value %s illegal: outside max Hasse bound' % value) + if value * self != E(0): + raise ValueError('Value %s illegal: %s * %s is not the identity' % (value, value, self)) self._order = value # ############################# end ################################ @@ -3497,7 +3508,16 @@ def _acted_upon_(self, other, side): try: pariQ = pari.ellmul(E, self, k) - except PariError: + except PariError as err: + if str(err.errdata().component(1)) == "Fp_inv": + val = err.errdata().component(2) + a = val.lift() + N = val.mod() + N1 = N.gcd(a) + N2 = N//N1 + raise ZeroDivisionError( + f"Inverse of {a} does not exist" + f" (characteristic = {N} = {N1}*{N2})") pariQ = None if pariQ is not None: @@ -3505,7 +3525,7 @@ def _acted_upon_(self, other, side): vQ = 0 else: assert len(pariQ) == 2 - vQ = Sequence(tuple(pariQ) + (1,), E.base_field()) + vQ = Sequence(tuple(pariQ) + (1,), E.base_ring()) Q = EllipticCurvePoint_finite_field(E, vQ, check=False) else: @@ -3543,7 +3563,7 @@ def discrete_log(self, Q, ord=None): - Otherwise (if this test is inconclusive), check that the Weil pairing of `P` and `Q` is trivial. - For anomalous curves with `\#E = p`, the + For anomalous curves with `\#E = p`, the :meth:`padic_elliptic_logarithm` function is called. INPUT: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index fc54013b7ba..c2b8436f2a1 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -52,15 +52,15 @@ from . import constructor from . import BSD -from .ell_generic import is_EllipticCurve +from .ell_generic import is_EllipticCurve from . import ell_modular_symbols -from .ell_number_field import EllipticCurve_number_field +from .ell_number_field import EllipticCurve_number_field from . import ell_point from . import ell_tate_curve from . import ell_torsion from . import heegner from . import mod5family -from .modular_parametrization import ModularParameterization +from .modular_parametrization import ModularParameterization from . import padics from sage.modular.modsym.modsym import ModularSymbols @@ -1827,7 +1827,7 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, sage: E = EllipticCurve('389a1') sage: E._known_points = [] # clear cached points sage: E.simon_two_descent() - (2, 2, [(1 : 0 : 1), (-11/9 : 28/27 : 1)]) + (2, 2, [(5/4 : 5/8 : 1), (-3/4 : 7/8 : 1)]) sage: E = EllipticCurve('5077a1') sage: E.simon_two_descent() (3, 3, [(1 : 0 : 1), (2 : 0 : 1), (0 : 2 : 1)]) @@ -4791,7 +4791,7 @@ def is_isogenous(self, other, proof=True, maxp=200): if not proof: return True else: - return E2 in E1.isogeny_class().curves + return E2 in E1.isogeny_class().curves def isogeny_degree(self, other): """ diff --git a/src/sage/schemes/elliptic_curves/ell_torsion.py b/src/sage/schemes/elliptic_curves/ell_torsion.py index acf7650f996..3097a2ed3de 100644 --- a/src/sage/schemes/elliptic_curves/ell_torsion.py +++ b/src/sage/schemes/elliptic_curves/ell_torsion.py @@ -376,7 +376,7 @@ def torsion_bound(E, number_of_places=20): p = ZZ(2) # so we start with 3 try: # special case, useful for base-changes from QQ - ainvs = [ZZ(ai) for ai in ainvs] + ainvs = [ZZ(ai) for ai in ainvs] while k < number_of_places: p = p.next_prime() if p.divides(disc_E) or p.divides(disc_f): 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 81ad2951607..d484a4a18bd 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -780,12 +780,12 @@ def deg_one_primes_iter(K, principal_only=False): [Fractional ideal (2, a + 1), Fractional ideal (3, a + 1), Fractional ideal (3, a + 2), - Fractional ideal (-a), + Fractional ideal (a), Fractional ideal (7, a + 3), Fractional ideal (7, a + 4)] sage: it = deg_one_primes_iter(K, True) sage: [next(it) for _ in range(6)] - [Fractional ideal (-a), + [Fractional ideal (a), Fractional ideal (-2*a + 3), Fractional ideal (2*a + 3), Fractional ideal (a + 6), diff --git a/src/sage/schemes/elliptic_curves/gp_simon.py b/src/sage/schemes/elliptic_curves/gp_simon.py index 28b97f34afe..9f7d1b60203 100644 --- a/src/sage/schemes/elliptic_curves/gp_simon.py +++ b/src/sage/schemes/elliptic_curves/gp_simon.py @@ -56,7 +56,7 @@ 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) - (2, 2, [(1 : 0 : 1), (-11/9 : 28/27 : 1)]) + (2, 2, [(5/4 : 5/8 : 1), (-3/4 : 7/8 : 1)]) TESTS:: @@ -117,7 +117,7 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, # The block below mimics the defaults in Simon's scripts, and needs to be changed # when these are updated. if K is QQ: - cmd = 'ellrank(%s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) + cmd = 'ellQ_ellrank(%s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) if lim1 is None: lim1 = 5 if lim3 is None: @@ -144,7 +144,7 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, if verbose > 0: print(s) v = gp.eval('ans') - if v=='ans': # then the call to ellrank() or bnfellrank() failed + 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) diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 2b699fd6cf8..298f44fedc6 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -117,7 +117,7 @@ from sage.modular.modsym.p1list import P1List -################################################################################## +############################################################################### # # The exported functions, which are in most cases enough to get the # user going working with Heegner points: @@ -125,7 +125,7 @@ # heegner_points -- all of them with given level, discriminant, conductor # heegner_point -- a specific one # -################################################################################## +############################################################################### def heegner_points(N, D=None, c=None): """ @@ -135,11 +135,11 @@ def heegner_points(N, D=None, c=None): INPUT: - - `N` -- level (positive integer) + - `N` -- level (positive integer) - - `D` -- discriminant (negative integer) + - `D` -- discriminant (negative integer) - - `c` -- conductor (positive integer) + - `c` -- conductor (positive integer) EXAMPLES:: @@ -155,9 +155,10 @@ def heegner_points(N, D=None, c=None): if D is not None and c is None: return HeegnerPoints_level_disc(N, D) if D is not None and c is not None: - return HeegnerPoints_level_disc_cond(N,D,c) + return HeegnerPoints_level_disc_cond(N, D, c) raise TypeError + def heegner_point(N, D=None, c=1): """ Return a specific Heegner point of level `N` with given @@ -167,11 +168,11 @@ def heegner_point(N, D=None, c=1): INPUT: - - `N` -- level (positive integer) + - `N` -- level (positive integer) - - `D` -- discriminant (optional: default first valid `D`) + - `D` -- discriminant (optional: default first valid `D`) - - `c` -- conductor (positive integer, optional, default: 1) + - `c` -- conductor (positive integer, optional, default: 1) EXAMPLES:: @@ -185,20 +186,20 @@ def heegner_point(N, D=None, c=1): Heegner point 1/778*sqrt(-20) - 165/389 of discriminant -20 on X_0(389) """ if D is not None: - return heegner_points(N,D,c)[0] + return heegner_points(N, D, c)[0] H = heegner_points(N) D = H.discriminants(1)[0] - return heegner_points(N,D,c)[0] + return heegner_points(N, D, c)[0] -################################################################################## +############################################################################### # # Ring class fields, represented as abstract objects. These do not # derive from number fields, since we do not need to work with their # elements, and explicitly representing them as number fields would be # far too difficult. # -################################################################################## +############################################################################### class RingClassField(SageObject): """ @@ -352,9 +353,8 @@ def _repr_(self): """ c = self.__c if c == 1: - return "Hilbert class field of QQ[sqrt(%s)]"%self.__D - else: - return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c) + return "Hilbert class field of QQ[sqrt(%s)]" % self.__D + return "Ring class field extension of QQ[sqrt(%s)] of conductor %s" % (self.__D, self.__c) @cached_method def degree_over_K(self): @@ -6533,7 +6533,8 @@ def heegner_point_height(self, D, prec=2, check_rank=True): return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F) -def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, verbose_mwrank=False, check_rank=True): +def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, + verbose_mwrank=False, check_rank=True): r""" Return an interval that contains the index of the Heegner point `y_K` in the group of `K`-rational points modulo torsion @@ -6745,7 +6746,7 @@ def _adjust_heegner_index(self, a): return a.sqrt() -def heegner_index_bound(self, D=0, prec=5, max_height=None): +def heegner_index_bound(self, D=0, prec=5, max_height=None): r""" Assume ``self`` has rank 0. diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 8d707e8e6b7..57562d841b8 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -10,6 +10,8 @@ - :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny` - :class:`~sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism` - :class:`~sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite` +- :class:`~sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar` +- :class:`~sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius` - :class:`~sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt` AUTHORS: @@ -20,7 +22,6 @@ - Lorenz Panny (2021): Refactor isogenies and isomorphisms into the common :class:`EllipticCurveHom` interface. """ - from sage.misc.cachefunc import cached_method from sage.structure.richcmp import richcmp_not_equal, richcmp, op_EQ, op_NE @@ -31,11 +32,46 @@ from sage.rings.finite_rings import finite_field_base from sage.rings.number_field import number_field_base +import sage.schemes.elliptic_curves.weierstrass_morphism as wm + class EllipticCurveHom(Morphism): """ Base class for elliptic-curve morphisms. """ + def __init__(self, *args, **kwds): + r""" + Constructor for elliptic-curve morphisms. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(257^2), [5,5]) + sage: P = E.lift_x(1) + sage: E.isogeny(P) # indirect doctest + Isogeny of degree 127 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 to Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='factored') # indirect doctest + Composite morphism of degree 127 = 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='velusqrt') # indirect doctest + Elliptic-curve isogeny (using √élu) of degree 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 119*x + 231 over Finite Field in z2 of size 257^2 + sage: E.montgomery_model(morphism=True) # indirect doctest + (Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2, + Elliptic-curve morphism: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2 + Via: (u,r,s,t) = (88*z2 + 253, 208*z2 + 90, 0, 0)) + """ + super().__init__(*args, **kwds) + + # Over finite fields, isogenous curves have the same number of + # rational points, hence we copy over the cached curve orders. + if isinstance(self.base_ring(), finite_field_base.FiniteField) and self.degree(): + self._codomain._fetch_cached_order(self._domain) + self._domain._fetch_cached_order(self._codomain) + def _repr_type(self): """ Return a textual representation of what kind of morphism @@ -75,12 +111,9 @@ def _composition_(self, other, homset): sage: phi * iso Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 9*x over Finite Field of size 19 to Elliptic Curve defined by y^2 = x^3 + 15*x over Finite Field of size 19 sage: phi.dual() * phi - Composite map: + Composite morphism of degree 4 = 2^2: From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19 To: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19 - Defn: Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19 to Elliptic Curve defined by y^2 = x^3 + 15*x over Finite Field of size 19 - then - Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 15*x over Finite Field of size 19 to Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19 """ if not isinstance(self, EllipticCurveHom) or not isinstance(other, EllipticCurveHom): raise TypeError(f'cannot compose {type(self)} with {type(other)}') @@ -93,9 +126,8 @@ def _composition_(self, other, homset): if ret is not NotImplemented: return ret - # fall back to generic formal composite map - return Morphism._composition_(self, other, homset) - + from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite + return EllipticCurveHom_composite.from_factors([other, self]) @staticmethod def _comparison_impl(left, right, op): @@ -146,13 +178,14 @@ def _richcmp_(self, other, op): :: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism sage: E = EllipticCurve([9,9]) sage: F = E.change_ring(GF(71)) - sage: wE = WeierstrassIsomorphism(E, (1,0,0,0)) - sage: wF = WeierstrassIsomorphism(F, (1,0,0,0)) - sage: mE = E.multiplication_by_m_isogeny(1) + sage: wE = identity_morphism(E) + sage: wF = identity_morphism(F) + sage: mE = E.scalar_multiplication(1) sage: mF = F.multiplication_by_m_isogeny(1) + doctest:warning ... DeprecationWarning: ... sage: [mE == wE, mF == wF] [True, True] sage: [a == b for a in (wE,mE) for b in (wF,mF)] @@ -197,7 +230,6 @@ def _richcmp_(self, other, op): return richcmp(self.rational_maps(), other.rational_maps(), op) - def degree(self): r""" Return the degree of this elliptic-curve morphism. @@ -221,7 +253,6 @@ def degree(self): is the product of the degrees of the individual factors:: sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite - doctest:warning ... sage: E = EllipticCurve(GF(419), [1,0]) sage: P, = E.gens() sage: phi = EllipticCurveHom_composite(E, P+P) @@ -259,6 +290,8 @@ def kernel_polynomial(self): - :meth:`EllipticCurveIsogeny.kernel_polynomial` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.kernel_polynomial` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.kernel_polynomial` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial` + - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.kernel_polynomial` TESTS:: @@ -279,6 +312,8 @@ def dual(self): - :meth:`EllipticCurveIsogeny.dual` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.dual` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.dual` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual` + - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.dual` TESTS:: @@ -301,6 +336,8 @@ def rational_maps(self): - :meth:`EllipticCurveIsogeny.rational_maps` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.rational_maps` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.rational_maps` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps` + - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.rational_maps` TESTS:: @@ -322,6 +359,8 @@ def x_rational_map(self): - :meth:`EllipticCurveIsogeny.x_rational_map` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.x_rational_map` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.x_rational_map` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.x_rational_map` + - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.x_rational_map` TESTS:: @@ -331,11 +370,10 @@ def x_rational_map(self): ... NotImplementedError: ... """ - #TODO: could have a default implementation that simply - # returns the first component of rational_maps() + # TODO: could have a default implementation that simply + # returns the first component of rational_maps() raise NotImplementedError('children must implement') - def scaling_factor(self): r""" Return the Weierstrass scaling factor associated to this @@ -352,6 +390,7 @@ def scaling_factor(self): - :meth:`EllipticCurveIsogeny.scaling_factor` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.scaling_factor` TESTS:: @@ -361,9 +400,9 @@ def scaling_factor(self): ... NotImplementedError: ... """ - #TODO: could have a default implementation that simply - # returns .formal()[1], but it seems safer to fail - # visibly to make sure we would notice regressions + # TODO: could have a default implementation that simply + # returns .formal()[1], but it seems safer to fail + # visibly to make sure we would notice regressions raise NotImplementedError('children must implement') def formal(self, prec=20): @@ -402,18 +441,17 @@ def formal(self, prec=20): Eh = self._domain.formal() f, g = self.rational_maps() xh = Eh.x(prec=prec) - assert xh.valuation() == -2, f"xh has valuation {xh.valuation()} (should be -2)" + assert not self.is_separable() or xh.valuation() == -2, f"xh has valuation {xh.valuation()} (should be -2)" yh = Eh.y(prec=prec) - assert yh.valuation() == -3, f"yh has valuation {yh.valuation()} (should be -3)" + assert not self.is_separable() or yh.valuation() == -3, f"yh has valuation {yh.valuation()} (should be -3)" fh = f(xh,yh) - assert fh.valuation() == -2, f"fh has valuation {fh.valuation()} (should be -2)" + assert not self.is_separable() or fh.valuation() == -2, f"fh has valuation {fh.valuation()} (should be -2)" gh = g(xh,yh) - assert gh.valuation() == -3, f"gh has valuation {gh.valuation()} (should be -3)" + assert not self.is_separable() or gh.valuation() == -3, f"gh has valuation {gh.valuation()} (should be -3)" th = -fh/gh - assert th.valuation() == +1, f"th has valuation {th.valuation()} (should be +1)" + assert not self.is_separable() or th.valuation() == +1, f"th has valuation {th.valuation()} (should be +1)" return th - def is_normalized(self): r""" Determine whether this morphism is a normalized isogeny. @@ -493,32 +531,27 @@ def is_normalized(self): """ return self.scaling_factor() == 1 - def is_separable(self): r""" Determine whether or not this morphism is separable. - .. NOTE:: - - This method currently always returns ``True`` as Sage does - not yet implement inseparable isogenies. This will probably - change in the future. - - EXAMPLES:: + Implemented by child classes. For examples, see: - sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) - sage: phi = EllipticCurveIsogeny(E, E((0,0))) - sage: phi.is_separable() - True + - :meth:`EllipticCurveIsogeny.is_separable` + - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.is_separable` + - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.is_separable` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.is_separable` + - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.is_separable` - :: + TESTS:: - sage: E = EllipticCurve('11a1') - sage: phi = EllipticCurveIsogeny(E, E.torsion_points()) - sage: phi.is_separable() - True + sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom + sage: EllipticCurveHom.is_separable(None) + Traceback (most recent call last): + ... + NotImplementedError: ... """ - return True + raise NotImplementedError('children must implement') def is_surjective(self): r""" @@ -586,7 +619,7 @@ def is_injective(self): True """ if not self.is_separable(): - #TODO: should implement .separable_degree() or similar + # TODO: should implement .separable_degree() or similar raise NotImplementedError return self.degree() == 1 @@ -625,9 +658,7 @@ def __neg__(self): sage: psi.rational_maps() == (f, -g) True """ - import sage.schemes.elliptic_curves.weierstrass_morphism as wm - a1,_,a3,_,_ = self.codomain().a_invariants() - return wm.WeierstrassIsomorphism(self.codomain(), (-1,0,-a1,-a3)) * self + return wm.negation_morphism(self.codomain()) * self @cached_method def __hash__(self): @@ -661,6 +692,36 @@ def __hash__(self): """ return hash((self.domain(), self.codomain(), self.kernel_polynomial())) + def as_morphism(self): + r""" + Return ``self`` as a morphism of projective schemes. + + EXAMPLES:: + + sage: k = GF(11) + sage: E = EllipticCurve(k, [1,1]) + sage: Q = E(6,5) + sage: phi = E.isogeny(Q) + sage: mor = phi.as_morphism() + sage: mor.domain() == E + True + sage: mor.codomain() == phi.codomain() + True + sage: mor(Q) == phi(Q) + True + + TESTS:: + + sage: mor(0*Q) + (0 : 1 : 0) + sage: mor(1*Q) + (0 : 1 : 0) + """ + from sage.schemes.curves.constructor import Curve + X_affine = Curve(self.domain()).affine_patch(2) + Y_affine = Curve(self.codomain()).affine_patch(2) + return X_affine.hom(self.rational_maps(), Y_affine).homogenize(2) + def compare_via_evaluation(left, right): r""" @@ -692,7 +753,7 @@ def compare_via_evaluation(left, right): sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: mu = EllipticCurveHom_composite.from_factors([phi, psi]) sage: from sage.schemes.elliptic_curves.hom import compare_via_evaluation - sage: compare_via_evaluation(mu, E.multiplication_by_m_isogeny(7)) + sage: compare_via_evaluation(mu, E.scalar_multiplication(7)) True .. SEEALSO:: @@ -713,11 +774,10 @@ def compare_via_evaluation(left, right): 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) + 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)) 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) @@ -725,7 +785,99 @@ def compare_via_evaluation(left, right): return left._eval(P) == right._eval(P) else: assert False, "couldn't find a point of infinite order" - else: raise NotImplementedError('not implemented for this base field') + +def find_post_isomorphism(phi, psi): + r""" + Given two isogenies `\phi: E\to E'` and `\psi: E\to E''` + which are equal up to post-isomorphism defined over the + same field, find that isomorphism. + + In other words, this function computes an isomorphism + `\alpha: E'\to E''` such that `\alpha\circ\phi = \psi`. + + ALGORITHM: + + Start with a list of all isomorphisms `E'\to E''`. Then + repeatedly evaluate `\phi` and `\psi` at random points + `P` to filter the list for isomorphisms `\alpha` with + `\alpha(\phi(P)) = \psi(P)`. Once only one candidate is + left, return it. Periodically extend the base field to + avoid getting stuck (say, if all candidate isomorphisms + act the same on all rational points). + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom import find_post_isomorphism + sage: E = EllipticCurve(GF(7^2), [1,0]) + sage: f = E.scalar_multiplication(1) + sage: g = choice(E.automorphisms()) + sage: find_post_isomorphism(f, g) == g + True + + :: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite + sage: F.<i> = GF(883^2, modulus=x^2+1) + sage: E = EllipticCurve(F, [1,0]) + sage: P = E.lift_x(117) + sage: Q = E.lift_x(774) + sage: w = WeierstrassIsomorphism(E, [i,0,0,0]) + sage: phi = EllipticCurveHom_composite(E, [P,w(Q)]) * w + sage: psi = EllipticCurveHom_composite(E, [Q,w(P)]) + sage: phi.kernel_polynomial() == psi.kernel_polynomial() + True + sage: find_post_isomorphism(phi, psi) + Elliptic-curve morphism: + From: Elliptic Curve defined by y^2 = x^3 + 320*x + 482 over Finite Field in i of size 883^2 + To: Elliptic Curve defined by y^2 = x^3 + 320*x + 401 over Finite Field in i of size 883^2 + Via: (u,r,s,t) = (882*i, 0, 0, 0) + """ + E = phi.domain() + if psi.domain() != E: + raise ValueError('domains do not match') + + isos = phi.codomain().isomorphisms(psi.codomain()) + if not isos: + raise ValueError('codomains not isomorphic') + + F = E.base_ring() + from sage.rings.finite_rings import finite_field_base + from sage.rings.number_field import number_field_base + + if isinstance(F, finite_field_base.FiniteField): + while len(isos) > 1: + for _ in range(20): + P = E.random_point() + im_phi, im_psi = (phi._eval(P), psi._eval(P)) + isos = [iso for iso in isos if iso._eval(im_phi) == im_psi] + if len(isos) <= 1: + break + else: + E = E.base_extend(E.base_field().extension(2)) + + elif isinstance(F, number_field_base.NumberField): + for _ in range(100): + P = E.lift_x(F.random_element(), extend=True) + if P.has_finite_order(): + continue + break + else: + assert False, "couldn't find a point of infinite order" + im_phi, im_psi = (phi._eval(P), psi._eval(P)) + isos = [iso for iso in isos if iso._eval(im_phi) == im_psi] + + else: + # fall back to generic method + sc = psi.scaling_factor() / phi.scaling_factor() + isos = [iso for iso in isos if iso.u == sc] + + assert len(isos) <= 1 + if isos: + return isos[0] + + # found no suitable isomorphism -- either doesn't exist or a bug + raise ValueError('isogenies not equal up to post-isomorphism') diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index 32b1fa9e0bb..576107582db 100644 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -7,12 +7,6 @@ while exposing (close to) the same interface as "normal", unfactored elliptic-curve isogenies. -.. WARNING:: - - This module is currently considered experimental. - It may change in a future release without prior warning, or even - be removed altogether if things turn out to be unfixably broken. - EXAMPLES: The following example would take quite literally forever with the @@ -20,8 +14,6 @@ decomposing into prime steps is exponentially faster:: sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite - doctest:warning - ... sage: p = 3 * 2^143 - 1 sage: GF(p^2).inject_variables() Defining z2 @@ -32,7 +24,9 @@ sage: EllipticCurveHom_composite(E, P) Composite morphism of degree 11150372599265311570767859136324180752990208 = 2^143: From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 33451117797795934712303577408972542258970623^2 - To: Elliptic Curve defined by y^2 = x^3 + (18676616716352953484576727486205473216172067*z2+32690199585974925193292786311814241821808308)*x + (3369702436351367403910078877591946300201903*z2+15227558615699041241851978605002704626689722) over Finite Field in z2 of size 33451117797795934712303577408972542258970623^2 + To: Elliptic Curve defined by y^2 = x^3 + (18676616716352953484576727486205473216172067*z2+32690199585974925193292786311814241821808308)*x + + (3369702436351367403910078877591946300201903*z2+15227558615699041241851978605002704626689722) + over Finite Field in z2 of size 33451117797795934712303577408972542258970623^2 Yet, the interface provided by :class:`EllipticCurveHom_composite` is identical to :class:`EllipticCurveIsogeny` and other instantiations @@ -48,10 +42,15 @@ sage: psi(E.lift_x(11)) (352 : 73 : 1) sage: psi.rational_maps() - ((x^35 + 162*x^34 + 186*x^33 + 92*x^32 - ... + 44*x^3 + 190*x^2 + 80*x - 72)/(x^34 + 162*x^33 - 129*x^32 + 41*x^31 + ... + 66*x^3 - 191*x^2 + 119*x + 21), - (x^51*y - 176*x^50*y + 115*x^49*y - 120*x^48*y + ... + 72*x^3*y + 129*x^2*y + 163*x*y + 178*y)/(x^51 - 176*x^50 + 11*x^49 + 26*x^48 - ... - 77*x^3 + 185*x^2 + 169*x - 128)) + ((x^35 + 162*x^34 + 186*x^33 + 92*x^32 - ... + 44*x^3 + 190*x^2 + 80*x - + 72)/(x^34 + 162*x^33 - 129*x^32 + 41*x^31 + ... + 66*x^3 - 191*x^2 + 119*x + + 21), (x^51*y - 176*x^50*y + 115*x^49*y - 120*x^48*y + ... + 72*x^3*y + + 129*x^2*y + 163*x*y + 178*y)/(x^51 - 176*x^50 + 11*x^49 + 26*x^48 - ... - + 77*x^3 + 185*x^2 + 169*x - 128)) sage: psi.kernel_polynomial() - x^17 + 81*x^16 + 7*x^15 + 82*x^14 + 49*x^13 + 68*x^12 + 109*x^11 + 326*x^10 + 117*x^9 + 136*x^8 + 111*x^7 + 292*x^6 + 55*x^5 + 389*x^4 + 175*x^3 + 43*x^2 + 149*x + 373 + x^17 + 81*x^16 + 7*x^15 + 82*x^14 + 49*x^13 + 68*x^12 + 109*x^11 + 326*x^10 + + 117*x^9 + 136*x^8 + 111*x^7 + 292*x^6 + 55*x^5 + 389*x^4 + 175*x^3 + + 43*x^2 + 149*x + 373 sage: psi.dual() Composite morphism of degree 35 = 7*5: From: Elliptic Curve defined by y^2 = x^3 + 101*x + 285 over Finite Field of size 419 @@ -88,12 +87,10 @@ from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic from sage.schemes.elliptic_curves.hom import EllipticCurveHom, compare_via_evaluation from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny -from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism +from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism -from sage.misc.superseded import experimental_warning -experimental_warning(32744, 'EllipticCurveHom_composite is experimental code.') +# TODO: Implement sparse strategies? (cf. the SIKE cryptosystem) -#TODO: implement sparse strategies? (cf. the SIKE cryptosystem) def _eval_factored_isogeny(phis, P): """ @@ -119,6 +116,7 @@ def _eval_factored_isogeny(phis, P): P = phi(P) return P + def _compute_factored_isogeny_prime_power(P, l, e): """ This method takes a point `P` of order `l^e` and returns @@ -147,6 +145,7 @@ def _compute_factored_isogeny_prime_power(P, l, e): phis.append(phi) return phis + def _compute_factored_isogeny_single_generator(P): """ This method takes a point `P` and returns a sequence of @@ -173,6 +172,7 @@ def _compute_factored_isogeny_single_generator(P): phis += psis return phis + def _compute_factored_isogeny(kernel): """ This method takes a set of points on an elliptic curve @@ -199,6 +199,7 @@ def _compute_factored_isogeny(kernel): phis += psis return phis + class EllipticCurveHom_composite(EllipticCurveHom): _degree = None @@ -271,7 +272,7 @@ def __init__(self, E, kernel, codomain=None, model=None): self._phis = _compute_factored_isogeny(kernel) if not self._phis: - self._phis = [WeierstrassIsomorphism(E, (1, 0, 0, 0))] + self._phis = [identity_morphism(E)] if model is not None: if codomain is not None: @@ -349,7 +350,7 @@ def from_factors(cls, maps, E=None, strict=True): TESTS:: sage: E = EllipticCurve('4730k1') - sage: EllipticCurveHom_composite.from_factors([], E) == E.multiplication_by_m_isogeny(1) + sage: EllipticCurveHom_composite.from_factors([], E) == E.scalar_multiplication(1) True :: @@ -374,7 +375,7 @@ def from_factors(cls, maps, E=None, strict=True): E = phi.codomain() if not maps: - maps = (WeierstrassIsomorphism(E, (1,0,0,0)),) + maps = (identity_morphism(E),) if len(maps) == 1 and not strict: return maps[0] @@ -397,6 +398,16 @@ def _call_(self, P): sage: R = E.lift_x(15/4 * (a+3)) sage: psi(R) # indirect doctest (1033648757/303450 : 58397496786187/1083316500*a - 62088706165177/2166633000 : 1) + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P, algorithm='factored') + sage: phi(Q)._order + 27 """ return _eval_factored_isogeny(self._phis, P) @@ -479,7 +490,6 @@ def factors(self): """ return self._phis - # EllipticCurveHom methods @staticmethod @@ -517,14 +527,14 @@ def _composition_impl(left, right): To: Elliptic Curve defined by y^2 + (I+1)*x*y = x^3 + I*x^2 + (-4)*x + (-6*I) over Number Field in I with defining polynomial x^2 + 1 with I = 1*I """ if isinstance(left, EllipticCurveHom_composite): - if isinstance(right, WeierstrassIsomorphism) and hasattr(left.factors()[0], '_set_pre_isomorphism'): #XXX bit of a hack + if isinstance(right, WeierstrassIsomorphism) and hasattr(left.factors()[0], '_set_pre_isomorphism'): # XXX bit of a hack return EllipticCurveHom_composite.from_factors((left.factors()[0] * right,) + left.factors()[1:], strict=False) if isinstance(right, EllipticCurveHom_composite): return EllipticCurveHom_composite.from_factors(right.factors() + left.factors()) if isinstance(right, EllipticCurveHom): return EllipticCurveHom_composite.from_factors((right,) + left.factors()) if isinstance(right, EllipticCurveHom_composite): - if isinstance(left, WeierstrassIsomorphism) and hasattr(right.factors()[-1], '_set_post_isomorphism'): #XXX bit of a hack + if isinstance(left, WeierstrassIsomorphism) and hasattr(right.factors()[-1], '_set_post_isomorphism'): # XXX bit of a hack return EllipticCurveHom_composite.from_factors(right.factors()[:-1] + (left * right.factors()[-1],), strict=False) if isinstance(left, EllipticCurveHom): return EllipticCurveHom_composite.from_factors(right.factors() + (left,)) @@ -553,7 +563,7 @@ def _comparison_impl(left, right, op): sage: psi = phi.codomain().isogeny(phi(Q)) sage: psi = psi.codomain().isomorphism_to(E) * psi sage: comp = psi * phi - sage: mu = E.multiplication_by_m_isogeny(phi.degree()) + sage: mu = E.scalar_multiplication(phi.degree()) sage: sum(a*comp == mu for a in E.automorphisms()) 1 @@ -670,9 +680,9 @@ def dual(self): Composite morphism of degree 9 = 3^2: From: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 28339*x + 59518 over Finite Field of size 65537 To: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 65537 - sage: psi * phi == phi.domain().multiplication_by_m_isogeny(phi.degree()) + sage: psi * phi == phi.domain().scalar_multiplication(phi.degree()) True - sage: phi * psi == psi.domain().multiplication_by_m_isogeny(psi.degree()) + sage: phi * psi == psi.domain().scalar_multiplication(psi.degree()) True """ phis = (phi.dual() for phi in self._phis[::-1]) @@ -776,52 +786,19 @@ def is_injective(self): """ return all(phi.is_injective() for phi in self._phis) - @staticmethod def make_default(): r""" - Calling this method will override the composition method - of :class:`EllipticCurveHom` such that it constructs a - :class:`EllipticCurveHom_composite` object by default, - rather than a :class:`sage.categories.map.FormalCompositeMap`. - - .. WARNING:: + This method does nothing and will be removed. - This method exists only temporarily to make testing more - convenient while :class:`EllipticCurveHom_composite` is - experimental. + (It is a leftover from the time when :class:`EllipticCurveHom_composite` + wasn't the default yet.) EXAMPLES:: sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite - sage: E = EllipticCurve(GF(587), [1,0]) - sage: P = E(3,404) - sage: phi = E.isogeny(7*P) - sage: psi = phi.codomain().isogeny(phi(P)) - sage: psi * phi - Composite map: - From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 587 - To: Elliptic Curve defined by y^2 = x^3 + 296*x + 164 over Finite Field of size 587 - Defn: Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 587 to Elliptic Curve defined by y^2 = x^3 + 126*x + 500 over Finite Field of size 587 - then - Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 126*x + 500 over Finite Field of size 587 to Elliptic Curve defined by y^2 = x^3 + 296*x + 164 over Finite Field of size 587 sage: EllipticCurveHom_composite.make_default() - sage: psi * phi - Composite morphism of degree 49 = 7^2: - From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 587 - To: Elliptic Curve defined by y^2 = x^3 + 296*x + 164 over Finite Field of size 587 - sage: (psi * phi).factors() - (Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 587 to Elliptic Curve defined by y^2 = x^3 + 126*x + 500 over Finite Field of size 587, - Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 126*x + 500 over Finite Field of size 587 to Elliptic Curve defined by y^2 = x^3 + 296*x + 164 over Finite Field of size 587) + doctest:warning ... """ - def _composition_(self, other, homset): - if not isinstance(self, EllipticCurveHom) or not isinstance(other, EllipticCurveHom): - raise TypeError(f'cannot compose {type(self)} with {type(other)}') - ret = self._composition_impl(self, other) - if ret is not NotImplemented: - return ret - ret = other._composition_impl(self, other) - if ret is not NotImplemented: - return ret - return EllipticCurveHom_composite.from_factors([other, self]) - EllipticCurveHom._composition_ = _composition_ + from sage.misc.superseded import deprecation + deprecation(34410, 'calling EllipticCurveHom_composite.make_default() is no longer necessary') diff --git a/src/sage/schemes/elliptic_curves/hom_frobenius.py b/src/sage/schemes/elliptic_curves/hom_frobenius.py new file mode 100644 index 00000000000..430f8a3f417 --- /dev/null +++ b/src/sage/schemes/elliptic_curves/hom_frobenius.py @@ -0,0 +1,538 @@ +r""" +Frobenius isogenies of elliptic curves + +Frobenius isogenies only exist in positive characteristic `p`. They +are given by `\pi_n:(x,y)\mapsto (x^{p^n},y^{p^n})`. + +This class implements `\pi_n` for `n \geq 0`. Together with existing +tools for composing isogenies (see :class:`EllipticCurveHom_composite`), +we can therefore represent arbitrary inseparable isogenies in Sage. + +EXAMPLES: + +Constructing a Frobenius isogeny is straightforward:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: z5, = GF(17^5).gens() + sage: E = EllipticCurve([z5,1]) + sage: pi = EllipticCurveHom_frobenius(E); pi + Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + (9*z5^4+7*z5^3+10*z5^2+z5+14)*x + 1 over Finite Field in z5 of size 17^5 + +By passing `n`, we can also construct higher-power Frobenius maps, +such as the Frobenius *endo*\morphism:: + + sage: z5, = GF(7^5).gens() + sage: E = EllipticCurve([z5,1]) + sage: pi = EllipticCurveHom_frobenius(E,5); pi + Frobenius endomorphism of degree 16807 = 7^5: + From: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 7^5 + To: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 7^5 + +The usual :class:`EllipticCurveHom` methods are supported:: + + sage: z5, = GF(7^5).gens() + sage: E = EllipticCurve([z5,1]) + sage: pi = EllipticCurveHom_frobenius(E,5) + sage: pi.degree() + 16807 + sage: pi.rational_maps() + (x^16807, y^16807) + sage: pi.formal() # known bug + ... + sage: pi.is_normalized() # known bug + ... + sage: pi.is_separable() + False + sage: pi.is_injective() + True + sage: pi.is_surjective() + True + +Computing the dual of Frobenius is supported as well:: + + sage: E = EllipticCurve([GF(17^6).gen(), 0]) + sage: pi = EllipticCurveHom_frobenius(E) + sage: pihat = pi.dual(); pihat + Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + (15*z6^5+5*z6^4+8*z6^3+12*z6^2+11*z6+7)*x over Finite Field in z6 of size 17^6 to Elliptic Curve defined by y^2 = x^3 + z6*x over Finite Field in z6 of size 17^6 + sage: pihat.is_separable() + True + sage: pihat * pi == EllipticCurveHom_scalar(E,17) # known bug -- #6413 + True + +A supersingular example (with purely inseparable dual):: + + sage: E = EllipticCurve([0, GF(17^6).gen()]) + sage: E.is_supersingular() + True + sage: pi1 = EllipticCurveHom_frobenius(E) + sage: pi1hat = pi1.dual(); pi1hat + Composite morphism of degree 17 = 17*1: + From: Elliptic Curve defined by y^2 = x^3 + (15*z6^5+5*z6^4+8*z6^3+12*z6^2+11*z6+7) over Finite Field in z6 of size 17^6 + To: Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6 + sage: pi6 = EllipticCurveHom_frobenius(E,6) + sage: pi6hat = pi6.dual(); pi6hat + Composite morphism of degree 24137569 = 24137569*1: + From: Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6 + To: Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6 + sage: pi6hat.factors() + (Frobenius endomorphism of degree 24137569 = 17^6: + From: Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6 + To: Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6, + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + z6 over Finite Field in z6 of size 17^6 + Via: (u,r,s,t) = (2*z6^5 + 10*z6^3 + z6^2 + 8, 0, 0, 0)) + + +TESTS:: + + sage: z5, = GF(17^5).gens() + sage: E = EllipticCurve([z5,1]) + sage: fs = [EllipticCurveHom_frobenius(E)] + sage: while fs[-1].codomain() != E: + ....: fs.append(EllipticCurveHom_frobenius(fs[-1].codomain())) + sage: fs + [Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + (9*z5^4+7*z5^3+10*z5^2+z5+14)*x + 1 over Finite Field in z5 of size 17^5, + Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + (9*z5^4+7*z5^3+10*z5^2+z5+14)*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + (14*z5^4+7*z5^3+16*z5^2+14*z5+1)*x + 1 over Finite Field in z5 of size 17^5, + Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + (14*z5^4+7*z5^3+16*z5^2+14*z5+1)*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + (16*z5^4+6*z5^3+7*z5^2+14*z5+6)*x + 1 over Finite Field in z5 of size 17^5, + Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + (16*z5^4+6*z5^3+7*z5^2+14*z5+6)*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + (12*z5^4+14*z5^3+z5^2+4*z5+13)*x + 1 over Finite Field in z5 of size 17^5, + Frobenius isogeny of degree 17: + From: Elliptic Curve defined by y^2 = x^3 + (12*z5^4+14*z5^3+z5^2+4*z5+13)*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 17^5] + sage: prod(fs[::-1]) + Composite morphism of degree 1419857 = 17^5: + From: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 17^5 + To: Elliptic Curve defined by y^2 = x^3 + z5*x + 1 over Finite Field in z5 of size 17^5 + +:: + + sage: EllipticCurveHom_frobenius(EllipticCurve(GF(5),[1,1]), -1) + Traceback (most recent call last): + ... + ValueError: negative powers of Frobenius are not isogenies + +:: + + sage: EllipticCurveHom_frobenius(EllipticCurve('11a1')) + Traceback (most recent call last): + ... + ValueError: Frobenius isogenies do not exist in characteristic zero + +AUTHORS: + +- Lorenz Panny (2021): implement :class:`EllipticCurveHom_frobenius` +- Mickaël Montessinos (2021): computing the dual of a Frobenius isogeny +""" + +from sage.misc.cachefunc import cached_method +from sage.structure.sequence import Sequence + +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +from sage.rings.finite_rings.finite_field_base import FiniteField + +from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic +from sage.schemes.elliptic_curves.constructor import EllipticCurve + +from sage.schemes.elliptic_curves.hom import EllipticCurveHom, find_post_isomorphism +from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny +from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite +from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + + +class EllipticCurveHom_frobenius(EllipticCurveHom): + + _degree = None + + def __init__(self, E, power=1): + r""" + Construct a Frobenius isogeny on a given curve with a given + power of the base-ring characteristic. + + Writing `n` for the parameter ``power`` (default: `1`), the + isogeny is defined by `(x,y) \to (x^{p^n}, y^{p^n})` where + `p` is the characteristic of the base ring. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(j=GF(11^2).gen()) + sage: EllipticCurveHom_frobenius(E) + Frobenius isogeny of degree 11: + From: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + To: Elliptic Curve defined by y^2 = x^3 + (9*z2+3)*x + (3*z2+7) over Finite Field in z2 of size 11^2 + sage: EllipticCurveHom_frobenius(E, 2) + Frobenius endomorphism of degree 121 = 11^2: + From: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + To: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + + TESTS:: + + sage: EllipticCurveHom_frobenius(EllipticCurve('11a1')) + Traceback (most recent call last): + ... + ValueError: Frobenius isogenies do not exist in characteristic zero + + :: + + sage: EllipticCurveHom_frobenius(E, -1) + Traceback (most recent call last): + ... + ValueError: negative powers of Frobenius are not isogenies + """ + if not isinstance(E, EllipticCurve_generic): + raise ValueError(f'not an elliptic curve: {E}') + + self._base_ring = E.base_ring() + + self._p = self._base_ring.characteristic() + if self._p == 0: + raise ValueError('Frobenius isogenies do not exist in characteristic zero') + + self._n = ZZ(power) + if self._n < 0: + raise ValueError('negative powers of Frobenius are not isogenies') + + self._degree = self._p ** self._n + + self._domain = E + as_ = [a**self._degree for a in self._domain.a_invariants()] + self._codomain = EllipticCurve(as_) + + EllipticCurveHom.__init__(self, self._domain, self._codomain) + + # over finite fields, isogenous curves have the same number of points + # (see #32786) + if isinstance(self._base_ring, FiniteField): + self._domain._fetch_cached_order(self._codomain) + self._codomain._fetch_cached_order(self._domain) + + self._poly_ring = PolynomialRing(self._base_ring, ['x'], sparse=True) + self._mpoly_ring = PolynomialRing(self._base_ring, ['x','y'], sparse=True) + self._xfield = self._poly_ring.fraction_field() + self._xyfield = self._mpoly_ring.fraction_field() + + def _call_(self, P): + """ + Evaluate this Frobenius isogeny at a point `P`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: z2 = GF(11^2).gen() + sage: E = EllipticCurve(j=z2) + sage: pi = EllipticCurveHom_frobenius(E) + sage: P = E(7, 9*z2+4) + sage: pi(P) # implicit doctest + (7 : 2*z2 + 7 : 1) + """ + return self._codomain(*(c**self._degree for c in P)) + + def _eval(self, P): + """ + Less strict evaluation method for internal use. + + In particular, this can be used to evaluate ``self`` at a + point defined over an extension field. + + INPUT: a sequence of 3 coordinates defining a point on ``self`` + + OUTPUT: the result of evaluating ``self`` at the given point + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E) + sage: P = E.change_ring(GF(11^6)).lift_x(GF(11^3).gen()); P + (6*z6^5 + 8*z6^4 + 8*z6^3 + 6*z6^2 + 10*z6 + 5 : 2*z6^5 + 2*z6^4 + 2*z6^3 + 4*z6 + 6 : 1) + sage: pi._eval(P) + (z6^5 + 3*z6^4 + 3*z6^3 + 6*z6^2 + 9 : z6^5 + 10*z6^4 + 10*z6^3 + 5*z6^2 + 4*z6 + 8 : 1) + """ + if self._domain.defining_polynomial()(*P): + raise ValueError(f'{P} not on {self._domain}') + k = Sequence(P).universe() + return self._codomain.base_extend(k)(*(c**self._degree for c in P)) + + def _repr_(self): + """ + Return basic facts about this Frobenius isogeny as a string. + + TESTS:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: z2 = GF(11^2).gen() + sage: E = EllipticCurve(j=z2) + sage: EllipticCurveHom_frobenius(E) + Frobenius isogeny of degree 11: + From: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + To: Elliptic Curve defined by y^2 = x^3 + (9*z2+3)*x + (3*z2+7) over Finite Field in z2 of size 11^2 + sage: EllipticCurveHom_frobenius(E, E.base_field().degree()) + Frobenius endomorphism of degree 121 = 11^2: + From: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + To: Elliptic Curve defined by y^2 = x^3 + (2*z2+6)*x + (8*z2+8) over Finite Field in z2 of size 11^2 + """ + kind = 'endomorphism' if self._codomain == self._domain else 'isogeny' + degs_str = '' if self._n == 1 else f' = {self._p}^{self._n}' + return f'Frobenius {kind} of degree {self._degree}{degs_str}:' \ + f'\n From: {self._domain}' \ + f'\n To: {self._codomain}' + + # EllipticCurveHom methods + + def degree(self): + """ + Return the degree of this Frobenius isogeny. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E, 4) + sage: pi.degree() + 14641 + """ + return self._degree + + def rational_maps(self): + """ + Return the explicit rational maps defining this Frobenius + isogeny as (sparse) bivariate rational maps in `x` and `y`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E, 4) + sage: pi.rational_maps() + (x^14641, y^14641) + + TESTS: + + See :trac:`34811`:: + + sage: pi.rational_maps()[0].parent() + Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field of size 11 + sage: pi.rational_maps()[1].parent() + Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field of size 11 + """ + x,y = self._xyfield.gens() + return (x**self._degree, y**self._degree) + + def x_rational_map(self): + """ + Return the `x`-coordinate rational map of this Frobenius + isogeny as a (sparse) univariate rational map in `x`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E, 4) + sage: pi.x_rational_map() + x^14641 + + TESTS: + + See :trac:`34811`:: + + sage: pi.x_rational_map().parent() + Fraction Field of Sparse Univariate Polynomial Ring in x over Finite Field of size 11 + """ + x, = self._xfield.gens() + return x**self._degree + + def scaling_factor(self): + r""" + Return the Weierstrass scaling factor associated to this + Frobenius morphism. + + The scaling factor is the constant `u` (in the base field) + such that `\varphi^* \omega_2 = u \omega_1`, where + `\varphi: E_1\to E_2` is this morphism and `\omega_i` are + the standard Weierstrass differentials on `E_i` defined by + `\mathrm dx/(2y+a_1x+a_3)`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E) + sage: pi.formal() + t^11 + O(t^33) + sage: pi.scaling_factor() + 0 + sage: pi = EllipticCurveHom_frobenius(E, 3) + sage: pi.formal() + t^1331 + O(t^1353) + sage: pi.scaling_factor() + 0 + sage: pi = EllipticCurveHom_frobenius(E, 0) + sage: pi == E.scalar_multiplication(1) + True + sage: pi.scaling_factor() + 1 + + The scaling factor lives in the base ring:: + + sage: pi.scaling_factor().parent() + Finite Field of size 11 + + ALGORITHM: Inseparable isogenies of degree `>1` have scaling + factor `0`. + """ + if self._degree == 1: + return self._base_ring.one() + return self._base_ring.zero() + + def kernel_polynomial(self): + """ + Return the kernel polynomial of this Frobenius isogeny + as a polynomial in `x`. This method always returns `1`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E, 5) + sage: pi.kernel_polynomial() + 1 + """ + return self._poly_ring(1) + + @cached_method + def dual(self): + """ + Compute the dual of this Frobenius isogeny. + + This method returns an :class:`EllipticCurveHom` object. + + EXAMPLES: + + An ordinary example:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(31), [0,1]) + sage: f = EllipticCurveHom_frobenius(E) + sage: f.dual() * f == EllipticCurveHom_scalar(f.domain(), 31) + True + sage: f * f.dual() == EllipticCurveHom_scalar(f.codomain(), 31) + True + + A supersingular example:: + + sage: E = EllipticCurve(GF(31), [1,0]) + sage: f = EllipticCurveHom_frobenius(E) + sage: f.dual() * f == EllipticCurveHom_scalar(f.domain(), 31) + True + sage: f * f.dual() == EllipticCurveHom_scalar(f.codomain(), 31) + True + + TESTS: + + Some random testing (including small characteristic):: + + sage: p = random_prime(50) + sage: q = p**randrange(1,10) + sage: n = randrange(20) + sage: while True: + ....: try: + ....: E = EllipticCurve([GF(q).random_element() for _ in range(5)]) + ....: break + ....: except ArithmeticError: + ....: pass + sage: f = EllipticCurveHom_frobenius(E, n) + sage: f.dual() * f == EllipticCurveHom_scalar(E, p**n) + True + sage: f * f.dual() == EllipticCurveHom_scalar(f.codomain(), p**n) + True + sage: f.dual().dual() == f # known bug -- broken in characteristic 2,3 + True + sage: p in (2,3) or f.dual().dual() == f + True + + ALGORITHM: + + - For supersingular curves, the dual of Frobenius is again purely + inseparable, so we start out with a Frobenius isogeny of equal + degree in the opposite direction. + + - For ordinary curves, we immediately reduce to the case of prime + degree. The kernel of the dual is the unique subgroup of size `p`, + which we compute from the `p`-division polynomial. + + In both cases, we then search for the correct post-isomorphism + using :meth:`find_post_isomorphism`. + """ + if self._degree == 1: + return self + + if self._domain.is_supersingular(): + Phi = EllipticCurveHom_frobenius(self._codomain, self._n) + + else: + E = self._domain + poly = self._domain.division_polynomial(self._p) + ker = self._poly_ring(list(poly)[::self._p]).monic() + Phis = [] + for _ in range(self._n): + Ep = EllipticCurve([a**self._p for a in E.a_invariants()]) + Phis.append(EllipticCurveIsogeny(Ep, ker, codomain=E)) + E, ker = Ep, ker.map_coefficients(lambda c: c**self._p) + Phi = EllipticCurveHom_composite.from_factors(Phis[::-1], self._codomain) + + scalar_mul = EllipticCurveHom_scalar(self._domain, self._degree) + iso = find_post_isomorphism(Phi * self, scalar_mul) + return iso * Phi + + def is_separable(self): + """ + Determine whether or not this Frobenius isogeny is separable. + + Since Frobenius isogenies are purely inseparable, this method + returns ``True`` if and only if the degree is `1`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E) + sage: pi.degree() + 11 + sage: pi.is_separable() + False + sage: pi = EllipticCurveHom_frobenius(E, 0) + sage: pi.degree() + 1 + sage: pi.is_separable() + True + """ + return self._degree == 1 + + def is_injective(self): + """ + Determine whether or not this Frobenius isogeny has trivial + kernel. + + Since Frobenius isogenies are purely inseparable, this method + always returns ``True``. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius + sage: E = EllipticCurve(GF(11), [1,1]) + sage: pi = EllipticCurveHom_frobenius(E, 5) + sage: pi.is_injective() + True + """ + return True diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py new file mode 100644 index 00000000000..e18e514bb35 --- /dev/null +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -0,0 +1,549 @@ +r""" +Scalar-multiplication morphisms of elliptic curves + +This class provides an :class:`EllipticCurveHom` instantiation for +multiplication-by-`m` maps on elliptic curves. + +EXAMPLES: + +We can construct and evaluate scalar multiplications:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: E = EllipticCurve('77a1') + sage: phi = E.scalar_multiplication(5); phi + Scalar-multiplication endomorphism [5] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: P = E(2,3) + sage: phi(P) + (30 : 164 : 1) + +The usual :class:`EllipticCurveHom` methods are supported:: + + sage: phi.degree() + 25 + sage: phi.kernel_polynomial() + x^12 + 124/5*x^10 + 19*x^9 - 84*x^8 + 24*x^7 - 483*x^6 - 696/5*x^5 - 448*x^4 - 37*x^3 - 332*x^2 - 84*x + 47/5 + sage: phi.rational_maps() + ((x^25 - 200*x^23 - 520*x^22 + 9000*x^21 + ... + 1377010*x^3 + 20360*x^2 - 39480*x + 2209), + (10*x^36*y - 620*x^36 + 3240*x^34*y - 44880*x^34 + ... + 424927560*x*y + 226380480*x + 42986410*y + 20974090)/(1250*x^36 + 93000*x^34 + 71250*x^33 + 1991400*x^32 + ... + 1212964050*x^3 + 138715800*x^2 - 27833400*x + 1038230)) + sage: phi.dual() + Scalar-multiplication endomorphism [5] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: phi.dual() is phi + True + sage: phi.formal() + 5*t - 310*t^4 - 2496*t^5 + 10540*t^7 + ... - 38140146674516*t^20 - 46800256902400*t^21 + 522178541079910*t^22 + O(t^23) + sage: phi.is_normalized() + False + sage: phi.is_separable() + True + sage: phi.is_injective() + False + sage: phi.is_surjective() + True + +Contrary to constructing an :class:`EllipticCurveIsogeny` from +the division polynomial, :class:`EllipticCurveHom_scalar` can +deal with huge scalars very quickly:: + + sage: E = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) + sage: phi = E.scalar_multiplication(9^99); phi + Scalar-multiplication endomorphism [29512665430652752148753480226197736314359272517043832886063884637676943433478020332709411004889] of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 170141183460469231731687303715884105727 + sage: phi(E(1,2)) + (82124533143060719620799539030695848450 : 17016022038624814655722682134021402379 : 1) + +Composition of scalar multiplications results in another scalar +multiplication:: + + sage: E = EllipticCurve(GF(19), [4,4]) + sage: phi = E.scalar_multiplication(-3); phi + Scalar-multiplication endomorphism [-3] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: psi = E.scalar_multiplication(7); psi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: phi * psi + Scalar-multiplication endomorphism [-21] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: psi * phi + Scalar-multiplication endomorphism [-21] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: phi * psi == psi * phi + True + sage: -phi == E.scalar_multiplication(-1) * phi + True + +The zero endomorphism `[0]` is supported:: + + sage: E = EllipticCurve(GF(71), [1,1]) + sage: zero = E.scalar_multiplication(0); zero + Scalar-multiplication endomorphism [0] of Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 71 + sage: zero.is_zero() + True + sage: zero.is_injective() + False + sage: zero.is_surjective() + False + sage: zero(E.random_point()) + (0 : 1 : 0) + +Due to a bug (:trac:`6413`), retrieving multiplication-by-`m` maps +when `m` is divisible by the characteristic currently fails:: + + sage: E = EllipticCurve(GF(7), [1,0]) + sage: phi = E.scalar_multiplication(7); phi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7 + sage: phi.rational_maps() # known bug -- #6413 + (x^49, y^49) + sage: phi.x_rational_map() + x^49 + +:: + + sage: E = EllipticCurve(GF(7), [0,1]) + sage: phi = E.scalar_multiplication(7); phi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 + sage: phi.rational_maps() # known bug -- #6413 + ((-3*x^49 - x^28 - x^7)/(x^42 - x^21 + 2), + (-x^72*y - 3*x^69*y - 3*x^66*y - x^63*y + 3*x^51*y + 2*x^48*y + 2*x^45*y + 3*x^42*y - x^9*y - 3*x^6*y - 3*x^3*y - y)/(x^63 + 2*x^42 - x^21 - 1)) + sage: phi.x_rational_map() + (4*x^49 + 6*x^28 + 6*x^7)/(x^42 + 6*x^21 + 2) + +TESTS:: + + sage: E = EllipticCurve(j = GF(65537^3).random_element()) + sage: m = randrange(-2^99, +2^99) + sage: phi = E.scalar_multiplication(m) + sage: phi.degree() == m**2 + True + sage: P = E.random_point() + sage: phi(P) == m*P + True + +AUTHORS: + +- Lorenz Panny (2021): implement :class:`EllipticCurveHom_scalar` +""" + +from sage.misc.cachefunc import cached_method +from sage.structure.richcmp import richcmp + +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic +from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism +from sage.schemes.elliptic_curves.hom import EllipticCurveHom + + +class EllipticCurveHom_scalar(EllipticCurveHom): + + def __init__(self, E, m): + """ + Construct a scalar-multiplication map on an elliptic curve. + + TESTS:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: E = EllipticCurve([1,1]) + sage: EllipticCurveHom_scalar(E, 123) + Scalar-multiplication endomorphism [123] of Elliptic Curve defined by y^2 = x^3 + x + 1 over Rational Field + """ + if not isinstance(E, EllipticCurve_generic): + raise ValueError(f'not an elliptic curve: {E}') + + self._m = ZZ(m) + + self._degree = self._m**2 + self._domain = self._codomain = E + + EllipticCurveHom.__init__(self, self._domain, self._codomain) + + # TODO: should probably be in EllipticCurveHom? + self._base_ring = self._domain.base_ring() + self._poly_ring = PolynomialRing(self._base_ring, ['x']) + self._mpoly_ring = PolynomialRing(self._base_ring, ['x','y']) + + self._rational_maps = None + + def _call_(self, P): + """ + Evaluate this scalar-multiplication map `[m]` at a point `P`, + i.e., return `[m]P`. + + TESTS:: + + sage: p = random_prime(2^22) + sage: q = p^randrange(1,5) + sage: E = EllipticCurve_from_j(GF(q).random_element()) + sage: m = randrange(-9^99, 9^99) + sage: phi = E.scalar_multiplication(m) + sage: P = E.random_point() + sage: phi(P) == m*P + True + """ + if P not in self._domain: + raise ValueError(f'{P} is not a point on {self._domain}') + return self._m * P + + def _eval(self, P): + """ + Less strict evaluation method for internal use. + + In particular, this can be used to evaluate ``self`` at a + point defined over an extension field. + + INPUT: a sequence of 3 coordinates defining a point on ``self`` + + OUTPUT: the result of evaluating ``self`` at the given point + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: E = EllipticCurve(j=Mod(1728,419)) + sage: psi = EllipticCurveHom_scalar(E, 13) + sage: P = E.change_ring(GF(419**2)).lift_x(5) + sage: P = min({P, -P}) # fix choice of y + sage: Q = psi._eval(P); Q + (134 : 210*z2 + 314 : 1) + sage: Q.curve() + Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 419^2 + """ + if self._domain.defining_polynomial()(*P): + raise ValueError(f'{P} not on {self._domain}') + return self._m * P + + + def _repr_(self): + """ + Return basic facts about this scalar multiplication as a string. + + TESTS:: + + sage: E = EllipticCurve([i,i]) + sage: E.scalar_multiplication(777) + Scalar-multiplication endomorphism [777] of Elliptic Curve defined by y^2 = x^3 + I*x + I over Number Field in I with defining polynomial x^2 + 1 with I = 1*I + """ + return f'Scalar-multiplication endomorphism [{self._m}] of {self._domain}' + + + # EllipticCurveHom methods + + @staticmethod + def _composition_impl(self, other): + """ + Helper method to compose other elliptic-curve morphisms with + :class:`EllipticCurveHom_scalar` objects. Called by + :meth:`EllipticCurveHom._composition_`. + + This method only handles composing two scalar multiplications; + all other cases are dealt with elsewhere. + + TESTS:: + + sage: E = EllipticCurve([1,2,3,4,5]) + sage: phi = E.scalar_multiplication(5) + sage: psi = E.scalar_multiplication(-7) + sage: phi * psi # implicit doctest + Scalar-multiplication endomorphism [-35] of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field + + :: + + sage: phi._composition_impl(phi, E.automorphisms()[0]) + NotImplemented + """ + if isinstance(self, EllipticCurveHom_scalar) and isinstance(other, EllipticCurveHom_scalar): + assert self._domain == other._domain + return EllipticCurveHom_scalar(self._domain, self._m * other._m) + return NotImplemented + + def degree(self): + """ + Return the degree of this scalar-multiplication morphism. + + The map `[m]` has degree `m^2`. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(23), [0,1]) + sage: phi = E.scalar_multiplication(1111111) + sage: phi.degree() + 1234567654321 + + TESTS: + + The degree is still `m^2` even in the inseparable case:: + + sage: E = EllipticCurve(GF(23), [1,1]) + sage: E.scalar_multiplication(23).degree() + 529 + sage: E = EllipticCurve(GF(23), [0,1]) + sage: E.scalar_multiplication(23).degree() + 529 + """ + return self._degree + + def _richcmp_(self, other, op): + """ + Compare this scalar multiplication to another elliptic-curve morphism. + + .. WARNING:: + + This method sometimes calls :meth:`EllipticCurveHom._richcmp_`, + which sometimes compares :meth:`rational_maps`. Therefore, the + complexity is at least quadratic in `m` in the worst case. + + EXAMPLES:: + + sage: E = EllipticCurve([i,i]) + sage: phi = E.scalar_multiplication(-5) + sage: psi = E.scalar_multiplication(5) + sage: phi == -psi + True + + TESTS:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism + sage: neg = negation_morphism(E) + sage: phi == neg*psi == psi*neg == -psi + True + """ + if isinstance(other, EllipticCurveHom_scalar): + return richcmp((self._domain, self._m), (other._domain, other._m), op) + return EllipticCurveHom._richcmp_(self, other, op) + + def rational_maps(self): + """ + Return the pair of explicit rational maps defining this scalar + multiplication. + + ALGORITHM: :meth:`EllipticCurve_generic.multiplication_by_m` + + EXAMPLES:: + + sage: E = EllipticCurve('77a1') + sage: phi = E.scalar_multiplication(5) + sage: phi.rational_maps() + ((x^25 - 200*x^23 - 520*x^22 + ... + 368660*x^2 + 163195*x + 16456)/(25*x^24 + 1240*x^22 + 950*x^21 + ... + 20360*x^2 - 39480*x + 2209), + (10*x^36*y - 620*x^36 + 3240*x^34*y - ... + 226380480*x + 42986410*y + 20974090)/(1250*x^36 + 93000*x^34 + 71250*x^33 + ... + 138715800*x^2 - 27833400*x + 1038230)) + sage: P = (2,3) + sage: Q = tuple(r(P) for r in phi.rational_maps()); Q + (30, 164) + sage: E(Q) == 5*E(P) + True + + TESTS:: + + sage: {r.parent() for r in phi.rational_maps()} + {Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field} + """ + if not self._rational_maps or None in self._rational_maps: + if not self._m: + raise ValueError('[0] is not expressible in (x,y) coordinates') + self._rational_maps = self._domain.multiplication_by_m(self._m) + return self._rational_maps + + def x_rational_map(self): + """ + Return the `x`-coordinate rational map of this scalar + multiplication. + + ALGORITHM: :meth:`EllipticCurve_generic.multiplication_by_m` + + EXAMPLES:: + + sage: E = EllipticCurve(GF(65537), [1,2,3,4,5]) + sage: phi = E.scalar_multiplication(7) + sage: phi.x_rational_map() == phi.rational_maps()[0] + True + + TESTS:: + + sage: phi.x_rational_map().parent() + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 65537 + """ + if not self._rational_maps: + if not self._m: + raise ValueError('[0] is not expressible in (x,y) coordinates') + h = self._domain.multiplication_by_m(self._m, x_only=True) + self._rational_maps = (self._mpoly_ring.fraction_field()(h), None) + f,g = map(self._poly_ring, (self._rational_maps[0].numerator(), + self._rational_maps[0].denominator())) + return f / g + + def scaling_factor(self): + r""" + Return the Weierstrass scaling factor associated to this + scalar multiplication. + + The scaling factor is the constant `u` (in the base field) + such that `\varphi^* \omega_2 = u \omega_1`, where + `\varphi: E_1\to E_2` is this morphism and `\omega_i` are + the standard Weierstrass differentials on `E_i` defined by + `\mathrm dx/(2y+a_1x+a_3)`. + + EXAMPLES:: + + sage: E = EllipticCurve('11a1') + sage: phi = E.scalar_multiplication(5) + sage: u = phi.scaling_factor() + sage: u == phi.formal()[1] + True + sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() + doctest:warning ... DeprecationWarning: ... + True + + The scaling factor lives in the base ring:: + + sage: E = EllipticCurve(GF(101^2), [5,5]) + sage: phi = E.scalar_multiplication(123) + sage: phi.scaling_factor() + 22 + sage: phi.scaling_factor().parent() + Finite Field in z2 of size 101^2 + + ALGORITHM: The scaling factor equals the scalar that is being + multiplied by. + """ + return self._base_ring(self._m) + + @cached_method + def kernel_polynomial(self): + r""" + Return the kernel polynomial of this scalar-multiplication map. + (When `m=0`, return `0`.) + + EXAMPLES:: + + sage: E = EllipticCurve(GF(997), [7,7,7,7,7]) + sage: phi = E.scalar_multiplication(5) + sage: phi.kernel_polynomial() + x^12 + 77*x^11 + 380*x^10 + 198*x^9 + 840*x^8 + 376*x^7 + 946*x^6 + 848*x^5 + 246*x^4 + 778*x^3 + 77*x^2 + 518*x + 28 + + :: + + sage: E = EllipticCurve(GF(997), [5,6,7,8,9]) + sage: phi = E.scalar_multiplication(11) + sage: phi.kernel_polynomial() + x^60 + 245*x^59 + 353*x^58 + 693*x^57 + 499*x^56 + 462*x^55 + 820*x^54 + 962*x^53 + ... + 736*x^7 + 939*x^6 + 429*x^5 + 267*x^4 + 116*x^3 + 770*x^2 + 491*x + 519 + + TESTS:: + + sage: E = EllipticCurve(j = GF(997^6).random_element()) + sage: m = choice([+1,-1]) * randrange(1,8) + sage: phi = E.scalar_multiplication(m) + sage: phi.kernel_polynomial() == phi.x_rational_map().denominator().monic().radical() + True + + :: + + sage: E.scalar_multiplication(randint(-10,+10)).kernel_polynomial().parent() + Univariate Polynomial Ring in x over Finite Field in z6 of size 997^6 + """ + if not self._m: + return self._poly_ring(0) + # TODO: inseparable case should be consistent with Frobenius' .kernel_polynomial() + return self._domain.division_polynomial(self._m.abs()).monic().radical() + + def dual(self): + """ + Return the dual isogeny of this scalar-multiplication map. + + This method simply returns ``self`` as scalars are self-dual. + + EXAMPLES:: + + sage: E = EllipticCurve([5,5]) + sage: phi = E.scalar_multiplication(5) + sage: phi.dual() is phi + True + """ + return self + + def is_separable(self): + """ + Determine whether this scalar-multiplication map is a + separable isogeny. (This is the case if and only if the + scalar `m` is coprime to the characteristic.) + + EXAMPLES:: + + sage: E = EllipticCurve(GF(11), [4,4]) + sage: E.scalar_multiplication(11).is_separable() + False + sage: E.scalar_multiplication(-11).is_separable() + False + sage: E.scalar_multiplication(777).is_separable() + True + sage: E.scalar_multiplication(-1).is_separable() + True + sage: E.scalar_multiplication(77).is_separable() + False + sage: E.scalar_multiplication(121).is_separable() + False + + TESTS:: + + sage: E.scalar_multiplication(0).is_separable() + Traceback (most recent call last): + ... + ValueError: [0] is not an isogeny + """ + if self._m.is_zero(): + raise ValueError('[0] is not an isogeny') + return bool(self.scaling_factor()) + + def is_injective(self): + """ + Determine whether this scalar multiplication defines an + injective map (over the algebraic closure). + + Equivalently, return ``True`` if and only if this scalar + multiplication is a purely inseparable isogeny. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(23), [1,0]) + sage: E.scalar_multiplication(4).is_injective() + False + sage: E.scalar_multiplication(5).is_injective() + False + sage: E.scalar_multiplication(1).is_injective() + True + sage: E.scalar_multiplication(-1).is_injective() + True + sage: E.scalar_multiplication(23).is_injective() + True + sage: E.scalar_multiplication(-23).is_injective() + True + sage: E.scalar_multiplication(0).is_injective() + False + """ + if self._m.is_zero(): + return False + p = self._domain.base_ring().characteristic() + return self._m.abs().is_power_of(p) and self._domain.is_supersingular() + + def __neg__(self): + """ + Negate this scalar-multiplication map, i.e., return `[-m]` + when this morphism equals `[m]`. + + If rational maps have been computed already, they will be + reused for the negated morphism. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(2^8), [1,0,1,0,1]) + sage: phi = E.scalar_multiplication(23) + sage: -phi + Scalar-multiplication endomorphism [-23] of Elliptic Curve defined by y^2 + x*y + y = x^3 + 1 over Finite Field in z8 of size 2^8 + + TESTS:: + + sage: E = EllipticCurve(GF(79), [7,7]) + sage: phi = E.scalar_multiplication(5) + sage: _ = phi.rational_maps() + sage: (-phi)._rational_maps + ((x^25 + 11*x^23 - 24*x^22 - ... - 7*x^2 + 34*x + 21)/(25*x^24 - 5*x^22 - 23*x^21 - ... - 11*x^2 + 36*x + 21), + (29*x^36*y + 22*x^34*y - 27*x^33*y - ... + 14*x^2*y - 33*x*y + 37*y)/(9*x^36 + 21*x^34 - 14*x^33 + ... - 26*x^2 + 18*x + 7)) + """ + result = EllipticCurveHom_scalar(self._domain, -self._m) + if self._rational_maps is not None: + w = negation_morphism(self._domain).rational_maps() + result._rational_maps = tuple(f(*w) if f is not None else None for f in self._rational_maps) + return result diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 20688dbb4d0..22ae56018a3 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -1,23 +1,18 @@ r""" -√élu Algorithm for Elliptic-Curve Isogenies - -The √élu algorithm computes isogenies of elliptic curves in time -`\tilde O(\sqrt\ell)` rather than naïvely `O(\ell)`, where `\ell` -is the degree. - -The core idea is to reindex the points in the kernel subgroup in -a baby-step-giant-step manner, then use fast resultant computations -to evaluate "elliptic polynomials" -(see :class:`FastEllipticPolynomial`) -in essentially square-root time. - -Based on experiments with Sage version 9.7, -the isogeny degree where -:class:`EllipticCurveHom_velusqrt` -begins to outperform +√élu algorithm for elliptic-curve isogenies + +The √élu algorithm computes isogenies of elliptic curves in time `\tilde +O(\sqrt\ell)` rather than naïvely `O(\ell)`, where `\ell` is the degree. + +The core idea is to reindex the points in the kernel subgroup in a +baby-step-giant-step manner, then use fast resultant computations to evaluate +"elliptic polynomials" (see :class:`FastEllipticPolynomial`) in essentially +square-root time. + +Based on experiments with Sage version 9.7, the isogeny degree where +:class:`EllipticCurveHom_velusqrt` begins to outperform :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny` -can be as low as `\approx 100`, -but is typically closer to `\approx 1000`, +can be as low as `\approx 100`, but is typically closer to `\approx 1000`, depending on the exact situation. REFERENCES: [BDLS2020]_ @@ -106,9 +101,11 @@ .. NOTE:: - Currently :class:`EllipticCurveHom_velusqrt` does not implement - all methods of :class:`EllipticCurveHom`. This will hopefully - change in the future. + Some of the methods inherited from :class:`EllipticCurveHom` compute data + whose size is linear in the degree; this includes kernel polynomial and + rational maps. In consequence, those methods cannot possibly run in the + otherwise advertised square-root complexity, as merely storing the result + already takes linear time. AUTHORS: @@ -128,7 +125,10 @@ from sage.structure.sequence import Sequence from sage.structure.all import coercion_model as cm +from sage.misc.cachefunc import cached_method + from sage.misc.misc_c import prod +from sage.rings.generic import ProductTree, prod_with_derivative from sage.structure.richcmp import op_EQ @@ -139,200 +139,6 @@ from sage.schemes.elliptic_curves.hom import EllipticCurveHom, compare_via_evaluation -#TODO: This is general. It should be elsewhere. -class ProductTree: - r""" - A simple product tree. - - INPUT: - - - ``leaves`` -- a sequence of elements in a common ring - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import ProductTree - sage: R.<x> = GF(101)[] - sage: vs = [x - i for i in range(1,10)] - sage: tree = ProductTree(vs) - sage: tree.value() - x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 - sage: tree.remainders(x^7 + x + 1) - [3, 30, 70, 27, 58, 72, 98, 98, 23] - sage: tree.remainders(x^100) - [1, 1, 1, 1, 1, 1, 1, 1, 1] - - :: - - sage: vs = prime_range(100) - sage: tree = ProductTree(vs) - sage: tree.value().factor() - 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71 * 73 * 79 * 83 * 89 * 97 - sage: tree.remainders(3599) - [1, 2, 4, 1, 2, 11, 12, 8, 11, 3, 3, 10, 32, 30, 27, 48, 0, 0, 48, 49, 22, 44, 30, 39, 10] - - We can access the individual layers of the tree:: - - sage: tree.layers - [(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97), - (6, 35, 143, 323, 667, 1147, 1763, 2491, 3599, 4757, 5767, 7387, 97), - (210, 46189, 765049, 4391633, 17120443, 42600829, 97), - (9699690, 3359814435017, 729345064647247, 97), - (32589158477190044730, 70746471270782959), - (2305567963945518424753102147331756070,)] - - .. NOTE:: - - Use this class if you need the :meth:`remainders` method. - To compute just the product, :func:`prod` is likely faster. - """ - def __init__(self, leaves): - r""" - Initialize a product tree having the given ring elements - as its leaves. - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import ProductTree - sage: vs = prime_range(100) - sage: tree = ProductTree(vs) - """ - V = tuple(leaves) - self.layers = [V] - while len(V) > 1: - V = tuple(prod(V[i:i+2]) for i in range(0,len(V),2)) - self.layers.append(V) - - def __len__(self): - r""" - Return the number of leaves of this product tree. - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import ProductTree - sage: R.<x> = GF(101)[] - sage: vs = [x - i for i in range(1,10)] - sage: tree = ProductTree(vs) - sage: len(tree) - 9 - sage: len(tree) == len(vs) - True - sage: len(tree.remainders(x^2)) - 9 - """ - return len(self.layers[0]) - - def value(self): - r""" - Return the value represented by this product tree - (i.e., the product of all leaves). - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import ProductTree - sage: R.<x> = GF(101)[] - sage: vs = [x - i for i in range(1,10)] - sage: tree = ProductTree(vs) - sage: tree.value() - x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 - sage: tree.value() == prod(vs) - True - """ - assert len(self.layers[-1]) == 1 - return self.layers[-1][0] - - def remainders(self, x): - r""" - Given a value `x`, return a list of all remainders of `x` - modulo the leaves of this product tree. - - The base ring must support the ``%`` operator for this - method to work. - - INPUT: - - - ``x`` -- an element of the base ring of this product tree - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import ProductTree - sage: vs = prime_range(100) - sage: tree = ProductTree(vs) - sage: n = 1085749272377676749812331719267 - sage: tree.remainders(n) - [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] - sage: [n % v for v in vs] - [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] - """ - X = [x] - for V in reversed(self.layers): - X = [X[i//2] % V[i] for i in range(len(V))] - return X - -#TODO: This is general. It should be elsewhere. -def prod_with_derivative(pairs): - r""" - Given a list of pairs `(f, \partial f)` of ring elements, return - the pair `(\prod f, \partial \prod f)`, assuming `\partial` is an - operator obeying the standard product rule. - - This function is entirely algebraic, hence still works when the - elements `f` and `\partial f` are all passed through some ring - homomorphism first. (See the polynomial-evaluation example below.) - - INPUT: - - - ``pairs`` -- a sequence of tuples `(f, \partial f)` of elements - of a common ring - - ALGORITHM: - - This function wraps the given pairs in a thin helper class that - automatically applies the product rule whenever multiplication - is invoked, then calls :func:`prod` on the wrapped pairs. - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.hom_velusqrt import prod_with_derivative - sage: R.<x> = ZZ[] - sage: fs = [x^2 + 2*x + 3, 4*x + 5, 6*x^7 + 8*x + 9] - sage: prod(fs) - 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 - sage: prod(fs).derivative() - 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 - sage: F, dF = prod_with_derivative((f, f.derivative()) for f in fs) - sage: F - 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 - sage: dF - 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 - - The main reason for this function to exist is that it allows us to - *evaluate* the derivative of a product of polynomials at a point - `\alpha` without ever fully expanding the product *as a polynomial*:: - - sage: alpha = 42 - sage: F(alpha) - 442943981574522759 - sage: dF(alpha) - 104645261461514994 - sage: us = [f(alpha) for f in fs] - sage: vs = [f.derivative()(alpha) for f in fs] - sage: prod_with_derivative(zip(us, vs)) - (442943981574522759, 104645261461514994) - """ - class _aux: - def __init__(self, f, df): - self.f, self.df = f, df - - def __mul__(self, other): - return _aux(self.f * other.f, self.df * other.f + self.f * other.df) - - def __iter__(self): - yield self.f - yield self.df - - return tuple(prod(_aux(*tup) for tup in pairs)) - - def _choose_IJK(n): r""" Helper function to choose an "index system" for the set @@ -412,6 +218,7 @@ def _points_range(rr, P, Q=None): for _ in range(a+s, b, s): yield (R := R + sP) + class FastEllipticPolynomial: r""" A class to represent and evaluate an *elliptic polynomial*, @@ -527,7 +334,7 @@ def __init__(self, E, n, P, Q=None): IJK = _choose_IJK(2*n+1) # [1,3,5,7,...,2n-1] = [0,1,2,3,...,n-2,n-1] self.base = E.base_ring() - R, Z = self.base['x'].objgen() + R, Z = self.base['Z'].objgen() # Cassels, Lectures on Elliptic Curves, p.132 A,B = E.a_invariants()[-2:] @@ -595,6 +402,7 @@ def __call__(self, alpha, *, derivative=False): R = self._hI_resultant(EJ, EJrems) hK = self.hK(alpha) res = hK * R / self.DeltaIJ + res = base(res) if not derivative: return res @@ -609,6 +417,7 @@ def __call__(self, alpha, *, derivative=False): dR = 0 dhK = self.dhK(alpha) dres = (dhK * R + hK * dR) / self.DeltaIJ + dres = base(dres) return res, dres @@ -629,7 +438,7 @@ def _hI_resultant(self, poly, rems=None): sage: E = EllipticCurve(GF(71), [5,5]) sage: P = E(4, 35) sage: hP = FastEllipticPolynomial(E, P.order(), P) - sage: f = GF(71)['x']([5,4,3,2,1]) + sage: f = GF(71)['Z']([5,4,3,2,1]) sage: hP._hI_resultant(f) 66 sage: prod(f(r) for fi in hP.hItree.layers[0] @@ -640,7 +449,7 @@ def _hI_resultant(self, poly, rems=None): sage: Q = E(0, 17) sage: hPQ = FastEllipticPolynomial(E, P.order(), P, Q) - sage: f = GF(71)['x']([9,8,7,6,5,4,3,2,1]) + sage: f = GF(71)['Z']([9,8,7,6,5,4,3,2,1]) sage: hPQ._hI_resultant(f) 36 sage: prod(f(r) for fi in hPQ.hItree.layers[0] @@ -738,7 +547,7 @@ def _point_outside_subgroup(P): F = E.base_field().extension(d) E = E.base_extend(F) P = E(P) -# assert E.cardinality() > n + # assert E.cardinality() > n for _ in range(1000): Q = E.random_point() if n*Q or not P.weil_pairing(Q,n).is_one(): @@ -746,6 +555,7 @@ def _point_outside_subgroup(P): else: raise NotImplementedError('could not find a point outside the kernel') + class EllipticCurveHom_velusqrt(EllipticCurveHom): r""" This class implements separable odd-degree isogenies of elliptic @@ -894,31 +704,30 @@ def __init__(self, E, P, *, codomain=None, model=None, Q=None): if codomain is not None and model is not None: raise ValueError('cannot specify a codomain curve and model name simultaneously') - try: - self._raw_domain = E.short_weierstrass_model() - except ValueError: - raise NotImplementedError('only implemented for curves having a short Weierstrass model') - self._pre_iso = E.isomorphism_to(self._raw_domain) - try: P = E(P) except TypeError: raise ValueError('given kernel point P does not lie on E') - self._P = self._pre_iso(P) - - self._degree = self._P.order() + self._degree = P.order() if self._degree % 2 != 1 or self._degree < 9: raise NotImplementedError('only implemented for odd degrees >= 9') + try: + self._raw_domain = E.short_weierstrass_model() + except ValueError: + raise NotImplementedError('only implemented for curves having a short Weierstrass model') + self._pre_iso = E.isomorphism_to(self._raw_domain) + self._P = self._pre_iso(P) + if Q is not None: - self._Q = E(Q) + self._Q = self._pre_iso(E(Q)) EE = E else: self._Q = _point_outside_subgroup(self._P) # may extend base field EE = self._Q.curve() self._P = EE(self._P) - self._base_ring = EE.base_ring() + self._internal_base_ring = EE.base_ring() self._h0 = FastEllipticPolynomial(EE, self._degree, self._P) self._h1 = FastEllipticPolynomial(EE, self._degree, self._P, self._Q) @@ -972,6 +781,13 @@ def _raw_eval(self, x, y=None): 50907 sage: phi._raw_eval(123, 456) (3805, 29941) + + TESTS:: + + sage: {t.parent() for t in phi._raw_eval(*Q.xy())} + {Finite Field of size 65537} + sage: {t.parent() for t in phi._raw_eval(123, 456)} + {Finite Field of size 65537} """ if y is None: h0 = self._h0(x) @@ -1041,7 +857,7 @@ def _compute_codomain(self, model=None): From: Elliptic Curve defined by y^2 + 3*t*x*y + (3*t+2)*y = x^3 + (2*t+4)*x^2 + (t+4)*x + 3*t over Finite Field in t of size 5^2 To: Elliptic Curve defined by y^2 = x^3 + (4*t+3)*x + 2 over Finite Field in t of size 5^2 """ - R, Z = self._base_ring['Z'].objgen() + R, Z = self._internal_base_ring['Z'].objgen() poly = self._raw_domain.two_division_polynomial().monic()(Z) f = 1 @@ -1049,7 +865,7 @@ def _compute_codomain(self, model=None): if g.degree() == 1: f *= Z - self._raw_eval(-g[0]) else: - K, X0 = self._base_ring.extension(g,'T').objgen() + K, X0 = self._internal_base_ring.extension(g,'T').objgen() imX0 = self._raw_eval(X0) try: imX0 = imX0.polynomial() # K is a FiniteField @@ -1193,6 +1009,185 @@ def _comparison_impl(left, right, op): return NotImplemented return compare_via_evaluation(left, right) + @cached_method + def kernel_polynomial(self): + r""" + Return the kernel polynomial of this √élu isogeny. + + .. NOTE:: + + The data returned by this method has size linear in the degree. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(65537^2,'a'), [5,5]) + sage: K = E.cardinality()//31 * E.gens()[0] + sage: phi = E.isogeny(K, algorithm='velusqrt') + sage: h = phi.kernel_polynomial(); h + x^15 + 21562*x^14 + 8571*x^13 + 20029*x^12 + 1775*x^11 + 60402*x^10 + 17481*x^9 + 46543*x^8 + 46519*x^7 + 18590*x^6 + 36554*x^5 + 36499*x^4 + 48857*x^3 + 3066*x^2 + 23264*x + 53937 + sage: h == E.isogeny(K).kernel_polynomial() + True + sage: h(K.xy()[0]) + 0 + + TESTS:: + + sage: phi.kernel_polynomial().parent() + Univariate Polynomial Ring in x over Finite Field in a of size 65537^2 + """ + R, x = self._domain.base_ring()['x'].objgen() + h0 = self._h0(x) + h = h0(self._pre_iso.x_rational_map()) + return R(h).monic() + + @cached_method + def dual(self): + r""" + Return the dual of this √élu isogeny as an :class:`EllipticCurveHom`. + + .. NOTE:: + + The dual is computed by :class:`EllipticCurveIsogeny`, + hence it does not benefit from the √élu speedup. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1]) + sage: K = E.cardinality() // 11 * E.gens()[0] + sage: phi = E.isogeny(K, algorithm='velusqrt'); phi + Elliptic-curve isogeny (using √élu) of degree 11: + From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 + To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 + sage: phi.dual() + Isogeny of degree 11 from Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 + sage: phi.dual() * phi == phi.domain().scalar_multiplication(11) + True + sage: phi * phi.dual() == phi.codomain().scalar_multiplication(11) + True + """ + # FIXME: This code fails if the degree is divisible by the characteristic. + F = self._raw_domain.base_ring() + from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + isom = ~WeierstrassIsomorphism(self._raw_domain, (~F(self._degree), 0, 0, 0)) + from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny + phi = EllipticCurveIsogeny(self._raw_codomain, None, isom.domain(), self._degree) + return ~self._pre_iso * isom * phi * ~self._post_iso + + @cached_method + def rational_maps(self): + r""" + Return the pair of explicit rational maps of this √élu isogeny + as fractions of bivariate polynomials in `x` and `y`. + + .. NOTE:: + + The data returned by this method has size linear in the degree. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1]) + sage: K = (E.cardinality() // 11) * E.gens()[0] + sage: phi = E.isogeny(K, algorithm='velusqrt'); phi + Elliptic-curve isogeny (using √élu) of degree 11: + From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 + To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 + sage: phi.rational_maps() + ((-17*x^11 - 34*x^10 - 36*x^9 + ... - 29*x^2 - 25*x - 25)/(x^10 + 10*x^9 + 19*x^8 - ... + x^2 + 47*x + 24), + (-3*x^16 - 6*x^15*y - 48*x^15 + ... - 49*x - 9*y + 46)/(x^15 + 15*x^14 - 35*x^13 - ... + 3*x^2 - 45*x + 47)) + + TESTS:: + + sage: phi.rational_maps()[0].parent() + Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field in z2 of size 101^2 + sage: phi.rational_maps()[1].parent() + Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field in z2 of size 101^2 + """ + S = self._internal_base_ring['x,y'] + fx, fy = map(S, self._pre_iso.rational_maps()) + fx, fy = self._raw_eval(fx, fy) + gx, gy = self._post_iso.rational_maps() + fx, fy = gx(fx, fy), gy(fx, fy) + R = self._domain.base_ring()['x,y'].fraction_field() + return R(fx), R(fy) + + @cached_method + def x_rational_map(self): + r""" + Return the `x`-coordinate rational map of this √élu isogeny + as a univariate rational function in `x`. + + .. NOTE:: + + The data returned by this method has size linear in the degree. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1]) + sage: K = (E.cardinality() // 11) * E.gens()[0] + sage: phi = E.isogeny(K, algorithm='velusqrt'); phi + Elliptic-curve isogeny (using √élu) of degree 11: + From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 + To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 + sage: phi.x_rational_map() + (84*x^11 + 67*x^10 + 65*x^9 + ... + 72*x^2 + 76*x + 76)/(x^10 + 10*x^9 + 19*x^8 + ... + x^2 + 47*x + 24) + sage: phi.x_rational_map() == phi.rational_maps()[0] + True + + TESTS:: + + sage: phi.x_rational_map().parent() + Fraction Field of Univariate Polynomial Ring in x over Finite Field in z2 of size 101^2 + """ + S = self._internal_base_ring['x'] + fx = S(self._pre_iso.x_rational_map()) + fx = self._raw_eval(fx) + gx = self._post_iso.x_rational_map() + fx = gx(fx) + R = self._domain.base_ring()['x'].fraction_field() + return R(fx) + + def scaling_factor(self): + r""" + Return the Weierstrass scaling factor associated to this + √élu isogeny. + + The scaling factor is the constant `u` (in the base field) + such that `\varphi^* \omega_2 = u \omega_1`, where + `\varphi: E_1\to E_2` is this isogeny and `\omega_i` are + the standard Weierstrass differentials on `E_i` defined by + `\mathrm dx/(2y+a_1x+a_3)`. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1]) + sage: K = (E.cardinality() // 11) * E.gens()[0] + sage: phi = E.isogeny(K, algorithm='velusqrt', model='montgomery'); phi + Elliptic-curve isogeny (using √élu) of degree 11: + From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 + To: Elliptic Curve defined by y^2 = x^3 + 61*x^2 + x over Finite Field in z2 of size 101^2 + sage: phi.scaling_factor() + 55 + sage: phi.scaling_factor() == phi.formal()[1] + True + """ + return self._pre_iso.scaling_factor() * self._post_iso.scaling_factor() + + def is_separable(self): + r""" + Determine whether or not this isogeny is separable. + + Since :class:`EllipticCurveHom_velusqrt` only implements + separable isogenies, this method always returns ``True``. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) + sage: phi = E.isogeny(E((1,2)), algorithm='velusqrt') + sage: phi.is_separable() + True + """ + return True + def _random_example_for_testing(): r""" @@ -1218,7 +1213,7 @@ def _random_example_for_testing(): sage: 5 <= K.order() True """ - from sage.all import prime_range, choice, randrange, GF, gcd + from sage.all import prime_range, choice, randrange, GF, lcm, Mod while True: p = choice(prime_range(2, 100)) e = randrange(1,5) @@ -1240,11 +1235,11 @@ def _random_example_for_testing(): deg = choice(ds) break G = A.torsion_subgroup(deg) + os = G.generator_orders() while True: - v = [randrange(deg) for _ in range(G.ngens())] - if gcd([deg] + v) == 1: + v = [randrange(o) for o in os] + if lcm(Mod(c,o).additive_order() for c,o in zip(v,os)) == deg: break K = G(v).element() assert K.order() == deg return E, K - diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index bb5ae25a56e..e635c77a458 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -1207,7 +1207,7 @@ def isogeny_degrees_cm(E, verbose=False): # Collect possible primes. First put in 2, and also 3 for # discriminant -3 (special case because of units): - L = Set([ZZ(2), ZZ(3)]) if d==-3 else Set([ZZ(2)]) + L = Set([ZZ(2), ZZ(3)]) if d==-3 else Set([ZZ(2)]) if verbose: print("initial primes: %s" % L) diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index a936deb74fb..f5657f2cf19 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -1205,17 +1205,8 @@ def isogenies_13_0(E, minimal_models=True): sage: K.<a> = NumberField(f) sage: E = EllipticCurve(j=K(0)); E.ainvs() (0, 0, 0, 0, 1) - sage: [phi.codomain().ainvs() for phi in isogenies_13_0(E)] # long time (4s) - [(0, - 0, - 20360599/165164973653422080*a^11 - 3643073/41291243413355520*a^10 - 101/8789110986240*a^9 + 5557619461/573489491852160*a^8 - 82824971/11947697746920*a^7 - 19487/21127670640*a^6 - 475752603733/29409717530880*a^5 + 87205112531/7352429382720*a^4 + 8349/521670880*a^3 + 5858744881/12764634345*a^2 - 1858703809/2836585410*a + 58759402/48906645, - -139861295/2650795873449984*a^11 - 3455957/5664093746688*a^10 - 345310571/50976843720192*a^9 - 500530795/118001953056*a^8 - 12860048113/265504394376*a^7 - 25007420461/44250732396*a^6 + 458134176455/1416023436672*a^5 + 16701880631/9077073312*a^4 + 155941666417/9077073312*a^3 + 3499310115/378211388*a^2 - 736774863/94552847*a - 21954102381/94552847, - 579363345221/13763747804451840*a^11 + 371192377511/860234237778240*a^10 + 8855090365657/1146978983704320*a^9 + 5367261541663/1633873196160*a^8 + 614883554332193/15930263662560*a^7 + 30485197378483/68078049840*a^6 - 131000897588387/2450809794240*a^5 - 203628705777949/306351224280*a^4 - 1587619388190379/204234149520*a^3 + 14435069706551/11346341640*a^2 + 7537273048614/472764235*a + 89198980034806/472764235), - (0, - 0, - 20360599/165164973653422080*a^11 - 3643073/41291243413355520*a^10 - 101/8789110986240*a^9 + 5557619461/573489491852160*a^8 - 82824971/11947697746920*a^7 - 19487/21127670640*a^6 - 475752603733/29409717530880*a^5 + 87205112531/7352429382720*a^4 + 8349/521670880*a^3 + 5858744881/12764634345*a^2 - 1858703809/2836585410*a + 58759402/48906645, - -6465569317/1325397936724992*a^11 - 112132307/1960647835392*a^10 - 17075412917/25488421860096*a^9 - 207832519229/531008788752*a^8 - 1218275067617/265504394376*a^7 - 9513766502551/177002929584*a^6 + 4297077855437/708011718336*a^5 + 354485975837/4538536656*a^4 + 4199379308059/4538536656*a^3 - 30841577919/189105694*a^2 - 181916484042/94552847*a - 2135779171614/94552847, - -132601797212627/3440936951112960*a^11 - 6212467020502021/13763747804451840*a^10 - 1515926454902497/286744745926080*a^9 - 15154913741799637/4901619588480*a^8 - 576888119803859263/15930263662560*a^7 - 86626751639648671/204234149520*a^6 + 16436657569218427/306351224280*a^5 + 1540027900265659087/2450809794240*a^4 + 375782662805915809/51058537380*a^3 - 14831920924677883/11346341640*a^2 - 7237947774817724/472764235*a - 84773764066089509/472764235)] + sage: len([phi.codomain().ainvs() for phi in isogenies_13_0(E)]) # long time (4s) + 2 """ if E.j_invariant()!=0: raise ValueError("j-invariant must be 0.") @@ -1873,7 +1864,7 @@ def isogenies_prime_degree_genus_plus_0_j1728(E, l, minimal_models=True): sage: [(p,len(isogenies_prime_degree_genus_plus_0_j1728(Emin,p))) for p in [17, 29, 41]] [(17, 2), (29, 2), (41, 2)] """ - if l not in hyperelliptic_primes: + if l not in hyperelliptic_primes: raise ValueError("%s must be one of %s."%(l,hyperelliptic_primes)) F = E.base_ring() if E.j_invariant() != 1728: @@ -1896,7 +1887,7 @@ def isogenies_prime_degree_genus_plus_0_j1728(E, l, minimal_models=True): c4, b2 = E.c4(), E.b2() kernels = [] - if l % 4 == 1 and F(-1).is_square(): + if l % 4 == 1 and F(-1).is_square(): i = F(-1).sqrt() endo = Fxuv(data['endo']) kernels += [endo(36*X+3*b2,i,-27*c4).monic(), endo(36*X+3*b2,-i,-27*c4).monic()] diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index f8e16b3407a..ccdc0dcc941 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -61,7 +61,7 @@ # https://www.gnu.org/licenses/ ###################################################################### -from sage.rings.integer_ring import ZZ +from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.padics.factory import Qp from sage.rings.infinity import infinity diff --git a/src/sage/schemes/elliptic_curves/padics.py b/src/sage/schemes/elliptic_curves/padics.py index d4b38156669..bf128244dfb 100644 --- a/src/sage/schemes/elliptic_curves/padics.py +++ b/src/sage/schemes/elliptic_curves/padics.py @@ -178,10 +178,10 @@ def padic_lseries(self, p, normalize=None, implementation='eclib', sage: L = e.padic_lseries(3, implementation = 'sage') sage: L.series(5,prec=10) 2 + 3 + 3^2 + 2*3^3 + 2*3^5 + 3^6 + O(3^7) + (1 + 3 + 2*3^2 + 3^3 + O(3^4))*T + (1 + 2*3 + O(3^4))*T^2 + (3 + 2*3^2 + O(3^3))*T^3 + (2*3 + 3^2 + O(3^3))*T^4 + (2 + 2*3 + 2*3^2 + O(3^3))*T^5 + (1 + 3^2 + O(3^3))*T^6 + (2 + 3^2 + O(3^3))*T^7 + (2 + 2*3 + 2*3^2 + O(3^3))*T^8 + (2 + O(3^2))*T^9 + O(T^10) - + Also the numerical modular symbols can be used. - This may allow for much larger conductor in some instances:: - + This may allow for much larger conductor in some instances:: + sage: E = EllipticCurve([101,103]) sage: L = E.padic_lseries(5, implementation="num") sage: L.series(2) @@ -1478,7 +1478,7 @@ def padic_E2(self, p, prec=20, check=False, check_hypotheses=True, algorithm="au if self.conductor() % p == 0: if not self.conductor() % (p**2) == 0: eq = self.tate_curve(p) - return eq.E2(prec=prec) + return eq.E2(prec=prec) X = self.minimal_model().short_weierstrass_model() frob_p = X.matrix_of_frobenius(p, prec, check, check_hypotheses, algorithm).change_ring(Integers(p**prec)) diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 85c76603092..7206ea74a7a 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1941,6 +1941,7 @@ def normalise_periods(w1, w2): def extended_agm_iteration(a, b, c): r""" Internal function for the extended AGM used in elliptic logarithm computation. + INPUT: - ``a``, ``b``, ``c`` (real or complex) -- three real or complex numbers. diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 33549debee1..b4f7afb1e39 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -27,27 +27,23 @@ from .constructor import EllipticCurve from sage.schemes.elliptic_curves.hom import EllipticCurveHom -from sage.structure.richcmp import (richcmp_method, richcmp, richcmp_not_equal, - op_NE) +from sage.structure.richcmp import (richcmp, richcmp_not_equal, op_EQ, op_NE) from sage.structure.sequence import Sequence from sage.rings.all import Integer, PolynomialRing - -@richcmp_method class baseWI(): r""" This class implements the basic arithmetic of isomorphisms between Weierstrass models of elliptic curves. - These are specified by lists of the form `[u,r,s,t]` (with - `u\not=0`) which specifies a transformation `(x,y) \mapsto (x',y')` - where + These are specified by lists of the form `[u,r,s,t]` (with `u \neq 0`) + which specifies a transformation `(x,y) \mapsto (x',y')` where `(x,y) = (u^2x'+r , u^3y' + su^2x' + t).` INPUT: - - ``u,r,s,t`` (default (1,0,0,0)) -- standard parameters of an + - ``u,r,s,t`` (default `(1,0,0,0)`) -- standard parameters of an isomorphism between Weierstrass models. EXAMPLES:: @@ -67,7 +63,7 @@ def __init__(self, u=1, r=0, s=0, t=0): INPUT: - - ``u,r,s,t`` (default (1,0,0,0)) -- standard parameters of an + - ``u,r,s,t`` (default `(1,0,0,0)`) -- standard parameters of an isomorphism between Weierstrass models. EXAMPLES:: @@ -81,45 +77,13 @@ def __init__(self, u=1, r=0, s=0, t=0): sage: baseWI(u,r,s,t) (u, r, s, t) """ - if u == 0: + if not u: raise ValueError("u!=0 required for baseWI") self.u = u self.r = r self.s = s self.t = t - def __richcmp__(self, other, op): - """ - Standard comparison function. - - The ordering is just lexicographic on the tuple `(u,r,s,t)`. - - .. NOTE:: - - In a list of automorphisms, there is no guarantee that the - identity will be first! - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import baseWI - sage: baseWI(1,2,3,4) == baseWI(1,2,3,4) - True - sage: baseWI(1,2,3,4) != baseWI(1,2,3,4) - False - sage: baseWI(1,2,3,4) < baseWI(1,2,3,5) - True - sage: baseWI(1,2,3,4) > baseWI(1,2,3,4) - False - - It will never return equality if ``other`` is of another type:: - - sage: baseWI() == 1 - False - """ - if not isinstance(other, baseWI): - return (op == op_NE) - return richcmp(self.tuple(), other.tuple(), op) - def tuple(self): r""" Return the parameters `u,r,s,t` as a tuple. @@ -174,7 +138,7 @@ def __invert__(self): (1, 0, 0, 0) """ u, r, s, t = self.tuple() - return baseWI(1/u, -r/(u**2), -s/u, (r*s-t)/(u**3)) + return baseWI(1/u, -r/u**2, -s/u, (r*s-t)/u**3) def __repr__(self): r""" @@ -255,38 +219,26 @@ def __call__(self, EorP): raise ValueError("baseWI(a) only for a=(x,y), (x:y:z) or (a1,a2,a3,a4,a6)") -def isomorphisms(E, F, JustOne=False): +def _isomorphisms(E, F): r""" - Return one or all isomorphisms between two elliptic curves. + Enumerate all isomorphisms between two elliptic curves, + as a generator object. INPUT: - ``E``, ``F`` (EllipticCurve) -- Two elliptic curves. - - ``JustOne`` (bool) If ``True``, returns one isomorphism, or ``None`` if - the curves are not isomorphic. If ``False``, returns a (possibly - empty) list of isomorphisms. - OUTPUT: - Either ``None``, or a 4-tuple `(u,r,s,t)` representing an isomorphism, - or a list of these. - - .. NOTE:: - - This function is not intended for users, who should use the - interface provided by ``ell_generic``. + A generator object producing 4-tuples `(u,r,s,t)` representing an isomorphism. EXAMPLES:: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import * - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3')) - [(-1, 0, 0, -1), (1, 0, 0, 0)] - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'),JustOne=True) - (1, 0, 0, 0) - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1')) + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import _isomorphisms + sage: list(_isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a3'))) + [(1, 0, 0, 0), (-1, 0, 0, -1)] + sage: list(_isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a1'))) [] - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'),JustOne=True) TESTS: @@ -294,23 +246,61 @@ def isomorphisms(E, F, JustOne=False): sage: z8 = GF(2^8).gen() sage: E1 = EllipticCurve([z8, z8, z8, z8, z8]) - sage: isomorphisms(E1, E1) + sage: list(_isomorphisms(E1, E1)) [(1, 0, 0, 0), (1, 0, z8, z8)] sage: E2 = EllipticCurve([z8^2, 0, 0, 0, z8^7 + z8^4]) - sage: isomorphisms(E1, E2) + sage: list(_isomorphisms(E1, E2)) [(z8^7 + z8^3 + z8^2 + z8, 1, 1, z8^7 + z8^3 + z8^2 + z8 + 1), (z8^7 + z8^3 + z8^2 + z8, 1, z8 + 1, z8^7 + z8^3 + z8^2 + z8 + 1)] + + Random testing:: + + sage: p = random_prime(100) + sage: F = GF(p).algebraic_closure() + sage: while True: + ....: try: + ....: E = EllipticCurve(list((F^5).random_element())) + ....: except ArithmeticError: + ....: continue + ....: break + sage: Aut = E.automorphisms() + sage: len(set(Aut)) == len(Aut) + True + sage: all(-a in Aut for a in Aut) + True + sage: len(Aut) in (2, 4, 6, 12, 24) + True + sage: j = E.j_invariant() + sage: { + ....: 2: j not in (0, 1728), + ....: 4: p >= 5 and j == 1728, + ....: 6: p >= 5 and j == 0, + ....: 12: p == 3 and j == 0, # note 1728 == 0 + ....: 24: p == 2 and j == 0, # note 1728 == 0 + ....: }[len(Aut)] + True + sage: u,r,s,t = (F^4).random_element() + sage: u = u or 1 + sage: F = E.change_weierstrass_model(u,r,s,t) + sage: Iso = E.isomorphisms(F) + sage: len(set(Iso)) == len(Iso) + True + sage: all(-f in Iso for f in Iso) + True + sage: len(Iso) == len(Aut) + True + sage: all({iso2*iso1 for iso1 in Iso} == set(Aut) for iso2 in F.isomorphisms(E)) + True """ from .ell_generic import is_EllipticCurve if not is_EllipticCurve(E) or not is_EllipticCurve(F): raise ValueError("arguments are not elliptic curves") - K = E.base_ring() j = E.j_invariant() if j != F.j_invariant(): - if JustOne: - return None - return [] + return + + K = E.base_ring() from sage.rings.polynomial.polynomial_ring import polygen x = polygen(K, 'x') @@ -322,71 +312,46 @@ def isomorphisms(E, F, JustOne=False): if char == 2: if j == 0: - ulist = (x**3-(a3E/a3F)).roots(multiplicities=False) - ans = [] + ulist = (x**3 - a3E/a3F).roots(multiplicities=False) for u in ulist: - slist = (x**4+a3E*x+(a2F**2+a4F)*u**4+a2E**2+a4E).roots(multiplicities=False) + slist = (x**4 + a3E*x + (a2F**2 + a4F)*u**4 + a2E**2 + a4E).roots(multiplicities=False) for s in slist: - r = s**2+a2E+a2F*u**2 + r = s**2 + a2E + a2F*u**2 tlist = (x**2 + a3E*x + r**3 + a2E*r**2 + a4E*r + a6E + a6F*u**6).roots(multiplicities=False) for t in tlist: - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t) else: - ans = [] u = a1E/a1F - r = (a3E+a3F*u**3)/a1E - slist = [s[0] for s in (x**2+a1E*x+(r+a2E+a2F*u**2)).roots()] + r = (a3E + a3F*u**3)/a1E + slist = (x**2 + a1E*x + r + a2E + a2F*u**2).roots(multiplicities=False) for s in slist: - t = (a4E+a4F*u**4 + s*a3E + r*s*a1E + r**2) / a1E - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans + t = (a4E + a4F*u**4 + s*a3E + r*s*a1E + r**2) / a1E + yield (u, r, s, t) + return b2E, b4E, b6E, b8E = E.b_invariants() b2F, b4F, b6F, b8F = F.b_invariants() if char == 3: if j == 0: - ulist = (x**4-(b4E/b4F)).roots(multiplicities=False) - ans = [] + ulist = (x**4 - b4E/b4F).roots(multiplicities=False) for u in ulist: - s = a1E-a1F*u - t = a3E-a3F*u**3 - rlist = (x**3-b4E*x+(b6E-b6F*u**6)).roots(multiplicities=False) + s = a1E - a1F*u + t = a3E - a3F*u**3 + rlist = (x**3 - b4E*x + b6E - b6F*u**6).roots(multiplicities=False) for r in rlist: - if JustOne: - return (u, r, s, t+r*a1E) - ans.append((u, r, s, t+r*a1E)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t + r*a1E) else: - ulist = (x**2 - b2E / b2F).roots(multiplicities=False) - ans = [] + ulist = (x**2 - b2E/b2F).roots(multiplicities=False) for u in ulist: r = (b4F * u**4 - b4E) / b2E - s = (a1E - a1F * u) - t = (a3E - a3F * u**3 + a1E * r) - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans - -# now char!=2,3: + s = a1E - a1F * u + t = a3E - a3F * u**3 + a1E * r + yield (u, r, s, t) + return + + # now char != 2,3: + c4E, c6E = E.c_invariants() c4F, c6F = F.c_invariants() @@ -396,19 +361,12 @@ def isomorphisms(E, F, JustOne=False): m, um = 4, c4E/c4F else: m, um = 2, (c6E*c4F)/(c6F*c4E) - ulist = (x**m-um).roots(multiplicities=False) - ans = [] + ulist = (x**m - um).roots(multiplicities=False) for u in ulist: s = (a1F*u - a1E)/2 r = (a2F*u**2 + a1E*s + s**2 - a2E)/3 t = (a3F*u**3 - a1E*r - a3E)/2 - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t) class WeierstrassIsomorphism(EllipticCurveHom, baseWI): @@ -516,8 +474,9 @@ def __init__(self, E=None, urst=None, F=None): E = EllipticCurve(baseWI.__call__(inv_urst, list(F.a_invariants()))) elif urst is None: # try to construct the morphism - urst = isomorphisms(E, F, True) - if urst is None: + try: + urst = next(_isomorphisms(E, F)) + except StopIteration: raise ValueError("elliptic curves not isomorphic") baseWI.__init__(self, *urst) @@ -528,6 +487,8 @@ def __init__(self, E=None, urst=None, F=None): self._mpoly_ring = PolynomialRing(base_ring, ['x','y']) self._poly_ring = PolynomialRing(base_ring, ['x']) + self._xyfield = self._mpoly_ring.fraction_field() + self._xfield = self._poly_ring.fraction_field() self._domain = E self._codomain = F @@ -549,7 +510,7 @@ def _comparison_impl(left, right, op): sage: w1 = E.isomorphism_to(F) sage: w1 == w1 True - sage: w2 = F.automorphisms()[0] *w1 + sage: w2 = F.automorphisms()[1] * w1 sage: w1 == w2 False @@ -578,7 +539,21 @@ def _comparison_impl(left, right, op): if lx != rx: return richcmp_not_equal(lx, rx, op) - return baseWI.__richcmp__(left, right, op) + if op in (op_EQ, op_NE): + return richcmp(left.tuple(), right.tuple(), op) + + # This makes sure that the identity and negation morphisms + # come first in a sorted list of WeierstrassIsomorphisms. + # More generally, we're making sure that a morphism and its + # negative appear next to each other, and that those pairs + # of isomorphisms satisfying u=+-1 come first. + def _sorting_key(iso): + v, w = iso.tuple(), (-iso).tuple() + i = 0 if (1,0,0,0) in (v,w) else 1 + j = 0 if v[0] == 1 else 1 if w[0] == 1 else 2 + return (i,) + min(v,w) + (j,) + v + + return richcmp(_sorting_key(left), _sorting_key(right), op) def _eval(self, P): r""" @@ -634,12 +609,28 @@ def __call__(self, P): (-3/4 : 3/4 : 1) sage: w(P).curve() == E.change_weierstrass_model((2,3,4,5)) True + + TESTS: + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431^2), [1,0]) + sage: i = next(a for a in E.automorphisms() if a^2 == -a^24) + sage: P,_ = E.gens() + sage: P._order + 432 + sage: i(P)._order + 432 + sage: E(i(P))._order + 432 """ if P[2] == 0: return self._codomain(0) - return self._codomain.point(baseWI.__call__(self, - tuple(P._coords)), - check=False) + res = baseWI.__call__(self, tuple(P._coords)) + Q = self._codomain.point(res, check=False) + if hasattr(P, '_order'): + Q._order = P._order + return Q def __invert__(self): r""" @@ -691,9 +682,11 @@ def _composition_impl(left, right): We should return ``NotImplemented`` when passed a combination of elliptic-curve morphism types that we don't handle here:: - sage: E = EllipticCurve([1,0]) - sage: phi = E.isogeny(E(0,0)) - sage: w1._composition_impl(phi.dual(), phi) + sage: E1 = EllipticCurve([1,0]) + sage: phi = E1.isogeny(E1(0,0)) + sage: E2 = phi.codomain() + sage: psi = E2.isogeny(E2(0,0)) + sage: w1._composition_impl(psi, phi) NotImplemented """ if isinstance(left, WeierstrassIsomorphism) and isinstance(right, WeierstrassIsomorphism): @@ -757,14 +750,16 @@ def rational_maps(self): sage: w(P).xy() == (f(P.xy()), g(P.xy())) True - TESTS:: + TESTS: + + Check for :trac:`34811`:: sage: iso.rational_maps()[0].parent() - Multivariate Polynomial Ring in x, y over Rational Field + Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field sage: iso.rational_maps()[1].parent() - Multivariate Polynomial Ring in x, y over Rational Field + Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field """ - return tuple(baseWI.__call__(self, self._mpoly_ring.gens())) + return tuple(baseWI.__call__(self, self._xyfield.gens())) def x_rational_map(self): """ @@ -784,12 +779,14 @@ def x_rational_map(self): sage: iso.x_rational_map() == iso.rational_maps()[0] True - TESTS:: + TESTS: + + Check for :trac:`34811`:: sage: iso.x_rational_map().parent() - Univariate Polynomial Ring in x over Rational Field + Fraction Field of Univariate Polynomial Ring in x over Rational Field """ - x, = self._poly_ring.gens() + x, = self._xfield.gens() return (x - self.r) / self.u**2 def kernel_polynomial(self): @@ -818,6 +815,21 @@ def kernel_polynomial(self): """ return self._poly_ring(1) + def is_separable(self): + r""" + Determine whether or not this isogeny is separable. + + Since :class:`WeierstrassIsomorphism` only implements + isomorphisms, this method always returns ``True``. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(31337), [0,1]) + sage: {f.is_separable() for f in E.automorphisms()} + {True} + """ + return True + def dual(self): """ Return the dual isogeny of this isomorphism. @@ -875,10 +887,10 @@ def __neg__(self): :: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism sage: E = EllipticCurve(QuadraticField(-1), [1,0]) sage: t = WeierstrassIsomorphism(E, (i,0,0,0)) - sage: -t^2 == WeierstrassIsomorphism(E, (1,0,0,0)) + sage: -t^2 == identity_morphism(E) True """ a1,_,a3,_,_ = self._domain.a_invariants() @@ -908,3 +920,36 @@ def scaling_factor(self): """ return self.u + +def identity_morphism(E): + r""" + Given an elliptic curve `E`, return the identity morphism + on `E` as a :class:`WeierstrassIsomorphism`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism + sage: E = EllipticCurve([5,6,7,8,9]) + sage: id_ = identity_morphism(E) + sage: id_.rational_maps() + (x, y) + """ + R = E.base_ring() + 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 + `[-1]` of `E` as a :class:`WeierstrassIsomorphism`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism + sage: E = EllipticCurve([5,6,7,8,9]) + sage: neg = negation_morphism(E) + sage: neg.rational_maps() + (x, -5*x - y - 7) + """ + R = E.base_ring() + return WeierstrassIsomorphism(E, (-R.one(), R.zero(), -E.a1(), -E.a3())) diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 22dbacd8fe0..60d883905fe 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -341,6 +341,22 @@ def ambient_space(self): """ return self.__A + def identity_morphism(self): + """ + Return the identity morphism. + + OUTPUT: the identity morphism of the scheme ``self`` + + EXAMPLES:: + + sage: X = Spec(QQ) + sage: X.identity_morphism() + Scheme endomorphism of Spectrum of Rational Field + Defn: Identity map + """ + from sage.schemes.generic.morphism import SchemeMorphism_polynomial_id + return SchemeMorphism_polynomial_id(self) + def embedding_morphism(self): r""" Return the default embedding morphism of ``self``. diff --git a/src/sage/schemes/generic/all.py b/src/sage/schemes/generic/all.py index baf7e9d1212..b8ba9585bfa 100644 --- a/src/sage/schemes/generic/all.py +++ b/src/sage/schemes/generic/all.py @@ -1,4 +1,4 @@ # code exports -from .spec import Spec -from .hypersurface import ProjectiveHypersurface, AffineHypersurface +from .spec import Spec +from .hypersurface import ProjectiveHypersurface, AffineHypersurface diff --git a/src/sage/schemes/generic/ambient_space.py b/src/sage/schemes/generic/ambient_space.py index bcf742c8b25..aa4645d4e88 100644 --- a/src/sage/schemes/generic/ambient_space.py +++ b/src/sage/schemes/generic/ambient_space.py @@ -1,8 +1,7 @@ """ Ambient spaces """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # # This program is free software: you can redistribute it and/or modify @@ -10,11 +9,11 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.scheme import Scheme @@ -41,7 +40,6 @@ class AmbientSpace(Scheme): INPUT: - - ``n`` - dimension - ``R`` - ring @@ -54,7 +52,7 @@ def __init__(self, n, R=ZZ): sage: A = AmbientSpace(5, ZZ) sage: TestSuite(A).run() # not tested (abstract scheme with no elements?) """ - if not isinstance(R, CommutativeRing): + if R not in CommutativeRings(): raise TypeError("R (={}) must be a commutative ring".format(R)) if n < 0: raise ValueError("n (={}) must be nonnegative".format(n)) @@ -235,7 +233,7 @@ def base_extend(self, R): ValueError: no natural map from the base ring (=Rational Field) to R (=Finite Field of size 5)! """ - if isinstance(R, CommutativeRing): + if R in CommutativeRings(): if self.base_ring() == R: return self if not R.has_coerce_map_from(self.base_ring()): @@ -279,6 +277,27 @@ def defining_polynomials(self): """ return () + def identity_morphism(self): + """ + Return the identity morphism. + + OUTPUT: the identity morphism of the scheme ``self`` + + EXAMPLES:: + + sage: A = AffineSpace(2, GF(3)) + sage: A.identity_morphism() + Scheme endomorphism of Affine Space of dimension 2 over Finite Field of size 3 + Defn: Identity map + + sage: P = ProjectiveSpace(3, ZZ) + sage: P.identity_morphism() + Scheme endomorphism of Projective Space of dimension 3 over Integer Ring + Defn: Identity map + """ + from sage.schemes.generic.morphism import SchemeMorphism_polynomial_id + return SchemeMorphism_polynomial_id(self) + ###################################################################### # Associated MPolynomial ring generators ###################################################################### diff --git a/src/sage/schemes/generic/divisor.py b/src/sage/schemes/generic/divisor.py index 0d0079dbc28..336fee8304f 100644 --- a/src/sage/schemes/generic/divisor.py +++ b/src/sage/schemes/generic/divisor.py @@ -433,11 +433,11 @@ def coefficient(self, P): -1 """ P = self.parent().scheme()(P) - if not(P in self.support()): + if P not in self.support(): return self.base_ring().zero() t, i = search(self.support(), P) assert t try: return self._points[i][0] except AttributeError: - raise NotImplementedError + raise NotImplementedError diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index 601e80b7f49..c918e2f4395 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -25,16 +25,15 @@ - Ben Hutz (June 2012): added support for projective ring """ - -#***************************************************************************** -# Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com> -# Copyright (C) 2006 William Stein <wstein@gmail.com> +# ***************************************************************************** +# Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com> +# Copyright (C) 2006 William Stein <wstein@gmail.com> # -# 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/ -#***************************************************************************** +# 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/ +# ***************************************************************************** from sage.categories.homset import HomsetWithBase from sage.structure.factory import UniqueFactory @@ -42,6 +41,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.scheme import AffineScheme, is_AffineScheme from sage.schemes.generic.morphism import ( @@ -70,9 +70,10 @@ def is_SchemeHomset(H): return isinstance(H, SchemeHomset_generic) -#******************************************************************* -# Factory for Hom sets of schemes -#******************************************************************* +# ******************************************************************* +# Factory for Hom sets of schemes +# ******************************************************************* + class SchemeHomsetFactory(UniqueFactory): """ Factory for Hom-sets of schemes. @@ -149,9 +150,10 @@ def create_key_and_extra_args(self, X, Y, category=None, base=None, 'base_ring': Rational Field, 'check': False} """ - if isinstance(X, CommutativeRing): + _CommRings = CommutativeRings() + if X in _CommRings: X = AffineScheme(X) - if isinstance(Y, CommutativeRing): + if Y in _CommRings: Y = AffineScheme(Y) if base is None: from sage.structure.element import coercion_model @@ -159,7 +161,7 @@ def create_key_and_extra_args(self, X, Y, category=None, base=None, if is_AffineScheme(base): base_spec = base base_ring = base.coordinate_ring() - elif isinstance(base, CommutativeRing): + elif base in _CommRings: base_spec = AffineScheme(base) base_ring = base else: @@ -212,10 +214,10 @@ def create_object(self, version, key, **extra_args): SchemeHomset = SchemeHomsetFactory('sage.schemes.generic.homset.SchemeHomset') +# ******************************************************************* +# Base class +# ******************************************************************* -#******************************************************************* -# Base class -#******************************************************************* class SchemeHomset_generic(HomsetWithBase): r""" The base class for Hom-sets of schemes. @@ -393,9 +395,11 @@ def _element_constructor_(self, x, check=True): raise TypeError("x must be a ring homomorphism, list or tuple") -#******************************************************************* -# Base class for points -#******************************************************************* + +# ******************************************************************* +# Base class for points +# ******************************************************************* + class SchemeHomset_points(SchemeHomset_generic): """ Set of rational points of the scheme. @@ -559,8 +563,8 @@ def _coerce_map_from_(self, other): True """ target = self.codomain() - #ring elements can be coerced to a space if we're affine dimension 1 - #and the base rings are coercible + # ring elements can be coerced to a space if we're affine dimension 1 + # and the base rings are coercible if isinstance(other, CommutativeRing): try: from sage.schemes.affine.affine_space import is_AffineSpace @@ -569,7 +573,7 @@ def _coerce_map_from_(self, other): return target.base_ring().has_coerce_map_from(other) else: return False - except AttributeError: #no .ambient_space + except AttributeError: # no .ambient_space return False elif isinstance(other, SchemeHomset_points): #we are converting between scheme points diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index 85d2c34db59..d3fe7abd885 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -667,6 +667,7 @@ def glue_along_domains(self, other): from . import glue return glue.GluedScheme(self, other) + class SchemeMorphism_id(SchemeMorphism): """ Return the identity morphism from `X` to itself. @@ -927,11 +928,11 @@ def ring_homomorphism(self): ############################################################################ -# Morphisms between schemes given on points -# The _affine and _projective below refer to the CODOMAIN. -# The domain can be either affine or projective regardless -# of the class +# Morphisms between schemes given on points. The _affine and _projective below +# refer to the CODOMAIN. The domain can be either affine or projective +# regardless of the class ############################################################################ + class SchemeMorphism_polynomial(SchemeMorphism): r""" A morphism of schemes determined by polynomials that define what @@ -1019,6 +1020,26 @@ def __init__(self, parent, polys, check=True): SchemeMorphism.__init__(self, parent) + def __eq__(self, other): + """ + Check equality of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a morphism + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(2, QQ) + sage: I = A.identity_morphism() + sage: I.parent().identity() == I + True + """ + if isinstance(other, SchemeMorphism_polynomial): + if self.parent() == other.parent() and self._polys == other._polys: + return True + raise TypeError('cannot determine equality') + def defining_polynomials(self): """ Return the defining polynomials. @@ -1207,7 +1228,6 @@ def _call_with_args(self, x, args, kwds): P = [f(x._coords) for f in self.defining_polynomials()] return self._codomain.point(P,check) - def _repr_defn(self): """ Return a string representation of the definition of ``self``. @@ -1731,6 +1751,36 @@ def _composition_(self, other, homset): return homset([p(*opolys) for p in self._polys]) +class SchemeMorphism_polynomial_id(SchemeMorphism_id, SchemeMorphism_polynomial): + """ + Return the identity morphism from `X` to itself. + + INPUT: + + - ``X`` -- an affine or projective scheme + + EXAMPLES:: + + sage: X = Spec(ZZ) + sage: X.identity_morphism() # indirect doctest + Scheme endomorphism of Spectrum of Integer Ring + Defn: Identity map + """ + def __init__(self, X): + """ + Initialize. + + TESTS:: + + sage: A = AffineSpace(2, GF(3)) + sage: A.identity_morphism().defining_polynomials() + (x0, x1) + """ + super().__init__(X) + variables = X.ambient_space().coordinate_ring().gens() + SchemeMorphism_polynomial.__init__(self, X.Hom(X), variables, check=False) + + ############################################################################ # Rational points on schemes, which we view as morphisms determined # by coordinates. diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index 31102ff5954..520b1fa6b6d 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -22,12 +22,12 @@ from sage.structure.parent import Parent from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.rings.ideal import is_Ideal from sage.structure.unique_representation import UniqueRepresentation - from sage.schemes.generic.point import SchemeTopologicalPoint_prime_ideal + def is_Scheme(x): """ Test whether ``x`` is a scheme. @@ -109,7 +109,7 @@ def __init__(self, X=None, category=None): self._base_scheme = X elif is_SchemeMorphism(X): self._base_morphism = X - elif isinstance(X, CommutativeRing): + elif X in CommutativeRings(): self._base_ring = X elif isinstance(X, Map) and X.category_for().is_subcategory(Rings()): # X is a morphism of Rings @@ -253,7 +253,7 @@ def __call__(self, *args): if len(args) == 1: from sage.schemes.generic.morphism import SchemeMorphism_point S = args[0] - if isinstance(S, CommutativeRing): + if S in CommutativeRings(): return self.point_homset(S) elif is_Scheme(S): return S.Hom(self) @@ -646,7 +646,7 @@ def _Hom_(self, Y, category=None, check=True): sage: E = EllipticCurve('37a1') sage: Hom(E, E).__class__ - <class 'sage.schemes.generic.homset.SchemeHomset_generic_with_category'> + <class 'sage.schemes.projective.projective_homset.SchemeHomset_polynomial_projective_space_with_category'> sage: Hom(Spec(ZZ), Spec(ZZ)).__class__ <class 'sage.schemes.generic.homset.SchemeHomset_generic_with_category_with_equality_by_id'> @@ -866,7 +866,6 @@ def __init__(self, R, S=None, category=None): sage: type(S) <class 'sage.schemes.generic.scheme.AffineScheme_with_category'> """ - from sage.categories.commutative_rings import CommutativeRings if R not in CommutativeRings(): raise TypeError("R (={}) must be a commutative ring".format(R)) self.__R = R @@ -1142,7 +1141,6 @@ def base_extend(self, R): sage: Spec(ZZ['x']).base_extend(Spec(QQ)) Spectrum of Univariate Polynomial Ring in x over Rational Field """ - from sage.categories.commutative_rings import CommutativeRings if R in CommutativeRings(): return AffineScheme(self.coordinate_ring().base_extend(R), self.base_ring()) if not self.base_scheme() == R.base_scheme(): diff --git a/src/sage/schemes/hyperelliptic_curves/constructor.py b/src/sage/schemes/hyperelliptic_curves/constructor.py index 5aa3ad0abd4..54556e08755 100644 --- a/src/sage/schemes/hyperelliptic_curves/constructor.py +++ b/src/sage/schemes/hyperelliptic_curves/constructor.py @@ -6,15 +6,13 @@ - David Kohel (2006): initial version - Anna Somoza (2019-04): dynamic class creation - """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 David Kohel <kohel@maths.usyd.edu> # 2019 Anna Somoza <anna.somoza.henares@gmail.com> # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.schemes.projective.projective_space import ProjectiveSpace @@ -31,6 +29,7 @@ from sage.structure.dynamic_class import dynamic_class + def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): r""" Returns the hyperelliptic curve `y^2 + h y = f`, for @@ -216,40 +215,39 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): if P(2) == 0: # characteristic 2 if h == 0: - raise ValueError("In characteristic 2, argument h (= %s) must be non-zero."%h) + raise ValueError("In characteristic 2, argument h (= %s) must be non-zero." % h) if h[g+1] == 0 and f[2*g+1]**2 == f[2*g+2]*h[g]**2: - raise ValueError("Not a hyperelliptic curve: " \ - "highly singular at infinity.") + raise ValueError("Not a hyperelliptic curve: " + "highly singular at infinity.") should_be_coprime = [h, f*h.derivative()**2+f.derivative()**2] else: # characteristic not 2 if not F.degree() in [2*g+1, 2*g+2]: - raise ValueError("Not a hyperelliptic curve: " \ - "highly singular at infinity.") + raise ValueError("Not a hyperelliptic curve: " + "highly singular at infinity.") should_be_coprime = [F, F.derivative()] try: - smooth = should_be_coprime[0].gcd(should_be_coprime[1]).degree()==0 + smooth = should_be_coprime[0].gcd(should_be_coprime[1]).degree() == 0 except (AttributeError, NotImplementedError, TypeError): try: - smooth = should_be_coprime[0].resultant(should_be_coprime[1])!=0 + smooth = should_be_coprime[0].resultant(should_be_coprime[1]) != 0 except (AttributeError, NotImplementedError, TypeError): - raise NotImplementedError("Cannot determine whether " \ - "polynomials %s have a common root. Use " \ - "check_squarefree=False to skip this check." % \ + raise NotImplementedError("Cannot determine whether " + "polynomials %s have a common root. Use " + "check_squarefree=False to skip this check." % should_be_coprime) if not smooth: - raise ValueError("Not a hyperelliptic curve: " \ - "singularity in the provided affine patch.") + raise ValueError("Not a hyperelliptic curve: " + "singularity in the provided affine patch.") R = P.base_ring() PP = ProjectiveSpace(2, R) if names is None: - names = ["x","y"] + names = ["x", "y"] superclass = [] cls_name = ["HyperellipticCurve"] - genus_classes = { - 2 : HyperellipticCurve_g2} + genus_classes = {2: HyperellipticCurve_g2} is_pAdicField = lambda x: isinstance(x, sage.rings.abc.pAdicField) diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx index 18ad918ac62..c08bb3cfb53 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx @@ -1,8 +1,8 @@ # distutils: language = c++ -# distutils: sources = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp -# distutils: depends = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h +# distutils: sources = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp +# distutils: depends = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.h # distutils: include_dirs = sage/libs/ntl/ sage/schemes/hyperelliptic_curves/hypellfrob/ NTL_INCDIR -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA @@ -59,7 +59,7 @@ cdef extern from "hypellfrob.h": void interval_products_wrapper \ "hypellfrob::hypellfrob_interval_products_wrapper" \ (mat_ZZ_p_c &output, const mat_ZZ_p_c &M0, const mat_ZZ_p_c &M1, - const vector[ZZ_c] target, int force_ntl) + const vector[ZZ_c] target) def interval_products(M0, M1, target): @@ -146,7 +146,7 @@ def interval_products(M0, M1, target): numintervals = len(target)/2 out.SetDims(dim, dim*numintervals) - interval_products_wrapper(out, mm0, mm1, targ, 1) + interval_products_wrapper(out, mm0, mm1, targ) sig_off() R = M0.matrix_space() diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp index 7af598473ab..ee55e41e919 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp @@ -26,8 +26,8 @@ #include "hypellfrob.h" #include "recurrences_ntl.h" -#include "recurrences_zn_poly.h" +#include <cassert> NTL_CLIENT @@ -74,17 +74,10 @@ The intervals are supplied in "target", simply as the list There are three possible underlying implementations: * ntl_interval_products (ZZ_p version), * ntl_interval_products (zz_p version), - * zn_poly_interval_products. -This function is a wrapper which takes ZZ_p input, calls one of the three +This function is a wrapper which takes ZZ_p input, calls one of the two above implementations depending on the size of the current ZZ_p modulus, and produces output in ZZ_p format. -If the force_ntl flag is set, it will never use the zn_poly version. - -Note that the zn_poly version occasionally fails; this happens more frequently -for smaller p, but is extremely rare for larger p. This wrapper detects this -and falls back on the zz_p/ZZ_p versions, which should never fail. - PRECONDITIONS: Let d = b[n-1] - a[0]. Then 2, 3, ... 1 + floor(sqrt(d)) must all be invertible. @@ -92,58 +85,10 @@ and falls back on the zz_p/ZZ_p versions, which should never fail. */ void interval_products_wrapper(vector<mat_ZZ_p>& output, const mat_ZZ_p& M0, const mat_ZZ_p& M1, - const vector<ZZ>& target, - int force_ntl = 0) + const vector<ZZ>& target) { const ZZ& modulus = ZZ_p::modulus(); - if (!force_ntl && modulus <= (1UL << (ULONG_BITS - 1)) - 1) - { - // Small modulus; let's try using zn_poly if we're allowed. - // (we don't allow the full ULONG_BITS bits, because I'm worried I - // sometimes use NTL code which requires longs rather than ulongs.) - zn_mod_t mod; - zn_mod_init(mod, to_ulong(modulus)); - - int r = M0.NumRows(); - - // convert to zn_mod format - vector<vector<ulong> > M0_temp(r, vector<ulong>(r)); - vector<vector<ulong> > M1_temp(r, vector<ulong>(r)); - vector<vector<vector<ulong> > > output_temp(target.size()/2, M0_temp); - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - M0_temp[y][x] = to_ulong(rep(M0[y][x])); - M1_temp[y][x] = to_ulong(rep(M1[y][x])); - } - - int success = zn_poly_interval_products(output_temp, M0_temp, M1_temp, - target, mod); - zn_mod_clear(mod); - - // note: if we failed, we fall back on the ZZ_p or zz_p versions below - if (success) - { - // convert back to ZZ_p format - output.clear(); - mat_ZZ_p temp; - temp.SetDims(r, r); - for (size_t i = 0; i < target.size()/2; i++) - { - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp[y][x] = output_temp[i][y][x]; - output.push_back(temp); - } - - return; - } - - // failed - } - if (!modulus.SinglePrecision()) { // Large modulus.... go straight to the ZZ_p version @@ -182,11 +127,10 @@ void interval_products_wrapper(vector<mat_ZZ_p>& output, void hypellfrob_interval_products_wrapper(mat_ZZ_p& output, const mat_ZZ_p& M0, const mat_ZZ_p& M1, - const vector<ZZ>& target, - int force_ntl = 0) + const vector<ZZ>& target) { vector<mat_ZZ_p> mat_vector; - interval_products_wrapper(mat_vector, M0, M1, target, force_ntl); + interval_products_wrapper(mat_vector, M0, M1, target); int r = M0.NumRows(); output.SetDims(r, r * mat_vector.size()); @@ -327,7 +271,7 @@ void padic_invert_matrix(mat_ZZ_p& B, const mat_ZZ_p& A, const ZZ& p, int N) /* The main function exported from this module. See hypellfrob.h for information. */ -int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) +int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q) { // check input conditions if (N < 1 || p < 3) @@ -521,8 +465,8 @@ int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) vector<mat_ZZ_p> MH, DH; MH.reserve(L); DH.reserve(L); - interval_products_wrapper(MH, MH0[j], MH1[j], s, force_ntl); - interval_products_wrapper(DH, DH0[j], DH1[j], s, force_ntl); + interval_products_wrapper(MH, MH0[j], MH1[j], s); + interval_products_wrapper(DH, DH0[j], DH1[j], s); // Use the vandermonde matrix to extend all the way up to L if necessary. if (L > N) @@ -709,8 +653,8 @@ int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) } modulusV.restore(); vector<mat_ZZ_p> MV, DV; - interval_products_wrapper(MV, MV0, MV1, s, force_ntl); - interval_products_wrapper(DV, DV0, DV1, s, force_ntl); + interval_products_wrapper(MV, MV0, MV1, s); + interval_products_wrapper(DV, DV0, DV1, s); // Divide out each MV[j] by DV[j]. Note that for 1 <= j < N, both of them // should be divisible by p too, so we take that into account here. diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h index e25629ced56..23a57464c48 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h @@ -44,18 +44,11 @@ The intervals are supplied in "target", simply as the list There are three possible underlying implementations: * ntl_interval_products (ZZ_p version), - * ntl_interval_products (zz_p version), - * zn_poly_interval_products. -This function is a wrapper which takes ZZ_p input, calls one of the three + * ntl_interval_products (zz_p version) +This function is a wrapper which takes ZZ_p input, calls one of the two above implementations depending on the size of the current ZZ_p modulus, and produces output in ZZ_p format. -If the force_ntl flag is set, it will never use the zn_poly version. - -Note that the zn_poly version occasionally fails; this happens more frequently -for smaller p, but is extremely rare for larger p. This wrapper detects this -and falls back on the zz_p/ZZ_p versions, which should never fail. - PRECONDITIONS: Let d = b[n-1] - a[0]. Then 2, 3, ... 1 + floor(sqrt(d)) must all be invertible. @@ -63,8 +56,7 @@ and falls back on the zz_p/ZZ_p versions, which should never fail. */ void hypellfrob_interval_products_wrapper(NTL::mat_ZZ_p& output, const NTL::mat_ZZ_p& M0, const NTL::mat_ZZ_p& M1, - const std::vector<NTL::ZZ>& target, - int force_ntl); + const std::vector<NTL::ZZ>& target); /* Computes frobenius matrix for given p, to precision p^N, for the @@ -82,8 +74,7 @@ RETURN VALUE: will not check that p is prime. That's up to you.) */ -int matrix(NTL::mat_ZZ& output, const NTL::ZZ& p, int N, const NTL::ZZX& Q, - int force_ntl = 0); +int matrix(NTL::mat_ZZ& output, const NTL::ZZ& p, int N, const NTL::ZZX& Q); }; diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp b/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp deleted file mode 100644 index b9023adb489..00000000000 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/* ============================================================================ - - recurrences_zn_poly.cpp: recurrences solved via zn_poly arithmetic - - This file is part of hypellfrob (version 2.1.1). - - Copyright (C) 2007, 2008, David Harvey - - 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 "recurrences_zn_poly.h" - - -NTL_CLIENT - - -namespace hypellfrob { - - -Shifter::~Shifter() -{ - zn_array_mulmid_precomp1_clear(kernel_precomp); - free(input_twist); -} - - -Shifter::Shifter(ulong d, ulong a, ulong b, const zn_mod_t mod) -{ - this->d = d; - this->mod = mod; - - input_twist = (ulong*) malloc(sizeof(ulong) * (3*d + 3)); - output_twist = input_twist + d + 1; - scratch = output_twist + d + 1; - - ZZ modulus; - modulus = zn_mod_get (mod); - - // ------------------------ compute input_twist ------------------------- - - // prod = (d!)^(-1) - ulong prod = 1; - for (ulong i = 2; i <= d; i++) - prod = zn_mod_mul(prod, i, mod); - prod = to_ulong(InvMod(to_ZZ(prod), modulus)); - - // input_twist[i] = ((d-i)!)^(-1) - input_twist[0] = prod; - for (ulong i = 1; i <= d; i++) - input_twist[i] = zn_mod_mul(input_twist[i-1], d - (i-1), mod); - - // input_twist[i] = ((d-i)!*i!)^(-1) - for (ulong i = 0; i <= d/2; i++) - { - input_twist[i] = zn_mod_mul(input_twist[i], input_twist[d-i], mod); - input_twist[d-i] = input_twist[i]; - } - - // input_twist[i] = \prod_{0 <= j <= d, j != i} (i-j)^(-1) - // = (-1)^(d-i) ((d-i)!*i!)^(-1) - for (long i = d - 1; i >= 0; i -= 2) - input_twist[i] = zn_mod_neg(input_twist[i], mod); - - // ----------------- compute output_twist and kernel -------------------- - - // need some temp space: - // c, accum, accum_inv each of length 2d+1 - ulong* c = (ulong*) malloc(sizeof(ulong) * (6*d+3)); - ulong* accum = c + 2*d + 1; - ulong* accum_inv = accum + 2*d + 1; - ulong* kernel = c; // overwrites c - - // c[i] = c_i = a + (i-d)*b for 0 <= i <= 2d - c[0] = zn_mod_sub(a, zn_mod_mul(zn_mod_reduce(d, mod), b, mod), mod); - for (ulong i = 1; i <= 2*d; i++) - c[i] = zn_mod_add(c[i-1], b, mod); - - // accum[i] = c_0 * c_1 * ... * c_i for 0 <= i <= 2d - accum[0] = c[0]; - for (ulong i = 1; i <= 2*d; i++) - accum[i] = zn_mod_mul(accum[i-1], c[i], mod); - - // accum_inv[i] = (c_0 * c_1 * ... * c_i)^(-1) for 0 <= i <= 2d - accum_inv[2*d] = to_ulong(InvMod(to_ZZ(accum[2*d]), modulus)); - - for (long i = 2*d - 1; i >= 0; i--) - accum_inv[i] = zn_mod_mul(accum_inv[i+1], c[i+1], mod); - - // output_twist[i] = b^{-d} * c_i * c_{i+1} * ... * c_{i+d} - // for 0 <= i <= d - ulong factor = to_long(PowerMod(to_ZZ(b), -((long)d), modulus)); - output_twist[0] = zn_mod_mul(factor, accum[d], mod); - for (ulong i = 1; i <= d; i++) - output_twist[i] = zn_mod_mul(zn_mod_mul(factor, accum[i+d], mod), - accum_inv[i-1], mod); - - // kernel[i] = (c_i)^(-1) for 0 <= i <= 2d - kernel[0] = accum_inv[0]; - for (ulong i = 1; i <= 2*d; i++) - kernel[i] = zn_mod_mul(accum_inv[i], accum[i-1], mod); - - // precompute FFT of kernel - zn_array_mulmid_precomp1_init(kernel_precomp, kernel, 2*d+1, d+1, mod); - - free(c); -} - - -void Shifter::shift(ulong* output, const ulong* input) -{ - // multiply inputs pointwise by input_twist - for (ulong i = 0; i <= d; i++) - scratch[i] = zn_mod_mul(input[i], input_twist[i], mod); - - // do middle product - zn_array_mulmid_precomp1_execute(output, scratch, kernel_precomp); - - // multiply outputs pointwise by output_twist - for (ulong i = 0; i <= d; i++) - output[i] = zn_mod_mul(output[i], output_twist[i], mod); -} - - -/* - Checks whether large_evaluate() will succeed for these choices of k and u. - Returns 1 if okay, otherwise 0. -*/ -int check_params(ulong k, ulong u, const zn_mod_t mod) -{ - ulong n = zn_mod_get (mod); - - if (k >= n || u >= n) - return 0; - - if (k <= 1) - return 1; - - if (k == n - 1) - return 0; - - ulong k2 = k / 2; - - // need the following elements to be invertible: - // u - // 1, 2, ..., k + 1 - // k2 + i*u for -k2 <= i <= k2 - ulong prod = u; - for (ulong i = 2; i <= k; i++) - prod = zn_mod_mul(prod, i, mod); - ulong temp = zn_mod_mul(k2, zn_mod_sub(1, u, mod), mod); - for (ulong i = 0; i <= 2*k2; i++) - { - prod = zn_mod_mul(prod, temp, mod); - temp = zn_mod_add(temp, u, mod); - } - - ZZ x, y; - x = prod; - y = n; - if (GCD(x, y) != 1) - return 0; - - // check recursively below - return check_params(k2, u, mod); -} - - - -/* - Let M0 and M1 be square matrices of size r*r. Let M(x) = M0 + x*M1; this - is a matrix of linear polys in x. The matrices M0 and M1 are passed in - row-major order. - - Let P(x) = M(x+1) M(x+2) ... M(x+k); this is a matrix of polynomials of - degree k. - - This class attempts to compute the matrices - P(0), P(u), P(2u), ..., P(ku). - - Usage: - - * Call the constructor. - - * Call evaluate() with half == 0. This computes the first half of the - outputs, specifically P(mu) for 0 <= m <= k2, where k2 = floor(k/2). - The (i, j) entry of P(mu) is stored in output[i*r+j][m+offset]. Each array - output[i*r+j] must be preallocated to length k2 + 3. (The extra two matrices - at the end are used for scratch space.) - - * Call evaluate() with half == 1. This computes the second half, namely - P(mu) for k2 + 1 <= m <= k. The (i, j) entry of P(mu) is stored in - output[i*r+j][m-(k2+1)+offset]. Each array output[i*r+j] must be - preallocated to length k2 + 3. It's okay for this second half to overwrite - the first half generated earlier (in fact this property is used by - zn_poly_interval_products to conserve memory). - - The computation may fail for certain bad choices of k and u. Let p = residue - characteristic (i.e. assume mod is p^m for some m). Typically this function - gets called for k ~ u and k*u ~ M*p, for some M much smaller than p. In this - situation, I expect most choices of (k, u) are not bad. Failure is very - bad: the program will crash. Luckily, you can call check_params() to test - whether (k, u) is bad. If it's bad, you probably should just increment u - until you find one that's not bad. (Sorry, I haven't done a proper analysis - of the situation yet.) The main routines fall back on the zz_pX version if - they can't find a good parameter choice. - - Must have 0 <= k < n and 0 < u < n. - -*/ -LargeEvaluator::LargeEvaluator(int r, ulong k, ulong u, - const vector<vector<ulong> >& M0, - const vector<vector<ulong> >& M1, - const zn_mod_t& mod) : M0(M0), M1(M1), mod(mod) -{ - assert(k < zn_mod_get(mod)); - assert(u < zn_mod_get(mod)); - assert(r >= 1); - assert(k >= 1); - - this->r = r; - this->k = k; - this->k2 = k / 2; - this->odd = k & 1; - this->u = u; - this->shifter = NULL; -} - - -LargeEvaluator::~LargeEvaluator() -{ - if (this->shifter) - delete shifter; -} - - -void LargeEvaluator::evaluate(int half, vector<ulong_array>& output, - ulong offset) -{ - // base cases - - if (k == 0) - { - // identity matrix - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - output[y*r + x].data[offset] = (x == y); - return; - } - - if (k == 1) - { - // evaluate M(1) (for half == 0) or M(u+1) (for half == 1) - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong temp = zn_mod_add(M0[y][x], M1[y][x], mod); - output[y*r + x].data[offset] = half ? - zn_mod_add(temp, zn_mod_mul(u, M1[y][x], mod), mod) : temp; - } - return; - } - - // recursive case - - // Let Q(x) = M(x+1) ... M(x+k2), where k2 = floor(k/2). - // Then we have either - // P(x) = Q(x) Q(x+k2) if k is even - // P(x) = Q(x) Q(x+k2) M(x+k) if k is odd - - if (half == 0) - { - // This is the first time through this function. - // Allocate scratch space. - scratch.resize(r*r); - for (int i = 0; i < r*r; i++) - scratch[i].resize(k2 + 3); - - { - // Recursively compute Q(0), Q(u), ..., Q(k2*u). - // These are stored in scratch. - LargeEvaluator recurse(r, k2, u, M0, M1, mod); - recurse.evaluate_all(scratch); - } - - // Precomputations for value-shifting - shifter = new Shifter(k2, k2, u, mod); - } - else // half == 1 - { - // Shift original sequence by (k2+1)*u to obtain - // Q((k2+1)*u), Q((k2+2)*u), ..., Q((2*k2+1)*u) - Shifter big_shifter(k2, zn_mod_mul(k2+1, u, mod), u, mod); - for (int i = 0; i < r*r; i++) - big_shifter.shift(scratch[i].data, scratch[i].data); - } - - // Let H = (k2+1)*u*half, so now scratch contains - // Q(H), Q(H + u), ..., Q(H + k2*u). - - // Shift by k2 to obtain Q(H + k2), Q(H + u + k2), ..., Q(H + k2*u + k2). - // Results are stored directly in output array. We put them one slot - // to the right, to make room for inplace matrix multiplies later on. - // (If k is odd, they're shifted right by yet another slot, to make room - // for another round of multiplies.) - for (int i = 0; i < r*r; i++) - shifter->shift(output[i].data + offset + odd + 1, scratch[i].data); - - // If k is odd, right-multiply each Q(H + i*u + k2) by M(H + i*u + k) - // (results are stored in output array, shifted one entry to the left) - if (odd) - { - ulong_array cruft(r*r); // for storing M(H + i*u + k) - ulong point = k; // evaluation point - if (half) - point = zn_mod_add(point, zn_mod_mul(k2 + 1, u, mod), mod); - - for (ulong i = 0; i <= k2; i++, point = zn_mod_add(point, u, mod)) - { - // compute M(H + i*u + k) = M0 + M1*point - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - cruft.data[y*r + x] = zn_mod_add(M0[y][x], - zn_mod_mul(M1[y][x], point, mod), mod); - - // multiply - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong accum = 0; - for (int z = 0; z < r; z++) - accum = zn_mod_add(accum, - zn_mod_mul(output[y*r + z].data[offset + i + 2], - cruft.data[z*r + x], mod), mod); - output[y*r + x].data[offset + i + 1] = accum; - } - } - } - - ulong n = zn_mod_get(mod); - - // Multiply to obtain P(H), P(H + u), ..., P(H + k2*u) - // (except for the last one, in the second half, if k is even) - // Store results directly in output array. - for (ulong i = 0; i + (half && !odd) <= k2; i++) - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong sum_hi = 0; - ulong sum_lo = 0; - for (int z = 0; z < r; z++) - { - ulong hi, lo; - ZNP_MUL_WIDE(hi, lo, scratch[y*r + z].data[i], - output[z*r + x].data[offset + i + 1]); - ZNP_ADD_WIDE(sum_hi, sum_lo, sum_hi, sum_lo, hi, lo); - if (sum_hi >= n) - sum_hi -= n; - } - output[y*r + x].data[offset + i] = - zn_mod_reduce_wide(sum_hi, sum_lo, mod); - } -} - - -/* - Evaluates both halves, storing results in output array. -*/ -void LargeEvaluator::evaluate_all(vector<ulong_array>& output) -{ - evaluate(0, output, 0); - evaluate(1, output, k/2 + 1); -} - - - -/* -See interval_products_wrapper(). - -NOTE 1: - I haven't proved that the algorithm here always succeeds, although I expect - that it almost always will, especially when p is very large. If it - succeeds, it returns 1. If it fails, it returns 0 (practically instantly). - In the latter case, at least the caller can fall back on - ntl_interval_products(). - -NOTE 2: - The algorithm here is similar to ntl_interval_products(). However, it - doesn't do the "refining step" -- it just handles the smaller intervals - in the naive fashion. Also, instead of breaking intervals into power-of-four - lengths, it just does the whole thing in one chunk. The performance ends up - being smoother, but it's harder to prove anything about avoiding - non-invertible elements. Hence the caveat in Note 1. - -*/ -int zn_poly_interval_products(vector<vector<vector<ulong> > >& output, - const vector<vector<ulong> >& M0, - const vector<vector<ulong> >& M1, - const vector<ZZ>& target, const zn_mod_t& mod) -{ - output.resize(target.size() / 2); - - // select k such that k*(k+1) >= total length of intervals - ulong k; - { - ZZ len = target.back() - target.front(); - ZZ kk = SqrRoot(len); - k = to_ulong(kk); - if (kk * (kk + 1) < len) - k++; - } - - int r = M0.size(); - - // try to find good parameters u and k - ulong u = k; - for (int trial = 0; ; trial++, u++) - { - if (check_params(k, u, mod)) - break; // found some good parameters - if (trial == 5) - return 0; // too many failures, give up - } - - ulong n = zn_mod_get(mod); - - // shift M0 over to account for starting index - vector<vector<ulong> > M0_shifted = M0; - ulong shift = target.front() % n; - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - M0_shifted[y][x] = zn_mod_add(M0[y][x], - zn_mod_mul(shift, M1[y][x], mod), mod); - - // prepare for evaluating products over the big intervals - // [0, k), [u, u+k), ..., [ku, ku+k) - LargeEvaluator evaluator(r, k, u, M0_shifted, M1, mod); - - vector<ulong_array> big(r*r); - for (int i = 0; i < r*r; i++) - big[i].resize(k/2 + 3); // space for half the products, plus two more - - // evaluate the first half of the products - evaluator.evaluate(0, big, 0); - // flag indicating which half we currently have stored in the "big" array - int half = 0; - - vector<vector<ulong> > accum(r, vector<ulong>(r)); - vector<vector<ulong> > temp1(r, vector<ulong>(r)); - vector<vector<ulong> > temp2(r, vector<ulong>(r)); - - // for each target interval.... - for (size_t i = 0; i < target.size()/2; i++) - { - // doing interval [s0, s1) - ZZ s0 = target[2*i]; - ZZ s1 = target[2*i+1]; - - // product accumulated so far is [t0, t1). - ZZ t0 = s0; - ZZ t1 = s0; - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - accum[x][y] = (x == y); // identity matrix - - while (t1 < s1) - { - // if we are exactly on the left end of a big interval, and the - // big interval fits inside the target interval, then roll it in - if ((t1 - target[0]) % u == 0 && t1 + k <= s1) - { - // compute which "big" interval we are rolling in - size_t index = to_ulong((t1 - target[0]) / u); - - if (index >= k/2 + 1) - { - // if the "big" interval is in the second half, and we haven't - // computed the second half of the intervals yet, then go and - // compute them (overwriting the first half) - if (half == 0) - { - evaluator.evaluate(1, big, 0); - half = 1; - } - index -= (k/2 + 1); - } - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp1[y][x] = big[y*r + x].data[index]; - - t1 += k; - } - else - { - // otherwise just multiply by the single matrix M(t1 + 1) - ulong e = (t1 + 1) % n; - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp1[y][x] = zn_mod_add(M0[y][x], - zn_mod_mul(M1[y][x], e, mod), mod); - - t1++; - } - - // multiply by whichever matrix we picked up above - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong sum_hi = 0; - ulong sum_lo = 0; - for (int z = 0; z < r; z++) - { - ulong hi, lo; - ZNP_MUL_WIDE(hi, lo, accum[y][z], temp1[z][x]); - ZNP_ADD_WIDE(sum_hi, sum_lo, sum_hi, sum_lo, hi, lo); - if (sum_hi >= n) - sum_hi -= n; - } - temp2[y][x] = zn_mod_reduce_wide(sum_hi, sum_lo, mod); - } - - accum.swap(temp2); - } - - // store result in output array - output[i] = accum; - } - - return 1; -} - - -}; // namespace hypellfrob - - -// ----------------------- end of file diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h b/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h deleted file mode 100644 index c5e936036c4..00000000000 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h +++ /dev/null @@ -1,158 +0,0 @@ -/* ============================================================================ - - recurrences_zn_poly.h: header for recurrences_zn_poly.cpp - - This file is part of hypellfrob (version 2.1.1). - - Copyright (C) 2007, 2008, David Harvey - - 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 <vector> -#include <NTL/ZZ.h> -#include <zn_poly/zn_poly.h> - - - -namespace hypellfrob { - - -/* -This struct stores precomputed information that can then be used to shift -evaluation values of a polynomial F(x) of degree d. - -Specifically, given the values - F(0), F(b), F(2*b), ..., F(d*b), -the shift() method computes - F(a), F(a + b), F(a + 2*b), ..., F(a + d*b). - -PRECONDITIONS: - d >= 0 - 1, 2, ..., d + 1 are invertible - a + i*b are invertible for -d <= i <= d - -*/ -struct Shifter -{ - ulong d; - - // input_twist is a vector of length d + 1. - // The i-th entry is \prod_{0 <= j <= d, j != i} (i-j)^(-1). - // todo: this is symmetric, so can make it d/2 + 1, but need to - // deal with even/odd cases separately? - ulong* input_twist; - - // output_twist is a vector of length d + 1. - // The i-th entry is b^(-d) \prod_{0 <= j <= d} (a + (i-j)*b). - ulong* output_twist; - - // precomputed info for performing middle product against a "kernel" - // polynomial of degree 2d. The coefficients of "kernel" are - // (a + k*b)^(-1) for -d <= k <= d. - zn_array_mulmid_precomp1_t kernel_precomp; - - // Scratch space for shift(), length d + 1 - ulong* scratch; - - // zn_poly modulus object - const zn_mod_struct* mod; - - ~Shifter(); - - // Constructor (performs various precomputations) - Shifter(ulong d, ulong a, ulong b, const zn_mod_t mod); - - // Shifts evaluation values as described above. - // Assumes both output and input have length d + 1. - void shift(ulong* output, const ulong* input); -}; - - -/* - Grrr..... I would rather use vector<ulong>, but then strictly speaking - I can't be guaranteed that the data is stored in a simple array format, - so here I go rolling my own.... -*/ -struct ulong_array -{ - ulong* data; - - ulong_array() - { - data = NULL; - } - - ulong_array(ulong amount) - { - data = (ulong*) malloc(sizeof(ulong) * amount); - } - - ~ulong_array() - { - if (data) - free(data); - } - - void resize(ulong amount) - { - if (data) - data = (ulong*) realloc(data, sizeof(ulong) * amount); - else - data = (ulong*) malloc(sizeof(ulong) * amount); - } -}; - - - -int check_params(ulong k, ulong u, const zn_mod_t mod); - - - -struct LargeEvaluator -{ - int r; - ulong k, u, k2, odd; - const std::vector<std::vector<ulong> >& M0; - const std::vector<std::vector<ulong> >& M1; - const zn_mod_t& mod; - Shifter* shifter; - std::vector<ulong_array> scratch; - - LargeEvaluator(int r, ulong k, ulong u, - const std::vector<std::vector<ulong> >& M0, - const std::vector<std::vector<ulong> >& M1, - const zn_mod_t& mod); - - ~LargeEvaluator(); - - void evaluate(int half, std::vector<ulong_array>& output, ulong offset); - void evaluate_all(std::vector<ulong_array>& output); -}; - - - -int zn_poly_interval_products( - std::vector<std::vector<std::vector<ulong> > >& output, - const std::vector<std::vector<ulong> >& M0, - const std::vector<std::vector<ulong> >& M1, - const std::vector<NTL::ZZ>& target, const zn_mod_t& mod); - - -}; - -// ----------------------- end of file diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 3d6588e413e..7627ce2f307 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -1365,7 +1365,7 @@ def cardinality(self, extension_degree=1): # We may: # - check for actual field of definition of the curve (up to isomorphism) - if e == 1 and h == 0 and f.degree() % 2 == 1: + if e == 1 and h == 0 and f.degree() % 2 == 1: N1 = self._frobenius_coefficient_bound_traces(n) N2 = self._frobenius_coefficient_bound_charpoly() if n < g and q > (2*g+1)*(2*N1-1): diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py index 373394b20af..a79843c0e99 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py @@ -1,15 +1,15 @@ """ Hyperelliptic curves over a `p`-adic field """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu> # # 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.all import (PowerSeriesRing, PolynomialRing, ZZ, QQ, @@ -27,10 +27,10 @@ class HyperellipticCurve_padic_field(hyperelliptic_generic.HyperellipticCurve_generic, ProjectivePlaneCurve_field): -# The functions below were prototyped at the 2007 Arizona Winter School by -# Robert Bradshaw and Ralf Gerkmann, working with Miljan Brakovevic and -# Kiran Kedlaya -# All of the below is with respect to the Monsky Washnitzer cohomology. + # The functions below were prototyped at the 2007 Arizona Winter School by + # Robert Bradshaw and Ralf Gerkmann, working with Miljan Brakovevic and + # Kiran Kedlaya + # All of the below is with respect to the Monsky Washnitzer cohomology. def local_analytic_interpolation(self, P, Q): """ @@ -158,7 +158,7 @@ def weierstrass_points(self): raise NotImplementedError() return [self((0,1,0))] + [self((x, 0, 1)) for x in f.roots(multiplicities=False)] - def is_in_weierstrass_disc(self,P): + def is_in_weierstrass_disc(self, P): """ Checks if `P` is in a Weierstrass disc @@ -186,12 +186,9 @@ def is_in_weierstrass_disc(self,P): - Jennifer Balakrishnan (2010-02) """ - if (P[1].valuation() == 0 and P != self(0,1,0)): - return False - else: - return True + return not (P[1].valuation() == 0 and P != self(0, 1, 0)) - def is_weierstrass(self,P): + def is_weierstrass(self, P): """ Checks if `P` is a Weierstrass point (i.e., fixed by the hyperelliptic involution) @@ -218,12 +215,8 @@ def is_weierstrass(self,P): AUTHOR: - Jennifer Balakrishnan (2010-02) - """ - if (P[1] == 0 or P[2] ==0): - return True - else: - return False + return (P[1] == 0 or P[2] == 0) def find_char_zero_weier_point(self, Q): """ @@ -260,7 +253,7 @@ def find_char_zero_weier_point(self, Q): if self.is_same_disc(P,Q): return P - def residue_disc(self,P): + def residue_disc(self, P): """ Gives the residue disc of `P` @@ -306,7 +299,7 @@ def residue_disc(self,P): else: return HF(0,1,0) - def is_same_disc(self,P,Q): + def is_same_disc(self, P, Q): """ Checks if `P,Q` are in same residue disc @@ -326,10 +319,7 @@ def is_same_disc(self,P,Q): sage: HK.is_same_disc(Q,S) False """ - if self.residue_disc(P) == self.residue_disc(Q): - return True - else: - return False + return self.residue_disc(P) == self.residue_disc(Q) def tiny_integrals(self, F, P, Q): r""" @@ -1001,12 +991,12 @@ def newton_sqrt(self, f, x0, prec): - Jennifer Balakrishnan """ z = x0 - loop_prec = (log(RR(prec))/log(RR(2))).ceil() + loop_prec = log(RR(prec), 2).ceil() for i in range(loop_prec): - z = (z + f/z) / 2 + z = (z + f / z) / 2 return z - def curve_over_ram_extn(self,deg): + def curve_over_ram_extn(self, deg): r""" Return ``self`` over `\QQ_p(p^(1/deg))`. @@ -1123,7 +1113,7 @@ def P_to_S(self, P, S): val = [I(S[1]) for I in integrals] return vector(val) - def coleman_integral_P_to_S(self,w,P,S): + def coleman_integral_P_to_S(self, w, P, S): r""" Given a finite Weierstrass point `P` and a point `S` in the same disc, computes the Coleman integral `\int_P^S w` @@ -1168,7 +1158,7 @@ def coleman_integral_P_to_S(self,w,P,S): int_sing_a = int_sing(S[1]) return int_sing_a - def S_to_Q(self,S,Q): + def S_to_Q(self, S, Q): r""" Given `S` a point on self over an extension field, computes the Coleman integrals `\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}` @@ -1248,13 +1238,12 @@ def S_to_Q(self,S,Q): b = V(L) M_sys = matrix(K, M_frob).transpose() - 1 B = (~M_sys) - v = [c.valuation() for c in B.list()] - vv = min(v) + vv = min(c.valuation() for c in B.list()) B = (p**(-vv)*B).change_ring(K) B = p**(vv)*B return B*(b-S_to_FS-FQ_to_Q) - def coleman_integral_S_to_Q(self,w,S,Q): + def coleman_integral_S_to_Q(self, w, S, Q): r""" Compute the Coleman integral `\int_S^Q w` @@ -1293,7 +1282,6 @@ def coleman_integral_S_to_Q(self,w,S,Q): AUTHOR: - Jennifer Balakrishnan - """ import sage.schemes.hyperelliptic_curves.monsky_washnitzer as monsky_washnitzer K = self.base_ring() @@ -1350,10 +1338,9 @@ def coleman_integral_from_weierstrass_via_boundary(self, w, P, Q, d): AUTHOR: - Jennifer Balakrishnan - """ HJ = self.curve_over_ram_extn(d) - S = self.get_boundary_point(HJ,P) - P_to_S = self.coleman_integral_P_to_S(w,P,S) - S_to_Q = HJ.coleman_integral_S_to_Q(w,S,Q) + S = self.get_boundary_point(HJ, P) + P_to_S = self.coleman_integral_P_to_S(w, P, S) + S_to_Q = HJ.coleman_integral_S_to_Q(w, S, Q) return P_to_S + S_to_Q diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py index 7167fe2208a..19262aece79 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py @@ -339,6 +339,7 @@ def cantor_composition(D1,D2,f,h,genus): a = a.monic() return (a, b) + class JacobianMorphism_divisor_class_field(AdditiveGroupElement, SchemeMorphism): r""" An element of a Jacobian defined over a field, i.e. in @@ -348,10 +349,13 @@ def __init__(self, parent, polys, check=True): r""" Create a new Jacobian element in Mumford representation. - INPUT: parent: the parent Homset polys: Mumford's `u` and - `v` polynomials check (default: True): if True, ensure that - polynomials define a divisor on the appropriate curve and are - reduced + INPUT: + + - parent -- the parent Homset + - polys -- Mumford's `u` and `v` polynomials + - check (default: ``True``) -- if ``True``, ensure that + polynomials define a divisor on the appropriate curve and are + reduced .. warning:: @@ -658,7 +662,7 @@ def __bool__(self): """ return self.__polys[0] != 1 - + def __neg__(self): r""" diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 05022c45113..b5454923998 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -40,6 +40,7 @@ from sage.categories.fields import Fields _Fields = Fields() + class ProjectiveConic_field(ProjectivePlaneCurve_field): r""" Create a projective plane conic curve over a field. @@ -113,10 +114,10 @@ def base_extend(self, S): if B == S: return self if not S.has_coerce_map_from(B): - raise ValueError("No natural map from the base ring of self " \ + raise ValueError("No natural map from the base ring of self " "(= %s) to S (= %s)" % (self, S)) from .constructor import Conic - con = Conic([S(c) for c in self.coefficients()], \ + con = Conic([S(c) for c in self.coefficients()], self.variable_names()) if self._rational_point is not None: pt = [S(c) for c in Sequence(self._rational_point)] @@ -188,9 +189,7 @@ def derivative_matrix(self): [1 2 1] [1 1 0] - An example in characteristic `2`: - - :: + An example in characteristic `2`:: sage: P.<t> = GF(2)[] sage: c = Conic([t, 1, t^2, 1, 1, 0]); c @@ -203,9 +202,9 @@ def derivative_matrix(self): [t^2 1 0] """ a, b, c, d, e, f = self.coefficients() - return Matrix([[ 2*a , b , c ], - [ b , 2*d , e ], - [ c , e , 2*f ]]) + return Matrix([[2 * a, b, c], + [b, 2 * d, e], + [c, e, 2 * f]]) def determinant(self): r""" @@ -497,8 +496,8 @@ def has_rational_point(self, point=False, # writing) fraction field elements are not converted automatically # from Magma to Sage. try: - return True, self.point( \ - [B(c.Numerator().sage()/c.Denominator().sage()) for c in pt]) + return True, self.point( + [B(c.Numerator().sage() / c.Denominator().sage()) for c in pt]) except (TypeError, NameError): pass @@ -539,8 +538,8 @@ def has_rational_point(self, point=False, if point: return ret return ret[0] - raise NotImplementedError("has_rational_point not implemented for " \ - "conics over base field %s" % B) + raise NotImplementedError("has_rational_point not implemented for " + "conics over base field %s" % B) def has_singular_point(self, point=False): r""" @@ -693,8 +692,8 @@ def hom(self, x, Y=None): if Y is None: Y = im elif not Y == im: - raise ValueError("The matrix x (= %s) does not define a " \ - "map from self (= %s) to Y (= %s)" % \ + raise ValueError("The matrix x (= %s) does not define a " + "map from self (= %s) to Y (= %s)" % (x, self, Y)) x = Sequence(x*vector(self.ambient_space().gens())) return self.Hom(Y)(x, check=False) @@ -920,7 +919,7 @@ def parametrization(self, point=None, morphism=True): L[c3] = Y*T1*point[c3] bezout = P(self.defining_polynomial()(L) / T0) t = [bezout([x,y,0,-1]),bezout([x,y,1,0])] - par = (tuple([Q(p([x,y,t[0],t[1]])/y) for p in L]), + par = (tuple([Q(p([x,y,t[0],t[1]])/y) for p in L]), tuple([gens[m]*point[c3]-gens[c3]*point[m] for m in [c2,c1]])) if self._parametrization is None: @@ -1120,12 +1119,12 @@ def rational_point(self, algorithm='default', read_cache=True): ... ValueError: Conic Projective Conic Curve over Real Field with 53 bits of precision defined by x^2 + y^2 + z^2 has no rational points over Real Field with 53 bits of precision! """ - bl,pt = self.has_rational_point(point=True, algorithm=algorithm, - read_cache=read_cache) + bl, pt = self.has_rational_point(point=True, algorithm=algorithm, + read_cache=read_cache) if bl: return pt - raise ValueError("Conic %s has no rational points over %s!" % \ - (self, self.ambient_space().base_ring())) + raise ValueError("Conic %s has no rational points over %s!" % + (self, self.ambient_space().base_ring())) def singular_point(self): r""" @@ -1149,8 +1148,8 @@ def singular_point(self): """ b = self.has_singular_point(point=True) if not b[0]: - raise ValueError("The conic self (= %s) has no rational " \ - "singular point" % self) + raise ValueError("The conic self (= %s) has no rational " + "singular point" % self) return b[1] def symmetric_matrix(self): @@ -1175,13 +1174,13 @@ def symmetric_matrix(self): a, b, c, d, e, f = self.coefficients() if self.base_ring().characteristic() == 2: if b == 0 and c == 0 and e == 0: - return Matrix([[a,0,0],[0,d,0],[0,0,f]]) - raise ValueError("The conic self (= %s) has no symmetric matrix " \ - "because the base field has characteristic 2" % \ - self) - return Matrix([[ a , b/2, c/2 ], - [ b/2, d , e/2 ], - [ c/2, e/2, f ]]) + return Matrix([[a, 0, 0], [0, d, 0], [0, 0, f]]) + raise ValueError("The conic self (= %s) has no symmetric matrix " + "because the base field has characteristic 2" % + self) + return Matrix([[a, b / 2, c / 2], + [b / 2, d, e / 2], + [c / 2, e / 2, f]]) def upper_triangular_matrix(self): r""" 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 66147eba580..0d1d457f085 100644 --- a/src/sage/schemes/plane_conics/con_rational_function_field.py +++ b/src/sage/schemes/plane_conics/con_rational_function_field.py @@ -138,7 +138,7 @@ def has_rational_point(self, point=False, algorithm='default', sage: F.<i> = QuadraticField(-1) sage: R.<t> = F[] sage: C = Conic([1,i*t,-t^2+4]) - sage: C.has_rational_point(point = True) + sage: C.has_rational_point(point=True) (True, (-t - 2*i : -2*i : 1)) It works on non-diagonal conics as well:: diff --git a/src/sage/schemes/plane_quartics/quartic_constructor.py b/src/sage/schemes/plane_quartics/quartic_constructor.py index 4b459b3c718..a0b5ce3c93a 100644 --- a/src/sage/schemes/plane_quartics/quartic_constructor.py +++ b/src/sage/schemes/plane_quartics/quartic_constructor.py @@ -51,12 +51,12 @@ def QuarticCurve(F, PP=None, check=False): """ if not is_MPolynomial(F): - raise ValueError("Argument F (=%s) must be a multivariate polynomial"%F) + raise ValueError(f"Argument F (={F}) must be a multivariate polynomial") P = F.parent() if not P.ngens() == 3: - raise ValueError("Argument F (=%s) must be a polynomial in 3 variables"%F) - if not(F.is_homogeneous() and F.degree()==4): - raise ValueError("Argument F (=%s) must be a homogeneous polynomial of degree 4"%F) + raise ValueError("Argument F (=%s) must be a polynomial in 3 variables" % F) + if not (F.is_homogeneous() and F.degree() == 4): + raise ValueError("Argument F (=%s) must be a homogeneous polynomial of degree 4" % F) if PP is not None: if not is_ProjectiveSpace(PP) and PP.dimension == 2: diff --git a/src/sage/schemes/product_projective/homset.py b/src/sage/schemes/product_projective/homset.py index 7f4e1de0773..28aa566bb73 100644 --- a/src/sage/schemes/product_projective/homset.py +++ b/src/sage/schemes/product_projective/homset.py @@ -146,7 +146,7 @@ def points(self, **kwds): (1 : 0 : 0 , 0 : 1), (1 : 0 : 0 , 1 : 0), (1 : 0 : 0 , 1 : 1), (1 : 0 : 0 , 2 : 1), (1 : 0 : 1 , 0 : 1), (1 : 0 : 1 , 1 : 0), (1 : 0 : 1 , 1 : 1), (1 : 0 : 1 , 2 : 1), (1 : 1 : 0 , 0 : 1), (1 : 1 : 0 , 1 : 0), (1 : 1 : 0 , 1 : 1), (1 : 1 : 0 , 2 : 1), - (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), + (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), (1 : 2 : 1 , 0 : 1), (1 : 2 : 1 , 1 : 0), (1 : 2 : 1 , 1 : 1), (1 : 2 : 1 , 2 : 1), (2 : 0 : 1 , 0 : 1), (2 : 0 : 1 , 1 : 0), (2 : 0 : 1 , 1 : 1), (2 : 0 : 1 , 2 : 1), (2 : 1 : 0 , 0 : 1), (2 : 1 : 0 , 1 : 0), (2 : 1 : 0 , 1 : 1), (2 : 1 : 0 , 2 : 1), diff --git a/src/sage/schemes/product_projective/morphism.py b/src/sage/schemes/product_projective/morphism.py index 8f2fadbff0f..b558d88b304 100644 --- a/src/sage/schemes/product_projective/morphism.py +++ b/src/sage/schemes/product_projective/morphism.py @@ -117,7 +117,7 @@ def __init__(self, parent, polys, check=True): for m in range(len(splitpolys)): d = dom._degree(splitpolys[m][0]) if not all(d == dom._degree(f) for f in splitpolys[m]): - raise TypeError("polys (=%s) must be multi-homogeneous of the same degrees (by component)"%polys) + raise TypeError("polys (=%s) must be multi-homogeneous of the same degrees (by component)"%polys) else: #we are mapping into some other kind of space target._validate(polys) diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index b75ec0cde05..53e08bcc171 100644 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -122,7 +122,7 @@ def enum_product_projective_rational_field(X, B): (0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 1), (0 : 1 : 0 , 0 : 1), (0 : 1 : 0 , 1 : 1), (1 : -1/2 : 1 , 0 : 1), (1 : -1/2 : 1 , 1 : 1)] """ - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) @@ -134,7 +134,7 @@ def enum_product_projective_rational_field(X, B): m = R.num_components() iters = [ R[i].points_of_bounded_height(bound=B) for i in range(m) ] dim = [R[i].dimension_relative() + 1 for i in range(m)] - + dim_prefix = [0, dim[0]] # prefixes dim list for i in range(1, len(dim)): dim_prefix.append(dim_prefix[i] + dim[i]) @@ -189,7 +189,7 @@ def enum_product_projective_number_field(X, **kwds): This is an implementation of the revised algorithm (Algorithm 4) in [DK2013]_. Algorithm 5 is used for imaginary quadratic fields. - + INPUT: kwds: @@ -225,7 +225,7 @@ def enum_product_projective_number_field(X, **kwds): tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) @@ -281,7 +281,7 @@ def enum_product_projective_finite_field(X): sage: len(enum_product_projective_finite_field(X)) 36 """ - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) @@ -540,5 +540,5 @@ def lift_all_points(): m.append(temp) rat_points = lift_all_points() - + return sorted(rat_points) diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index 7bd6ea88ec1..604adcc8a41 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -45,9 +45,9 @@ from sage.rings.integer import Integer from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields +from sage.categories.commutative_rings import CommutativeRings from sage.rings.polynomial.polydict import ETuple from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme from sage.schemes.generic.ambient_space import AmbientSpace @@ -145,9 +145,9 @@ def ProductProjectiveSpaces(n, R=None, names='x'): X = ProductProjectiveSpaces_ring(N, R, names) X._components = n else: - if not isinstance(n,(list,tuple)): + if not isinstance(n,(list, tuple)): raise ValueError("need list or tuple of dimensions") - if not isinstance(R, CommutativeRing): + if R not in CommutativeRings(): raise ValueError("must be a commutative ring") from sage.structure.category_object import normalize_names n_vars = sum(d + 1 for d in n) @@ -230,7 +230,7 @@ def __init__(self, N, R=QQ, names=None): """ assert isinstance(N, (tuple, list)) N = [Integer(n) for n in N] - assert isinstance(R, CommutativeRing) + assert R in CommutativeRings() if len(N) < 2: raise ValueError("must be at least two components for a product") AmbientSpace.__init__(self, sum(N), R) @@ -1259,19 +1259,19 @@ def __iter__(self): (1 : 0 : 0 , 1 : 0)] """ iters = [iter(T) for T in self._components] - L=[] + L = [] for x in iters: - L.append(next(x)) # put at zero - yield(self(L)) + L.append(next(x)) # put at zero + yield self(L) j = 0 while j < self.num_components(): try: L[j] = next(iters[j]) - yield(self(L)) + yield self(L) j = 0 except StopIteration: iters[j] = iter(self[j]) # reset - L[j] = next(iters[j]) # put at zero + L[j] = next(iters[j]) # put at zero j += 1 def rational_points(self, F=None): diff --git a/src/sage/schemes/projective/proj_bdd_height.py b/src/sage/schemes/projective/proj_bdd_height.py new file mode 100644 index 00000000000..33d5ec1d3bc --- /dev/null +++ b/src/sage/schemes/projective/proj_bdd_height.py @@ -0,0 +1,349 @@ +r""" +Points of bounded height in projective spaces + +This module defines functions to compute points of bounded height of a given +number field with height less than a specified bound in projective spaces. + +Sage functions to list all elements of a given number field with height less +than a specified bound. + +AUTHORS: + +- Jing Guo (2022): initial version based on David Krumm's code + +REFERENCES: + +- [Krumm2016] + +""" + +import itertools + +from math import floor + +from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.rings.rational_field import QQ +from sage.rings.all import RealField +from sage.rings.number_field.unit_group import UnitGroup +from sage.arith.all import gcd +from sage.matrix.constructor import matrix, column_matrix +from sage.libs.pari.all import pari +from sage.modules.free_module_element import vector +from sage.rings.integer import Integer +from sage.geometry.polyhedron.constructor import Polyhedron + + +def QQ_points_of_bounded_height(dim, bound): + r""" + Return an iterator of the points in ``self`` of absolute multiplicative + height of at most ``bound`` in the rational field. + + INPUT: + + - ``dim`` -- a positive integer + + - ``bound`` -- a real number + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height + sage: sorted(list(QQ_points_of_bounded_height(1, 1))) + [(-1 : 1), (0 : 1), (1 : 0), (1 : 1)] + sage: len(list(QQ_points_of_bounded_height(1, 5))) + 40 + + There are no points of negative height:: + + sage: from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height + sage: list(QQ_points_of_bounded_height(1, -3)) + [] + """ + if bound < 1: + return iter(set([])) + + PN = ProjectiveSpace(QQ, dim) + unit_tuples = list(itertools.product([-1, 1], repeat=dim)) + points_of_bounded_height = set([]) + increasing_tuples = itertools.combinations_with_replacement(range(floor(bound + 1)), dim + 1) + for t in increasing_tuples: + if gcd(t) == 1: + for p in itertools.permutations(t): + for u in unit_tuples: + point = PN([a*b for a, b in zip(u, p)] + [p[dim]]) + if point not in points_of_bounded_height: + points_of_bounded_height.add(point) + yield point + + +def IQ_points_of_bounded_height(PN, K, dim, bound): + r""" + Return an iterator of the points in ``self`` of absolute multiplicative + height of at most ``bound`` in the imaginary quadratic field ``K``. + + INPUT: + + - ``PN`` -- a projective space + + - ``K`` -- a number field + + - ``dim`` -- a positive interger + + - ``bound`` -- a real number + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import IQ_points_of_bounded_height + sage: CF.<a> = CyclotomicField(3) + sage: P.<x,y,z> = ProjectiveSpace(CF, 2) + sage: len(list(IQ_points_of_bounded_height(P, CF, 2, -1))) + 0 + sage: len(list(IQ_points_of_bounded_height(P, CF, 2, 1))) + 57 + """ + if bound < 1: + return iter([]) + + unit_tuples = list(itertools.product(K.roots_of_unity(), repeat=dim)) + + class_group_ideals = [c.ideal() for c in K.class_group()] + class_group_ideal_norms = [i.norm() for i in class_group_ideals] + class_number = len(class_group_ideals) + + possible_norm_set = set([]) + for i in range(class_number): + for k in range(1, floor(bound + 1)): + possible_norm_set.add(k*class_group_ideal_norms[i]) + + coordinate_space = dict() + coordinate_space[0] = [K(0)] + for m in possible_norm_set: + coordinate_space[m] = K.elements_of_norm(m) + + for i in range(class_number): + a = class_group_ideals[i] + a_norm = class_group_ideal_norms[i] + a_norm_bound = bound * a_norm + a_coordinates = [] + + for m in coordinate_space: + if m <= a_norm_bound: + for x in coordinate_space[m]: + if x in a: + a_coordinates.append(x) + + points_in_class_a = set([]) + t = len(a_coordinates) - 1 + increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) + for index_tuple in increasing_tuples: + point_coordinates = [a_coordinates[i] for i in index_tuple] + if a == K.ideal(point_coordinates): + for p in itertools.permutations(point_coordinates): + for u in unit_tuples: + point = PN([i*j for i, j in zip(u, p)] + [p[dim]]) + if point not in points_in_class_a: + points_in_class_a.add(point) + yield point + + +def points_of_bounded_height(PN, K, dim, bound, prec=53): + r""" + Return an iterator of the points in ``K`` with dimension ``dim`` of + absolute multiplicative height of at most ``bound``. + + ALGORITHM: + + This is an implementation of Algorithm 6 in [Krumm2016]_. + + INPUT: + + - ``PN`` -- a projective space + + - ``K`` -- a number field + + - ``dim`` -- a positive interger + + - ``bound`` -- a real number + + - ``prec`` -- (default: 53) a positive integer + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import points_of_bounded_height + sage: K.<a> = NumberField(x^3 - 7) + sage: P.<x,y,z> = ProjectiveSpace(K, 2) + sage: len(list(points_of_bounded_height(P, K, 2, 1))) + 13 + """ + if bound < 1: + return iter([]) + + r1, r2 = K.signature() + r = r1 + r2 - 1 + + if K.is_relative(): + K_degree = K.relative_degree() + else: + K_degree = K.degree() + + roots_of_unity = K.roots_of_unity() + unit_tuples = list(itertools.product(roots_of_unity, repeat=dim)) + + log_embed = K.logarithmic_embedding() + + Reals = RealField(prec) + logB = Reals(bound).log() + + class_group_ideals = [c.ideal() for c in K.class_group()] + class_number = len(class_group_ideals) + + if K.is_relative(): + class_group_ideal_norms = [i.absolute_norm() for i in class_group_ideals] + else: + class_group_ideal_norms = [i.norm() for i in class_group_ideals] + + norm_bound = bound * max(class_group_ideal_norms) + fundamental_units = UnitGroup(K).fundamental_units() + fund_unit_logs = list(map(log_embed, fundamental_units)) + mat = column_matrix(fund_unit_logs) + + test_matrix = mat + try: + test_matrix.change_ring(QQ) + except ValueError: + raise ValueError('prec too low.') + + cut_fund_unit_logs = mat.delete_rows([r]) + lll_fund_units = [] + for c in pari(cut_fund_unit_logs).qflll().python(): + new_unit = 1 + for i in range(r): + new_unit *= fundamental_units[i]**c[i] + lll_fund_units.append(new_unit) + fundamental_units = lll_fund_units + fund_unit_logs = list(map(log_embed, fundamental_units)) + + possible_norm_set = set([]) + for i in range(class_number): + for k in range(1, floor(bound + 1)): + possible_norm_set.add(k*class_group_ideal_norms[i]) + + principal_ideal_gens = dict() + negative_norm_units = K.elements_of_norm(-1) + if len(negative_norm_units) == 0: + for m in possible_norm_set: + principal_ideal_gens[m] = K.elements_of_norm(m) + K.elements_of_norm(-m) + else: + for m in possible_norm_set: + principal_ideal_gens[m] = K.elements_of_norm(m) + + pr_ideal_gen_logs = dict() + for key in principal_ideal_gens: + for y in principal_ideal_gens[key]: + pr_ideal_gen_logs[y] = log_embed(y) + + fund_parallelotope_vertices = [] + for coefficient_tuple in itertools.product([-1/2, 1/2], repeat=r): + vertex = sum([coefficient_tuple[i]*fund_unit_logs[i] for i in range(r)]) + fund_parallelotope_vertices.append(vertex) + + D_numbers = [] + for v in range(r + 1): + D_numbers.append(max([vertex[v] for vertex in fund_parallelotope_vertices])) + + A_numbers = [] + for v in range(r + 1): + A_numbers.append(min([pr_ideal_gen_logs[y][v] for y in pr_ideal_gen_logs])) + + aux_constant = (1/K_degree) * Reals(norm_bound).log() + + L_numbers = [] + for v in range(r1): + L_numbers.append(aux_constant + D_numbers[v] - A_numbers[v]) + for v in range(r1, r + 1): + L_numbers.append(2*aux_constant + D_numbers[v] - A_numbers[v]) + L_numbers = vector(L_numbers).change_ring(QQ) + + T = column_matrix(fund_unit_logs).delete_rows([r]).change_ring(QQ) + + # insert_row only takes integers, see https://trac.sagemath.org/ticket/11328 + M = ((-1)*matrix.identity(r)).insert_row(r, [Integer(1) for i in range(r)]) + M = M.transpose().insert_row(0, [Integer(0) for i in range(r + 1)]).transpose() + M = M.change_ring(QQ) + M.set_column(0, L_numbers) + vertices = map(vector, Polyhedron(ieqs=list(M)).vertices()) + + T_it = T.inverse().transpose() + unit_polytope = Polyhedron([v*T_it for v in vertices]) + + coordinate_space = dict() + coordinate_space[0] = [[K(0), log_embed(0)]] + int_points = unit_polytope.integral_points() + + units_with_logs = dict() + for n in int_points: + new_unit = 1 + for j in range(r): + new_unit *= fundamental_units[j]**n[j] + new_unit_log = sum([n[j]*fund_unit_logs[j] for j in range(r)]) + units_with_logs[n] = [new_unit, new_unit_log] + + for norm in principal_ideal_gens: + coordinate_list = [] + for y in principal_ideal_gens[norm]: + for n in int_points: + unit, unit_log = units_with_logs[n] + y_log = pr_ideal_gen_logs[y] + g_log = unit_log + y_log + bool1 = all(g_log[i] <= aux_constant + D_numbers[i] for i in range(r1)) + bool2 = all(g_log[j] <= 2 * aux_constant + D_numbers[j] for j in range(r1, r + 1)) + if bool1 and bool2: + g = unit * y + coordinate_list.append([g, g_log]) + if len(coordinate_list) > 0: + coordinate_space[norm] = coordinate_list + + for m in range(class_number): + a = class_group_ideals[m] + a_norm = class_group_ideal_norms[m] + log_a_norm = Reals(a_norm).log() + a_const = (logB + log_a_norm)/K_degree + a_coordinates = [] + + for k in range(floor(bound + 1)): + norm = k * a_norm + if norm in coordinate_space: + for pair in coordinate_space[norm]: + g, g_log = pair + if g in a: + bool1 = all(g_log[i] <= a_const + D_numbers[i] for i in range(r1)) + bool2 = all(g_log[j] <= 2 * a_const + D_numbers[j] for j in range(r1, r + 1)) + if bool1 and bool2: + a_coordinates.append(pair) + + t = len(a_coordinates) - 1 + points_in_class_a = set([]) + increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) + log_arch_height_bound = logB + log_a_norm + for index_tuple in increasing_tuples: + point_coordinates = [a_coordinates[i][0] for i in index_tuple] + point_coordinate_logs = [a_coordinates[i][1] for i in index_tuple] + log_arch_height = sum([max([x[i] for x in point_coordinate_logs]) for i in range(r + 1)]) + if log_arch_height <= log_arch_height_bound and a == K.ideal(point_coordinates): + for p in itertools.permutations(point_coordinates): + for u in unit_tuples: + point = PN([i*j for i, j in zip(u, p)] + [p[dim]]) + if point not in points_in_class_a: + points_in_class_a.add(point) + yield point diff --git a/src/sage/schemes/projective/projective_homset.py b/src/sage/schemes/projective/projective_homset.py index 0f7b6abce52..3ffd4a6b6e4 100644 --- a/src/sage/schemes/projective/projective_homset.py +++ b/src/sage/schemes/projective/projective_homset.py @@ -27,21 +27,20 @@ - Ben Hutz (2018): add numerical point support """ - -#***************************************************************************** -# Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com> -# Copyright (C) 2006 William Stein <wstein@gmail.com> +# ***************************************************************************** +# Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com> +# Copyright (C) 2006 William Stein <wstein@gmail.com> # -# 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/ -#***************************************************************************** +# 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/ +# ***************************************************************************** from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.rings.cc import CC -from sage.schemes.generic.homset import SchemeHomset_points +from sage.schemes.generic.homset import SchemeHomset_points, SchemeHomset_generic from sage.misc.verbose import verbose @@ -53,9 +52,11 @@ from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme from copy import copy -#******************************************************************* -# Projective varieties -#******************************************************************* + +# ******************************************************************* +# Projective varieties +# ******************************************************************* + class SchemeHomset_points_projective_field(SchemeHomset_points): """ Set of rational points of a projective variety over a field. @@ -124,7 +125,7 @@ def points(self, **kwds): sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(bound=1.8)) - 381 + 309 :: @@ -459,6 +460,7 @@ def numerical_points(self, F=None, **kwds): return rat_points raise NotImplementedError('numerical approximation of points only for dimension 0 subschemes') + class SchemeHomset_points_projective_ring(SchemeHomset_points): """ Set of rational points of a projective variety over a commutative ring. @@ -527,9 +529,43 @@ def points(self, B=0): raise TypeError("unable to enumerate points over %s"%R) -#******************************************************************* -# Abelian varieties -#******************************************************************* +class SchemeHomset_polynomial_projective_space(SchemeHomset_generic): + """ + Set of morphisms of a projective space. + + EXAMPLES:: + + sage: P.<x,y,z> = ProjectiveSpace(2, QQ) + sage: Hom(P, P) + Set of morphisms + From: Projective Space of dimension 2 over Rational Field + To: Projective Space of dimension 2 over Rational Field + """ + def identity(self): + """ + Return the identity morphism of this hom-set. + + EXAMPLES:: + + sage: P.<x,y,z> = ProjectiveSpace(2, QQ) + sage: Hom(P, P) + Set of morphisms + From: Projective Space of dimension 2 over Rational Field + To: Projective Space of dimension 2 over Rational Field + sage: _.identity() + Scheme endomorphism of Projective Space of dimension 2 over Rational Field + Defn: Identity map + """ + if self.is_endomorphism_set(): + from sage.schemes.generic.morphism import SchemeMorphism_polynomial_id + return SchemeMorphism_polynomial_id(self.domain()) + raise TypeError("identity map is only defined for endomorphisms") + + +# ******************************************************************* +# Abelian varieties +# ******************************************************************* + class SchemeHomset_points_abelian_variety_field(SchemeHomset_points_projective_field): r""" Set of rational points of an Abelian variety. diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index fb3c7d4781b..c49dfbf41ca 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -83,6 +83,7 @@ from sage.rings.quotient_ring import QuotientRing_generic from sage.rings.rational_field import QQ from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.categories.finite_fields import FiniteFields from sage.categories.number_fields import NumberFields @@ -2280,6 +2281,41 @@ def __call__(self, x): pass raise ValueError('the morphism is not defined at this point') + def __eq__(self, other): + """ + EXAMPLES:: + + sage: R.<x,y,z> = QQ[] + sage: C = Curve(7*x^2 + 2*y*z + z^2) # conic + sage: f, g = C.parametrization() + sage: f*g == C.identity_morphism() + True + + sage: C = Curve(x^2 + y^2 - z^2) + sage: P.<u, v> = ProjectiveSpace(QQ, 1) + sage: f = C.hom([x + z, y], P) + sage: g = C.hom([y, z - x], P) + sage: f == g + True + sage: h = C.hom([z, x - y], P) + sage: f == h + False + """ + Y = self.codomain() + + if not isinstance(other, SchemeMorphism_polynomial): + return False + if self.domain() != other.domain() or Y != other.codomain(): + return False + + if not Y.is_projective(): # codomain is affine + e = Y.projective_embedding(0) + return (e * self) == (e * other) + + R = self.domain().coordinate_ring() + mat = matrix([self.defining_polynomials(), other.defining_polynomials()]) + return all(R(minor).is_zero() for minor in mat.minors(2)) + @cached_method def representatives(self): """ @@ -2669,7 +2705,7 @@ def projective_degrees(self): sage: k = GF(11) sage: E = EllipticCurve(k,[1,1]) sage: Q = E(6,5) - sage: phi = E.multiplication_by_m_isogeny(2) + sage: phi = E.scalar_multiplication(2) sage: mor = phi.as_morphism() sage: mor.projective_degrees() (12, 3) @@ -2711,7 +2747,7 @@ def degree(self): sage: k = GF(11) sage: E = EllipticCurve(k,[1,1]) sage: Q = E(6,5) - sage: phi = E.multiplication_by_m_isogeny(2) + sage: phi = E.scalar_multiplication(2) sage: mor = phi.as_morphism() sage: mor.degree() 4 diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 95547692e86..0217a943d09 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -165,7 +165,6 @@ def __init__(self, X, v, check=True): from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from sage.rings.ring import CommutativeRing d = X.codomain().ambient_space().ngens() - if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) else: diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index 93789be8d65..738f28cb993 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -167,7 +167,7 @@ def enum_projective_number_field(X, **kwds): This is an implementation of the revised algorithm (Algorithm 4) in [DK2013]_. Algorithm 5 is used for imaginary quadratic fields. - + INPUT: kwds: @@ -191,8 +191,7 @@ def enum_projective_number_field(X, **kwds): sage: P.<x,y,z> = ProjectiveSpace(K, 2) sage: X = P.subscheme([x - y]) sage: enum_projective_number_field(X(K), bound=RR(5^(1/3)), prec=2^10) - [(0 : 0 : 1), (-1 : -1 : 1), (1 : 1 : 1), (-1/5*v^2 : -1/5*v^2 : 1), (-v : -v : 1), - (1/5*v^2 : 1/5*v^2 : 1), (v : v : 1), (1 : 1 : 0)] + [(0 : 0 : 1), (1 : 1 : 0), (-1 : -1 : 1), (1 : 1 : 1)] :: diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index ed44ebcd6aa..670304af237 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -80,7 +80,6 @@ # **************************************************************************** from sage.arith.misc import gcd, binomial -from sage.arith.srange import srange from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.integer import Integer @@ -88,10 +87,10 @@ from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.ring import CommutativeRing from sage.rings.rational_field import is_RationalField from sage.categories.fields import Fields +from sage.categories.rings import Rings from sage.categories.number_fields import NumberFields from sage.categories.homset import Hom from sage.categories.map import Map @@ -112,7 +111,8 @@ from sage.modules.free_module_element import prepare from sage.schemes.generic.ambient_space import AmbientSpace from sage.schemes.projective.projective_homset import (SchemeHomset_points_projective_ring, - SchemeHomset_points_projective_field) + SchemeHomset_points_projective_field, + SchemeHomset_polynomial_projective_space) from sage.schemes.projective.projective_point import (SchemeMorphism_point_projective_ring, SchemeMorphism_point_projective_field, SchemeMorphism_point_projective_finite_field) @@ -123,6 +123,8 @@ # for better efficiency _Fields = Fields() +_Rings = Rings() +_CommRings = _Rings.Commutative() def is_ProjectiveSpace(x): @@ -260,10 +262,9 @@ def ProjectiveSpace(n, R=None, names=None): return ProjectiveSpace_rational_field(n, R, names) else: return ProjectiveSpace_field(n, R, names) - elif isinstance(R, CommutativeRing): + elif R in _CommRings: return ProjectiveSpace_ring(n, R, names) - else: - raise TypeError("R (=%s) must be a commutative ring" % R) + raise TypeError("R (=%s) must be a commutative ring" % R) class ProjectiveSpace_ring(UniqueRepresentation, AmbientSpace): @@ -459,7 +460,7 @@ def _validate(self, polynomials): sage: P._validate([x*y - z^2, x]) [x*y - z^2, x] - :: + :: sage: P.<x, y, z> = ProjectiveSpace(2, ZZ) sage: P._validate((x*y - z, x)) @@ -467,7 +468,7 @@ def _validate(self, polynomials): ... TypeError: x*y - z is not a homogeneous polynomial - :: + :: sage: P.<x, y, z> = ProjectiveSpace(2, ZZ) sage: P._validate(x*y - z) @@ -741,6 +742,20 @@ def _morphism(self, *args, **kwds): """ return SchemeMorphism_polynomial_projective_space(*args, **kwds) + def _homset(self, *args, **kwds): + """ ii + Construct the Hom-set + + EXAMPLES:: + + sage: P.<x,y,z> = ProjectiveSpace(2, QQ) + sage: Hom(P, P) + Set of morphisms + From: Projective Space of dimension 2 over Rational Field + To: Projective Space of dimension 2 over Rational Field + """ + return SchemeHomset_polynomial_projective_space(*args, **kwds) + def _point_homset(self, *args, **kwds): """ Construct a point Hom-set. @@ -1378,7 +1393,7 @@ def point_transformation_matrix(self, points_source, points_target, normalize=Tr sage: P1.<a,b,c>=ProjectiveSpace(RR, 2) sage: points_source=[P1([1, 4, 1]), P1([1, 2, 2]), P1([3, 5, 1]), P1([1, -1, 1])] sage: points_target=[P1([5, -2, 7]), P1([3, -2, 3]), P1([6, -5, 9]), P1([3, 6, 7])] - sage: P1.point_transformation_matrix(points_source, points_target) + sage: P1.point_transformation_matrix(points_source, points_target) # abs tol 1e-13 [-0.0619047619047597 -0.609523809523810 -0.119047619047621] [ 0.853968253968253 0.0380952380952380 0.0412698412698421] [ -0.214285714285712 -0.933333333333333 0.280952380952379] @@ -1547,8 +1562,8 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x) sage: plane2 = P.subscheme(y) sage: m = P.hyperplane_transformation_matrix(plane1, plane2); m - [-1 -1] - [ 1 0] + [0 1] + [1 0] sage: plane2(m*P((0,1))) (1 : 0) @@ -1558,10 +1573,10 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + 2*y + z) sage: plane2 = P.subscheme(2*x + y + z) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ -3 0 0 0] - [ 9 6 0 0] - [-3/2 -3 3/2 0] - [-1/2 -1 -1/2 1] + [1 0 0 0] + [0 4 0 0] + [0 0 2 0] + [0 0 0 1] :: @@ -1569,8 +1584,8 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + y) sage: plane2 = P.subscheme(y) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ 1 0] - [-1 -1] + [-1 0] + [ 1 1] :: @@ -1580,9 +1595,9 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane2 = P.subscheme(x + v*y + v*z) sage: m = P.hyperplane_transformation_matrix(plane1, plane2) sage: m - [ -6/7*v - 2/7 0 0] - [ 2/7*v + 10/7 -4/7*v + 8/7 0] - [ -4/7*v + 1/7 -10/7*v - 8/7 1] + [ v 0 0] + [ 0 -2*v 0] + [ 0 0 1] :: @@ -1592,10 +1607,10 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(k*x + 2*k*y + z) sage: plane2 = P.subscheme(7*k*x + y + 9*z) sage: m = P.hyperplane_transformation_matrix(plane1, plane2); m - [ 297/410*k + 279/410 0 0 0] - [-3609/410*k + 4437/410 -1656/205*k + 2358/205 0 0] - [ 511/410*k - 24/205 511/205*k - 48/205 -107/205*k + 327/410 0] - [ 83/410*k - 107/205 83/205*k - 214/205 107/205*k + 83/410 1] + [ 1 0 0 0] + [ 0 14*k 0 0] + [ 0 0 7/9 0] + [ 0 0 0 1] :: @@ -1627,9 +1642,9 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + 9*t*y + z) sage: plane2 = P.subscheme(x + z) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ -1/9*t -t^2 0] - [ -t^2 + 1/9*t 0 0] - [ 1/81 1/9*t -1/9*t + 1/81] + [ 1 9*t 0] + [ 1 0 0] + [ 0 0 1] TESTS:: @@ -1698,7 +1713,7 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): source_points.append(self(point)) base_list = [list(s) for s in source_points] elif len(source_points) == N + 1: - Ms = matrix(base_list + [point]) + Ms = matrix(base_list + [point.change_ring(self.base_ring())]) if not any([m == 0 for m in Ms.minors(N + 1)]): source_points.append(self(point)) break @@ -1804,6 +1819,7 @@ def is_linearly_independent(self, points, n=None): break return linearly_independent + class ProjectiveSpace_field(ProjectiveSpace_ring): def _point_homset(self, *args, **kwds): """ @@ -1852,18 +1868,12 @@ def _morphism(self, *args, **kwds): def points_of_bounded_height(self, **kwds): r""" - Returns an iterator of the points in self of absolute height of at most the given bound. + Return an iterator of the points in ``self`` of absolute multiplicative + height of at most the given bound. - Bound check is strict for the rational field. Requires self to be projective space - over a number field. Uses the - Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for - computing algebraic numbers up to a given height [DK2013]_. + ALGORITHM: - The algorithm requires floating point arithmetic, so the user is - allowed to specify the precision for such calculations. - Additionally, due to floating point issues, points - slightly larger than the bound may be returned. This can be controlled - by lowering the tolerance. + This is an implementation of Algorithm 6 in [Krumm2016]_. INPUT: @@ -1871,74 +1881,113 @@ def points_of_bounded_height(self, **kwds): - ``bound`` - a real number - - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - - - ``precision`` - the precision to use for computing the elements of bounded height of number fields. + - ``precision`` - (default: 53) a positive integer OUTPUT: - - an iterator of points in this space + - an iterator of points of bounded height EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ, 1) - sage: sorted(list(P.points_of_bounded_height(bound=5))) - [(0 : 1), (1 : -5), (1 : -4), (1 : -3), (1 : -2), (1 : -1), (1 : 0), - (1 : 1), (1 : 2), (1 : 3), (1 : 4), (1 : 5), (2 : -5), (2 : -3), - (2 : -1), (2 : 1), (2 : 3), (2 : 5), (3 : -5), (3 : -4), (3 : -2), - (3 : -1), (3 : 1), (3 : 2), (3 : 4), (3 : 5), (4 : -5), (4 : -3), - (4 : -1), (4 : 1), (4 : 3), (4 : 5), (5 : -4), (5 : -3), (5 : -2), - (5 : -1), (5 : 1), (5 : 2), (5 : 3), (5 : 4)] + sage: sorted(list(P.points_of_bounded_height(bound=2))) + [(-2 : 1), (-1 : 1), (-1/2 : 1), (0 : 1), + (1/2 : 1), (1 : 0), (1 : 1), (2 : 1)] :: sage: u = QQ['u'].0 sage: P.<x,y,z> = ProjectiveSpace(NumberField(u^2 - 2, 'v'), 2) - sage: len(list(P.points_of_bounded_height(bound=1.5, tolerance=0.1))) + sage: len(list(P.points_of_bounded_height(bound=2))) + 265 + + :: + + sage: CF.<a> = CyclotomicField(3) + sage: R.<x> = CF[] + sage: L.<l> = CF.extension(x^3 + 2) + sage: Q.<x,y> = ProjectiveSpace(L, 1) + sage: sorted(list(Q.points_of_bounded_height(bound=1))) + [(0 : 1), (1 : 0), (a + 1 : 1), (a : 1), + (-1 : 1), (-a - 1 : 1), (-a : 1), (1 : 1)] + + :: + + sage: R.<x> = QQ[] + sage: F.<a> = NumberField(x^4 - 8*x^2 + 3) + sage: P.<x,y,z> = ProjectiveSpace(F, 2) + sage: all([exp(p.global_height()) <= 1 for p in P.points_of_bounded_height(bound=1)]) + True + + :: + + sage: K.<a> = CyclotomicField(3) + sage: P.<x,y,z> = ProjectiveSpace(K, 2) + sage: len(list(P.points_of_bounded_height(bound=1))) 57 + + :: + + sage: u = QQ['u'].0 + sage: K.<k> = NumberField(u^2 - 2) + sage: P.<x,y> = ProjectiveSpace(K, 1) + sage: len(list(P.points_of_bounded_height(bound=2))) + 24 + + :: + + sage: R.<x> = QQ[] + sage: K.<k> = NumberField(x^4 - 8*x^2 + 3) + sage: P.<x,y> = ProjectiveSpace(K, 1) + sage: len(list(P.points_of_bounded_height(bound=2))) + 108 + + :: + + sage: R.<x> = QQ[] + sage: K.<v> = NumberField(x^5 + x^3 + 1) + sage: P.<x,y,z> = ProjectiveSpace(K, 2) + sage: L = P.points_of_bounded_height(bound=1.2) + sage: len(list(L)) + 109 """ - if is_RationalField(self.base_ring()): - ftype = False # stores whether the field is a number field or the rational field - elif self.base_ring() in NumberFields(): # true for rational field as well, so check is_RationalField first - ftype = True + from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height, IQ_points_of_bounded_height, points_of_bounded_height + + R = self.base_ring() + + # whether the field is a number field or the rational field + if is_RationalField(R): + field_type = False + elif R in NumberFields(): + # true for rational field as well, so check is_RationalField first + field_type = True else: raise NotImplementedError("self must be projective space over a number field") bound = kwds.pop('bound') - B = bound**(self.base_ring().absolute_degree()) # convert to relative height + prec = kwds.pop('precision', 53) - n = self.dimension_relative() - R = self.base_ring() - if ftype: - zero = R.zero() - i = n - while not i < 0: - P = [zero for _ in range(i)] + [R.one()] - P += [zero for _ in range(n - i)] - yield self(P) - tol = kwds.pop('tolerance', 1e-2) - prec = kwds.pop('precision', 53) - iters = [R.elements_of_bounded_height(bound=B, tolerance=tol, precision=prec) for _ in range(i)] - for x in iters: - next(x) # put at zero - j = 0 - while j < i: - try: - P[j] = next(iters[j]) - yield self(P) - j = 0 - except StopIteration: - iters[j] = R.elements_of_bounded_height(bound=B, tolerance=tol, precision=prec) # reset - next(iters[j]) # put at zero - P[j] = zero - j += 1 - i -= 1 - else: # base ring QQ - zero = (0,) * (n + 1) - for c in cartesian_product_iterator([srange(-B, B + 1) - for _ in range(n + 1)]): - if gcd(c) == 1 and c > zero: - yield self.point(c, check=False) + # Convert between absolute and relative height for calling Krumm's algorithm + bound = bound**R.absolute_degree() + + dim = self.dimension_relative() + + if field_type: + # for imaginary quadratic field + r1, r2 = R.signature() + r = r1 + r2 - 1 + + if R.is_relative(): + deg = R.relative_degree() + else: + deg = R.degree() + + if deg == 2 and r == 0: + return IQ_points_of_bounded_height(self, R, dim, bound) + + return points_of_bounded_height(self, R, dim, bound, prec) + else: + return QQ_points_of_bounded_height(dim, bound) def subscheme_from_Chow_form(self, Ch, dim): r""" @@ -2100,6 +2149,7 @@ def line_through(self, p, q): m = matrix(3, list(self.gens()) + list(p) + list(q)) return Curve([f for f in m.minors(3) if f]) + class ProjectiveSpace_finite_field(ProjectiveSpace_field): def _point(self, *args, **kwds): """ diff --git a/src/sage/schemes/projective/projective_subscheme.py b/src/sage/schemes/projective/projective_subscheme.py index 3c0faa498a6..8a93cb30d83 100644 --- a/src/sage/schemes/projective/projective_subscheme.py +++ b/src/sage/schemes/projective/projective_subscheme.py @@ -110,7 +110,7 @@ def point(self, v, check=True): x^2 + 2*y^2 """ from sage.rings.infinity import infinity - if v is infinity or\ + if v is infinity or\ (isinstance(v, (list,tuple)) and len(v) == 1 and v[0] is infinity): if self.ambient_space().dimension_relative() > 1: raise ValueError("%s not well defined in dimension > 1"%v) @@ -982,7 +982,7 @@ def dual(self): from sage.libs.singular.function_factory import ff K = self.base_ring() - if not(is_RationalField(K) or is_FiniteField(K)): + if not (is_RationalField(K) or is_FiniteField(K)): raise NotImplementedError("base ring must be QQ or a finite field") I = self.defining_ideal() m = I.ngens() @@ -1163,19 +1163,19 @@ def multiplicity(self, P): sage: C.multiplicity(Q) 8 """ - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("subscheme must be defined over a field") # check whether P is a point on this subscheme try: P = self(P) except TypeError: - raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + raise TypeError("(=%s) is not a point on (=%s)" % (P,self)) # find an affine chart of the ambient space of self that contains P i = 0 - while(P[i] == 0): - i = i + 1 + while P[i] == 0: + i += 1 X = self.affine_patch(i) return X.multiplicity(X(P.dehomogenize(i))) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index f85a7295dbc..b219cb975e2 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -268,9 +268,9 @@ def numerical_inverse(C): ....: -0.091587 + 0.19276*I, ....: 3.9443e-31 + 0.38552*I]) sage: from sage.schemes.riemann_surfaces.riemann_surface import numerical_inverse - sage: max(abs(c) for c in (C^(-1)*C-C^0).list()) < 1e-10 - False - sage: max(abs(c) for c in (numerical_inverse(C)*C-C^0).list()) < 1e-10 + sage: 3e-16 < (C^-1*C-C^0).norm() < 1e-15 + True + sage: (numerical_inverse(C)*C-C^0).norm() < 3e-16 True """ R = C.parent() @@ -478,7 +478,7 @@ def reparameterize_differential_minpoly(minpoly, z0): return mt -class RiemannSurface(object): +class RiemannSurface(): r""" Construct a Riemann Surface. This is specified by the zeroes of a bivariate polynomial with rational coefficients `f(z,w) = 0`. @@ -3082,7 +3082,7 @@ def initialise(z, i): # converge happens silently, thus allowing the user to get *an* # answer out of the integration, but numerical imprecision is to be # expected. As such, we set the maximum number of steps in the sequence - # of DE integrations to be lower in the latter case. + # of DE integrations to be lower in the latter case. if raise_errors: n_steps = self._prec - 1 else: @@ -3240,9 +3240,9 @@ def fv(hj, previous_estimate): Nh *= 2 # Note that throughout this loop there is a return statement, intended # to be activated when the sequence of integral approximations is - # deemed to have converged by the heuristic error. If this has no + # deemed to have converged by the heuristic error. If this has no # happened by the time we have gone through the process n_steps times, - # we have one final error handle. Again, this will throw an error if + # we have one final error handle. Again, this will throw an error if # the raise_errors flag is true, but will just return the answer otherwise. if raise_errors: raise ConvergenceError("Newton iteration fails to converge") diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index f028b1d10af..8647225c77b 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -1402,7 +1402,7 @@ def _sheaf_cohomology(self, cplx): degree = h[0] + 1 cohomology_dim = h[1].dimension() if degree > d or degree < 0: - assert(cohomology_dim == 0) + assert cohomology_dim == 0 continue HH_list[degree] = cohomology_dim diff --git a/src/sage/schemes/toric/divisor_class.pyx b/src/sage/schemes/toric/divisor_class.pyx index 1fde30da963..50e23646087 100644 --- a/src/sage/schemes/toric/divisor_class.pyx +++ b/src/sage/schemes/toric/divisor_class.pyx @@ -329,7 +329,7 @@ def _ToricRationalDivisorClass_unpickle_v1(parent, entries, v._init(degree, parent) cdef Rational z cdef Py_ssize_t i - for i from 0 <= i < degree: + for i in range(degree): z = Rational(entries[i]) mpq_set(v._entries[i], z.value) v._is_immutable = not is_mutable diff --git a/src/sage/schemes/toric/homset.py b/src/sage/schemes/toric/homset.py index b5c97e10f83..a057ecdf1ab 100644 --- a/src/sage/schemes/toric/homset.py +++ b/src/sage/schemes/toric/homset.py @@ -84,7 +84,7 @@ From: Projective Space of dimension 2 over Rational Field To: 2-d CPR-Fano toric variety covered by 3 affine patches sage: type(native_to_toric) - <class 'sage.schemes.generic.homset.SchemeHomset_generic_with_category'> + <class 'sage.schemes.projective.projective_homset.SchemeHomset_polynomial_projective_space_with_category'> sage: native_to_toric([u^2, v^2, w^2]) Scheme morphism: From: Projective Space of dimension 2 over Rational Field diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index 1c31df6c495..e5902d83ffd 100644 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -420,9 +420,9 @@ def subtract(e, power): def divide_by_x_n(p): d_old = p.dict() - power = min([ e[0] for e in d_old.keys() ]) - d_new = dict((subtract(exponent, power), coefficient) - for exponent, coefficient in d_old.items()) + power = min(e[0] for e in d_old) + d_new = {subtract(exponent, power): coefficient + for exponent, coefficient in d_old.items()} return p.parent()(d_new) basis = [divide_by_x_n(b) for b in basis] quotient = ring.ideal(basis) diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index 984243c8934..c8ecbf6a587 100644 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -1391,7 +1391,7 @@ def WP(self, *q, **kw): Closed subscheme of 2-d toric variety covered by 3 affine patches defined by: -x^6 + z^6 + y^2 """ - if len(q)==1: + if len(q) == 1: # tuples and lists of weights are acceptable input if isinstance(q[0], (list, tuple)): q = q[0] @@ -1403,7 +1403,7 @@ def WP(self, *q, **kw): for i in range(m): try: q[i] = ZZ(q[i]) - except(TypeError): + except TypeError: raise TypeError("the weights (=%s) must be integers" % q) if q[i] <= 0: raise ValueError("the weights (=%s) must be positive integers" % q) diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index 02cde67d017..eb6b04f4b9c 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -237,7 +237,7 @@ cdef class DisjointSet_class(SageObject): sage: e == d True """ - from sage.sets.all import Set + from sage.sets.set import Set s = Set(map(Set, self.root_to_elements_dict().values())) try: t = Set(map(Set, other.root_to_elements_dict().values())) @@ -887,7 +887,6 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): {{0}, {1, 2, 3, 4}} sage: d.to_digraph().edges(sort=True) [(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)] - """ d = {} for i from 0 <= i < self.cardinality(): @@ -896,4 +895,3 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): d[e] = [p] from sage.graphs.graph import DiGraph return DiGraph(d) - diff --git a/src/sage/sets/disjoint_union_enumerated_sets.py b/src/sage/sets/disjoint_union_enumerated_sets.py index 1820a79256f..c1276dc9edf 100644 --- a/src/sage/sets/disjoint_union_enumerated_sets.py +++ b/src/sage/sets/disjoint_union_enumerated_sets.py @@ -542,11 +542,11 @@ def _element_constructor_facade(self, el): sage: p = X._element_constructor_((0, [])) # indirect doctest sage: p[1].parent() Partitions of the integer 0 - + Test that facade parents can create and properly access elements that are tuples (fixed by :trac:`22382`):: - sage: f = lambda mu: cartesian_product([mu.standard_tableaux(), + sage: f = lambda mu: cartesian_product([mu.standard_tableaux(), ....: mu.standard_tableaux()]) sage: tabs = DisjointUnionEnumeratedSets(Family(Partitions(4), f)) sage: s = StandardTableau([[1,3],[2,4]]) @@ -602,6 +602,4 @@ def Element(self): """ if not self._facade: return ElementWrapper - else: - return NotImplemented - + return NotImplemented diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index c1bf734381c..10c59a02490 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -1380,6 +1380,23 @@ def __setstate__(self, state): """ self.__init__(state['_enumeration']) + def map(self, f, name=None): + r""" + Return the family `( f(\mathtt{self}[i]) )_{i \in I}`, + where `I` is the index set of ``self``. + + The result is again a :class:`TrivialFamily`. + + EXAMPLES:: + + sage: from sage.sets.family import TrivialFamily + sage: f = TrivialFamily(['a', 'b', 'd']) + sage: g = f.map(lambda x: x + '1'); g + Family ('a1', 'b1', 'd1') + """ + # tuple([... for ...]) is faster than tuple(... for ...) + return Family(tuple([f(x) for x in self._enumeration]), name=name) + from sage.sets.non_negative_integers import NonNegativeIntegers from sage.rings.infinity import Infinity diff --git a/src/sage/sets/finite_enumerated_set.py b/src/sage/sets/finite_enumerated_set.py index d8745d13844..a2f5dc759ec 100644 --- a/src/sage/sets/finite_enumerated_set.py +++ b/src/sage/sets/finite_enumerated_set.py @@ -109,7 +109,7 @@ def __init__(self, elements): self._elements = elements Parent.__init__(self, facade=True, category=FiniteEnumeratedSets()) - def __bool__(self): + def __bool__(self) -> bool: r""" Conversion to boolean. @@ -122,8 +122,6 @@ def __bool__(self): """ return bool(self._elements) - - def _repr_(self): """ TESTS:: diff --git a/src/sage/sets/finite_set_maps.py b/src/sage/sets/finite_set_maps.py index fd4a3ed08ba..ce5029d8032 100644 --- a/src/sage/sets/finite_set_maps.py +++ b/src/sage/sets/finite_set_maps.py @@ -585,4 +585,3 @@ def __init__(self, domain, action, category=None): self._action = action Element = FiniteSetEndoMap_Set - diff --git a/src/sage/sets/image_set.py b/src/sage/sets/image_set.py index 44a219a3f24..54b78eede7a 100644 --- a/src/sage/sets/image_set.py +++ b/src/sage/sets/image_set.py @@ -79,7 +79,7 @@ def __init__(self, map, domain_subset, *, category=None, is_injective=None, inve sage: f = H(lambda v: v[0]*x + v[1]*(x^2-y) + v[2]^2*(y+2) + v[3] - v[0]^2) sage: Im = f.image() sage: TestSuite(Im).run(skip=['_test_an_element', '_test_pickling', - ....: '_test_some_elements']) + ....: '_test_some_elements', '_test_elements']) """ if not is_Parent(domain_subset): from sage.sets.set import Set diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index 9b01ad6f3d4..9b5def119e0 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -95,6 +95,7 @@ def __contains__(self, elt): """ EXAMPLES:: + sage: NN = NonNegativeIntegers() sage: 1 in NN True sage: -1 in NN diff --git a/src/sage/sets/primes.py b/src/sage/sets/primes.py index 1c36c3a2563..96558ba9215 100644 --- a/src/sage/sets/primes.py +++ b/src/sage/sets/primes.py @@ -17,7 +17,7 @@ from sage.rings.integer_ring import ZZ from .set import Set_generic from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.arith.all import nth_prime +from sage.arith.misc import nth_prime from sage.structure.unique_representation import UniqueRepresentation diff --git a/src/sage/sets/set_from_iterator.py b/src/sage/sets/set_from_iterator.py index 3a2360383ea..74015c4433d 100644 --- a/src/sage/sets/set_from_iterator.py +++ b/src/sage/sets/set_from_iterator.py @@ -526,7 +526,9 @@ def _sage_argspec_(self): sage: d = Decorator() sage: d.f = find_local_minimum sage: sage_getargspec(d) # indirect doctest - ArgSpec(args=['f', 'a', 'b', 'tol', 'maxfun'], varargs=None, keywords=None, defaults=(1.48e-08, 500)) + FullArgSpec(args=['f', 'a', 'b', 'tol', 'maxfun'], + varargs=None, varkw=None, defaults=(1.48e-08, 500), + kwonlyargs=[], kwonlydefaults=None, annotations={}) """ from sage.misc.sageinspect import sage_getargspec return sage_getargspec(self.f) diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index 11b3cfd52ff..eb2d4ed6856 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -1109,8 +1109,9 @@ cdef class GaussianMixtureHiddenMarkovModel(GaussianHiddenMarkovModel): if self.N*self.N != len(self.A): raise ValueError("number of entries of transition matrix A must be the square of the number of entries of pi") - self.mixture = [b if isinstance(b, GaussianMixtureDistribution) else \ - GaussianMixtureDistribution([flatten(x) for x in b]) for b in B] + self.mixture = [b if isinstance(b, GaussianMixtureDistribution) else + GaussianMixtureDistribution([flatten(x) for x in b]) + for b in B] if len(self.mixture) != self.N: raise ValueError("number of GaussianMixtures must be the same as number of entries of pi") @@ -1559,4 +1560,3 @@ def unpickle_gaussian_mixture_hmm_v1(A, B, pi, mixture): m.pi = pi m.mixture = mixture return m - diff --git a/src/sage/stats/intlist.pyx b/src/sage/stats/intlist.pyx index 6de4f36d70c..f29d6519d97 100644 --- a/src/sage/stats/intlist.pyx +++ b/src/sage/stats/intlist.pyx @@ -95,7 +95,7 @@ cdef class IntList: [1, -2] """ cdef TimeSeries T - if isinstance(values, (int,long,Integer)): + if isinstance(values, (int, Integer)): self._length = values values = None elif isinstance(values, TimeSeries): diff --git a/src/sage/stats/time_series.pyx b/src/sage/stats/time_series.pyx index 2624f7ebf2a..c90ea577bc8 100644 --- a/src/sage/stats/time_series.pyx +++ b/src/sage/stats/time_series.pyx @@ -128,7 +128,7 @@ cdef class TimeSeries: cdef cnumpy.ndarray np cdef double *np_data cdef unsigned int j - if isinstance(values, (int, long, Integer)): + if isinstance(values, (int, Integer)): self._length = values values = None elif isinstance(values, (Vector_real_double_dense, cnumpy.ndarray)): diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 227a62cb3ef..86664258c8c 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -150,7 +150,7 @@ cpdef py_scalar_parent(py_type): sage: py_scalar_parent(gmpy2.mpc) Complex Double Field """ - if issubclass(py_type, int) or issubclass(py_type, long): + if issubclass(py_type, int): import sage.rings.integer_ring return sage.rings.integer_ring.ZZ if py_type is FractionType: @@ -266,7 +266,7 @@ cpdef py_scalar_to_element(x): """ if isinstance(x, Element): return x - elif isinstance(x, (int, long)): + elif isinstance(x, int): from sage.rings.integer import Integer return Integer(x) elif type(x) is FractionType: @@ -344,7 +344,7 @@ cpdef bint parent_is_integers(P) except -1: 2*f """ if isinstance(P, type): - if issubclass(P, int) or issubclass(P, long): + if issubclass(P, int): return True elif is_numpy_type(P): from numpy import integer @@ -969,7 +969,7 @@ cdef class CoercionModel: res = self.division_parent(res) return all, res - if isinstance(yp, Parent) and xp in [int, long, float, complex, bool]: + if isinstance(yp, Parent) and xp in [int, float, complex, bool]: mor = yp._internal_coerce_map_from(xp) if mor is not None: mor = mor.__copy__() @@ -982,7 +982,7 @@ cdef class CoercionModel: elif type(xp) is type: all.append("Left operand is not Sage element, will try _sage_.") - if isinstance(xp, Parent) and yp in [int, long, float, complex, bool]: + if isinstance(xp, Parent) and yp in [int, float, complex, bool]: mor = xp._internal_coerce_map_from(yp) if mor is not None: mor = mor.__copy__() @@ -1331,8 +1331,8 @@ cdef class CoercionModel: return x_elt,y_elt self._coercion_error(x, x_map, x_elt, y, y_map, y_elt) - cdef bint x_numeric = isinstance(x, (int, long, float, complex)) - cdef bint y_numeric = isinstance(y, (int, long, float, complex)) + cdef bint x_numeric = isinstance(x, (int, float, complex)) + cdef bint y_numeric = isinstance(y, (int, float, complex)) if not x_numeric and is_numpy_type(type(x)): import numpy diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index b5d83ef71b6..78fe97f1507 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -946,7 +946,7 @@ cdef class Element(SageObject): """ Use this method to implement ``self`` acting on ``x``. - Return None or raise a CoercionException if no + Return ``None`` or raise a ``CoercionException`` if no such action is defined here. """ return None @@ -955,15 +955,14 @@ cdef class Element(SageObject): """ Use this method to implement ``self`` acted on by x. - Return None or raise a CoercionException if no + Return ``None`` or raise a ``CoercionException`` if no such action is defined here. """ return None - def __xor__(self, right): - raise RuntimeError("Use ** for exponentiation, not '^', which means xor\n"+\ - "in Python, and has the wrong precedence.") + raise RuntimeError("Use ** for exponentiation, not '^', which means xor\n" + "in Python, and has the wrong precedence.") def __pos__(self): return self @@ -2705,10 +2704,10 @@ cdef class RingElement(ModuleElement): with Singular 4:: sage: K.<x,y> = ZZ[] - sage: (x^12345)^54321 + sage: (x^123456)^654321 Traceback (most recent call last): ... - OverflowError: exponent overflow (670592745) + OverflowError: exponent overflow (...) """ return arith_generic_power(self, n) diff --git a/src/sage/structure/formal_sum.py b/src/sage/structure/formal_sum.py index 0e14ab6dd1e..98178afca79 100644 --- a/src/sage/structure/formal_sum.py +++ b/src/sage/structure/formal_sum.py @@ -85,13 +85,14 @@ class FormalSum(ModuleElement): def __init__(self, x, parent=None, check=True, reduce=True): """ INPUT: - - ``x`` -- object - - ``parent`` -- FormalSums(R) module (default: FormalSums(ZZ)) - - ``check`` -- bool (default: True) if False, might not coerce - coefficients into base ring, which can speed - up constructing a formal sum. - - ``reduce`` -- reduce (default: True) if False, do not - combine common terms + + - ``x`` -- object + - ``parent`` -- FormalSums(R) module (default: FormalSums(ZZ)) + - ``check`` -- bool (default: ``True``) if ``False``, might not coerce + coefficients into base ring, which can speed + up constructing a formal sum. + - ``reduce`` -- reduce (default: ``True``) if ``False``, do not + combine common terms EXAMPLES:: @@ -265,7 +266,7 @@ def _rmul_(self, s): """ return self.__class__([(s*c, x) for (c, x) in self], check=False, parent=self.parent()) - def __bool__(self): + def __bool__(self) -> bool: """ EXAMPLES:: @@ -278,8 +279,6 @@ def __bool__(self): """ return not all(c.is_zero() for c, _ in self._data) - - def reduce(self): """ EXAMPLES:: diff --git a/src/sage/structure/global_options.py b/src/sage/structure/global_options.py index a529d39cd5e..f4d983a0658 100644 --- a/src/sage/structure/global_options.py +++ b/src/sage/structure/global_options.py @@ -613,7 +613,7 @@ def __rmul__(self, other): """ return other * self._options[self._name] - def __bool__(self): + def __bool__(self) -> bool: r""" Return the value of this option interpreted as a boolean. @@ -630,9 +630,6 @@ def __bool__(self): """ return bool(self._options[self._name]) - # for the less sensibly named python 2 family - - def __call__(self, *args, **kwds): r""" Get or set value of the option ``self``. @@ -1393,7 +1390,7 @@ def __eq__(self, other): sage: Partitions.options == Tableaux.options False """ - return self.__getstate__() == other.__getstate__() + return isinstance(other, GlobalOptions) and self.__getstate__() == other.__getstate__() def _add_option(self, option, specifications): r""" diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index da04658324f..4cf80c3522d 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -818,7 +818,7 @@ def standardize_names_index_set(names=None, index_set=None, ngens=None): index_set = tuple(names) from sage.sets.finite_enumerated_set import FiniteEnumeratedSet - if isinstance(index_set, dict): # dict of {name: index} -- not likely to be used + if isinstance(index_set, dict): # dict of {name: index} -- not likely to be used if names is not None: raise ValueError("cannot give index_set as a dict and names") names = normalize_names(-1, tuple(index_set.keys())) @@ -841,4 +841,3 @@ def standardize_names_index_set(names=None, index_set=None, ngens=None): " the number of generators") return (names, index_set) - diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index 125f32762ef..61a6823fc11 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -921,9 +921,7 @@ cdef class ClonableArray(ClonableElement): sage: type(el._hash_()) == int True """ - cdef long hv - hv = hash(tuple(self._list)) - return hash(self._parent) + hv + return hash(tuple(self._list)) def __reduce__(self): """ @@ -1280,7 +1278,7 @@ cdef class ClonableIntArray(ClonableElement): if self._list is not NULL: raise ValueError("resizing is forbidden") self._alloc_(len(lst)) - for i from 0 <= i < self._len: + for i in range(self._len): self._list[i] = lst[i] self._is_immutable = immutable @@ -1392,7 +1390,7 @@ cdef class ClonableIntArray(ClonableElement): cdef int i cdef list L = <list> PyList_New(self._len) cdef object o - for i from 0<=i<self._len: + for i in range(self._len): o = PyInt_FromLong(self._list[i]) Py_INCREF(o) PyList_SET_ITEM(L, i, o) @@ -1526,7 +1524,7 @@ cdef class ClonableIntArray(ClonableElement): False """ cdef int i - for i from 0 <= i < self._len: + for i in range(self._len): if item == self._list[i]: return True return False @@ -1547,7 +1545,7 @@ cdef class ClonableIntArray(ClonableElement): ValueError: list.index(x): x not in list """ cdef int i - for i from 0 <= i < self._len: + for i in range(self._len): if item == self._list[i]: return i raise ValueError("list.index(x): x not in list") @@ -1666,7 +1664,7 @@ cdef class ClonableIntArray(ClonableElement): res._parent = self._parent if self: res._alloc_(self._len) - for i from 0 <= i < res._len: + for i in range(self._len): res._list[i] = self._list[i] if HAS_DICTIONARY(self): res.__dict__ = self.__dict__.copy() @@ -1709,7 +1707,7 @@ cdef class ClonableIntArray(ClonableElement): hv = hash(None) else: hv = hash(tuple(self)) - return hash(self._parent) + hv + return hv def __reduce__(self): """ diff --git a/src/sage/structure/mutability.pyx b/src/sage/structure/mutability.pyx index b35d26a3ccb..4aa466513c8 100644 --- a/src/sage/structure/mutability.pyx +++ b/src/sage/structure/mutability.pyx @@ -69,9 +69,9 @@ cdef class Mutability: cpdef _require_mutable(self): r""" Whenever mutability is required, this method can be called. - + EXAMPLES:: - + sage: class A(SageObject, Mutability): ....: def __init__(self, val): ....: self._val = val @@ -87,7 +87,7 @@ cdef class Mutability: Traceback (most recent call last): ... ValueError: object is immutable; please change a copy instead - + """ if self._is_immutable: raise ValueError("object is immutable; please change a copy instead") @@ -95,9 +95,9 @@ cdef class Mutability: cpdef _require_immutable(self): r""" Whenever immutability is required, this method can be called. - + EXAMPLES:: - + sage: class A(SageObject, Mutability): ....: def __init__(self, val): ....: self._val = val @@ -112,7 +112,7 @@ cdef class Mutability: Traceback (most recent call last): ... ValueError: object is mutable; please make it immutable first - + """ if not self._is_immutable: raise ValueError("object is mutable; please make it immutable first") @@ -160,11 +160,11 @@ cdef class Mutability: """ Return ``True`` if this object is mutable (can be changed) and ``False`` if it is not. - + To make this object immutable use ``self.set_immutable()``. - + EXAMPLES:: - + sage: v = Sequence([1,2,3,4/5]) sage: v[0] = 5 sage: v @@ -180,9 +180,9 @@ cdef class Mutability: def __getstate__(self): r""" Get the current state of ``self`` including the mutability status. - + TESTS:: - + sage: class A(SageObject, Mutability): ....: def __init__(self, val): ....: self._val = val @@ -203,7 +203,7 @@ cdef class Mutability: <class 'sage.structure.sage_object.SageObject'>, <sage.structure.sage_object.SageObject object at ...>), {'_is_immutable': False, '_val': 4}) - + """ state = getattr(self, '__dict__', {}) state['_is_immutable'] = self._is_immutable diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 585ec9e559e..21447d96b1a 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -114,7 +114,6 @@ from sage.cpython.type cimport can_assign_class cimport sage.categories.morphism as morphism cimport sage.categories.map as map from sage.structure.debug_options cimport debug -from sage.structure.richcmp cimport rich_to_bool from sage.structure.sage_object cimport SageObject from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/structure/parent_base.pyx b/src/sage/structure/parent_base.pyx index 91c62424dae..dd697f90135 100644 --- a/src/sage/structure/parent_base.pyx +++ b/src/sage/structure/parent_base.pyx @@ -31,7 +31,7 @@ cdef class ParentWithBase(Parent_old): check_old_coerce(self) from sage.misc.superseded import deprecation deprecation(33497, "_coerce_c_impl is deprecated, use coerce instead") - if not self._base is self: + if self._base is not self: return self(self._base._coerce_(x)) else: raise TypeError("No canonical coercion found.") @@ -41,4 +41,3 @@ cdef class ParentWithBase(Parent_old): check_old_coerce(self) raise CoercionException("BUG: the base_extend method must be defined for '%s' (class '%s')" % (self, type(self))) - diff --git a/src/sage/structure/parent_gens.pyx b/src/sage/structure/parent_gens.pyx index 45e82720f43..c4155736e99 100644 --- a/src/sage/structure/parent_gens.pyx +++ b/src/sage/structure/parent_gens.pyx @@ -373,5 +373,3 @@ cdef class localvars: def __exit__(self, type, value, traceback): self._obj.__temporarily_change_names(self._orig[0], self._orig[1]) - - diff --git a/src/sage/structure/proof/proof.py b/src/sage/structure/proof/proof.py index c667704db92..24532380e8a 100644 --- a/src/sage/structure/proof/proof.py +++ b/src/sage/structure/proof/proof.py @@ -253,4 +253,3 @@ def __exit__(self, *args): True """ _proof_prefs._require_proof[self._subsystem] = self._t_orig - diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index 706de96005d..4616fd2880b 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -883,7 +883,6 @@ class assuming: [x == -2, x == 2] sage: with assuming(x > 0): ....: solve(x^2 == 4,x) - ....: [x == 2] sage: assumptions() [] diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index 4a123ec0e33..e570b06bc98 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -358,7 +358,7 @@ def _repr_(self): sage: R._repr_() 'Callable function ring with arguments (x, y, theta)' - We verify that :trac:`12298` has been fixed:: + We verify that :trac:`12298` has been fixed:: sage: S = CallableSymbolicExpressionRing([var('z')]) sage: S._repr_() diff --git a/src/sage/symbolic/complexity_measures.py b/src/sage/symbolic/complexity_measures.py index 1b5823e38bf..528d1bf6906 100644 --- a/src/sage/symbolic/complexity_measures.py +++ b/src/sage/symbolic/complexity_measures.py @@ -6,6 +6,7 @@ return a number. """ + def string_length(expr): """ Returns the length of ``expr`` after converting it to a string. @@ -22,11 +23,11 @@ def string_length(expr): If the expression is longer on-screen, then a human would probably consider it more complex. - + EXAMPLES: This expression has three characters, ``x``, ``^``, and ``2``:: - + sage: from sage.symbolic.complexity_measures import string_length sage: f = x^2 sage: string_length(f) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 7ee103f8555..0e924c009d7 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -4712,7 +4712,7 @@ cdef class Expression(Expression_abc): return self.gradient() else: raise ValueError("No differentiation variable specified.") - if not isinstance(deg, (int, long, sage.rings.integer.Integer)) \ + if not isinstance(deg, (int, sage.rings.integer.Integer)) \ or deg < 1: raise TypeError("argument deg should be an integer >= 1.") cdef Expression symbol = self.coerce_in(symb) @@ -7000,7 +7000,7 @@ cdef class Expression(Expression_abc): # the following is a temporary fix for GiNaC bug #9505 if is_a_mul(ss._gobj): # necessarily n=1 here res = self - for i from 0 <= i < ss._gobj.nops(): + for i in range(ss._gobj.nops()): res = res.coefficient(new_Expression_from_GEx(self._parent, ss._gobj.op(i))) return res sig_on() @@ -10135,7 +10135,7 @@ cdef class Expression(Expression_abc): sig_off() return new_Expression_from_GEx(self._parent, ex) elif is_a_mul(self._gobj): - for i from 0 <= i < self._gobj.nops(): + for i in range(self._gobj.nops()): oper = self._gobj.op(i) if not is_a_power(oper): vec.push_back(oper) @@ -10225,7 +10225,7 @@ cdef class Expression(Expression_abc): sig_off() return new_Expression_from_GEx(self._parent, ex) elif is_a_mul(self._gobj): - for i from 0 <= i < self._gobj.nops(): + for i in range(self._gobj.nops()): oper = self._gobj.op(i) if is_a_power(oper): ex = oper.op(0) @@ -10318,7 +10318,7 @@ cdef class Expression(Expression_abc): return (new_Expression_from_GEx(self._parent, ex.op(0)), new_Expression_from_GEx(self._parent, ex.op(1))) elif is_a_mul(self._gobj): - for i from 0 <= i < self._gobj.nops(): + for i in range(self._gobj.nops()): oper = self._gobj.op(i) if is_a_power(oper): # oper = ex^power ex = oper.op(0) @@ -11290,10 +11290,11 @@ cdef class Expression(Expression_abc): def canonicalize_radical(self): r""" - Choose a canonical branch of the given expression. The square - root, cube root, natural log, etc. functions are multi-valued. The - ``canonicalize_radical()`` method will choose *one* of these values - based on a heuristic. + Choose a canonical branch of the given expression. + + The square root, cube root, natural log, etc. functions are + multi-valued. The ``canonicalize_radical()`` method will + choose *one* of these values based on a heuristic. For example, ``sqrt(x^2)`` has two values: ``x``, and ``-x``. The ``canonicalize_radical()`` function will choose @@ -11901,7 +11902,7 @@ cdef class Expression(Expression_abc): Check that :trac:`33640` is fixed:: - sage: ((x + 1)^2 - 2*x - 1).factor() + sage: ((x + 1)^2 - 2*x - 1).factor() x^2 """ from sage.calculus.calculus import symbolic_expression_from_maxima_string @@ -13722,10 +13723,11 @@ cpdef new_Expression(parent, x): from sage.symbolic.constants import NaN return NaN exp = x - elif isinstance(x, long): - exp = x elif isinstance(x, int): - exp = GEx(<long>x) + try: + exp = GEx(<long>x) + except OverflowError: + exp = x elif x is infinity: return new_Expression_from_GEx(parent, g_Infinity) elif x is minus_infinity: diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 06e626c7748..0baedce69c0 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -502,7 +502,7 @@ cdef class Function(SageObject): sage: (out, parent(out)) (0, Integer Ring) - Check that `real_part` and `imag_part` still works after :trac:`21216`:: + Check that ``real_part`` and ``imag_part`` still works after :trac:`21216`:: sage: import numpy sage: a = numpy.array([1+2*I, -2-3*I], dtype=complex) diff --git a/src/sage/symbolic/ginac/ex.h b/src/sage/symbolic/ginac/ex.h index 7d220d2d25d..6a164af270f 100644 --- a/src/sage/symbolic/ginac/ex.h +++ b/src/sage/symbolic/ginac/ex.h @@ -677,19 +677,19 @@ std::ostream & operator<<(std::ostream & os, const exset & e); std::ostream & operator<<(std::ostream & os, const exmap & e); /* Function objects for STL sort() etc. */ -struct ex_is_less : public std::binary_function<ex, ex, bool> { +struct ex_is_less { bool operator() (const ex &lh, const ex &rh) const { return lh.compare(rh) < 0; } }; -struct ex_is_equal : public std::binary_function<ex, ex, bool> { +struct ex_is_equal { bool operator() (const ex &lh, const ex &rh) const { return lh.is_equal(rh); } }; -struct op0_is_equal : public std::binary_function<ex, ex, bool> { +struct op0_is_equal { bool operator() (const ex &lh, const ex &rh) const { return lh.op(0).is_equal(rh.op(0)); } }; -struct ex_swap : public std::binary_function<ex, ex, void> { +struct ex_swap { void operator() (ex &lh, ex &rh) const { lh.swap(rh); } }; diff --git a/src/sage/symbolic/ginac/expair.h b/src/sage/symbolic/ginac/expair.h index 75177f6e49a..38b34e18404 100644 --- a/src/sage/symbolic/ginac/expair.h +++ b/src/sage/symbolic/ginac/expair.h @@ -91,7 +91,7 @@ class expair }; /** Function object for insertion into third argument of STL's sort() etc. */ -struct expair_is_less : public std::binary_function<expair, expair, bool> { +struct expair_is_less { bool operator()(const expair &lh, const expair &rh) const { return lh.is_less(rh); } }; @@ -99,11 +99,11 @@ struct expair_is_less : public std::binary_function<expair, expair, bool> { * into third argument of STL's sort(). Note that this does not define a * strict weak ordering since for any symbol x we have neither 3*x<2*x or * 2*x<3*x. Handle with care! */ -struct expair_rest_is_less : public std::binary_function<expair, expair, bool> { +struct expair_rest_is_less { bool operator()(const expair &lh, const expair &rh) const { return (lh.rest.compare(rh.rest)<0); } }; -struct expair_swap : public std::binary_function<expair, expair, void> { +struct expair_swap { void operator()(expair &lh, expair &rh) const { lh.swap(rh); } }; diff --git a/src/sage/symbolic/ginac/function.cpp b/src/sage/symbolic/ginac/function.cpp index 689e2b8a22f..67d608c6240 100644 --- a/src/sage/symbolic/ginac/function.cpp +++ b/src/sage/symbolic/ginac/function.cpp @@ -976,7 +976,7 @@ ex function::evalf(int level, PyObject* kwds) const // convert seq to a PyTuple of Expressions PyObject* args = py_funcs.exvector_to_PyTuple(eseq); // call opt.evalf_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast<PyObject*>(opt.evalf_f), "_evalf_"), args, kwds); Py_DECREF(args); @@ -1056,7 +1056,7 @@ ex function::series(const relational & r, int order, unsigned options) const // add the point of expansion as a keyword argument PyDict_SetItemString(kwds, "at", py_funcs.ex_to_pyExpression(r.rhs())); // call opt.series_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast<PyObject*>(opt.series_f), "_series_"), args, kwds); Py_DECREF(args); @@ -1321,7 +1321,7 @@ ex function::derivative(const symbol & s) const PyObject* kwds = Py_BuildValue("{s:O}","diff_param", symb); // call opt.derivative_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString( reinterpret_cast<PyObject*>(opt.derivative_f), "_tderivative_"), args, kwds); @@ -1478,7 +1478,7 @@ ex function::pderivative(unsigned diff_param) const // partial differentiation // create a dictionary {'diff_param': diff_param} PyObject* kwds = Py_BuildValue("{s:I}","diff_param",diff_param); // call opt.derivative_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast<PyObject*>(opt.derivative_f), "_derivative_"), args, kwds); Py_DECREF(args); @@ -1557,7 +1557,7 @@ ex function::power(const ex & power_param) const // power of function PyObject* kwds = PyDict_New(); PyDict_SetItemString(kwds, "power_param", py_funcs.ex_to_pyExpression(power_param)); // call opt.power_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast<PyObject*>(opt.power_f), "_power_"), args, kwds); Py_DECREF(args); diff --git a/src/sage/symbolic/ginac/numeric.cpp b/src/sage/symbolic/ginac/numeric.cpp index 22060441760..b40ed64edb5 100644 --- a/src/sage/symbolic/ginac/numeric.cpp +++ b/src/sage/symbolic/ginac/numeric.cpp @@ -52,7 +52,6 @@ #define register #define PY_SSIZE_T_CLEAN #include <Python.h> -#include <longintrepr.h> #include "flint/fmpz.h" #include "flint/fmpz_factor.h" diff --git a/src/sage/symbolic/ginac/order.h b/src/sage/symbolic/ginac/order.h index 63d5373ae90..7c65d6a7d69 100644 --- a/src/sage/symbolic/ginac/order.h +++ b/src/sage/symbolic/ginac/order.h @@ -35,7 +35,7 @@ namespace GiNaC { -class print_order : public std::binary_function<ex, ex, bool> { +class print_order { private: const tinfo_t& function_id() const; const tinfo_t& fderivative_id() const; @@ -96,9 +96,7 @@ class print_order_mul : public print_order { // We have to define the following class to sort held expressions // E.g. 3*x+2*x which does not get simplified to 5*x. -class print_order_pair : - public std::binary_function<expair, expair, bool> -{ +class print_order_pair { public: bool operator() (const expair &lh, const expair &rh) const; bool compare_degrees(const expair &lhex, const expair &rhex) const; diff --git a/src/sage/symbolic/ginac/ptr.h b/src/sage/symbolic/ginac/ptr.h index 7f3061cfe43..531e30ca869 100644 --- a/src/sage/symbolic/ginac/ptr.h +++ b/src/sage/symbolic/ginac/ptr.h @@ -158,8 +158,7 @@ namespace std { /** Specialization of std::less for ptr<T> to enable ordering of ptr<T> * objects (e.g. for the use as std::map keys). */ -template <class T> struct less< GiNaC::ptr<T> > - : public binary_function<GiNaC::ptr<T>, GiNaC::ptr<T>, bool> { +template <class T> struct less< GiNaC::ptr<T> > { bool operator()(const GiNaC::ptr<T> &lhs, const GiNaC::ptr<T> &rhs) const { return less<T*>()(lhs.p, rhs.p); diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index f9914e438a1..34d96b1c731 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -194,6 +194,16 @@ def __init__(self): sage: from sage.symbolic.integration.integral import definite_integral sage: definite_integral(sin(x),x,0,pi) 2 + + TESTS: + + Check for :trac:`32354`:: + + sage: ex = 1/max_symbolic(x, 1)**2 + sage: integral(ex, x, 0, 2, algorithm='giac') + 3/2 + sage: integral(1/max_symbolic(x, 1)**2, x, 0, oo, algorithm='giac') + 2 """ # The automatic evaluation routine will try these integrators # in the given order. This is an attribute of the class instead of diff --git a/src/sage/symbolic/pynac_function_impl.pxi b/src/sage/symbolic/pynac_function_impl.pxi index 6dae90ce7a4..f20c8faf91a 100644 --- a/src/sage/symbolic/pynac_function_impl.pxi +++ b/src/sage/symbolic/pynac_function_impl.pxi @@ -42,7 +42,7 @@ cpdef call_registered_function(unsigned serial, cdef GEx res cdef GExVector vec if nargs == 0 or nargs > 3: - for i from 0 <= i < len(args): + for i in range(len(args)): vec.push_back((<Expression>args[i])._gobj) res = g_function_evalv(serial, vec, hold) elif nargs == 1: diff --git a/src/sage/symbolic/pynac_impl.pxi b/src/sage/symbolic/pynac_impl.pxi index d5934b565bc..0af6aae7fe1 100644 --- a/src/sage/symbolic/pynac_impl.pxi +++ b/src/sage/symbolic/pynac_impl.pxi @@ -1018,7 +1018,7 @@ cdef py_real(x): sage: py_real(complex(2,2)) 2.0 """ - if isinstance(x, (float, int, long)): + if isinstance(x, (float, int)): return x elif isinstance(x, complex): return x.real @@ -1118,7 +1118,7 @@ cdef py_conjugate(x): cdef bint py_is_rational(x): return (type(x) is Rational or type(x) is Integer or - isinstance(x, (int, long))) + isinstance(x, int)) cdef bint py_is_equal(x, y): @@ -1156,7 +1156,7 @@ cdef bint py_is_integer(x): sage: py_is_integer(3.0r) False """ - if isinstance(x, (int, long, Integer)): + if isinstance(x, (int, Integer)): return True if not isinstance(x, Element): return False @@ -1220,7 +1220,7 @@ def py_is_crational_for_doctest(x): cdef bint py_is_real(a): - if isinstance(a, (int, long, Integer, float)): + if isinstance(a, (int, Integer, float)): return True try: P = parent(a) @@ -1246,7 +1246,7 @@ cdef bint py_is_prime(n): cdef bint py_is_exact(x): - if isinstance(x, (int, long, Integer)): + if isinstance(x, (int, Integer)): return True if not isinstance(x, Element): return False @@ -1281,7 +1281,7 @@ cdef py_numer(n): sage: py_numer(no_numer()) 42 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, (int, Integer)): return n try: return n.numerator() @@ -1319,7 +1319,7 @@ cdef py_denom(n): sage: py_denom(2/3*i) 3 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, (int, Integer)): return 1 try: return n.denominator() @@ -1441,7 +1441,7 @@ cdef py_tgamma(x): sage: py_tgamma(1/2) 1.77245385090552 """ - if isinstance(x, (int, long)): + if isinstance(x, int): x = float(x) if type(x) is float: return math.tgamma(PyFloat_AS_DOUBLE(x)) @@ -1759,7 +1759,7 @@ cdef py_log(x): """ cdef gsl_complex res cdef double real, imag - if isinstance(x, (int, long)): + if isinstance(x, int): x = float(x) if type(x) is float: real = PyFloat_AS_DOUBLE(x) diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index 6bd6469dd22..b8896a94be9 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -935,8 +935,7 @@ def solve(f, *args, **kwds): 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] + ...[-2*arctan(sqrt(2)), 0, 2*arctan(sqrt(2)), pi] sage: x, y = SR.var('x,y') sage: solve([x+y-4,x*y-3],[x,y],algorithm='giac') @@ -1197,12 +1196,16 @@ def _solve_expression(f, x, explicit_solutions, multiplicities, Catch error message from Maxima:: sage: solve(acot(x),x) - [] + Traceback (most recent call last): + ... + TypeError: ECL says: cot: argument 0 isn't in the domain of cot. :: sage: solve(acot(x),x,to_poly_solve=True) - [] + Traceback (most recent call last): + ... + TypeError: ECL says: cot: argument 0 isn't in the domain of cot. :trac:`7491` fixed:: @@ -1436,19 +1439,15 @@ def _giac_solver(f, x, solution_dict=False): EXAMPLES:: sage: solve([(2/3)^x-2], [x], algorithm='giac') - ... - [[-log(2)/(log(3) - log(2))]] + ...[[-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))}] + ...[{x: -log(2)/(log(3) - log(2))}] 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] + ...[-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}] + ...[{x: -2*arctan(sqrt(2))}, {x: 0}, {x: 2*arctan(sqrt(2))}, {x: pi}] sage: x, y = SR.var('x,y') sage: solve([x+y-7,x*y-10],[x,y],algorithm='giac') diff --git a/src/sage/tensor/modules/alternating_contr_tensor.py b/src/sage/tensor/modules/alternating_contr_tensor.py index 8af7be2363d..03047253b36 100644 --- a/src/sage/tensor/modules/alternating_contr_tensor.py +++ b/src/sage/tensor/modules/alternating_contr_tensor.py @@ -438,7 +438,7 @@ def display(self, basis=None, format_spec=None): if is_atomic(coef_latex): terms_latex.append(coef_latex + basis_term_latex) else: - terms_latex.append(r'\left(' + coef_latex + \ + terms_latex.append(r'\left(' + coef_latex + r'\right)' + basis_term_latex) if not terms_txt: expansion_txt = '0' diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 62f9fd7d07f..d766fb67d3d 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -4351,7 +4351,7 @@ def symmetrize(self, *pos): if len(pos) < 2: raise ValueError("at least two index positions must be given") if len(pos) > self._nid: - raise ValueError("number of index positions larger than the " \ + raise ValueError("number of index positions larger than the " "total number of indices") pos = tuple(pos) pos_set = set(pos) @@ -4613,7 +4613,7 @@ def antisymmetrize(self, *pos): if len(pos) < 2: raise ValueError("at least two index positions must be given") if len(pos) > self._nid: - raise ValueError("number of index positions larger than the " \ + raise ValueError("number of index positions larger than the " "total number of indices") pos = tuple(pos) pos_set = set(pos) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index d9db1b5a05b..ec6858a1b47 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -85,7 +85,7 @@ class ExtPowerFreeModule(FiniteRankFreeModule_abstract): `R`, where `n` is the rank of `M`. Accordingly, the class :class:`ExtPowerFreeModule` inherits from the class - :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. This is a Sage *parent* class, whose *element* class is :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor` @@ -465,7 +465,7 @@ class ExtPowerDualFreeModule(FiniteRankFreeModule_abstract): `R`, where `n` is the rank of `M`. Accordingly, the class :class:`ExtPowerDualFreeModule` inherits from the class - :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. This is a Sage *parent* class, whose *element* class is :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`. diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 8c007727799..534c99d0b3f 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -548,8 +548,14 @@ class :class:`~sage.modules.free_module.FreeModule_generic` from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement from sage.tensor.modules.free_module_tensor import FreeModuleTensor +from sage.tensor.modules.reflexive_module import ( + ReflexiveModule_abstract, + ReflexiveModule_base, + ReflexiveModule_dual, +) -class FiniteRankFreeModule_abstract(UniqueRepresentation, Parent): + +class FiniteRankFreeModule_abstract(UniqueRepresentation, ReflexiveModule_abstract): r""" Abstract base class for free modules of finite rank over a commutative ring. """ @@ -619,108 +625,6 @@ def _latex_(self): else: return self._latex_name - def tensor_power(self, n): - r""" - Return the ``n``-fold tensor product of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_power(3) - Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_power(3) - Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field - """ - tensor_type = self.tensor_type() - return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) - - def tensor_product(self, *others): - r""" - Return the tensor product of ``self`` and ``others``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_product(M) - Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.dual()) - Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field - sage: M.dual().tensor_product(M, M.dual()) - Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.tensor_module(1,2)) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_product(M) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) - Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field - - sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M - Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M - Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) - sage: Sym01x23M._index_maps - ((0, 1), (2, 3)) - - sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N - Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), - with antisymmetry on the index positions (3, 4) - sage: NxN = N.tensor_product(N); NxN - Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), - with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) - sage: NxN._index_maps - ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) - """ - from sage.modules.free_module_element import vector - from .comp import CompFullySym, CompFullyAntiSym, CompWithSym - - base_module = self.base_module() - if not all(module.base_module() == base_module for module in others): - raise NotImplementedError('all factors must be tensor modules over the same base module') - factors = [self] + list(others) - result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) - result_sym = [] - result_antisym = [] - # Keep track of reordering of the contravariant and covariant indices - # (compatible with FreeModuleTensor.__mul__) - index_maps = [] - running_indices = vector([0, result_tensor_type[0]]) - for factor in factors: - tensor_type = factor.tensor_type() - index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) - index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) - index_maps.append(index_map) - - if tensor_type[0] + tensor_type[1] > 1: - basis_sym = factor._basis_sym() - all_indices = tuple(range(tensor_type[0] + tensor_type[1])) - if isinstance(basis_sym, CompFullySym): - sym = [all_indices] - antisym = [] - elif isinstance(basis_sym, CompFullyAntiSym): - sym = [] - antisym = [all_indices] - elif isinstance(basis_sym, CompWithSym): - sym = basis_sym._sym - antisym = basis_sym._antisym - else: - sym = antisym = [] - - def map_isym(isym): - return tuple(index_map[i] for i in isym) - - result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) - result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) - - running_indices += vector(tensor_type) - - result = base_module.tensor_module(*result_tensor_type, - sym=result_sym, antisym=result_antisym) - result._index_maps = tuple(index_maps) - return result - def rank(self) -> int: r""" Return the rank of the free module ``self``. @@ -1070,7 +974,7 @@ def _test_isomorphism_with_fixed_basis(self, **options): tester.assertEqual(morphism.codomain().rank(), self.rank()) -class FiniteRankFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankFreeModule(ReflexiveModule_base, FiniteRankFreeModule_abstract): r""" Free module of finite rank over a commutative ring. @@ -2122,7 +2026,7 @@ def _test_basis(self, tester=None, **options): TestSuite(b).run(verbose=tester._verbose, prefix=tester._prefix + " ", raise_on_failure=is_sub_testsuite) - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None): r""" Construct a tensor on the free module ``self``. @@ -2131,10 +2035,81 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - ``tensor_type`` -- pair ``(k, l)`` with ``k`` being the contravariant rank and ``l`` the covariant rank + - ``name`` -- (default: ``None``) string; name given to the tensor + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the tensor; if none is provided, the LaTeX symbol is set to ``name`` + + - ``sym`` -- (default: ``None``) a symmetry or an iterable of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention ``position = 0`` for the first argument. For instance: + + * ``sym = (0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym = [(0,2), (1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments. + + - ``antisym`` -- (default: ``None``) antisymmetry or iterable of + antisymmetries among the arguments, with the same convention + as for ``sym`` + + OUTPUT: + + - instance of + :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor` + representing the tensor defined on ``self`` with the provided + characteristics + + EXAMPLES: + + Tensors on a rank-3 free module:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: t = M._tensor((1,0), name='t') ; t + Element t of the Rank-3 free module M over the Integer Ring + """ + from .comp import CompWithSym + sym, antisym = CompWithSym._canonicalize_sym_antisym( + tensor_type[0] + tensor_type[1], sym, antisym) + # Special cases: + if tensor_type == (1,0): + return self.element_class(self, name=name, latex_name=latex_name) + elif tensor_type == (0,1): + return self.linear_form(name=name, latex_name=latex_name) + elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: + if len(antisym[0]) == tensor_type[1]: + return self.alternating_form(tensor_type[1], name=name, + latex_name=latex_name) + elif tensor_type[0] > 1 and tensor_type[1] == 0 and antisym: + if len(antisym[0]) == tensor_type[0]: + return self.alternating_contravariant_tensor(tensor_type[0], + name=name, latex_name=latex_name) + # Generic case: + return self.tensor_module(*tensor_type).element_class(self, + tensor_type, name=name, latex_name=latex_name, + sym=sym, antisym=antisym) + + def tensor(self, *args, **kwds): + r""" + Construct a tensor on the free module ``self`` or a tensor product with other modules. + + If ``args`` consist of other parents, just delegate to :meth:`tensor_product`. + + Otherwise, construct a tensor from the following input. + + INPUT: + + - ``tensor_type`` -- pair ``(k, l)`` with ``k`` being the + contravariant rank and ``l`` the covariant rank + + - ``name`` -- (default: ``None``) string; name given to the tensor + + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the tensor; if none is provided, the LaTeX symbol is set + to ``name`` + - ``sym`` -- (default: ``None``) a symmetry or an iterable of symmetries among the tensor arguments: each symmetry is described by a tuple containing the positions of the involved arguments, with the @@ -2189,26 +2164,12 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, sage: M.tensor((3,0), antisym=[[]]) Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring """ - from .comp import CompWithSym - sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym) - # Special cases: - if tensor_type == (1,0): - return self.element_class(self, name=name, latex_name=latex_name) - elif tensor_type == (0,1): - return self.linear_form(name=name, latex_name=latex_name) - elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: - if len(antisym[0]) == tensor_type[1]: - return self.alternating_form(tensor_type[1], name=name, - latex_name=latex_name) - elif tensor_type[0] > 1 and tensor_type[1] == 0 and antisym: - if len(antisym[0]) == tensor_type[0]: - return self.alternating_contravariant_tensor(tensor_type[0], - name=name, latex_name=latex_name) - # Generic case: - return self.tensor_module(*tensor_type).element_class(self, - tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so this method + # also needs to double as the tensor product construction + if isinstance(args[0], Parent): + return self.tensor_product(*args, **kwds) + return self._tensor(*args, **kwds) def tensor_from_comp(self, tensor_type, comp, name=None, latex_name=None): r""" @@ -3351,34 +3312,8 @@ def identity_map(self, name='Id', latex_name=None): self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map - def base_module(self): - r""" - Return the free module on which ``self`` is constructed, namely ``self`` itself. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.base_module() is M - True - - """ - return self - - def tensor_type(self): - r""" - Return the tensor type of ``self``, the pair `(1, 0)`. - - EXAMPLES:: - sage: M = FiniteRankFreeModule(ZZ, 3) - sage: M.tensor_type() - (1, 0) - - """ - return (1, 0) - - -class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankDualFreeModule(ReflexiveModule_dual, FiniteRankFreeModule_abstract): r""" Dual of a free module of finite rank over a commutative ring. @@ -3505,24 +3440,6 @@ def __init__(self, fmodule, name=None, latex_name=None): latex_name=latex_name) fmodule._all_modules.add(self) - def construction(self): - r""" - Return the functorial construction of ``self``. - - This implementation just returns ``None``, as no functorial construction is implemented. - - TESTS:: - - sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: e = M.basis('e') - sage: A = M.dual() - sage: A.construction() is None - True - """ - # No construction until we extend VectorFunctor with a parameter 'dual' - return None - #### Parent methods def _element_constructor_(self, comp=[], basis=None, name=None, @@ -3657,16 +3574,3 @@ def base_module(self): """ return self._fmodule - - def tensor_type(self): - r""" - Return the tensor type of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.dual().tensor_type() - (0, 1) - - """ - return (0, 1) diff --git a/src/sage/tensor/modules/free_module_alt_form.py b/src/sage/tensor/modules/free_module_alt_form.py index 2e8abc33249..3569f686681 100644 --- a/src/sage/tensor/modules/free_module_alt_form.py +++ b/src/sage/tensor/modules/free_module_alt_form.py @@ -428,7 +428,7 @@ def _display_expansion(self, basis=None, format_spec=None): if is_atomic(coef_latex): terms_latex.append(coef_latex + basis_term_latex) else: - terms_latex.append(r'\left(' + coef_latex + \ + terms_latex.append(r'\left(' + coef_latex + r'\right)' + basis_term_latex) if not terms_txt: expansion_txt = '0' diff --git a/src/sage/tensor/modules/free_module_homset.py b/src/sage/tensor/modules/free_module_homset.py index da0021b309b..d240882961e 100644 --- a/src/sage/tensor/modules/free_module_homset.py +++ b/src/sage/tensor/modules/free_module_homset.py @@ -381,8 +381,8 @@ def _element_constructor_(self, matrix_rep, bases=None, name=None, basis = tensor.pick_a_basis() tcomp = tensor.comp(basis) fmodule = tensor.base_module() - mat = [[ tcomp[[i,j]] for j in fmodule.irange()] \ - for i in fmodule.irange()] + mat = [[ tcomp[[i,j]] for j in fmodule.irange()] + for i in fmodule.irange()] if isinstance(tensor, FreeModuleAutomorphism): is_identity = tensor._is_identity else: diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py index 5eb8e66cdcc..abb24e89203 100644 --- a/src/sage/tensor/modules/free_module_morphism.py +++ b/src/sage/tensor/modules/free_module_morphism.py @@ -259,10 +259,10 @@ def __init__(self, parent, matrix_rep, bases=None, name=None, if is_identity: # Construction of the identity endomorphism if fmodule1 != fmodule2: - raise TypeError("the domain and codomain must coincide " + \ + raise TypeError("the domain and codomain must coincide " + "for the identity endomorphism.") if bases[0] != bases[1]: - raise TypeError("the two bases must coincide for " + \ + raise TypeError("the two bases must coincide for " + "constructing the identity endomorphism.") self._is_identity = True zero = ring.zero() @@ -1105,7 +1105,7 @@ def matrix(self, basis1=None, basis2=None): if basis1 is None: basis1 = fmodule1.default_basis() elif basis1 not in fmodule1.bases(): - raise TypeError(str(basis1) + " is not a basis on the " + \ + raise TypeError(str(basis1) + " is not a basis on the " + str(fmodule1) + ".") if basis2 is None: if self.is_endomorphism(): @@ -1113,7 +1113,7 @@ def matrix(self, basis1=None, basis2=None): else: basis2 = fmodule2.default_basis() elif basis2 not in fmodule2.bases(): - raise TypeError(str(basis2) + " is not a basis on the " + \ + raise TypeError(str(basis2) + " is not a basis on the " + str(fmodule2) + ".") if (basis1, basis2) not in self._matrices: if self._is_identity: diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 2ffa00cc476..a7865299688 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -3064,7 +3064,7 @@ def symmetrize(self, *pos, **kwargs): for k in range(1,len(pos)): if pos[k] < pos_cov: raise TypeError( - str(pos[0]) + " is a covariant position, while " + \ + str(pos[0]) + " is a covariant position, while " + str(pos[k]) + " is a contravariant position; \n" "symmetrization is meaningful only on tensor " + "arguments of the same type") @@ -3305,7 +3305,7 @@ def antisymmetrize(self, *pos, **kwargs): for k in range(1,len(pos)): if pos[k] < pos_cov: raise TypeError( - str(pos[0]) + " is a covariant position, while " + \ + str(pos[0]) + " is a covariant position, while " + str(pos[k]) + " is a contravariant position; \n" "antisymmetrization is meaningful only on tensor " + "arguments of the same type") diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py new file mode 100644 index 00000000000..f969d547811 --- /dev/null +++ b/src/sage/tensor/modules/reflexive_module.py @@ -0,0 +1,385 @@ +r""" +Base classes for reflexive modules +""" + +from sage.misc.abstract_method import abstract_method +from sage.structure.parent import Parent + + +class ReflexiveModule_abstract(Parent): + r""" + Abstract base class for reflexive modules. + + An `R`-module `M` is *reflexive* if the natural map from `M` to its double + dual `M^{**}` is an isomorphism. + + In the category of `R`-modules, the dual module `M^*` is + the `R`-module of linear functionals `\phi:\ M \longrightarrow R`. + However, we do not make the assumption that the dual module + (obtained by :meth:`dual`) is in the category :class:`Homsets`. + + We identify the double dual `M^{**}` with `M`. + + Tensor products of reflexive modules are reflexive. We identify all + tensor products of `k` copies of `M` and `l` copies of `M^*` and + denote it by `T^{(k,l)}(M)`. The :meth:`tensor_type` of such a tensor + product is the pair `(k, l)`, and `M` is called its :meth:`base_module`. + + There are three abstract subclasses: + + - :class:`ReflexiveModule_base` is the base class for implementations + of base modules `M`. + + - :class:`ReflexiveModule_dual` is the base class for implementations + of duals `M^*`. + + - :class:`ReflexiveModule_tensor` is the base class for implementations + of tensor modules `T^{(k,l)}(M)`. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ( + ....: ReflexiveModule_abstract, ReflexiveModule_base, + ....: ReflexiveModule_dual, ReflexiveModule_tensor) + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: isinstance(M, ReflexiveModule_abstract) + True + sage: isinstance(M, ReflexiveModule_base) + True + sage: isinstance(M.dual(), ReflexiveModule_abstract) + True + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_abstract) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True + """ + + @abstract_method(optional=True) + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + OUTPUT: + + - pair `(k,l)` such that ``self`` is the module tensor product + `T^{(k,l)}(M)`, where `M` is the :meth:`base_module` of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: T = M.tensor_module(1, 2) + sage: T.tensor_type() + (1, 2) + """ + + @abstract_method + def base_module(self): + r""" + Return the module on which ``self`` is constructed. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.base_module() is M + True + sage: M.dual().base_module() is M + True + sage: M.tensor_module(1, 2).base_module() is M + True + """ + + def dual(self): + r""" + Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.dual() + Dual of the Rank-3 free module over the Integer Ring + sage: M.dual().dual() + Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2).dual() + Free module of type-(2,1) tensors on the Rank-3 free module over the Integer Ring + """ + k, l = self.tensor_type() + return self.base_module().tensor_module(l, k) + + def tensor(self, *args, **kwds): + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so here we delegate. + r""" + Return the tensor product of ``self`` and ``others``. + + This method is invoked when :class:`~sage.categories.tensor.TensorProductFunctor` + is applied to parents. + + It just delegates to :meth:`tensor_product`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2); M + 2-dimensional vector space over the Rational Field + sage: M20 = M.tensor_module(2, 0); M20 + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: tensor([M20, M20]) + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field + """ + return self.tensor_product(*args, **kwds) + + def tensor_power(self, n): + r""" + Return the ``n``-fold tensor product of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_power(3) + Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_power(3) + Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field + """ + tensor_type = self.tensor_type() + return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) + + def tensor_product(self, *others): + r""" + Return the tensor product of ``self`` and ``others``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_product(M) + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.dual()) + Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field + sage: M.dual().tensor_product(M, M.dual()) + Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.tensor_module(1,2)) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_product(M) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) + Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field + + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) + sage: Sym01x23M._index_maps + ((0, 1), (2, 3)) + + sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N + Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), + with antisymmetry on the index positions (3, 4) + sage: NxN = N.tensor_product(N); NxN + Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), + with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) + sage: NxN._index_maps + ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) + """ + from sage.modules.free_module_element import vector + from .comp import CompFullySym, CompFullyAntiSym, CompWithSym + + base_module = self.base_module() + if not all(module.base_module() == base_module for module in others): + raise NotImplementedError('all factors must be tensor modules over the same base module') + factors = [self] + list(others) + result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) + result_sym = [] + result_antisym = [] + # Keep track of reordering of the contravariant and covariant indices + # (compatible with FreeModuleTensor.__mul__) + index_maps = [] + running_indices = vector([0, result_tensor_type[0]]) + for factor in factors: + tensor_type = factor.tensor_type() + index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) + index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) + index_maps.append(index_map) + + if tensor_type[0] + tensor_type[1] > 1: + basis_sym = factor._basis_sym() + all_indices = tuple(range(tensor_type[0] + tensor_type[1])) + if isinstance(basis_sym, CompFullySym): + sym = [all_indices] + antisym = [] + elif isinstance(basis_sym, CompFullyAntiSym): + sym = [] + antisym = [all_indices] + elif isinstance(basis_sym, CompWithSym): + sym = basis_sym._sym + antisym = basis_sym._antisym + else: + sym = antisym = [] + + def map_isym(isym): + return tuple(index_map[i] for i in isym) + + result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) + result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) + + running_indices += vector(tensor_type) + + result = base_module.tensor_module(*result_tensor_type, + sym=result_sym, antisym=result_antisym) + result._index_maps = tuple(index_maps) + return result + + +class ReflexiveModule_base(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_base + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M, ReflexiveModule_base) + True + """ + + def base_module(self): + r""" + Return the free module on which ``self`` is constructed, namely ``self`` itself. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.base_module() is M + True + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.base_module() is XM + True + """ + return self + + def tensor_type(self): + r""" + Return the tensor type of ``self``, the pair `(1, 0)`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_type() + (1, 0) + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor_type() + (1, 0) + """ + return (1, 0) + + def dual(self): + r""" + Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual() + Dual of the Rank-3 free module M over the Integer Ring + """ + return self.tensor_module(0, 1) + + @abstract_method + def tensor_module(self, k, l, **kwds): + r""" + Return the module of all tensors of type `(k, l)` defined on ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring + """ + + +class ReflexiveModule_dual(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are the duals of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_dual + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + """ + + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual().tensor_type() + (0, 1) + """ + return (0, 1) + + def construction(self): + r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: A.construction() is None + True + """ + # Until https://trac.sagemath.org/ticket/34605 is done + return None + + +class ReflexiveModule_tensor(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are tensor products of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True + """ + + def tensor_factors(self): + r""" + Return the tensor factors of this tensor module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T = M.tensor_module(2, 3) + sage: T.tensor_factors() + [Rank-3 free module M over the Integer Ring, + Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring] + """ + tensor_type = self.tensor_type() + if tensor_type == (0,1): # case of the dual + raise NotImplementedError + bmodule = self.base_module() + factors = [bmodule] * tensor_type[0] + dmodule = bmodule.dual() + if tensor_type[1]: + factors += [dmodule] * tensor_type[1] + return factors diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 39174258ebb..8e56dbc0b76 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -29,7 +29,7 @@ `T^{(k,l)}(M)` is itself a free module over `R`, of rank `n^{k+l}`, `n` being the rank of `M`. Accordingly the class :class:`TensorFreeModule` inherits from the class -:class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. +:class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. .. TODO:: @@ -58,6 +58,7 @@ # http://www.gnu.org/licenses/ #****************************************************************************** +from sage.categories.modules import Modules from sage.misc.cachefunc import cached_method from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule_abstract from sage.tensor.modules.free_module_tensor import FreeModuleTensor @@ -66,9 +67,11 @@ from sage.tensor.modules.free_module_morphism import \ FiniteRankFreeModuleMorphism from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym -class TensorFreeModule(FiniteRankFreeModule_abstract): +class TensorFreeModule(ReflexiveModule_tensor, FiniteRankFreeModule_abstract): r""" Class for the free modules over a commutative ring `R` that are tensor products of a given free module `M` over `R` with itself and its @@ -126,7 +129,7 @@ class TensorFreeModule(FiniteRankFreeModule_abstract): ``T`` is a module (actually a free module) over `\ZZ`:: sage: T.category() - Category of finite dimensional modules over Integer Ring + Category of tensor products of finite dimensional modules over Integer Ring sage: T in Modules(ZZ) True sage: T.rank() @@ -336,7 +339,7 @@ class TensorFreeModule(FiniteRankFreeModule_abstract): Element = FreeModuleTensor - def __init__(self, fmodule, tensor_type, name=None, latex_name=None): + def __init__(self, fmodule, tensor_type, name=None, latex_name=None, category=None): r""" TESTS:: @@ -347,34 +350,25 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None): """ self._fmodule = fmodule self._tensor_type = tuple(tensor_type) + ring = fmodule._ring rank = pow(fmodule._rank, tensor_type[0] + tensor_type[1]) if self._tensor_type == (0,1): # case of the dual + category = Modules(ring).FiniteDimensional().or_subcategory(category) if name is None and fmodule._name is not None: name = fmodule._name + '*' if latex_name is None and fmodule._latex_name is not None: latex_name = fmodule._latex_name + r'^*' else: + category = Modules(ring).FiniteDimensional().TensorProducts().or_subcategory(category) if name is None and fmodule._name is not None: name = 'T^' + str(self._tensor_type) + '(' + fmodule._name + \ ')' if latex_name is None and fmodule._latex_name is not None: latex_name = r'T^{' + str(self._tensor_type) + r'}\left(' + \ fmodule._latex_name + r'\right)' - super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name) + super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name, category=category) fmodule._all_modules.add(self) - def construction(self): - r""" - TESTS:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: T = M.tensor_module(2, 3) - sage: T.construction() is None - True - """ - # No construction until https://trac.sagemath.org/ticket/31276 provides tensor_product methods - return None - #### Parent Methods def _element_constructor_(self, comp=[], basis=None, name=None, diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 5f6964f8546..ff4f739ac0d 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -178,6 +178,24 @@ def power_name(op, s, latex=False): latex_name=latex_name, category=category, ambient=ambient) + def construction(self): + # TODO: Define the symmetry group and its action (https://trac.sagemath.org/ticket/34495), + # return the construction functor for invariant subobjects. + r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring + sage: Sym2M.construction() is None + True + """ + return None + @cached_method def _basis_sym(self): r""" diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/calculus_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/calculus_doctest.py index 2e188829ed8..976b912de2a 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/calculus_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/calculus_doctest.py @@ -552,4 +552,3 @@ ) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/combinat_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/combinat_doctest.py index 9dc4f6e430a..864549d1958 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/combinat_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/combinat_doctest.py @@ -121,8 +121,7 @@ Sage example in ./combinat.tex, line 661:: - sage: C = L() - sage: C._name = 'C' + sage: C = L.undefined(valuation=1) sage: C.define( z + C * C ) Sage example in ./combinat.tex, line 666:: @@ -889,7 +888,7 @@ Sage example in ./combinat.tex, line 2697:: - sage: BT = CombinatorialSpecies() + sage: BT = CombinatorialSpecies(min=1) sage: Leaf = SingletonSpecies() sage: BT.define( Leaf + (BT*BT) ) @@ -906,7 +905,7 @@ Sage example in ./combinat.tex, line 2727:: sage: g = BT.isotype_generating_series(); g - x + x^2 + 2*x^3 + 5*x^4 + 14*x^5 + O(x^6) + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8) Sage example in ./combinat.tex, line 2733:: @@ -922,7 +921,7 @@ Sage example in ./combinat.tex, line 2752:: - sage: L = FW.isotype_generating_series().coefficients(15); L + sage: L = FW.isotype_generating_series()[:15]; L [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987] Sage example in ./combinat.tex, line 2769:: @@ -1053,4 +1052,3 @@ 645490122795799841856164638490742749440 """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/domaines_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/domaines_doctest.py index e08cf0fb5bb..94c50977d79 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/domaines_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/domaines_doctest.py @@ -442,4 +442,3 @@ (4) * (x + 2)^2 * (x^2 + 3) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py index abc31568dd3..aa3eed32f3b 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py @@ -476,4 +476,3 @@ 1.73205080756887729352744634151? """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/graphique_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/graphique_doctest.py index 472b7167ac9..0caad449666 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/graphique_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/graphique_doctest.py @@ -260,4 +260,3 @@ Graphics3d Object """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py index 35d367d8de7..c1d8fa977e5 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py @@ -417,4 +417,3 @@ sage: g.show(edge_colors=edge_coloring(g, hex_colors=True)) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py index 518e958cad4..fcb293eb698 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py @@ -289,4 +289,3 @@ mpf('2.7135204235459511323824699502438') """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py index 902b3c1aec2..5b99bdfa6ac 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py @@ -458,4 +458,3 @@ True """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py index d4910d7b691..f3aa2201ac8 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py @@ -233,4 +233,3 @@ ....: if p.get_values(B(u,v), convert=ZZ, tolerance=1e-3) == 1] ) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/mpoly_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/mpoly_doctest.py index f36b207d7e3..bef4a2b6c62 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/mpoly_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/mpoly_doctest.py @@ -559,4 +559,3 @@ 45 """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/nonlinear_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/nonlinear_doctest.py index 909d7d3c746..3a7104637ec 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/nonlinear_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/nonlinear_doctest.py @@ -489,4 +489,3 @@ 1/2*pi - (e^(1/2*pi) - 10)*e^(-1/2*pi) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/numbertheory_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/numbertheory_doctest.py index 8a1bed213bc..46a4d4d2bec 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/numbertheory_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/numbertheory_doctest.py @@ -154,4 +154,3 @@ 17 """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py index 951bfb2b5c6..219afcd22fd 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py @@ -257,7 +257,7 @@ Sage example in ./polynomes.tex, line 1531:: sage: C = ComplexField(15) - sage: Frac(C['x'])(r).partial_fraction_decomposition() + sage: Frac(C['x'])(r).partial_fraction_decomposition() #abs tol 2e-4 (x^4 - x^2 + 6.000, [0.5312/(x - 1.000), 0.06250/(x^2 - 2.000*x + 1.000), 4.385*I/(x - 1.732*I), (-4.385*I)/(x + 1.732*I), (-0.5312)/(x + 1.000), 0.06250/(x^2 + 2.000*x + 1.000)]) @@ -363,28 +363,27 @@ Sage example in ./polynomes.tex, line 2028:: sage: L.<x> = LazyPowerSeriesRing(QQ) - sage: lazy_exp = x.exponential(); lazy_exp - O(1) + sage: lazy_exp = x.exp(); lazy_exp + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) Sage example in ./polynomes.tex, line 2039:: sage: lazy_exp[5] 1/120 sage: lazy_exp - 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + O(x^6) + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) Sage example in ./polynomes.tex, line 2062:: sage: f = L(1) # the constant lazy series 1 sage: for i in range(5): - ....: f = (x*f).exponential() - ....: f.compute_coefficients(5) # forces the computation + ....: f = (x*f).exp() ....: print(f) # of the first coefficients - 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + O(x^6) - 1 + x + 3/2*x^2 + 5/3*x^3 + 41/24*x^4 + 49/30*x^5 + O(x^6) - 1 + x + 3/2*x^2 + 8/3*x^3 + 101/24*x^4 + 63/10*x^5 + O(x^6) - 1 + x + 3/2*x^2 + 8/3*x^3 + 125/24*x^4 + 49/5*x^5 + O(x^6) - 1 + x + 3/2*x^2 + 8/3*x^3 + 125/24*x^4 + 54/5*x^5 + O(x^6) + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) + 1 + x + 3/2*x^2 + 5/3*x^3 + 41/24*x^4 + 49/30*x^5 + 1057/720*x^6 + O(x^7) + 1 + x + 3/2*x^2 + 8/3*x^3 + 101/24*x^4 + 63/10*x^5 + 6607/720*x^6 + O(x^7) + 1 + x + 3/2*x^2 + 8/3*x^3 + 125/24*x^4 + 49/5*x^5 + 12847/720*x^6 + O(x^7) + 1 + x + 3/2*x^2 + 8/3*x^3 + 125/24*x^4 + 54/5*x^5 + 16087/720*x^6 + O(x^7) Sage example in ./polynomes.tex, line 2091:: @@ -393,10 +392,9 @@ Sage example in ./polynomes.tex, line 2105:: - sage: from sage.combinat.species.series import LazyPowerSeries - sage: f = LazyPowerSeries(L, name='f') - sage: f.define((x*f).exponential()) - sage: f.coefficients(8) + sage: f = L.undefined(valuation=0) + sage: f.define((x*f).exp()) + sage: f[:8] [1, 1, 3/2, 8/3, 125/24, 54/5, 16807/720, 16384/315] Sage example in ./polynomes.tex, line 2158:: @@ -406,4 +404,3 @@ (x^562949953421312 + 1, 562949953421312*x^562949953421311) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py index 7b8218b4899..fae01daa748 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py @@ -179,4 +179,3 @@ bla """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/programmation_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/programmation_doctest.py index dd2db19fd8a..3f036d5d362 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/programmation_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/programmation_doctest.py @@ -661,4 +661,3 @@ ....: return len(D) == len (Set(D.values())) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py index f53f813d793..50f936f8cba 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py @@ -385,4 +385,3 @@ 2**n*C0 + 2**(n + 1)*n """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/calculus_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/calculus_doctest.py index ca748ba2059..947f9f53a22 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/calculus_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/calculus_doctest.py @@ -263,4 +263,3 @@ True """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/combinat_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/combinat_doctest.py index 4cd1f78259c..5f372f505b0 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/combinat_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/combinat_doctest.py @@ -216,4 +216,3 @@ [0, 1, 1, 2, 5, 14, 42, 132, 429] """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/domaines_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/domaines_doctest.py index 03b4731f545..df3eb03d8fe 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/domaines_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/domaines_doctest.py @@ -58,4 +58,3 @@ <class 'sage.modules.with_basis.morphism.DiagonalModuleMorphism_with_category'> """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/float_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/float_doctest.py index 7e1d7f0c0e4..5d5d4686ec7 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/float_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/float_doctest.py @@ -140,4 +140,3 @@ [-1.0000000000000000 .. 1.0000000000000000] """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/integration_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/integration_doctest.py index e11b6bad8d9..bb9550918bf 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/integration_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/integration_doctest.py @@ -55,4 +55,3 @@ [-0.285398163397448, -0.00524656673640445, -0.00125482109302663] """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linalg_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linalg_doctest.py index 4e24775c753..7e164a3bffc 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linalg_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linalg_doctest.py @@ -55,4 +55,3 @@ False """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linsolve_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linsolve_doctest.py index 839ad0d7ec7..e89d7c06fb5 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linsolve_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/linsolve_doctest.py @@ -24,4 +24,3 @@ True """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/lp_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/lp_doctest.py index 5219f6f6552..d60adc9dd8b 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/lp_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/lp_doctest.py @@ -46,4 +46,3 @@ True """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/mpoly_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/mpoly_doctest.py index f056b349b3e..d4b92c1dfd6 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/mpoly_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/mpoly_doctest.py @@ -114,4 +114,3 @@ 1/16*u^2*v^2 - 3/8*u^2*v + 7/16*u^2 + 1/8*v^2 - 1/8*v - 1/8 """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/nonlinear_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/nonlinear_doctest.py index afa1a637b7e..f99860f7b9c 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/nonlinear_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/nonlinear_doctest.py @@ -110,4 +110,3 @@ 1/2*pi - (e^(1/2*pi) - 10)*e^(-1/2*pi) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/numbertheory_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/numbertheory_doctest.py index ceae289f561..2dbd0b018e5 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/numbertheory_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/numbertheory_doctest.py @@ -166,4 +166,3 @@ + 4/5*s3^3*x3^15 - 9/32*s3^2*x3^16 + 1/17*s3*x3^17 """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py index 2908f38254d..f8cefd2f6e8 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py @@ -106,4 +106,3 @@ + 21844/6081075*x^13 + O(x^15) """ - diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/recequadiff_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/recequadiff_doctest.py index 2dfe2109434..01d0e1bc143 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/recequadiff_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/recequadiff_doctest.py @@ -57,4 +57,3 @@ -sqrt(2*_C + 2*log(x))*x """ - diff --git a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py index daa0f3f6954..2d7b13c00f7 100644 --- a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py @@ -56,7 +56,6 @@ sage: D = Poset([X, R]) sage: D.plot() # not tested - ~~~~~~~~~~~~~~~~~~~~~~ :: @@ -73,7 +72,6 @@ sage: Q = Posets.PentagonPoset() sage: Q.plot() # not tested - ~~~~~~~~~~~~~~~~~~~~~~ :: @@ -154,7 +152,7 @@ ~~~~~~~~~~~~~~~~~~~~~~ :: sage: linear = P.linear_extension(); linear - [18, 9, 11, 6, 10, 0, 2, 1, 8, 3, 15, + [18, 9, 11, 6, 10, 0, 2, 1, 8, 3, 15, 7, 4, 14, 5, 19, 16, 13, 17, 12] ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/tests/books/judson-abstract-algebra/rings-sage.py b/src/sage/tests/books/judson-abstract-algebra/rings-sage.py index 67ed4823924..6d9571d00cd 100644 --- a/src/sage/tests/books/judson-abstract-algebra/rings-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/rings-sage.py @@ -90,7 +90,6 @@ sage: F.<a> = FiniteField(3^2) sage: P.<x> = Z7[] sage: S.<f,g,h> = QuaternionAlgebra(-7, 3) - ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index 1570b9c9b6a..12e73393206 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -725,7 +725,7 @@ def test_executable(args, input="", timeout=100.0, pydebug_ignore_warnings=False except KeyError: pass - __with_pydebug = hasattr(sys, 'gettotalrefcount') # This is a Python debug build (--with-pydebug) + __with_pydebug = hasattr(sys, 'gettotalrefcount') # This is a Python debug build (--with-pydebug) if __with_pydebug and pydebug_ignore_warnings: pexpect_env['PYTHONWARNINGS'] = ','.join([ 'ignore::DeprecationWarning', diff --git a/src/sage/tests/combinatorial_hopf_algebras.py b/src/sage/tests/combinatorial_hopf_algebras.py index 83a732a170f..6ac40f7aad5 100644 --- a/src/sage/tests/combinatorial_hopf_algebras.py +++ b/src/sage/tests/combinatorial_hopf_algebras.py @@ -48,4 +48,3 @@ sage: all(go2(n) for n in range(6)) # not tested (needs more morphisms) True """ - diff --git a/src/sage/tests/functools_partial_src.py b/src/sage/tests/functools_partial_src.py index 01e4af0f574..1fb24e15b34 100644 --- a/src/sage/tests/functools_partial_src.py +++ b/src/sage/tests/functools_partial_src.py @@ -22,4 +22,3 @@ def base(x): return x test_func = partial(base, 6) - diff --git a/src/sage/tests/gosper-sum.py b/src/sage/tests/gosper-sum.py index 95266ac235c..abdb622b18c 100644 --- a/src/sage/tests/gosper-sum.py +++ b/src/sage/tests/gosper-sum.py @@ -214,4 +214,3 @@ sage: t.simplify_full().is_trivial_zero() False """ - diff --git a/src/sage/tests/startup.py b/src/sage/tests/startup.py index debc9e6c37e..70629908446 100644 --- a/src/sage/tests/startup.py +++ b/src/sage/tests/startup.py @@ -6,7 +6,8 @@ not work. Instead, we test this by starting a new Python process:: sage: from sage.tests.cmdline import test_executable - sage: cmd = "from sage.all import *\nprint('IPython' in sys.modules)\n" + sage: environment = "sage.all" + sage: cmd = f"from {environment} import *\nprint('IPython' in sys.modules)\n" sage: print(test_executable(["sage", "--python"], cmd)[0]) # long time False diff --git a/src/sage/topology/delta_complex.py b/src/sage/topology/delta_complex.py index 8cbdcf5d293..420fd2fad5a 100644 --- a/src/sage/topology/delta_complex.py +++ b/src/sage/topology/delta_complex.py @@ -235,7 +235,7 @@ class DeltaComplex(GenericCellComplex): sage: delta_complexes.RealProjectivePlane() Delta complex with 2 vertices and 8 simplices - Type ``delta_complexes.`` and then hit the TAB key to get the + Type ``delta_complexes.`` and then hit the :kbd:`Tab` key to get the full list. """ def __init__(self, data=None, check_validity=True): diff --git a/src/sage/topology/filtered_simplicial_complex.py b/src/sage/topology/filtered_simplicial_complex.py index cbc0da0db16..a8193680b29 100644 --- a/src/sage/topology/filtered_simplicial_complex.py +++ b/src/sage/topology/filtered_simplicial_complex.py @@ -90,6 +90,7 @@ from sage.rings.infinity import infinity from sage.misc.cachefunc import cached_method + class FilteredSimplicialComplex(SageObject): r""" Define a filtered complex. @@ -321,7 +322,7 @@ def filtration(self, s, filtration_value=None): If ``filtration_value`` is set, this function inserts the simplex into the complex with the specified value. - See documentation of ``insert`` for more details. + See documentation of :meth:`insert` for more details. EXAMPLES:: @@ -338,7 +339,7 @@ def filtration(self, s, filtration_value=None): else: self._insert(s, filtration_value) - def prune(self,threshold): + def prune(self, threshold): r""" Return a copy of the filtered complex, where simplices above the threshold value have been removed. @@ -367,7 +368,7 @@ def prune(self,threshold): return result_complex - @cached_method(key=lambda self,f,s,v:(f,s)) + @cached_method(key=lambda self, f, s, v: (f, s)) def _persistent_homology(self, field=2, strict=True, verbose=False): """ Compute the homology intervals of the complex. @@ -445,7 +446,7 @@ def key(s): # Initialize data structures for the algo self._marked = [False] * n self._T = [None] * n - intervals = [[] for i in range(self._dimension+1)] + intervals = [[] for i in range(self._dimension + 1)] self.pairs = [] self._strict = strict @@ -491,7 +492,7 @@ def _add_interval(self, s, t, intervals): Add a new interval (i.e. homology element). This method should not be called by users, it is used in - the ``_compute_persistence`` method. The simplex of + the :meth:`_persistent_homology` method. The simplex of death may be ``None``, in which case the interval is infinite. INPUT: @@ -544,7 +545,7 @@ def _remove_pivot_rows(self, s, simplices): This method implements the subroutine of the same name in [ZC2005]_. This method should not be called by users, - it is used in the ``compute_persistence`` method. + it is used in the :meth:`_persistent_homology` method. TESTS:: @@ -565,11 +566,11 @@ def _remove_pivot_rows(self, s, simplices): return d # Initialize the boundary chain - for (i, f) in enumerate(s.faces()): + for i, f in enumerate(s.faces()): d += (-1)**i * self._chaingroup(f) # Remove all unmarked elements - for (s, x_s) in d: + for s, x_s in d: j = self._index_of_simplex[s] if not self._marked[j]: d = d - x_s * self._chaingroup(s) @@ -586,7 +587,7 @@ def _remove_pivot_rows(self, s, simplices): c = self._T[max_index][1] q = c[t] - d = d - ((q**(-1))*c) + d = d - ((q**(-1)) * c) return d @@ -595,8 +596,8 @@ def _max_index(self, d): Return the maximal index of all simplices with nonzero coefficient in ``d``. - This method is called in ``_remove_pivot_rows`` and - ``compute_persistence``. It should not be called by users + This method is called in :meth:`_remove_pivot_rows` and + :meth:`_persistent_homology`. It should not be called by users outside of those methods. TESTS:: @@ -611,7 +612,7 @@ def _max_index(self, d): 6 """ currmax = -1 - for (s, x_s) in d: + for s, x_s in d: j = self._index_of_simplex[s] if j > currmax: currmax = j @@ -664,7 +665,7 @@ def betti_number(self, k, a, b, field=2, strict=True, verbose=None): persistent homology computation; the default is the verbosity of ``self`` - The Betti number ``\beta_k^{a,a+b}`` counts the number of + The Betti number `\beta_k^{a,a+b}` counts the number of homology elements which are alive throughout the whole duration ``[a, a+b]``. @@ -686,7 +687,7 @@ def betti_number(self, k, a, b, field=2, strict=True, verbose=None): if verbose is None: verbose = self._verbose intervals = self._persistent_homology(field, strict, verbose=verbose) - return Integer(sum(1 for (i, j) in intervals[k] + return Integer(sum(1 for i, j in intervals[k] if (i <= a and a + b < j) and a >= 0)) def _repr_(self): @@ -720,7 +721,7 @@ def _repr_(self): def _simplicial_(self): """ - Return the associated simplicial complex + Return the associated simplicial complex. All simplices of the filtered simplicial complex are included in the resulting simplicial complex. diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 736df96ef20..79393f7317b 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -165,6 +165,7 @@ from sage.structure.parent import Parent from sage.rings.integer import Integer from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.polynomial_ring import polygens from sage.sets.set import Set from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -1521,6 +1522,10 @@ def f_triangle(self): The `f`-triangle is given by `f_{i,j}` being the number of faces `F` of size `j` such that `i = \max_{G \subseteq F} |G|`. + .. SEEALSO:: + + Not to be confused with :meth:`F_triangle` . + EXAMPLES:: sage: X = SimplicialComplex([[1,2,3], [3,4,5], [1,4], [1,5], [2,4], [2,5]]) @@ -1577,6 +1582,48 @@ def h_triangle(self): for k in range(j+1)) return ret + def F_triangle(self, S): + """ + Return the F-triangle of ``self`` with respect + to one maximal simplex ``S``. + + This is the bivariate generating polynomial of all faces, + according to the number of elements in ``S`` and outside ``S``. + + OUTPUT: + + an :class:`~sage.combinat.triangles_FHM.F_triangle` + + .. SEEALSO:: + + Not to be confused with :meth:`f_triangle` . + + EXAMPLES:: + + sage: cs = simplicial_complexes.Torus() + sage: cs.F_triangle(cs.facets()[0]) + F: x^3 + 9*x^2*y + 3*x*y^2 + y^3 + 6*x^2 + 12*x*y + + 3*y^2 + 4*x + 3*y + 1 + + TESTS:: + + sage: S = SimplicialComplex([]) + sage: S.F_triangle(S.facets()[0]) + F: 1 + """ + x, y = polygens(ZZ, 'x, y') + from sage.combinat.triangles_FHM import F_triangle + + def nega(f): + return sum(1 for v in f if v in S) + + def posi(f): + return f.dimension() + 1 - nega(f) + + poly = sum(x**posi(fa) * y**nega(fa) + for fa in self.face_iterator()) + return F_triangle(poly) + def flip_graph(self): """ If ``self`` is pure, then it returns the flip graph of ``self``, @@ -4081,7 +4128,7 @@ def fundamental_group(self, base_point=None, simplify=True): return self.connected_component(Simplex([base_point])).fundamental_group(simplify=simplify) from sage.groups.free_group import FreeGroup - from sage.interfaces.gap import gap + from sage.libs.gap.libgap import libgap as gap G = self.graph() # If the vertices and edges of G are not sortable, e.g., a mix # of str and int, Sage+Python 3 may raise a TypeError when diff --git a/src/sage/topology/simplicial_complex_catalog.py b/src/sage/topology/simplicial_complex_catalog.py index 07a0de43b4a..6e86db14cb5 100644 --- a/src/sage/topology/simplicial_complex_catalog.py +++ b/src/sage/topology/simplicial_complex_catalog.py @@ -49,7 +49,7 @@ - :meth:`~sage.topology.examples.ZieglerBall` You can also get a list by typing ``simplicial_complexes.`` and hitting the -TAB key. +:kbd:`Tab` key. EXAMPLES:: diff --git a/src/sage/topology/simplicial_complex_examples.py b/src/sage/topology/simplicial_complex_examples.py index a21391beab3..65914f47830 100644 --- a/src/sage/topology/simplicial_complex_examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -49,7 +49,7 @@ - :func:`ZieglerBall` You can also get a list by typing ``simplicial_complexes.`` and hitting the -TAB key. +:kbd:`Tab` key. EXAMPLES:: diff --git a/src/sage/topology/simplicial_set.py b/src/sage/topology/simplicial_set.py index 0fa0e6495d9..0ca2ff5bcb2 100644 --- a/src/sage/topology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -62,7 +62,7 @@ sage: simplicial_sets.ClassifyingSpace(Sigma4) Classifying space of Symmetric group of order 4! as a permutation group -Type ``simplicial_sets.`` and hit the ``TAB`` key to get a full list +Type ``simplicial_sets.`` and hit the :kbd:`Tab` key to get a full list of the predefined simplicial sets. You can construct new simplicial sets from old by taking quotients, @@ -3819,7 +3819,9 @@ def standardize_degeneracies(*L): - ``L`` -- list of integers, representing a composition of degeneracies in a simplicial set. - OUTPUT: an equivalent list of degeneracies, standardized to be + OUTPUT: + + an equivalent list of degeneracies, standardized to be written in decreasing order, using the simplicial identity .. MATH:: @@ -3923,7 +3925,9 @@ def standardize_face_maps(*L): - ``L`` -- list of integers, representing a composition of face maps in a simplicial set. - OUTPUT: an equivalent list of face maps, standardized to be + OUTPUT: + + an equivalent list of face maps, standardized to be written in non-increasing order, using the simplicial identity .. MATH:: diff --git a/src/sage/typeset/unicode_characters.py b/src/sage/typeset/unicode_characters.py index 4eacf326c43..c4aa7bdd5fb 100644 --- a/src/sage/typeset/unicode_characters.py +++ b/src/sage/typeset/unicode_characters.py @@ -99,5 +99,3 @@ unicode_mathbbR = '\u211D' # 'ℝ' unicode_mathbbC = '\u2102' # 'ℂ' - - diff --git a/src/sage/version.py b/src/sage/version.py index 94cf8afef07..5c277db17d7 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 = '9.8.beta0' -date = '2022-09-25' -banner = 'SageMath version 9.8.beta0, Release Date: 2022-09-25' +version = '10.0.beta0' +date = '2023-02-12' +banner = 'SageMath version 10.0.beta0, Release Date: 2023-02-12' diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 245fce04aeb..4c00a1d338b 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -187,6 +187,24 @@ def sphinx_plot(graphics, **kwds): highlighting.lexers['ipython'] = IPyLexer() highlight_language = 'ipycon' +# Create table of contents entries for domain objects (e.g. functions, classes, +# attributes, etc.). Default is True. +toc_object_entries = True + +# A string that determines how domain objects (e.g. functions, classes, +# attributes, etc.) are displayed in their table of contents entry. +# +# Use "domain" to allow the domain to determine the appropriate number of parents +# to show. For example, the Python domain would show Class.method() and +# function(), leaving out the module. level of parents. This is the default +# setting. +# +# Use "hide" to only show the name of the element without any parents (i.e. method()). +# +# Use "all" to show the fully-qualified name for the object (i.e. module.Class.method()), +# displaying all parents. +toc_object_entries_show_parents = 'hide' + # Extension configuration # ----------------------- @@ -196,6 +214,7 @@ def sphinx_plot(graphics, **kwds): # Cross-links to other project's online documentation. python_version = sys.version_info.major + def set_intersphinx_mappings(app, config): """ Add precompiled inventory (the objects.inv) @@ -207,7 +226,7 @@ def set_intersphinx_mappings(app, config): app.config.intersphinx_mapping = {} return - app.config.intersphinx_mapping = { + app.config.intersphinx_mapping = { 'python': ('https://docs.python.org/', os.path.join(SAGE_DOC_SRC, "common", "python{}.inv".format(python_version))), @@ -232,6 +251,7 @@ def set_intersphinx_mappings(app, config): intersphinx.normalize_intersphinx_mapping(app, config) + # By default document are not master. multidocs_is_master = True @@ -819,6 +839,7 @@ def nitpick_patch_config(app): '__builtin__', ] + def check_nested_class_picklability(app, what, name, obj, skip, options): """ Print a warning if pickling is broken for nested classes. @@ -878,6 +899,7 @@ class will be properly documented inside its surrounding class. return skip + # This replaces the setup() in sage.misc.sagedoc_conf def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) @@ -904,4 +926,3 @@ def setup(app): app.connect('missing-reference', find_sage_dangling_links) app.connect('builder-inited', nitpick_patch_config) app.connect('html-page-context', add_page_context) - diff --git a/src/sage_docbuild/ext/multidocs.py b/src/sage_docbuild/ext/multidocs.py index 39121ef90ac..70096178b9f 100644 --- a/src/sage_docbuild/ext/multidocs.py +++ b/src/sage_docbuild/ext/multidocs.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- """ - multi documentation in Sphinx - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The goal of this extension is to manage a multi documentation in Sphinx. - To be able to compile Sage's huge documentation in parallel, the - documentation is cut into a bunch of independent documentations called - "subdocs", which are compiled separately. There is a master document which - points to all the subdocs. The intersphinx extension ensures that the - cross-link between the subdocs are correctly resolved. However some work - is needed to build a global index. This is the goal of multidocs. - - More precisely this extension ensures the correct merging of - - the todo list if this extension is activated; - - the python indexes; - - the list of python modules; - - the javascript index; - - the citations. -""" +Sage multidocs extension + +The goal of this extension is to manage a multi-documentation in Sphinx. To be +able to compile Sage's huge documentation in parallel, the documentation is cut +into a bunch of independent documentations called "sub-docs", which are +compiled separately. There is a master document which points to all the +sub-docs. The intersphinx extension ensures that the cross-link between the +sub-docs are correctly resolved. However some work is needed to build a global +index. This is the goal of the ``multidocs`` extension. + +More precisely this extension ensures the correct merging of + +- the todo list if this extension is activated +- the python indexes +- the list of python modules +- the javascript index +- the citations +""" import os import pickle import shutil @@ -35,15 +35,15 @@ def merge_environment(app, env): """ - Merges the following attributes of the sub-docs environment into the main + Merge the following attributes of the sub-docs environment into the main environment: - - titles # Titles - - todo_all_todos # ToDo's - - indexentries # global python index - - all_docs # needed by the js index - - citations # citations - - domaindata['py']['modules'] # list of python modules + - ``titles`` -- Titles + - ``todo_all_todos`` -- todo's + - ``indexentries`` -- global python index + - ``all_docs`` -- needed by the js index + - ``citations`` -- citations + - ``domaindata['py']['modules']`` -- list of python modules """ logger.info(bold('Merging environment/index files...')) for curdoc in app.env.config.multidocs_subdoc_list: @@ -81,7 +81,7 @@ def merge_environment(app, env): env.all_docs.update(newalldoc) # needed by env.check_consistency (sphinx.environment, line 1734) for ind in newalldoc: - # treat subdocument source as orphaned file and don't complain + # treat sub-document source as orphaned file and don't complain md = env.metadata.get(ind, dict()) md['orphan'] = 1 env.metadata[ind] = md @@ -146,6 +146,10 @@ def merge_js_index(app): titles = app.builder.indexer._titles for (res, title) in index._titles.items(): titles[fixpath(res)] = title + # merge the alltitles + alltitles = app.builder.indexer._all_titles + for (res, alltitle) in index._all_titles.items(): + alltitles[fixpath(res)] = alltitle # merge the filenames filenames = app.builder.indexer._filenames for (res, filename) in index._filenames.items(): @@ -192,14 +196,14 @@ def get_js_index(app, curdoc): def fix_path_html(app, pagename, templatename, ctx, event_arg): """ - Fixes the context so that the files - - search.html - - genindex.html - - py-modindex.html - point to the right place, that is in - reference/ - instead of - reference/subdocument + Fix the context so that the files + + - :file:`search.html` + - :file:`genindex.html` + - :file:`py-modindex.html` + + point to the right place, that is in :file:`reference/` instead of + :file:`reference/subdocument`. """ # sphinx/builder/html.py line 702 # def pathto(otheruri, resource=False, @@ -265,7 +269,7 @@ def fetch_citation(app: Sphinx, env): def init_subdoc(app): """ - Init the merger depending on if we are compiling a subdoc or the master + Init the merger depending on if we are compiling a sub-doc or the master doc itself. """ if app.config.multidocs_is_master: diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 5b97b272718..b14c0e04fe3 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1,18 +1,23 @@ -# -*- coding: utf-8 -*- """ -Sage autodoc extension based on sphinx.ext.autodoc from Sphinx +Sage autodoc extension -From sphinx.ext.autodoc: +This is :mod:`sphinx.ext.autodoc` extension modified for Sage objects. + +The original header of :mod:`sphinx.ext.autodoc`: + + Extension to create automatic documentation from code docstrings. Automatically insert docstrings for functions, classes or whole modules into the doctree, thus avoiding duplication between docstrings and documentation for those who like elaborate docstrings. - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. +This module is currently based on :mod:`sphinx.ext.autodoc` from Sphinx version +5.3.0. Compare against the upstream original source file +`sphinx/ext/autodoc/__init__.py +<https://github.com/sphinx-doc/sphinx/blob/v5.3.0/sphinx/ext/autodoc/__init__.py>`_. -The upstream original can be found at -https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/autodoc/__init__.py +In the source file of this module, major modifications are delimited by a pair +of comment dividers. To lessen maintenance burden, we aim at reducing the modifications. AUTHORS: @@ -23,38 +28,49 @@ - Simon King (2011-04): use sageinspect; include public cython attributes only in the documentation if they have a docstring -- Kwankyu Lee (2018-12-26): rebase on the latest sphinx.ext.autodoc +- Kwankyu Lee (2018-12-26, 2022-11-08): rebased on the latest sphinx.ext.autodoc """ -import inspect import re -import sys +import warnings +from inspect import Parameter, Signature +from types import ModuleType +from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Sequence, + Set, Tuple, Type, TypeVar, Union) -from docutils.statemachine import ViewList +from docutils.statemachine import StringList import sphinx -from sphinx.ext.autodoc import mock, ObjectMember -from sphinx.ext.autodoc.importer import import_object, get_object_members +from sphinx.application import Sphinx +from sphinx.config import ENUM, Config +from sphinx.deprecation import RemovedInSphinx60Warning +from sphinx.environment import BuildEnvironment +from sphinx.ext.autodoc.importer import (get_class_members, get_object_members, import_module, + import_object) +from sphinx.ext.autodoc.mock import ismock, mock, undecorate from sphinx.locale import _, __ -from sphinx.pycode import ModuleAnalyzer -from sphinx.errors import PycodeError -from sphinx.util import logging -from sphinx.util.docstrings import prepare_docstring -from sphinx.util.inspect import isdescriptor, \ - safe_getattr, object_description, is_builtin_class_method, \ - isenumattribute, isclassmethod, isstaticmethod, getdoc - +from sphinx.pycode import ModuleAnalyzer, PycodeError +from sphinx.util import inspect, logging +from sphinx.util.docstrings import prepare_docstring, separate_metadata +from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr, + stringify_signature) +from sphinx.util.typing import OptionSpec, get_type_hints, restify +from sphinx.util.typing import stringify as stringify_typehint + +# ------------------------------------------------------------------ from sage.misc.sageinspect import (sage_getdoc_original, sage_getargspec, isclassinstance, sage_formatargspec, is_function_or_cython_function) -from sage.misc.lazy_import import LazyImport -# This is used to filter objects of classes that inherit from -# ClasscallMetaclass. See the class AttributeDocumenter below. -from sage.misc.classcall_metaclass import ClasscallMetaclass +_getdoc = getdoc +def getdoc(obj, *args, **kwargs): + return sage_getdoc_original(obj) +# ------------------------------------------------------------------ +if TYPE_CHECKING: + from sphinx.ext.autodoc.directive import DocumenterBridge logger = logging.getLogger(__name__) @@ -64,7 +80,7 @@ MethodDescriptorType = type(type.__subclasses__) -#: extended signature RE: with explicit module name separated by :: +#: extended signature RE: with explicit module name separated by ``::``. py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name ([\w.]+\.)? # module and/or class name(s) @@ -73,116 +89,119 @@ (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) +special_member_re = re.compile(r'^__\S+__$') -def identity(x): - # type: (Any) -> Any +def identity(x: Any) -> Any: return x -ALL = object() +class _All: + """A special value for ``:*-members:`` that matches to any member.""" + + def __contains__(self, item: Any) -> bool: + return True + + def append(self, item: Any) -> None: + pass # nothing + + +class _Empty: + """A special value for :exclude-members: that never matches to any member.""" + + def __contains__(self, item: Any) -> bool: + return False + + +ALL = _All() +EMPTY = _Empty() +UNINITIALIZED_ATTR = object() INSTANCEATTR = object() +SLOTSATTR = object() -def members_option(arg): - # type: (Any) -> Union[object, List[unicode]] +def members_option(arg: Any) -> Union[object, List[str]]: """Used to convert the :members: option to auto directives.""" - if arg is None: + if arg in (None, True): return ALL - return [x.strip() for x in arg.split(',')] + elif arg is False: + return None + else: + return [x.strip() for x in arg.split(',') if x.strip()] -def members_set_option(arg): - # type: (Any) -> Union[object, Set[unicode]] - """Used to convert the :members: option to auto directives.""" - if arg is None: - return ALL - return set(x.strip() for x in arg.split(',')) +def exclude_members_option(arg: Any) -> Union[object, Set[str]]: + """Used to convert the :exclude-members: option.""" + if arg in (None, True): + return EMPTY + return {x.strip() for x in arg.split(',') if x.strip()} + + +def inherited_members_option(arg: Any) -> Set[str]: + """Used to convert the :inherited-members: option to auto directives.""" + if arg in (None, True): + return {'object'} + elif arg: + return {x.strip() for x in arg.split(',')} + else: + return set() + + +def member_order_option(arg: Any) -> Optional[str]: + """Used to convert the :member-order: option to auto directives.""" + if arg in (None, True): + return None + elif arg in ('alphabetical', 'bysource', 'groupwise'): + return arg + else: + raise ValueError(__('invalid value for member-order option: %s') % arg) + + +def class_doc_from_option(arg: Any) -> Optional[str]: + """Used to convert the :class-doc-from: option to autoclass directives.""" + if arg in ('both', 'class', 'init'): + return arg + else: + raise ValueError(__('invalid value for class-doc-from option: %s') % arg) SUPPRESS = object() -def annotation_option(arg): - # type: (Any) -> Any - if arg is None: +def annotation_option(arg: Any) -> Any: + if arg in (None, True): # suppress showing the representation of the object return SUPPRESS else: return arg -def bool_option(arg): - # type: (Any) -> bool +def bool_option(arg: Any) -> bool: """Used to convert flag options to auto directives. (Instead of directives.flag(), which returns None). """ return True -def formatargspec(function, args, varargs=None, varkw=None, defaults=None, - kwonlyargs=(), kwonlydefaults={}, annotations={}): - """ - Sphinx's version of formatargspec is deprecated, so use Sage's instead. - """ - return sage_formatargspec(args, varargs=varargs, varkw=varkw, defaults=defaults, - kwonlyargs=kwonlyargs, kwonlydefaults=kwonlydefaults, - annotations=annotations) - - -class AutodocReporter(): +def merge_members_option(options: Dict) -> None: + """Merge :private-members: and :special-members: options to the + :members: option. """ - A reporter replacement that assigns the correct source name - and line number to a system message, as recorded in a ViewList. - """ - def __init__(self, viewlist, reporter): - # type: (ViewList, Reporter) -> None - self.viewlist = viewlist - self.reporter = reporter - - def __getattr__(self, name): - # type: (unicode) -> Any - return getattr(self.reporter, name) - - def system_message(self, level, message, *children, **kwargs): - # type: (int, unicode, Any, Any) -> nodes.system_message - if 'line' in kwargs and 'source' not in kwargs: - try: - source, line = self.viewlist.items[kwargs['line']] - except IndexError: - pass - else: - kwargs['source'] = source - kwargs['line'] = line - return self.reporter.system_message(level, message, - *children, **kwargs) - - def debug(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - if self.reporter.debug_flag: - return self.system_message(0, *args, **kwargs) - - def info(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(1, *args, **kwargs) - - def warning(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(2, *args, **kwargs) - - def error(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(3, *args, **kwargs) + if options.get('members') is ALL: + # merging is not needed when members: ALL + return - def severe(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(4, *args, **kwargs) + members = options.setdefault('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: + members.append(member) # Some useful event listener factories for autodoc-process-docstring. -def cut_lines(pre, post=0, what=None): - # type: (int, int, unicode) -> Callable +def cut_lines(pre: int, post: int = 0, what: str = None) -> Callable: """Return a listener that removes the first *pre* and last *post* lines of every docstring. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. @@ -192,10 +211,10 @@ def cut_lines(pre, post=0, what=None): from sphinx.ext.autodoc import cut_lines app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) - This can (and should) be used in place of :confval:`automodule_skip_lines`. + This can (and should) be used in place of ``automodule_skip_lines``. """ - def process(app, what_, name, obj, options, lines): - # type: (Sphinx, unicode, unicode, Any, Any, List[unicode]) -> None + def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str] + ) -> None: if what and what_ not in what: return del lines[:pre] @@ -210,8 +229,8 @@ def process(app, what_, name, obj, options, lines): return process -def between(marker, what=None, keepempty=False, exclude=False): - # type: (unicode, Sequence[unicode], bool, bool) -> Callable +def between(marker: str, what: Sequence[str] = None, keepempty: bool = False, + exclude: bool = False) -> Callable: """Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made @@ -222,8 +241,8 @@ def between(marker, what=None, keepempty=False, exclude=False): """ marker_re = re.compile(marker) - def process(app, what_, name, obj, options, lines): - # type: (Sphinx, unicode, unicode, Any, Any, List[unicode]) -> None + def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str] + ) -> None: if what and what_ not in what: return deleted = 0 @@ -246,7 +265,50 @@ def process(app, what_, name, obj, options, lines): return process -class Documenter(): +# This class is used only in ``sphinx.ext.autodoc.directive``, +# But we define this class here to keep compatibility (see #4538) +class Options(dict): + """A dict/attribute hybrid that returns None on nonexisting keys.""" + def copy(self) -> "Options": + return Options(super().copy()) + + def __getattr__(self, name: str) -> Any: + try: + return self[name.replace('_', '-')] + except KeyError: + return None + + +class ObjectMember(tuple): + """A member of object. + + This is used for the result of ``Documenter.get_object_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 __new__(cls, name: str, obj: Any, **kwargs: Any) -> Any: + return super().__new__(cls, (name, obj)) # type: ignore + + def __init__(self, name: str, obj: Any, docstring: Optional[str] = None, + class_: Any = None, skipped: bool = False) -> None: + self.__name__ = name + self.object = obj + self.docstring = docstring + self.skipped = skipped + self.class_ = class_ + + +ObjectMembers = Union[List[ObjectMember], List[Tuple[str, Any]]] + + +class Documenter: """ A Documenter knows how to autodocument a single object type. When registered with the AutoDirective, it will be used to document objects @@ -259,13 +321,13 @@ class Documenter(): A Documenter has an *option_spec* that works like a docutils directive's; in fact, it will be used to parse an auto directive's options that matches - the documenter. + the Documenter. """ #: name by which the directive is called (auto...) and the default #: generated directive name objtype = 'object' #: indentation by which to indent the directive content - content_indent = u' ' + content_indent = ' ' #: priority if multiple documenters return True from can_document_member priority = 0 #: order if autodoc_member_order is set to 'groupwise' @@ -273,57 +335,59 @@ class Documenter(): #: true if the generated content may contain titles titles_allowed = False - option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable] + option_spec: OptionSpec = { + 'noindex': bool_option + } - def get_attr(self, obj, name, *defargs): - # type: (Any, unicode, Any) -> Any + def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any: """getattr() override for types such as Zope interfaces.""" return autodoc_attrgetter(self.env.app, obj, name, *defargs) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - """Called to see if a member can be documented by this documenter.""" + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + """Called to see if a member can be documented by this Documenter.""" raise NotImplementedError('must be implemented in subclasses') - def __init__(self, directive, name, indent=u''): - # type: (DocumenterBridge, unicode, unicode) -> None + def __init__(self, directive: "DocumenterBridge", name: str, indent: str = '') -> None: self.directive = directive - self.env = directive.env # type: BuildEnvironment + self.config: Config = directive.env.config + self.env: BuildEnvironment = directive.env self.options = directive.genopt self.name = name self.indent = indent # the module and object path within the module, and the fully # qualified name (all set after resolve_name succeeds) - self.modname = None # type: str - self.module = None # type: ModuleType - self.objpath = None # type: List[unicode] - self.fullname = None # type: unicode + self.modname: str = None + self.module: ModuleType = None + self.objpath: List[str] = None + self.fullname: str = None # extra signature items (arguments and return annotation, # also set after resolve_name succeeds) - self.args = None # type: unicode - self.retann = None # type: unicode + self.args: str = None + self.retann: str = None # the object to document (set after import_object succeeds) - self.object = None # type: Any - self.object_name = None # type: unicode + self.object: Any = None + self.object_name: str = None # the parent/owner of the object to document - self.parent = None # type: Any + self.parent: Any = None # the module analyzer to get at attribute docs, or None - self.analyzer = None # type: Any + self.analyzer: ModuleAnalyzer = None @property - def documenters(self): - # type: () -> Dict[unicode, Type[Documenter]] + def documenters(self) -> Dict[str, Type["Documenter"]]: """Returns registered Documenter classes""" - return get_documenters(self.env.app) + return self.env.app.registry.documenters - def add_line(self, line, source, *lineno): - # type: (unicode, unicode, int) -> None + def add_line(self, line: str, source: str, *lineno: int) -> None: """Append one line of generated reST to the output.""" - self.directive.result.append(self.indent + line, source, *lineno) + if line.strip(): # not a blank line + self.directive.result.append(self.indent + line, source, *lineno) + else: + self.directive.result.append('', source, *lineno) - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: """Resolve the module and name of the object to document given by the arguments and the current module/class. @@ -333,8 +397,7 @@ def resolve_name(self, modname, parents, path, base): """ raise NotImplementedError('must be implemented in subclasses') - def parse_name(self): - # type: () -> bool + def parse_name(self) -> bool: """Determine what module to import and what attribute to document. Returns True and sets *self.modname*, *self.objpath*, *self.fullname*, @@ -344,52 +407,57 @@ def parse_name(self): # functions can contain a signature which is then used instead of # an autogenerated one try: - explicit_modname, path, base, args, retann = \ - py_ext_sig_re.match(self.name).groups() # type: ignore + matched = py_ext_sig_re.match(self.name) + explicit_modname, path, base, args, retann = matched.groups() except AttributeError: - 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 # support explicit module and class name separation via :: if explicit_modname is not None: modname = explicit_modname[:-2] - parents = path and path.rstrip('.').split('.') or [] + parents = path.rstrip('.').split('.') if path else [] else: modname = None parents = [] - self.modname, self.objpath = self.resolve_name(modname, parents, path, base) + with mock(self.config.autodoc_mock_imports): + self.modname, self.objpath = self.resolve_name(modname, parents, path, base) if not self.modname: return False self.args = args self.retann = retann - self.fullname = (self.modname or '') + \ - (self.objpath and '.' + '.'.join(self.objpath) or '') + self.fullname = ((self.modname or '') + + ('.' + '.'.join(self.objpath) if self.objpath else '')) return True - def import_object(self): - # type: () -> bool + def import_object(self, raiseerror: bool = False) -> bool: """Import the object given by *self.modname* and *self.objpath* and set it as *self.object*. Returns True if successful, False if an error occurred. """ - with mock(self.env.config.autodoc_mock_imports): + with mock(self.config.autodoc_mock_imports): try: ret = import_object(self.modname, self.objpath, self.objtype, attrgetter=self.get_attr, - warningiserror=self.env.config.autodoc_warningiserror) + warningiserror=self.config.autodoc_warningiserror) self.module, self.parent, self.object_name, self.object = ret + if ismock(self.object): + self.object = undecorate(self.object) return True except ImportError as exc: - logger.warning(exc.args[0], type='autodoc', subtype='import_object') - self.env.note_reread() - return False + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False - def get_real_modname(self): - # type: () -> str + def get_real_modname(self) -> str: """Get the real module name of an object to document. It can differ from the name of the module through which the object was @@ -397,29 +465,27 @@ def get_real_modname(self): """ return self.get_attr(self.object, '__module__', None) or self.modname - def check_module(self): - # type: () -> bool + def check_module(self) -> bool: """Check if *self.object* is really defined in the module given by *self.modname*. """ if self.options.imported_members: return True - modname = self.get_attr(self.object, '__module__', None) + subject = inspect.unpartial(self.object) + modname = self.get_attr(subject, '__module__', None) if modname and modname != self.modname: return False return True - def format_args(self): - # type: () -> unicode + def format_args(self, **kwargs: Any) -> str: """Format the argument signature of *self.object*. Should return None if the object does not have a signature. """ return None - def format_name(self): - # type: () -> unicode + def format_name(self) -> str: """Format the name of *self.object*. This normally should be something that can be parsed by the generated @@ -430,68 +496,88 @@ def format_name(self): # directives of course) return '.'.join(self.objpath) or self.modname - def format_signature(self): - # type: () -> unicode + def _call_format_args(self, **kwargs: Any) -> str: + if kwargs: + try: + return self.format_args(**kwargs) + except TypeError: + # avoid chaining exceptions, by putting nothing here + pass + + # retry without arguments for old documenters + return self.format_args() + + def format_signature(self, **kwargs: Any) -> str: """Format the signature (arguments and return annotation) of the object. Let the user process it via the ``autodoc-process-signature`` event. """ if self.args is not None: # signature given explicitly - args = "(%s)" % self.args # type: unicode + args = "(%s)" % self.args + retann = self.retann else: # try to introspect the signature try: - args = self.format_args() - except Exception as err: - logger.warning('error while formatting arguments for %s: %s' % - (self.fullname, err)) + retann = None + args = self._call_format_args(**kwargs) + if args: + matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args) + if matched: + args = matched.group(1) + retann = matched.group(2) + except Exception as exc: + logger.warning(__('error while formatting arguments for %s: %s'), + self.fullname, exc, type='autodoc') args = None - retann = self.retann - - result = self.env.app.emit_firstresult( - 'autodoc-process-signature', self.objtype, self.fullname, - self.object, self.options, args, retann) + result = self.env.events.emit_firstresult('autodoc-process-signature', + self.objtype, self.fullname, + self.object, self.options, args, retann) if result: args, retann = result if args is not None: - return args + (retann and (' -> %s' % retann) or '') + return args + ((' -> %s' % retann) if retann else '') else: return '' - def add_directive_header(self, sig): - # type: (unicode) -> None + def add_directive_header(self, sig: str) -> None: """Add the directive header and options to the generated content.""" domain = getattr(self, 'domain', 'py') directive = getattr(self, 'directivetype', self.objtype) name = self.format_name() sourcename = self.get_sourcename() - self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig), - sourcename) + + # one signature per line, indented by column + prefix = '.. %s:%s:: ' % (domain, directive) + for i, sig_line in enumerate(sig.split("\n")): + self.add_line('%s%s%s' % (prefix, name, sig_line), + sourcename) + if i == 0: + prefix = " " * len(prefix) + if self.options.noindex: - self.add_line(u' :noindex:', sourcename) + self.add_line(' :noindex:', sourcename) if self.objpath: # Be explicit about the module, this is necessary since .. class:: # etc. don't support a prepended module name - self.add_line(u' :module: %s' % self.modname, sourcename) - - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - """Decode and return lines of the docstring(s) for the object.""" - docstring = sage_getdoc_original(self.object) - if docstring is None and self.env.config.autodoc_inherit_docstrings: - docstring = getdoc(self.object) - # make sure we have Unicode docstrings, then sanitize and split - # into lines - if isinstance(docstring, str): - return [prepare_docstring(docstring)] - # ... else it is something strange, let's ignore it + self.add_line(' :module: %s' % self.modname, sourcename) + + def get_doc(self) -> Optional[List[List[str]]]: + """Decode and return lines of the docstring(s) for the object. + + When it returns None, autodoc-process-docstring will not be called for this + object. + """ + docstring = getdoc(self.object, self.get_attr, self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tab_width)] return [] - def process_doc(self, docstrings): - # type: (List[List[unicode]]) -> Iterator[unicode] + def process_doc(self, docstrings: List[List[str]]) -> Iterator[str]: """Let the user process the docstrings before adding them.""" for docstringlines in docstrings: if self.env.app: @@ -499,24 +585,31 @@ def process_doc(self, docstrings): self.env.app.emit('autodoc-process-docstring', self.objtype, self.fullname, self.object, self.options, docstringlines) - for line in docstringlines: - yield line - def get_sourcename(self): - # type: () -> unicode + if docstringlines and docstringlines[-1] != '': + # append a blank line to the end of the docstring + docstringlines.append('') + + yield from docstringlines + + def get_sourcename(self) -> str: + if (inspect.safe_getattr(self.object, '__module__', None) and + inspect.safe_getattr(self.object, '__qualname__', None)): + # Get the correct location of docstring from self.object + # to support inherited methods + fullname = '%s.%s' % (self.object.__module__, self.object.__qualname__) + else: + fullname = self.fullname + if self.analyzer: - # prevent encoding errors when the file name is non-ASCII - if not isinstance(self.analyzer.srcname, str): - filename = str(self.analyzer.srcname, - sys.getfilesystemencoding(), 'replace') - else: - filename = self.analyzer.srcname - return u'%s:docstring of %s' % (filename, self.fullname) - return u'docstring of %s' % self.fullname + return '%s:docstring of %s' % (self.analyzer.srcname, fullname) + else: + return 'docstring of %s' % fullname - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None + def add_content(self, more_content: Optional[StringList]) -> None: """Add content from docstrings, attribute documentation and user.""" + docstring = True + # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: @@ -524,57 +617,64 @@ def add_content(self, more_content, no_docstring=False): if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: - no_docstring = True - docstrings = [attr_docs[key]] + docstring = False + # make a copy of docstring for attributes to avoid cache + # the change of autodoc-process-docstring event. + docstrings = [list(attr_docs[key])] + for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings - if not no_docstring: - encoding = self.analyzer - docstrings = self.get_doc(encoding) - if not docstrings: - # append at least a dummy docstring, so that the event - # autodoc-process-docstring is fired and can add some - # content if desired - docstrings.append([]) - for i, line in enumerate(self.process_doc(docstrings)): - self.add_line(line, sourcename, i) + if docstring: + docstrings = self.get_doc() + if docstrings is None: + # Do not call autodoc-process-docstring on get_doc() returns None. + pass + else: + if not docstrings: + # append at least a dummy docstring, so that the event + # autodoc-process-docstring is fired and can add some + # content if desired + docstrings.append([]) + for i, line in enumerate(self.process_doc(docstrings)): + self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) - def get_object_members(self, want_all): - # type: (bool) -> Tuple[bool, List[Tuple[unicode, Any]]] - """Return `(members_check_module, members)` where `members` is a - list of `(membername, member)` pairs of the members of *self.object*. + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: + """Return ``(members_check_module, members)`` where ``members`` is a + list of ``(membername, member)`` pairs of the members of *self.object*. If *want_all* is True, return all members. Else, only return those - members given by *self.options.members* (which may also be none). + members given by *self.options.members* (which may also be None). """ + warnings.warn('The implementation of Documenter.get_object_members() will be ' + 'removed from Sphinx-6.0.', RemovedInSphinx60Warning) members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer) if not want_all: if not self.options.members: - return False, [] + return False, [] # type: ignore # specific members given selected = [] for name in self.options.members: if name in members: selected.append((name, members[name].value)) else: - logger.warning('missing attribute %s in object %s' % - (name, self.fullname)) - return False, sorted(selected) + logger.warning(__('missing attribute %s in object %s') % + (name, self.fullname), type='autodoc') + return False, selected elif self.options.inherited_members: - return False, sorted((m.name, m.value) for m in members.values()) + return False, [(m.name, m.value) for m in members.values()] else: - return False, sorted((m.name, m.value) for m in members.values() - if m.directly_defined) + return False, [(m.name, m.value) for m in members.values() + if m.directly_defined] - def filter_members(self, members, want_all): - # type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]] + def filter_members(self, members: ObjectMembers, want_all: bool + ) -> List[Tuple[str, Any, bool]]: """Filter the given member list. Members are skipped if @@ -588,6 +688,23 @@ def filter_members(self, members, want_all): The user can override the skipping decision by connecting to the ``autodoc-skip-member`` event. """ + def is_filtered_inherited_member(name: str, obj: Any) -> bool: + inherited_members = self.options.inherited_members or set() + + if inspect.isclass(self.object): + for cls in self.object.__mro__: + if cls.__name__ in inherited_members and cls != self.object: + # given member is a member of specified *super class* + return True + elif name in cls.__dict__: + return False + elif name in self.get_attr(cls, '__annotations__', {}): + return False + elif isinstance(obj, ObjectMember) and obj.class_ is cls: + return False + + return False + ret = [] # search for members in source code too @@ -599,81 +716,129 @@ def filter_members(self, members, want_all): attr_docs = {} # process members and determine which to skip - for (membername, member) in members: - # Immediately skip lazy imports to avoid deprecation - # messages (#17455). - if isinstance(member, LazyImport): - continue + for obj in members: + try: + membername, member = obj + # --------------------------------------------------- + # Trac #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 + if member is INSTANCEATTR: + isattr = True + elif (namespace, membername) in attr_docs: + isattr = True + else: + isattr = False - # if isattr is True, the member is documented as an attribute - isattr = False + doc = getdoc(member, self.get_attr, self.config.autodoc_inherit_docstrings, + self.object, membername) + if not isinstance(doc, str): + # Ignore non-string __doc__ + doc = None - doc = self.get_attr(member, '__doc__', None) - if doc is None and self.env.config.autodoc_inherit_docstrings: - doc = getdoc(member) + # if the member __doc__ is the same as self's __doc__, it's just + # inherited and therefore not the member's doc + cls = self.get_attr(member, '__class__', None) + if cls: + cls_doc = self.get_attr(cls, '__doc__', None) + if cls_doc == doc: + doc = None + + if isinstance(obj, ObjectMember) and obj.docstring: + # hack for ClassDocumenter to inject docstring via ObjectMember + doc = obj.docstring + + doc, metadata = separate_metadata(doc) + has_doc = bool(doc) + + if 'private' in metadata: + # consider a member private if docstring has "private" metadata + isprivate = True + elif 'public' in metadata: + # consider a member public if docstring has "public" metadata + isprivate = False + else: + isprivate = membername.startswith('_') - # if the member __doc__ is the same as self's __doc__, it's just - # inherited and therefore not the member's doc - cls = self.get_attr(member, '__class__', None) - if cls: - cls_doc = self.get_attr(cls, '__doc__', None) - if cls_doc == doc: - doc = None - has_doc = bool(doc) - - keep = False - if want_all and membername.startswith('__') and \ - membername.endswith('__') and len(membername) > 4: - # special __methods__ - if self.options.special_members is ALL and \ - membername != '__doc__': - keep = has_doc or self.options.undoc_members - elif self.options.special_members and \ - self.options.special_members is not ALL and \ - membername in self.options.special_members: - keep = has_doc or self.options.undoc_members - elif (namespace, membername) in attr_docs: - if want_all and membername.startswith('_'): - # ignore members whose name starts with _ by default - keep = self.options.private_members + keep = False + if ismock(member) and (namespace, membername) not in attr_docs: + # mocked module or object + pass + elif (self.options.exclude_members and + membername in self.options.exclude_members): + # remove members given by exclude-members + keep = False + elif want_all and special_member_re.match(membername): + # special __methods__ + if (self.options.special_members and + membername in self.options.special_members): + if membername == '__doc__': + keep = False + elif is_filtered_inherited_member(membername, obj): + keep = False + else: + keep = has_doc or self.options.undoc_members + else: + keep = False + elif (namespace, membername) in attr_docs: + if want_all and isprivate: + if self.options.private_members is None: + keep = False + else: + keep = membername in self.options.private_members + else: + # keep documented attributes + keep = True + elif want_all and isprivate: + if has_doc or self.options.undoc_members: + if self.options.private_members is None: + keep = False + elif is_filtered_inherited_member(membername, obj): + keep = False + else: + keep = membername in self.options.private_members + else: + keep = False else: - # keep documented attributes - keep = True - isattr = True - elif want_all and membername.startswith('_'): - # ignore members whose name starts with _ by default - keep = self.options.private_members and \ - (has_doc or self.options.undoc_members) - else: - # ignore undocumented members if :undoc-members: is not given - keep = has_doc or self.options.undoc_members + if (self.options.members is ALL and + is_filtered_inherited_member(membername, obj)): + keep = False + else: + # ignore undocumented members if :undoc-members: is not given + keep = has_doc or self.options.undoc_members + + if isinstance(obj, ObjectMember) and obj.skipped: + # forcedly skipped member (ex. a module attribute not defined in __all__) + keep = False - # give the user a chance to decide whether this member - # should be skipped - if self.env.app: - # let extensions preprocess docstrings - try: + # give the user a chance to decide whether this member + # should be skipped + if self.env.app: + # let extensions preprocess docstrings skip_user = self.env.app.emit_firstresult( 'autodoc-skip-member', self.objtype, membername, member, not keep, self.options) if skip_user is not None: keep = not skip_user - except Exception as exc: - logger.warning(__('autodoc: failed to determine %r to be documented.' - 'the following exception was raised:\n%s'), - member, exc) - keep = False + except Exception as exc: + logger.warning(__('autodoc: failed to determine %s.%s (%r) to be documented, ' + 'the following exception was raised:\n%s'), + self.name, membername, member, exc, type='autodoc') + keep = False if keep: ret.append((membername, member, isattr)) return ret - def document_members(self, all_members=False): - # type: (bool) -> None + def document_members(self, all_members: bool = False) -> None: """Generate reST for member documentation. - If *all_members* is True, do all members, else those given by + If *all_members* is True, document all members, else those given by *self.options.members*. """ # set current namespace for finding members @@ -681,18 +846,14 @@ def document_members(self, all_members=False): if self.objpath: self.env.temp_data['autodoc:class'] = self.objpath[0] - want_all = all_members or self.options.inherited_members or \ - self.options.members is ALL + want_all = (all_members or + self.options.inherited_members or + self.options.members is ALL) # find out which members are documentable members_check_module, members = self.get_object_members(want_all) - # remove members given by exclude-members - if self.options.exclude_members: - members = [(membername, member) for (membername, member) in members - if membername not in self.options.exclude_members] - # document non-skipped members - memberdocumenters = [] # type: List[Tuple[Documenter, bool]] + memberdocumenters: List[Tuple[Documenter, bool]] = [] for (mname, member, isattr) in self.filter_members(members, want_all): classes = [cls for cls in self.documenters.values() if cls.can_document_member(member, mname, isattr, self)] @@ -703,25 +864,12 @@ def document_members(self, all_members=False): classes.sort(key=lambda cls: cls.priority) # give explicitly separated module name, so that members # of inner classes can be documented - full_mname = self.modname + '::' + \ - '.'.join(self.objpath + [mname]) + full_mname = self.modname + '::' + '.'.join(self.objpath + [mname]) documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) - member_order = self.options.member_order or \ - self.env.config.autodoc_member_order - if member_order == 'groupwise': - # sort by group; relies on stable sort to keep items in the - # same group sorted alphabetically - memberdocumenters.sort(key=lambda e: e[0].member_order) - elif member_order == 'bysource' and self.analyzer: - # sort by source order, by virtue of the module analyzer - tagorder = self.analyzer.tagorder - - def keyfunc(entry): - # type: (Tuple[Documenter, bool]) -> int - fullname = entry[0].name.split('::')[1] - return tagorder.get(fullname, len(tagorder)) - memberdocumenters.sort(key=keyfunc) + + member_order = self.options.member_order or self.config.autodoc_member_order + memberdocumenters = self.sort_members(memberdocumenters, member_order) for documenter, isattr in memberdocumenters: documenter.generate( @@ -732,9 +880,33 @@ def keyfunc(entry): self.env.temp_data['autodoc:module'] = None self.env.temp_data['autodoc:class'] = None - def generate(self, more_content=None, real_modname=None, - check_module=False, all_members=False): - # type: (Any, str, bool, bool) -> None + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + """Sort the given member list.""" + if order == 'groupwise': + # sort by group; alphabetically within groups + documenters.sort(key=lambda e: (e[0].member_order, e[0].name)) + elif order == 'bysource': + if self.analyzer: + # sort by source order, by virtue of the module analyzer + tagorder = self.analyzer.tagorder + + def keyfunc(entry: Tuple[Documenter, bool]) -> int: + fullname = entry[0].name.split('::')[1] + return tagorder.get(fullname, len(tagorder)) + documenters.sort(key=keyfunc) + else: + # Assume that member discovery order matches source order. + # This is a reasonable assumption in Python 3.6 and up, where + # module.__dict__ is insertion-ordered. + pass + else: # alphabetical + documenters.sort(key=lambda e: e[0].name) + + return documenters + + def generate(self, more_content: Optional[StringList] = None, real_modname: str = None, + check_module: bool = False, all_members: bool = False) -> None: """Generate reST for the object given by *self.name*, and possibly for its members. @@ -746,10 +918,10 @@ def generate(self, more_content=None, real_modname=None, if not self.parse_name(): # need a module to import 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)' % - self.name) + __('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)') % + self.name, type='autodoc') return # now, import the module and get object to document @@ -761,7 +933,8 @@ def generate(self, more_content=None, real_modname=None, # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. - self.real_modname = real_modname or self.get_real_modname() # type: str + guess_modname = self.get_real_modname() + self.real_modname: str = real_modname or guess_modname # try to also get a source code analyzer for attribute docs try: @@ -769,25 +942,29 @@ def generate(self, more_content=None, real_modname=None, # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() - except PycodeError as err: - logger.debug('[autodoc] module analyzer failed: %s', err) + except PycodeError as exc: + logger.debug('[autodoc] module analyzer failed: %s', exc) # no source file -- e.g. for builtin and C modules self.analyzer = None - # at least add the module as a dependency - name = self.module.__name__ if hasattr(self.module, '__name__') else None - fname = self.module.__file__ if hasattr(self.module, '__file__') else None - if name != self.real_modname: - # !!! SageMath Specific for make fast-rebuild-clean !!! - # try not to record a dependency to a .pyc file but to the corresponding .py files instead. - try: - fname = ModuleAnalyzer.for_module(name).srcname - except PycodeError: - pass - if fname is not None: - self.directive.record_dependencies.add(fname) + # at least add the module.__file__ as a dependency + if hasattr(self.module, '__file__') and self.module.__file__: + self.directive.record_dependencies.add(self.module.__file__) else: self.directive.record_dependencies.add(self.analyzer.srcname) + if self.real_modname != guess_modname: + # Add module to dependency list if target object is defined in other module. + try: + analyzer = ModuleAnalyzer.for_module(guess_modname) + self.directive.record_dependencies.add(analyzer.srcname) + except PycodeError: + pass + + docstrings: List[str] = sum(self.get_doc() or [], []) + if ismock(self.object) and not docstrings: + logger.warning(__('A mocked object is detected: %r'), + self.name, type='autodoc') + # check __module__ of object (for members not given explicitly) if check_module: if not self.check_module(): @@ -801,7 +978,12 @@ def generate(self, more_content=None, real_modname=None, self.add_line('', sourcename) # format the object's signature, if any - sig = self.format_signature() + try: + sig = self.format_signature() + except Exception as exc: + logger.warning(__('error while formatting signature for %s: %s'), + self.fullname, exc, type='autodoc') + return # generate the directive header and options, if applicable self.add_directive_header(sig) @@ -822,114 +1004,154 @@ class ModuleDocumenter(Documenter): Specialized Documenter subclass for modules. """ objtype = 'module' - content_indent = u'' + content_indent = '' titles_allowed = True + _extra_indent = ' ' - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, - 'noindex': bool_option, 'inherited-members': bool_option, + 'noindex': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, - 'member-order': identity, 'exclude-members': members_set_option, - 'private-members': bool_option, 'special-members': members_option, - 'imported-members': bool_option, - } # type: Dict[unicode, Callable] + 'member-order': member_order_option, 'exclude-members': exclude_members_option, + 'private-members': members_option, 'special-members': members_option, + 'imported-members': bool_option, 'ignore-module-all': bool_option, + 'no-value': bool_option, + } + + def __init__(self, *args: Any) -> None: + super().__init__(*args) + merge_members_option(self.options) + self.__all__: Optional[Sequence[str]] = None + + def add_content(self, more_content: Optional[StringList]) -> None: + old_indent = self.indent + self.indent += self._extra_indent + super().add_content(None) + self.indent = old_indent + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: # don't document submodules automatically return False - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is not None: - logger.warning('"::" in automodule name doesn\'t make sense') + logger.warning(__('"::" in automodule name doesn\'t make sense'), + type='autodoc') return (path or '') + base, [] - def parse_name(self): - # type: () -> bool - ret = Documenter.parse_name(self) + 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) + logger.warning(__('signature arguments or return annotation ' + 'given for automodule %s') % self.fullname, + type='autodoc') return ret - def add_directive_header(self, sig): - # type: (unicode) -> None + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + + try: + if not self.options.ignore_module_all: + self.__all__ = inspect.getall(self.object) + 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') + + return ret + + def add_directive_header(self, sig: str) -> None: Documenter.add_directive_header(self, sig) sourcename = self.get_sourcename() # add some module-specific options if self.options.synopsis: - self.add_line( - u' :synopsis: ' + self.options.synopsis, sourcename) + self.add_line(' :synopsis: ' + self.options.synopsis, sourcename) if self.options.platform: - self.add_line( - u' :platform: ' + self.options.platform, sourcename) + self.add_line(' :platform: ' + self.options.platform, sourcename) if self.options.deprecated: - self.add_line(u' :deprecated:', sourcename) + self.add_line(' :deprecated:', sourcename) - def get_module_members(self): + def get_module_members(self) -> Dict[str, ObjectMember]: """Get members of target module.""" if self.analyzer: attr_docs = self.analyzer.attr_docs else: attr_docs = {} - members = {} # type: Dict[str, ObjectMember] + members: Dict[str, ObjectMember] = {} for name in dir(self.object): try: value = safe_getattr(self.object, name, None) + if ismock(value): + value = undecorate(value) docstring = attr_docs.get(('', name), []) members[name] = ObjectMember(name, value, docstring="\n".join(docstring)) except AttributeError: continue # annotation only member (ex. attr: int) - try: - for name in inspect.getannotations(self.object): - if name not in members: - docstring = attr_docs.get(('', name), []) - members[name] = ObjectMember(name, INSTANCEATTR, - docstring="\n".join(docstring)) - except AttributeError: - pass + for name in inspect.getannotations(self.object): + if name not in members: + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, INSTANCEATTR, + docstring="\n".join(docstring)) return members - def get_object_members(self, want_all): - # type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]] + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: members = self.get_module_members() if want_all: - if not hasattr(self.object, '__all__'): + if self.__all__ is None: # for implicit module members, check __module__ to avoid # documenting imported objects return True, list(members.values()) else: - memberlist = self.object.__all__ - # Sometimes __all__ is broken... - if not isinstance(memberlist, (list, tuple)) or not \ - all(isinstance(entry, str) for entry in memberlist): - logger.warning( - '__all__ should be a list of strings, not %r ' - '(in module %s) -- ignoring __all__' % - (memberlist, self.fullname)) - # fall back to all members - return True, list(members.values()) + for member in members.values(): + if member.__name__ not in self.__all__: + member.skipped = True + + return False, list(members.values()) else: memberlist = self.options.members or [] - ret = [] - for mname in memberlist: - if mname in members: - ret.append(members[mname]) - else: - logger.warning( - 'missing attribute mentioned in :members: or __all__: ' - 'module %s, attribute %s' % - (safe_getattr(self.object, '__name__', '???'), mname)) - return False, ret + ret = [] + for name in memberlist: + if name in members: + ret.append(members[name]) + else: + logger.warning(__('missing attribute mentioned in :members: option: ' + 'module %s, attribute %s') % + (safe_getattr(self.object, '__name__', '???'), name), + type='autodoc') + return False, ret + + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + if order == 'bysource' and self.__all__: + # Sort alphabetically first (for members not listed on the __all__) + documenters.sort(key=lambda e: e[0].name) + + # Sort by __all__ + def keyfunc(entry: Tuple[Documenter, bool]) -> int: + name = entry[0].name.split('::')[1] + if self.__all__ and name in self.__all__: + return self.__all__.index(name) + else: + return len(self.__all__) + documenters.sort(key=keyfunc) + + return documenters + else: + return super().sort_members(documenters, order) class ModuleLevelDocumenter(Documenter): @@ -937,8 +1159,8 @@ class ModuleLevelDocumenter(Documenter): Specialized Documenter subclass for objects on module level (functions, classes, data/constants). """ - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is None: if path: modname = path.rstrip('.') @@ -958,8 +1180,8 @@ class ClassLevelDocumenter(Documenter): Specialized Documenter subclass for objects on class level (methods, attributes). """ - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is None: if path: mod_cls = path.rstrip('.') @@ -975,7 +1197,7 @@ def resolve_name(self, modname, parents, path, base): # ... if still None, there's no way to know if mod_cls is None: return None, [] - modname, _, cls = mod_cls.rpartition('.') # type: ignore + modname, sep, cls = mod_cls.rpartition('.') parents = [cls] # if the module name is still missing, get it like above if not modname: @@ -986,57 +1208,82 @@ def resolve_name(self, modname, parents, path, base): return modname, parents + [base] -class DocstringSignatureMixin(): +class DocstringSignatureMixin: """ Mixin for FunctionDocumenter and MethodDocumenter to provide the feature of reading the signature from the docstring. """ - - def _find_signature(self, encoding=None): - # type: (unicode) -> Tuple[str, str] - docstrings = self.get_doc(encoding) + _new_docstrings: List[List[str]] = None + _signatures: List[str] = None + + def _find_signature(self) -> Tuple[str, str]: + # candidates of the object name + valid_names = [self.objpath[-1]] # type: ignore + if isinstance(self, ClassDocumenter): + valid_names.append('__init__') + if hasattr(self.object, '__mro__'): + valid_names.extend(cls.__name__ for cls in self.object.__mro__) + + docstrings = self.get_doc() + if docstrings is None: + return None, None self._new_docstrings = docstrings[:] + self._signatures = [] result = None for i, doclines in enumerate(docstrings): - # no lines in docstring, no match - if not doclines: - continue - # match first line of docstring against signature RE - match = py_ext_sig_re.match(doclines[0]) # type: ignore - if not match: - continue - exmod, path, base, args, retann = match.groups() - # the base name must match ours - valid_names = [self.objpath[-1]] # type: ignore - if isinstance(self, ClassDocumenter): - valid_names.append('__init__') - if hasattr(self.object, '__mro__'): - valid_names.extend(cls.__name__ for cls in self.object.__mro__) - if base not in valid_names: - continue - # re-prepare docstring to ignore more leading indentation - self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[1:])) - result = args, retann - # don't look any further - break + for j, line in enumerate(doclines): + if not line: + # no lines in docstring, no match + break + + if line.endswith('\\'): + line = line.rstrip('\\').rstrip() + + # match first line of docstring against signature RE + match = py_ext_sig_re.match(line) + if not match: + break + exmod, path, base, args, retann = match.groups() + + # the base name must match ours + if base not in valid_names: + break + + # re-prepare docstring to ignore more leading indentation + tab_width = self.directive.state.document.settings.tab_width # type: ignore + self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[j + 1:]), + tab_width) + + if result is None: + # first signature + result = args, retann + else: + # subsequent signatures + self._signatures.append("(%s) -> %s" % (args, retann)) + + if result: + # finish the loop when signature found + break + return result - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - lines = getattr(self, '_new_docstrings', None) - if lines is not None: - return lines - return Documenter.get_doc(self, encoding, ignore) # type: ignore + def get_doc(self) -> List[List[str]]: + if self._new_docstrings is not None: + return self._new_docstrings + return super().get_doc() # type: ignore - def format_signature(self): - # type: () -> unicode - if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore + def format_signature(self, **kwargs: Any) -> str: + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() if result is not None: self.args, self.retann = result - return Documenter.format_signature(self) + sig = super().format_signature(**kwargs) # type: ignore + if self._signatures: + return "\n".join([sig] + self._signatures) + else: + return sig class DocstringStripSignatureMixin(DocstringSignatureMixin): @@ -1044,9 +1291,8 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin): Mixin for AttributeDocumenter to provide the feature of stripping any function signature from the docstring. """ - def format_signature(self): - # type: () -> unicode - if self.args is None and self.env.config.autodoc_docstring_signature: + def format_signature(self, **kwargs: Any) -> str: + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() @@ -1055,10 +1301,10 @@ def format_signature(self): # DocstringSignatureMixin.format_signature. # Documenter.format_signature use self.args value to format. _args, self.retann = result - return Documenter.format_signature(self) + return super().format_signature(**kwargs) -class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): +class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for functions. """ @@ -1066,136 +1312,284 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): member_order = 30 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - # It can be documented if it is a genuine function. + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + # -------------------------------------------------------------------- + # supports functions, builtins but, unlike Sphinx' autodoc, + # does not support bound methods exported at the module level + if is_function_or_cython_function(member) or inspect.isbuiltin(member): + return True + # Trac #9976: It can be documented if it is a genuine function. # Often, a class instance has the same documentation as its class, - # and then we typically want to document the class and not the instance. - # However, there is an exception: CachedFunction(f) returns a class instance, - # whose doc string coincides with that of f and is thus different from - # that of the class CachedFunction. In that situation, we want that f is documented. - # This is part of trac #9976. - return (is_function_or_cython_function(member) or inspect.isbuiltin(member) - or (isclassinstance(member) - and sage_getdoc_original(member) != sage_getdoc_original(member.__class__))) - - # Trac #9976: This function has been rewritten to support the - # _sage_argspec_ attribute which makes it possible to get argument - # specification of decorated callables in documentation correct. - # See e.g. sage.misc.decorators.sage_wraps - def args_on_obj(self, obj): - if hasattr(obj, "_sage_argspec_"): - return obj._sage_argspec_() - if inspect.isbuiltin(obj) or \ - inspect.ismethoddescriptor(obj): - # cannot introspect arguments of a C function or method - # unless a function to do so is supplied - if self.env.config.autodoc_builtin_argspec: - argspec = self.env.config.autodoc_builtin_argspec(obj) - return argspec - else: + # and then we typically want to document the class and not the + # instance. However, there is an exception: CachedFunction(f) returns + # a class instance, whose doc string coincides with that of f and is + # thus different from that of the class CachedFunction. In that + # situation, we want that f is documented. + return (isclassinstance(member) and + sage_getdoc_original(member) != sage_getdoc_original(member.__class__)) + # -------------------------------------------------------------------- + + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + try: + self.env.app.emit('autodoc-before-process-signature', self.object, False) + # ---------------------------------------------------------------- + # Trac #9976: Support the _sage_argspec_ attribute which makes it + # possible to get argument specification of decorated callables in + # documentation correct. See e.g. sage.misc.decorators.sage_wraps + obj = self.object + + if hasattr(obj, "_sage_argspec_"): + argspec = obj._sage_argspec_() + if inspect.isbuiltin(obj) or \ + inspect.ismethoddescriptor(obj): + # cannot introspect arguments of a C function or method + # unless a function to do so is supplied + argspec = sage_getargspec(obj) + argspec = sage_getargspec(obj) + if isclassinstance(obj) or inspect.isclass(obj): + # if a class should be documented as function, we try + # to use the constructor signature as function + # signature without the first argument. + if argspec is not None and argspec[0]: + del argspec[0][0] + + if argspec is None: return None - argspec = sage_getargspec(obj) - if isclassinstance(obj) or inspect.isclass(obj): - # if a class should be documented as function, we try - # to use the constructor signature as function - # signature without the first argument. - if argspec is not None and argspec[0]: - del argspec[0][0] - return argspec - - def format_args(self): - # type: () -> unicode - argspec = self.args_on_obj(self.object) - if argspec is None: + args = sage_formatargspec(*argspec) + # ---------------------------------------------------------------- + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) return None - args = formatargspec(self.object, *argspec) - # escape backslashes for reST - args = args.replace('\\', '\\\\') + except ValueError: + args = '' + + if self.config.strip_signature_backslash: + # escape backslashes for reST + args = args.replace('\\', '\\\\') return args - def document_members(self, all_members=False): - # type: (bool) -> None + def document_members(self, all_members: bool = False) -> None: pass + def add_directive_header(self, sig: str) -> None: + sourcename = self.get_sourcename() + super().add_directive_header(sig) + + if inspect.iscoroutinefunction(self.object) or inspect.isasyncgenfunction(self.object): + self.add_line(' :async:', sourcename) -class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): + def format_signature(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + sigs = [] + if (self.analyzer and + '.'.join(self.objpath) in self.analyzer.overloads and + self.config.autodoc_typehints != 'none'): + # Use signatures for overloaded functions instead of the implementation function. + overloaded = True + else: + overloaded = False + sig = super().format_signature(**kwargs) + sigs.append(sig) + + if inspect.is_singledispatch_function(self.object): + # append signature of singledispatch'ed functions + for typ, func in self.object.registry.items(): + if typ is object: + pass # default implementation. skipped. + else: + dispatchfunc = self.annotate_to_first_argument(func, typ) + if dispatchfunc: + documenter = FunctionDocumenter(self.directive, '') + documenter.object = dispatchfunc + documenter.objpath = [None] + sigs.append(documenter.format_signature()) + if overloaded: + actual = inspect.signature(self.object, + type_aliases=self.config.autodoc_type_aliases) + __globals__ = safe_getattr(self.object, '__globals__', {}) + for overload in self.analyzer.overloads.get('.'.join(self.objpath)): + overload = self.merge_default_value(actual, overload) + overload = evaluate_signature(overload, __globals__, + self.config.autodoc_type_aliases) + + sig = stringify_signature(overload, **kwargs) + sigs.append(sig) + + return "\n".join(sigs) + + def merge_default_value(self, actual: Signature, overload: Signature) -> Signature: + """Merge default values of actual implementation to the overload variants.""" + parameters = list(overload.parameters.values()) + for i, param in enumerate(parameters): + actual_param = actual.parameters.get(param.name) + if actual_param and param.default == '...': + parameters[i] = param.replace(default=actual_param.default) + + return overload.replace(parameters=parameters) + + def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]: + """Annotate type hint to the first argument of function if needed.""" + try: + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + if len(sig.parameters) == 0: + return None + + def dummy(): + pass + + params = list(sig.parameters.values()) + if params[0].annotation is Parameter.empty: + params[0] = params[0].replace(annotation=typ) + try: + dummy.__signature__ = sig.replace(parameters=params) # type: ignore + return dummy + except (AttributeError, TypeError): + # failed to update signature (ex. built-in or extension types) + return None + + return func + + +class DecoratorDocumenter(FunctionDocumenter): + """ + Specialized Documenter subclass for decorator functions. + """ + objtype = 'decorator' + + # must be lower than FunctionDocumenter + priority = -1 + + def format_args(self, **kwargs: Any) -> Any: + args = super().format_args(**kwargs) + if ',' in args: + return args + else: + return None + + +# Types which have confusing metaclass signatures it would be best not to show. +# These are listed by name, rather than storing the objects themselves, to avoid +# needing to import the modules. +_METACLASS_CALL_BLACKLIST = [ + 'enum.EnumMeta.__call__', +] + + +# Types whose __new__ signature is a pass-through. +_CLASS_NEW_BLACKLIST = [ + 'typing.Generic.__new__', +] + + +class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for classes. """ objtype = 'class' member_order = 20 - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, - 'noindex': bool_option, 'inherited-members': bool_option, - 'show-inheritance': bool_option, 'member-order': identity, - 'exclude-members': members_set_option, - 'private-members': bool_option, 'special-members': members_option, + 'noindex': bool_option, 'inherited-members': inherited_members_option, + 'show-inheritance': bool_option, 'member-order': member_order_option, + 'exclude-members': exclude_members_option, + 'private-members': members_option, 'special-members': members_option, + 'class-doc-from': class_doc_from_option, } + _signature_class: Any = None + _signature_method_name: str = None + + def __init__(self, *args: Any) -> None: + super().__init__(*args) + + if self.config.autodoc_class_signature == 'separated': + self.options = self.options.copy() + + # show __init__() method + if self.options.special_members is None: + self.options['special-members'] = ['__new__', '__init__'] + else: + self.options.special_members.append('__new__') + self.options.special_members.append('__init__') + + merge_members_option(self.options) + @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(member, type) - def import_object(self): - # type: () -> Any - ret = ModuleLevelDocumenter.import_object(self) - # Objective: if the class is documented under another name, document it + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + # if the class is documented under another name, document it # as data/attribute - # - # Notes from trac #27692: - # - # For Python 3, we make use of self.object.__qualname__. - # - # Notes from trac #7448: - # - # The original goal of this was that if some class is aliased, the - # alias is generated as a link rather than duplicated. For example in - # - # class A: - # pass - # B = A - # - # Then B is an alias of A, and should be generated as such. - # - # The way it was solved is to compare the name under which the - # current class is found and the actual name of the class (stored in - # the attribute __name__): - # - # if hasattr(self.object, '__name__'): - # self.doc_as_attr = (self.objpath[-1] != self.object.__name__) - # else: - # self.doc_as_attr = True - # - # Now, to work around a pickling bug of nested class in Python, - # by using the metaclass NestedMetaclass, we change the attribute - # __name__ of the nested class. For example, in - # - # class A(metaclass=NestedMetaclass): - # class B(): - # pass - # - # the class B get its name changed to 'A.B'. Such dots '.' in names - # are not supposed to occur in normal python name. I use it to check - # if the class is a nested one and to compare its __name__ with its - # path. - # - # The original implementation as well as the new one here doesn't work - # if a class is aliased from a different place under the same name. For - # example, in the following, - # - # class A: - # pass - # class Container: - # A = A - # - # The nested copy Container.A is also documented. Actually, it seems - # that there is no way to solve this by introspection. I'll submbit - # this problem on sphinx trac. - # - # References: trac #5986, file sage/misc/nested_class.py if ret: + if hasattr(self.object, '__name__'): + self.doc_as_attr = (self.objpath[-1] != self.object.__name__) + # ------------------------------------------------------------------- + # Trac #27692, #7448: The original goal of this was that if some + # class is aliased, the alias is generated as a link rather than + # duplicated. For example in + # + # class A: + # pass + # B = A + # + # Then B is an alias of A, and should be generated as such. + # + # The way it was solved is to compare the name under which the + # current class is found and the actual name of the class (stored in + # the attribute __name__): + # + # if hasattr(self.object, '__name__'): + # self.doc_as_attr = (self.objpath[-1] != self.object.__name__) + # else: + # self.doc_as_attr = True + # + # Now, to work around a pickling bug of nested class in Python, + # by using the metaclass NestedMetaclass, we change the attribute + # __name__ of the nested class. For example, in + # + # class A(metaclass=NestedMetaclass): + # class B(): + # pass + # + # the class B get its name changed to 'A.B'. Such dots '.' in names + # are not supposed to occur in normal python name. I use it to check + # if the class is a nested one and to compare its __name__ with its + # path. + # + # The original implementation as well as the new one here doesn't work + # if a class is aliased from a different place under the same name. For + # example, in the following, + # + # class A: + # pass + # class Container: + # A = A + # + # The nested copy Container.A is also documented. Actually, it seems + # that there is no way to solve this by introspection. I'll submbit + # this problem on sphinx trac. + # + # References: trac #5986, file sage/misc/nested_class.py + import sys module = getattr(self.object, '__module__', False) name = getattr(self.object, '__name__', False) qualname = getattr(self.object, '__qualname__', name) @@ -1209,135 +1603,344 @@ def import_object(self): cls = getattr(cls, part, None) self.doc_as_attr = (self.objpath != qualname_parts and self.object is cls) + # ------------------------------------------------------------------- else: self.doc_as_attr = True return ret - def format_args(self): - # type: () -> unicode - # for classes, the relevant signature is the __init__ method's - initmeth = self.get_attr(self.object, '__init__', None) - # classes without __init__ method, default __init__ or - # __init__ written in C? - if initmeth is None or \ - is_builtin_class_method(self.object, '__init__') or \ - not(inspect.ismethod(initmeth) or is_function_or_cython_function(initmeth)): - return None - try: - argspec = sage_getargspec(initmeth) - if argspec[0] and argspec[0][0] in ('cls', 'self'): - del argspec[0][0] - return formatargspec(initmeth, *argspec) - except TypeError: - # still not possible: happens e.g. for old-style classes - # with __init__ in C - return None + def _get_signature(self) -> Tuple[Optional[Any], Optional[str], Optional[Signature]]: + def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: + """ Get the `attr` function or method from `obj`, if it is user-defined. """ + if inspect.is_builtin_class_method(obj, attr): + return None + attr = self.get_attr(obj, attr, None) + if not (inspect.ismethod(attr) or inspect.isfunction(attr)): + return None + return attr - def format_signature(self): - # type: () -> unicode - if self.doc_as_attr: - return '' + # This sequence is copied from inspect._signature_from_callable. + # ValueError means that no signature could be found, so we keep going. - return DocstringSignatureMixin.format_signature(self) + # First, we check the obj has a __signature__ attribute + if (hasattr(self.object, '__signature__') and + isinstance(self.object.__signature__, Signature)): + return None, None, self.object.__signature__ - def add_directive_header(self, sig): - # type: (unicode) -> None - if self.doc_as_attr: - self.directivetype = 'attribute' - Documenter.add_directive_header(self, sig) + # Next, let's see if it has an overloaded __call__ defined + # in its metaclass + call = get_user_defined_function_or_method(type(self.object), '__call__') - # add inheritance info, if wanted - if not self.doc_as_attr and self.options.show_inheritance: - sourcename = self.get_sourcename() - self.add_line(u'', sourcename) - if hasattr(self.object, '__bases__') and len(self.object.__bases__): - bases = [b.__module__ in ('__builtin__', 'builtins') and - u':class:`%s`' % b.__name__ or - u':class:`%s.%s`' % (b.__module__, b.__name__) - for b in self.object.__bases__] - self.add_line(u' ' + _(u'Bases: %s') % ', '.join(bases), - sourcename) - - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - lines = getattr(self, '_new_docstrings', None) - if lines is not None: - return lines + if call is not None: + if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST: + call = None - content = self.env.config.autoclass_content + if call is not None: + self.env.app.emit('autodoc-before-process-signature', call, True) + try: + sig = inspect.signature(call, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return type(self.object), '__call__', sig + except ValueError: + pass - docstrings = [] - attrdocstring = sage_getdoc_original(self.object) - if attrdocstring: - docstrings.append(attrdocstring) + # Now we check if the 'obj' class has a '__new__' method + new = get_user_defined_function_or_method(self.object, '__new__') - # for classes, what the "docstring" is can be controlled via a - # config value; the default is only the class docstring - if content in ('both', 'init'): - initdocstring = sage_getdoc_original( - self.get_attr(self.object, '__init__', None)) - # for new-style classes, no __init__ means default __init__ - if (initdocstring is not None and - (initdocstring == object.__init__.__doc__ or # for pypy - initdocstring.strip() == object.__init__.__doc__)): # for !pypy - initdocstring = None - if not initdocstring: - # try __new__ - initdocstring = self.get_attr( - self.get_attr(self.object, '__new__', None), '__doc__') - # for new-style classes, no __new__ means default __new__ - if (initdocstring is not None and - (initdocstring == object.__new__.__doc__ or # for pypy - initdocstring.strip() == object.__new__.__doc__)): # for !pypy - initdocstring = None - if initdocstring: - if content == 'init': - docstrings = [initdocstring] - else: - docstrings.append(initdocstring) - doc = [] - for docstring in docstrings: - if isinstance(docstring, str): - doc.append(prepare_docstring(docstring)) - return doc - - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - if self.doc_as_attr: - # We cannot rely on __qualname__ yet for Python 2, because of a - # Cython bug: https://github.com/cython/cython/issues/2772. See - # trac #27002. - classname = safe_getattr(self.object, '__qualname__', None) - if not classname: - classname = safe_getattr(self.object, '__name__', None) - if classname: - module = safe_getattr(self.object, '__module__', None) - parentmodule = safe_getattr(self.parent, '__module__', None) - if module and module != parentmodule: - classname = str(module) + u'.' + str(classname) - content = ViewList( - [_('alias of :class:`%s`') % classname], source='') - ModuleLevelDocumenter.add_content(self, content, - no_docstring=True) - else: - ModuleLevelDocumenter.add_content(self, more_content) + if new is not None: + if "{0.__module__}.{0.__qualname__}".format(new) in _CLASS_NEW_BLACKLIST: + new = None - def document_members(self, all_members=False): - # type: (bool) -> None - if self.doc_as_attr: - return - ModuleLevelDocumenter.document_members(self, all_members) + if new is not None: + self.env.app.emit('autodoc-before-process-signature', new, True) + try: + sig = inspect.signature(new, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return self.object, '__new__', sig + except ValueError: + pass - def generate(self, more_content=None, real_modname=None, - check_module=False, all_members=False): + # Finally, we should have at least __init__ implemented + init = get_user_defined_function_or_method(self.object, '__init__') + if init is not None: + self.env.app.emit('autodoc-before-process-signature', init, True) + try: + sig = inspect.signature(init, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return self.object, '__init__', sig + except ValueError: + pass + + # None of the attributes are user-defined, so fall back to let inspect + # handle it. + # We don't know the exact method that inspect.signature will read + # the signature from, so just pass the object itself to our hook. + self.env.app.emit('autodoc-before-process-signature', self.object, False) + try: + sig = inspect.signature(self.object, bound_method=False, + type_aliases=self.config.autodoc_type_aliases) + return None, None, sig + except ValueError: + pass + + # Still no signature: happens e.g. for old-style classes + # with __init__ in C and no `__text_signature__`. + return None, None, None + + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + try: + self._signature_class, self._signature_method_name, sig = self._get_signature() + except TypeError as exc: + # __signature__ attribute contained junk + logger.warning(__("Failed to get a constructor signature for %s: %s"), + self.fullname, exc) + return None + + if sig is None: + return None + + return stringify_signature(sig, show_return_annotation=False, **kwargs) + + def _find_signature(self) -> Tuple[str, str]: + result = super()._find_signature() + if result is not None: + # Strip a return value from signature of constructor in docstring (first entry) + result = (result[0], None) + + for i, sig in enumerate(self._signatures): + if sig.endswith(' -> None'): + # Strip a return value from signatures of constructor in docstring (subsequent + # entries) + self._signatures[i] = sig[:-8] + + return result + + def format_signature(self, **kwargs: Any) -> str: + if self.doc_as_attr: + return '' + if self.config.autodoc_class_signature == 'separated': + # do not show signatures + return '' + + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + sig = super().format_signature() + sigs = [] + + overloads = self.get_overloaded_signatures() + if overloads and self.config.autodoc_typehints != 'none': + # Use signatures for overloaded methods instead of the implementation method. + method = safe_getattr(self._signature_class, self._signature_method_name, None) + __globals__ = safe_getattr(method, '__globals__', {}) + for overload in overloads: + overload = evaluate_signature(overload, __globals__, + self.config.autodoc_type_aliases) + + parameters = list(overload.parameters.values()) + overload = overload.replace(parameters=parameters[1:], + return_annotation=Parameter.empty) + sig = stringify_signature(overload, **kwargs) + sigs.append(sig) + else: + sigs.append(sig) + + return "\n".join(sigs) + + def get_overloaded_signatures(self) -> List[Signature]: + if self._signature_class and self._signature_method_name: + for cls in self._signature_class.__mro__: + try: + analyzer = ModuleAnalyzer.for_module(cls.__module__) + analyzer.analyze() + qualname = '.'.join([cls.__qualname__, self._signature_method_name]) + if qualname in analyzer.overloads: + return analyzer.overloads.get(qualname) + elif qualname in analyzer.tagorder: + # the constructor is defined in the class, but not overridden. + return [] + except PycodeError: + pass + + return [] + + def get_canonical_fullname(self) -> Optional[str]: + __modname__ = safe_getattr(self.object, '__module__', self.modname) + __qualname__ = safe_getattr(self.object, '__qualname__', None) + if __qualname__ is None: + __qualname__ = safe_getattr(self.object, '__name__', None) + if __qualname__ and '<locals>' in __qualname__: + # No valid qualname found if the object is defined as locals + __qualname__ = None + + if __modname__ and __qualname__: + return '.'.join([__modname__, __qualname__]) + else: + return None + + def add_directive_header(self, sig: str) -> None: + sourcename = self.get_sourcename() + + if self.doc_as_attr: + self.directivetype = 'attribute' + super().add_directive_header(sig) + + 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 canonical_fullname and self.fullname != canonical_fullname: + self.add_line(' :canonical: %s' % canonical_fullname, sourcename) + + # add inheritance info, if wanted + if not self.doc_as_attr and self.options.show_inheritance: + if inspect.getorigbases(self.object): + # A subclass of generic types + # refs: PEP-560 <https://peps.python.org/pep-0560/> + bases = list(self.object.__orig_bases__) + elif hasattr(self.object, '__bases__') and len(self.object.__bases__): + # A normal class + bases = list(self.object.__bases__) + else: + bases = [] + + self.env.events.emit('autodoc-process-bases', + self.fullname, self.object, self.options, bases) + + if self.config.autodoc_typehints_format == "short": + base_classes = [restify(cls, "smart") for cls in bases] + else: + base_classes = [restify(cls) for cls in bases] + + sourcename = self.get_sourcename() + self.add_line('', sourcename) + self.add_line(' ' + _('Bases: %s') % ', '.join(base_classes), sourcename) + + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: + members = get_class_members(self.object, self.objpath, self.get_attr, + self.config.autodoc_inherit_docstrings) + if not want_all: + if not self.options.members: + return False, [] # type: ignore + # specific members given + selected = [] + for name in self.options.members: + if name in members: + selected.append(members[name]) + else: + 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()) + else: + return False, [m for m in members.values() if m.class_ == self.object] + + def get_doc(self) -> Optional[List[List[str]]]: + if self.doc_as_attr: + # Don't show the docstring of the class when it is an alias. + comment = self.get_variable_comment() + if comment: + return [] + else: + return None + + lines = getattr(self, '_new_docstrings', None) + if lines is not None: + return lines + + classdoc_from = self.options.get('class-doc-from', self.config.autoclass_content) + + docstrings = [] + attrdocstring = getdoc(self.object, self.get_attr) + if attrdocstring: + docstrings.append(attrdocstring) + + # for classes, what the "docstring" is can be controlled via a + # config value; the default is only the class docstring + if classdoc_from in ('both', 'init'): + __init__ = self.get_attr(self.object, '__init__', None) + initdocstring = getdoc(__init__, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.object, '__init__') + # for new-style classes, no __init__ means default __init__ + if (initdocstring is not None and + (initdocstring == object.__init__.__doc__ or # for pypy + initdocstring.strip() == object.__init__.__doc__)): # for !pypy + initdocstring = None + if not initdocstring: + # try __new__ + __new__ = self.get_attr(self.object, '__new__', None) + initdocstring = getdoc(__new__, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.object, '__new__') + # for new-style classes, no __new__ means default __new__ + if (initdocstring is not None and + (initdocstring == object.__new__.__doc__ or # for pypy + initdocstring.strip() == object.__new__.__doc__)): # for !pypy + initdocstring = None + if initdocstring: + if classdoc_from == 'init': + docstrings = [initdocstring] + else: + docstrings.append(initdocstring) + + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tab_width) for docstring in docstrings] + + def get_variable_comment(self) -> Optional[List[str]]: + try: + key = ('', '.'.join(self.objpath)) + if self.doc_as_attr: + analyzer = ModuleAnalyzer.for_module(self.modname) + else: + analyzer = ModuleAnalyzer.for_module(self.get_real_modname()) + analyzer.analyze() + return list(analyzer.attr_docs.get(key, [])) + except PycodeError: + return None + + def add_content(self, more_content: Optional[StringList]) -> None: + if self.doc_as_attr and self.modname != self.get_real_modname(): + try: + # override analyzer to obtain doccomment around its definition. + self.analyzer = ModuleAnalyzer.for_module(self.modname) + self.analyzer.analyze() + except PycodeError: + pass + + if self.doc_as_attr and not self.get_variable_comment(): + try: + if self.config.autodoc_typehints_format == "short": + alias = restify(self.object, "smart") + else: + alias = restify(self.object) + more_content = StringList([_('alias of %s') % alias], source='') + except AttributeError: + pass # Invalid class object is passed. + + super().add_content(more_content) + + def document_members(self, all_members: bool = False) -> None: + if self.doc_as_attr: + return + super().document_members(all_members) + + def generate(self, more_content: Optional[StringList] = None, real_modname: str = None, + check_module: bool = False, all_members: bool = False) -> None: # Do not pass real_modname and use the name from the __module__ # attribute of the class. # If a class gets imported into the module real_modname # the analyzer won't find the source of the class, if # it looks in real_modname. - return super(ClassDocumenter, self).generate(more_content=more_content, - check_module=check_module, - all_members=all_members) + return super().generate(more_content=more_content, + check_module=check_module, + all_members=all_members) + class ExceptionDocumenter(ClassDocumenter): """ @@ -1350,65 +1953,317 @@ class ExceptionDocumenter(ClassDocumenter): priority = 10 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(member, type) and issubclass(member, BaseException) -class DataDocumenter(ModuleLevelDocumenter): +class DataDocumenterMixinBase: + # define types of instance variables + config: Config = None + env: BuildEnvironment = None + modname: str = None + parent: Any = None + object: Any = None + objpath: List[str] = None + + def should_suppress_directive_header(self) -> bool: + """Check directive header should be suppressed.""" + return False + + def should_suppress_value_header(self) -> bool: + """Check :value: header should be suppressed.""" + return False + + def update_content(self, more_content: StringList) -> None: + """Update docstring for the NewType object.""" + pass + + +class GenericAliasMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting GenericAliases. + """ + + def should_suppress_directive_header(self) -> bool: + return (inspect.isgenericalias(self.object) or + super().should_suppress_directive_header()) + + def update_content(self, more_content: StringList) -> None: + if inspect.isgenericalias(self.object): + if self.config.autodoc_typehints_format == "short": + alias = restify(self.object, "smart") + else: + alias = restify(self.object) + + more_content.append(_('alias of %s') % alias, '') + more_content.append('', '') + + super().update_content(more_content) + + +class NewTypeMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting NewTypes. + """ + + def should_suppress_directive_header(self) -> bool: + return (inspect.isNewType(self.object) or + super().should_suppress_directive_header()) + + def update_content(self, more_content: StringList) -> None: + if inspect.isNewType(self.object): + if self.config.autodoc_typehints_format == "short": + supertype = restify(self.object.__supertype__, "smart") + else: + supertype = restify(self.object.__supertype__) + + more_content.append(_('alias of %s') % supertype, '') + more_content.append('', '') + + super().update_content(more_content) + + +class TypeVarMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting TypeVars. + """ + + def should_suppress_directive_header(self) -> bool: + return (isinstance(self.object, TypeVar) or + super().should_suppress_directive_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if isinstance(self.object, TypeVar): + if self.object.__doc__ != TypeVar.__doc__: + return super().get_doc() # type: ignore + else: + return [] + else: + return super().get_doc() # type: ignore + + def update_content(self, more_content: StringList) -> None: + if isinstance(self.object, TypeVar): + attrs = [repr(self.object.__name__)] + for constraint in self.object.__constraints__: + if self.config.autodoc_typehints_format == "short": + attrs.append(stringify_typehint(constraint, "smart")) + else: + attrs.append(stringify_typehint(constraint)) + if self.object.__bound__: + if self.config.autodoc_typehints_format == "short": + bound = restify(self.object.__bound__, "smart") + else: + bound = restify(self.object.__bound__) + attrs.append(r"bound=\ " + bound) + if self.object.__covariant__: + attrs.append("covariant=True") + if self.object.__contravariant__: + attrs.append("contravariant=True") + + more_content.append(_('alias of TypeVar(%s)') % ", ".join(attrs), '') + more_content.append('', '') + + super().update_content(more_content) + + +class UninitializedGlobalVariableMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter to provide the feature for supporting uninitialized + (type annotation only) global variables. + """ + + def import_object(self, raiseerror: bool = False) -> bool: + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + # annotation only instance variable (PEP-526) + try: + 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) + if self.objpath[-1] in annotations: + self.object = UNINITIALIZED_ATTR + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is UNINITIALIZED_ATTR or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is UNINITIALIZED_ATTR: + return [] + else: + return super().get_doc() # type: ignore + + +class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, + UninitializedGlobalVariableMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ objtype = 'data' member_order = 40 priority = -10 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(parent, ModuleDocumenter) and isattr - def add_directive_header(self, sig): - # type: (unicode) -> None - ModuleLevelDocumenter.add_directive_header(self, sig) + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + annotations = dict(inspect.getannotations(parent)) + parent.__annotations__ = annotations + + try: + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + for (classname, attrname), annotation in analyzer.annotations.items(): + if classname == '' and attrname not in annotations: + annotations[attrname] = annotation + except PycodeError: + pass + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + if self.parent: + self.update_annotations(self.parent) + + return ret + + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + docstring, metadata = separate_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False + + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) sourcename = self.get_sourcename() - if not self.options.annotation: + if self.options.annotation is SUPPRESS or self.should_suppress_directive_header(): + pass + elif self.options.annotation: + self.add_line(' :annotation: %s' % self.options.annotation, + sourcename) + else: + if self.config.autodoc_typehints != 'none': + # obtain annotation for this data + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(annotations.get(self.objpath[-1]), + "smart") + else: + objrepr = stringify_typehint(annotations.get(self.objpath[-1])) + self.add_line(' :type: ' + objrepr, sourcename) + try: - objrepr = object_description(self.object) + if (self.options.no_value or self.should_suppress_value_header() or + ismock(self.object)): + pass + else: + objrepr = object_description(self.object) + self.add_line(' :value: ' + objrepr, sourcename) except ValueError: pass - else: - self.add_line(u' :annotation: = ' + objrepr, sourcename) - elif self.options.annotation is SUPPRESS: + + def document_members(self, all_members: bool = False) -> None: + pass + + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + + def get_module_comment(self, attrname: str) -> Optional[List[str]]: + try: + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + key = ('', attrname) + if key in analyzer.attr_docs: + return list(analyzer.attr_docs[key]) + except PycodeError: pass + + return None + + def get_doc(self) -> Optional[List[List[str]]]: + # Check the variable has a docstring-comment + comment = self.get_module_comment(self.objpath[-1]) + if comment: + return [comment] else: - self.add_line(u' :annotation: %s' % self.options.annotation, - sourcename) + return super().get_doc() - def document_members(self, all_members=False): - # type: (bool) -> None - pass + def add_content(self, more_content: Optional[StringList]) -> None: + # Disable analyzing variable comment on Documenter.add_content() to control it on + # DataDocumenter.add_content() + self.analyzer = None + + if not more_content: + more_content = StringList() + self.update_content(more_content) + super().add_content(more_content) -class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): + +class NewTypeDataDocumenter(DataDocumenter): + """ + Specialized Documenter subclass for NewTypes. + + Note: This must be invoked before FunctionDocumenter because NewType is a kind of + function object. + """ + + objtype = 'newtypedata' + directivetype = 'data' + priority = FunctionDocumenter.priority + 1 + + @classmethod + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return inspect.isNewType(member) and isattr + + +class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore """ Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' + directivetype = 'method' member_order = 50 priority = 1 # must be more than FunctionDocumenter @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - return inspect.isroutine(member) and not \ - isinstance(parent, ModuleDocumenter) - - def import_object(self): - # type: () -> Any - ret = ClassLevelDocumenter.import_object(self) + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return inspect.isroutine(member) and not isinstance(parent, ModuleDocumenter) + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) if not ret: return ret @@ -1417,195 +2272,649 @@ def import_object(self): if obj is None: obj = self.object - if isclassmethod(obj): - self.directivetype = 'classmethod' + if (inspect.isclassmethod(obj) or + inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name)): # document class and static members before ordinary ones self.member_order = self.member_order - 1 - elif isstaticmethod(obj, cls=self.parent, name=self.object_name): - self.directivetype = 'staticmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' + return ret - # Trac #9976: This function has been rewritten to support the - # _sage_argspec_ attribute which makes it possible to get argument - # specification of decorated callables in documentation correct. - # See e.g. sage.misc.decorators.sage_wraps. - # - # Note, however, that sage.misc.sageinspect.sage_getargspec already - # uses a method _sage_argspec_, that only works on objects, not on classes, though. - def args_on_obj(self, obj): + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + # ----------------------------------------------------------------- + # Trac #9976: Support the _sage_argspec_ attribute which makes it + # possible to get argument specification of decorated callables in + # documentation correct. See e.g. sage.misc.decorators.sage_wraps. + # + # Note, however, that sage.misc.sageinspect.sage_getargspec already + # uses a method _sage_argspec_, that only works on objects, not on + # classes, though. + obj = self.object if hasattr(obj, "_sage_argspec_"): argspec = obj._sage_argspec_() elif inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj): # can never get arguments of a C function or method unless # a function to do so is supplied - if self.env.config.autodoc_builtin_argspec: - argspec = self.env.config.autodoc_builtin_argspec(obj) - else: - return None + argspec = sage_getargspec(obj) else: # The check above misses ordinary Python methods in Cython # files. argspec = sage_getargspec(obj) + if argspec is not None and argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] - return argspec - - def format_args(self): - # type: () -> unicode - argspec = self.args_on_obj(self.object) if argspec is None: return None - args = formatargspec(self.object, *argspec) - # escape backslashes for reST - args = args.replace('\\', '\\\\') + args = sage_formatargspec(*argspec) + # ----------------------------------------------------------------- + + if self.config.strip_signature_backslash: + # escape backslashes for reST + args = args.replace('\\', '\\\\') return args - def document_members(self, all_members=False): - # type: (bool) -> None + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) + + sourcename = self.get_sourcename() + obj = self.parent.__dict__.get(self.object_name, self.object) + if inspect.isabstractmethod(obj): + self.add_line(' :abstractmethod:', sourcename) + if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj): + self.add_line(' :async:', sourcename) + if inspect.isclassmethod(obj): + self.add_line(' :classmethod:', sourcename) + if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): + self.add_line(' :staticmethod:', sourcename) + if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: + self.add_line(' :final:', sourcename) + + def document_members(self, all_members: bool = False) -> None: pass + # ------------------------------------------------------------------------ + # Trac #34730: The format_signature() of the class MethodDocumenter + # supports overloaded methods via inspect.signature(), which does not work + # with Sage yet. Hence the method was removed from here. + # ------------------------------------------------------------------------ + + def merge_default_value(self, actual: Signature, overload: Signature) -> Signature: + """Merge default values of actual implementation to the overload variants.""" + parameters = list(overload.parameters.values()) + for i, param in enumerate(parameters): + actual_param = actual.parameters.get(param.name) + if actual_param and param.default == '...': + parameters[i] = param.replace(default=actual_param.default) + + return overload.replace(parameters=parameters) + + def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]: + """Annotate type hint to the first argument of function if needed.""" + try: + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) + except TypeError as exc: + logger.warning(__("Failed to get a method signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + if len(sig.parameters) == 1: + return None -class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore + def dummy(): + pass + + params = list(sig.parameters.values()) + if params[1].annotation is Parameter.empty: + params[1] = params[1].replace(annotation=typ) + try: + dummy.__signature__ = sig.replace(parameters=params) # type: ignore + return dummy + except (AttributeError, TypeError): + # failed to update signature (ex. built-in or extension types) + return None + + return func + + def get_doc(self) -> Optional[List[List[str]]]: + if self._new_docstrings is not None: + # docstring already returned previously, then modified by + # `DocstringSignatureMixin`. Just return the previously-computed + # result, so that we don't lose the processing done by + # `DocstringSignatureMixin`. + return self._new_docstrings + if self.objpath[-1] == '__init__': + docstring = getdoc(self.object, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if (docstring is not None and + (docstring == object.__init__.__doc__ or # for pypy + docstring.strip() == object.__init__.__doc__)): # for !pypy + docstring = None + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tabsize=tab_width)] + else: + return [] + elif self.objpath[-1] == '__new__': + docstring = getdoc(self.object, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if (docstring is not None and + (docstring == object.__new__.__doc__ or # for pypy + docstring.strip() == object.__new__.__doc__)): # for !pypy + docstring = None + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tabsize=tab_width)] + else: + return [] + else: + return super().get_doc() + + +class NonDataDescriptorMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting non + data-descriptors. + + .. note:: This mix-in must be inherited after other mix-ins. Otherwise, docstring + and :value: header will be suppressed unexpectedly. + """ + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) # type: ignore + if ret and not inspect.isattributedescriptor(self.object): + self.non_data_descriptor = True + else: + self.non_data_descriptor = False + + return ret + + def should_suppress_value_header(self) -> bool: + return (not getattr(self, 'non_data_descriptor', False) or + super().should_suppress_directive_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if getattr(self, 'non_data_descriptor', False): + # the docstring of non datadescriptor is very probably the wrong thing + # to display + return None + else: + return super().get_doc() # type: ignore + + +class SlotsMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting __slots__. + """ + + def isslotsattribute(self) -> bool: + """Check the subject is an attribute in __slots__.""" + try: + __slots__ = inspect.getslots(self.parent) + if __slots__ and self.objpath[-1] in __slots__: + return True + else: + return False + except (ValueError, TypeError): + return False + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) # type: ignore + if self.isslotsattribute(): + self.object = SLOTSATTR + + return ret + + def should_suppress_value_header(self) -> bool: + if self.object is SLOTSATTR: + return True + else: + return super().should_suppress_value_header() + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is SLOTSATTR: + try: + __slots__ = inspect.getslots(self.parent) + if __slots__ and __slots__.get(self.objpath[-1]): + docstring = prepare_docstring(__slots__[self.objpath[-1]]) + return [docstring] + else: + return [] + except ValueError as exc: + logger.warning(__('Invalid __slots__ found on %s. Ignored.'), + (self.parent.__qualname__, exc), type='autodoc') + return [] + else: + return super().get_doc() # type: ignore + + @property + def _datadescriptor(self) -> bool: + warnings.warn('AttributeDocumenter._datadescriptor() is deprecated.', + RemovedInSphinx60Warning) + if self.object is SLOTSATTR: + return True + else: + return False + + +class RuntimeInstanceAttributeMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting runtime + instance attributes (that are defined in __init__() methods with doc-comments). + + Example: + + class Foo: + def __init__(self): + self.attr = None #: This is a target of this mix-in. + """ + + RUNTIME_INSTANCE_ATTRIBUTE = object() + + def is_runtime_instance_attribute(self, parent: Any) -> bool: + """Check the subject is an attribute defined in __init__().""" + # An instance variable defined in __init__(). + if self.get_attribute_comment(parent, self.objpath[-1]): # type: ignore + return True + elif self.is_runtime_instance_attribute_not_commented(parent): + return True + else: + return False + + def is_runtime_instance_attribute_not_commented(self, parent: Any) -> bool: + """Check the subject is an attribute defined in __init__() without comment.""" + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + if qualname and self.objpath: + key = '.'.join([qualname, self.objpath[-1]]) + if key in analyzer.tagorder: + return True + except (AttributeError, PycodeError): + pass + + return None + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the existence of runtime instance attribute after failing to import the + attribute.""" + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + try: + with mock(self.config.autodoc_mock_imports): + ret = import_object(self.modname, self.objpath[:-1], 'class', + attrgetter=self.get_attr, # type: ignore + warningiserror=self.config.autodoc_warningiserror) + parent = ret[3] + if self.is_runtime_instance_attribute(parent): + self.object = self.RUNTIME_INSTANCE_ATTRIBUTE + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is self.RUNTIME_INSTANCE_ATTRIBUTE or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if (self.object is self.RUNTIME_INSTANCE_ATTRIBUTE and + self.is_runtime_instance_attribute_not_commented(self.parent)): + return None + else: + return super().get_doc() # type: ignore + + +class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting uninitialized + instance attributes (PEP-526 styled, annotation only attributes). + + Example: + + class Foo: + attr: int #: This is a target of this mix-in. + """ + + 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) + if self.objpath[-1] in annotations: + return True + else: + return False + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the exisitence of uninitialized instance attribute when failed to import + the attribute.""" + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + try: + ret = import_object(self.modname, self.objpath[:-1], 'class', + attrgetter=self.get_attr, # type: ignore + warningiserror=self.config.autodoc_warningiserror) + parent = ret[3] + if self.is_uninitialized_instance_attribute(parent): + self.object = UNINITIALIZED_ATTR + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is UNINITIALIZED_ATTR or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is UNINITIALIZED_ATTR: + return None + else: + return super().get_doc() # type: ignore + + +class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore + TypeVarMixin, RuntimeInstanceAttributeMixin, + UninitializedInstanceAttributeMixin, NonDataDescriptorMixin, + DocstringStripSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for attributes. """ objtype = 'attribute' member_order = 60 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option # must be higher than the MethodDocumenter, else it will recognize # some non-data descriptors as methods priority = 10 @staticmethod - def is_function_or_method(obj): - return is_function_or_cython_function(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) + def is_function_or_method(obj: Any) -> bool: + return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - non_attr_types = (type, MethodDescriptorType) - isdatadesc = isdescriptor(member) and not \ - inspect.isroutine(member) and not \ - isinstance(member, non_attr_types) and not \ - type(member).__name__ == "instancemethod" - - isattribute = isdatadesc or (not isinstance(parent, ModuleDocumenter) and isattr) - - # Trac #26522: This condition is here just to pass objects of classes - # that inherit ClasscallMetaclass as attributes rather than method - # descriptors. - isattribute = isattribute or isinstance(type(member), ClasscallMetaclass) - - return isattribute - - # We ignore the obscure case supported in the following return - # statement. The additional check opens a door for attributes without - # docstrings to appear in the Sage documentation, and more seriously - # effectively prevents certain attributes to get properly documented. - # See trac #28698. - - # That last condition addresses an obscure case of C-defined - # methods using a deprecated type in Python 3, that is not otherwise - # exported anywhere by Python - return isattribute or (not isinstance(parent, ModuleDocumenter) and - not inspect.isroutine(member) and - not isinstance(member, type)) - - def document_members(self, all_members=False): - # type: (bool) -> None + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + if isinstance(parent, ModuleDocumenter): + return False + # --------------------------------------------------------------------- + # Trac #34730: Do not pass objects of the class CachedMethodCaller as + # attributes. + # + # sage: from sphinx.util import inspect + # sage: A = AlgebrasWithBasis(QQ).example() + # sage: member = A.one_basis + # sage: type(member) + # <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'> + # sage: inspect.isattributedescriptor(member) + # True + # sage: inspect.isroutine(member) + # True + # + if inspect.isattributedescriptor(member) and not inspect.isroutine(member): + return True + # Trac #26522: Pass objects of classes that inherit ClasscallMetaclass + # as attributes rather than method descriptors. + from sage.misc.classcall_metaclass import ClasscallMetaclass + if isinstance(type(member), ClasscallMetaclass): + return True + # --------------------------------------------------------------------- + elif not inspect.isroutine(member) and not isinstance(member, type): + return True + else: + return False + + def document_members(self, all_members: bool = False) -> None: pass - def import_object(self): - # type: () -> Any - ret = ClassLevelDocumenter.import_object(self) - if isenumattribute(self.object): + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + try: + annotations = dict(inspect.getannotations(parent)) + parent.__annotations__ = annotations + + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + for (classname, attrname), annotation in analyzer.annotations.items(): + if classname == qualname and attrname not in annotations: + annotations[attrname] = annotation + except (AttributeError, PycodeError): + pass + except (AttributeError, TypeError): + # Failed to set __annotations__ (built-in, extensions, etc.) + pass + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + if inspect.isenumattribute(self.object): self.object = self.object.value - if isdescriptor(self.object) and \ - not self.is_function_or_method(self.object): - self._datadescriptor = True - else: - # if it's not a data descriptor - self._datadescriptor = False + if self.parent: + self.update_annotations(self.parent) + return ret - def get_real_modname(self): - # type: () -> str - return self.get_attr(self.parent or self.object, '__module__', None) \ - or self.modname + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + if doc: + docstring, metadata = separate_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False - def add_directive_header(self, sig): - # type: (unicode) -> None - ClassLevelDocumenter.add_directive_header(self, sig) + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) sourcename = self.get_sourcename() - if not self.options.annotation: - if not self._datadescriptor: - try: - objrepr = object_description(self.object) - except ValueError: - pass - else: - self.add_line(u' :annotation: = ' + objrepr, sourcename) - elif self.options.annotation is SUPPRESS: + if self.options.annotation is SUPPRESS or self.should_suppress_directive_header(): pass + elif self.options.annotation: + self.add_line(' :annotation: %s' % self.options.annotation, sourcename) else: - self.add_line(u' :annotation: %s' % self.options.annotation, - sourcename) + if self.config.autodoc_typehints != 'none': + # obtain type annotation for this attribute + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(annotations.get(self.objpath[-1]), + "smart") + else: + objrepr = stringify_typehint(annotations.get(self.objpath[-1])) + self.add_line(' :type: ' + objrepr, sourcename) - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - if not self._datadescriptor: - # if it's not a data descriptor, its docstring is very probably the - # wrong thing to display - no_docstring = True - ClassLevelDocumenter.add_content(self, more_content, no_docstring) + try: + if (self.options.no_value or self.should_suppress_value_header() or + ismock(self.object)): + pass + else: + objrepr = object_description(self.object) + self.add_line(' :value: ' + objrepr, sourcename) + except ValueError: + pass + + def get_attribute_comment(self, parent: Any, attrname: str) -> Optional[List[str]]: + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + if qualname and self.objpath: + key = (qualname, attrname) + if key in analyzer.attr_docs: + return list(analyzer.attr_docs[key]) + except (AttributeError, PycodeError): + pass + return None -class InstanceAttributeDocumenter(AttributeDocumenter): + def get_doc(self) -> Optional[List[List[str]]]: + # Check the attribute has a docstring-comment + comment = self.get_attribute_comment(self.parent, self.objpath[-1]) + if comment: + return [comment] + + try: + # Disable `autodoc_inherit_docstring` temporarily to avoid to obtain + # 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 + return super().get_doc() + finally: + self.config.autodoc_inherit_docstrings = orig # type: ignore + + def add_content(self, more_content: Optional[StringList]) -> None: + # Disable analyzing attribute comment on Documenter.add_content() to control it on + # AttributeDocumenter.add_content() + self.analyzer = None + + if more_content is None: + more_content = StringList() + self.update_content(more_content) + super().add_content(more_content) + + +class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore """ - Specialized Documenter subclass for attributes that cannot be imported - because they are instance attributes (e.g. assigned in __init__). + Specialized Documenter subclass for properties. """ - objtype = 'instanceattribute' - directivetype = 'attribute' + objtype = 'property' member_order = 60 - # must be higher than AttributeDocumenter - priority = 11 + # before AttributeDocumenter + priority = AttributeDocumenter.priority + 1 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - """This documents only INSTANCEATTR members.""" - return isattr and (member is INSTANCEATTR) - - def import_object(self): - # type: () -> bool - """Never import anything.""" - # disguise as an attribute - self.objtype = 'attribute' - self._datadescriptor = False - return True + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + if isinstance(parent, ClassDocumenter): + if inspect.isproperty(member): + return True + else: + __dict__ = safe_getattr(parent.object, '__dict__', {}) + obj = __dict__.get(membername) + return isinstance(obj, classmethod) and inspect.isproperty(obj.__func__) + else: + return False + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the exisitence of uninitialized instance attribute when failed to import + the attribute.""" + ret = super().import_object(raiseerror) + if ret and not inspect.isproperty(self.object): + __dict__ = safe_getattr(self.parent, '__dict__', {}) + obj = __dict__.get(self.objpath[-1]) + if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__): + self.object = obj.__func__ + self.isclassmethod = True + return True + else: + return False - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - """Never try to get a docstring from the object.""" - AttributeDocumenter.add_content(self, more_content, no_docstring=True) + self.isclassmethod = False + return ret + def document_members(self, all_members: bool = False) -> None: + pass -def get_documenters(app): - # type: (Sphinx) -> Dict[unicode, Type[Documenter]] - """Returns registered Documenter classes""" - return app.registry.documenters + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) + sourcename = self.get_sourcename() + if inspect.isabstractmethod(self.object): + self.add_line(' :abstractmethod:', sourcename) + if self.isclassmethod: + self.add_line(' :classmethod:', sourcename) + + if safe_getattr(self.object, 'fget', None): # property + func = self.object.fget + elif safe_getattr(self.object, 'func', None): # cached_property + func = self.object.func + else: + func = None -def autodoc_attrgetter(app, obj, name, *defargs): - # type: (Sphinx, Any, unicode, Any) -> Any + if func and self.config.autodoc_typehints != 'none': + try: + signature = inspect.signature(func, + type_aliases=self.config.autodoc_type_aliases) + if signature.return_annotation is not Parameter.empty: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(signature.return_annotation, "smart") + else: + objrepr = stringify_typehint(signature.return_annotation) + self.add_line(' :type: ' + objrepr, sourcename) + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + +class NewTypeAttributeDocumenter(AttributeDocumenter): + """ + Specialized Documenter subclass for NewTypes. + + Note: This must be invoked before MethodDocumenter because NewType is a kind of + function object. + """ + + objtype = 'newvarattribute' + directivetype = 'attribute' + priority = MethodDocumenter.priority + 1 + + @classmethod + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return not isinstance(parent, ModuleDocumenter) and inspect.isNewType(member) + + +def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any: """Alternative getattr() for types""" for typ, func in app.registry.autodoc_attrgettrs.items(): if isinstance(obj, typ): @@ -1614,41 +2923,43 @@ def autodoc_attrgetter(app, obj, name, *defargs): return safe_getattr(obj, name, *defargs) -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] +def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) + app.add_autodocumenter(NewTypeDataDocumenter) app.add_autodocumenter(FunctionDocumenter) + app.add_autodocumenter(DecoratorDocumenter) app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(AttributeDocumenter) - app.add_autodocumenter(InstanceAttributeDocumenter) + app.add_autodocumenter(PropertyDocumenter) + app.add_autodocumenter(NewTypeAttributeDocumenter) - app.add_config_value('autoclass_content', 'class', True) - app.add_config_value('autodoc_member_order', 'alphabetic', True) - app.add_config_value('autodoc_default_flags', [], True) # deprecated since Sphinx 1.8 + app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init')) + app.add_config_value('autodoc_member_order', 'alphabetical', True, + ENUM('alphabetical', 'bysource', 'groupwise')) + app.add_config_value('autodoc_class_signature', 'mixed', True, ENUM('mixed', 'separated')) app.add_config_value('autodoc_default_options', {}, True) - app.add_config_value('autodoc_docstring_signature', False, True) + app.add_config_value('autodoc_docstring_signature', True, True) app.add_config_value('autodoc_mock_imports', [], True) + app.add_config_value('autodoc_typehints', "signature", True, + ENUM("signature", "description", "none", "both")) + app.add_config_value('autodoc_typehints_description_target', 'all', True, + ENUM('all', 'documented', 'documented_params')) + app.add_config_value('autodoc_type_aliases', {}, True) + app.add_config_value('autodoc_typehints_format', "short", 'env', + ENUM("fully-qualified", "short")) app.add_config_value('autodoc_warningiserror', True, True) app.add_config_value('autodoc_inherit_docstrings', True, True) - app.add_config_value('autodoc_builtin_argspec', None, True) - + app.add_event('autodoc-before-process-signature') app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') + app.add_event('autodoc-process-bases') - return {'version': sphinx.__display_version__, 'parallel_read_safe': True} - + app.setup_extension('sphinx.ext.autodoc.preserve_defaults') + app.setup_extension('sphinx.ext.autodoc.type_comment') + app.setup_extension('sphinx.ext.autodoc.typehints') -class testcls: - """test doc string""" - - def __getattr__(self, x): - # type: (Any) -> Any - return x - - def __setattr__(self, x, y): - # type: (Any, Any) -> None - """Attr setter.""" + return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/src/sage_setup/autogen/giacpy-mkkeywords.py b/src/sage_setup/autogen/giacpy-mkkeywords.py index 3a918b20aeb..3e8ddac9be9 100644 --- a/src/sage_setup/autogen/giacpy-mkkeywords.py +++ b/src/sage_setup/autogen/giacpy-mkkeywords.py @@ -1,31 +1,32 @@ -#see the discussion in trac #29171 about where to store this file. +# see the discussion in trac #29171 about where to store this file. """ -This file is to help the maintainer to upgrade the giac keywords in +This file is to help the maintainer to upgrade the giac keywords in giacpy or giacpy_sage or sage.libs.giac -it creates auto-methods.pxi, keywords.pxi, newkeyword.pxi -it needs: the cas_help program from a giac installation - the aide_cas.txt file that you can built yourself like this: +It creates auto-methods.pxi, keywords.pxi, newkeyword.pxi +It needs: -grep -E '^#' share/giac/doc/aide_cas |sed -e 's/^# //' >aide_cas.txt +- the ``cas_help`` program from a giac installation +- the ``aide_cas.txt`` file that you can built yourself like this:: + grep -E '^#' share/giac/doc/aide_cas |sed -e 's/^# //' >aide_cas.txt It should not be used on the fly for an automatic installation script, because auto-methods.pxi is -quite long to be built and adding new giac keywords often break the built of giacpy +quite long to be built and adding new giac keywords often break the built of giacpy while not implementing a new giac keyword doesn't break the giacpy built. newkeywords.pxi is just created to check things manually but is not used in iacpy or sage.libs.giac AUTHORS: -- Frederic Han (2020-07) +- Frederic Han (2020-07) """ from subprocess import PIPE, Popen -blacklist=['eval', 'cas_setup', 'i', 'list', 'input', 'in', 'sto', 'string', 'and', 'break', 'continue', 'else', 'for', 'from', 'if', 'not', 'or', 'pow', 'print', 'return', 'set[]', 'try', 'while', 'open', 'output', 'do', 'of', 'Request','i[]', '[]', 'ffunction', 'sleep', '[..]'] +blacklist = ['eval', 'cas_setup', 'i', 'list', 'input', 'in', 'sto', 'string', 'and', 'break', 'continue', 'else', 'for', 'from', 'if', 'not', 'or', 'pow', 'print', 'return', 'set[]', 'try', 'while', 'open', 'output', 'do', 'of', 'Request', 'i[]', '[]', 'ffunction', 'sleep', '[..]'] -toremove=['!', '!=', '#', '$', '%', '/%', '%/', '%{%}', '&&', '&*', '&^', "'", '()', '*', '*=', '+', '-', '+&', '+=', '+infinity', '-<', '-=', '->', '-infinity', '.*', '.+', '.-', './', '.^', '/=', ':=', '<', '<=', '=', '=<', '==', '=>', '>', '>=', '?', '@', '@@', 'ACOSH', 'ACOT', 'ACSC', 'ASEC', 'ASIN', 'ASINH', 'ATAN', 'ATANH', 'COND', 'COS', 'COSH', 'COT', 'CSC', 'CST', 'Celsius2Fahrenheit', 'ClrDraw', 'ClrGraph', 'ClrIO', 'CyclePic', 'DIGITS', 'DOM_COMPLEX', 'DOM_FLOAT', 'DOM_FUNC', 'DOM_IDENT', 'DOM_INT', 'DOM_LIST', 'DOM_RAT', 'DOM_STRING', 'DOM_SYMBOLIC', 'DOM_int', 'DelFold', 'DelVar', 'Det', 'Dialog', 'Digits', 'Disp', 'DispG', 'DispHome', 'DrawFunc', 'DrawInv', 'DrawParm', 'DrawPol', 'DrawSlp', 'DropDown', 'DrwCtour', 'ERROR', 'EXP', 'EndDlog', 'FALSE', 'False', 'Fahrenheit2Celsius', 'Fill', 'Gcd', 'GetFold', 'Graph', 'IFTE', 'Input', 'InputStr', 'Int', 'Inverse', 'LN', 'LQ', 'LSQ', 'NORMALD', 'NewFold', 'NewPic', 'Nullspace', 'Output', 'Ox_2d_unit_vector', 'Ox_3d_unit_vector', 'Oy_2d_unit_vector', 'Oy_3d_unit_vector', 'Oz_3d_unit_vector', 'Pause', 'PopUp', 'Quo', 'REDIM', 'REPLACE', 'RclPic', 'Rem', 'Resultant', 'RplcPic', 'Rref', 'SCALE', 'SCALEADD', 'SCHUR', 'SIN', 'SVD', 'SVL', 'SWAPCOL', 'SWAPROW', 'SetFold', 'Si', 'StoPic', 'Store', 'TAN', 'TRUE', 'True', 'TeX', 'Text', 'Title', 'Unarchiv', 'WAIT', '^', '_(cm/s)', '_(ft/s)', '_(ft*lb)', '_(m/s)', '_(m/s^2)', '_(rad/s)', '_(rad/s^2)', '_(tr/min)', '_(tr/s)', '_A', '_Angstrom', '_Bq', '_Btu', '_Ci', '_F', '_F_', '_Fdy', '_G_', '_Gal', '_Gy', '_H', '_Hz', '_I0_', '_J', '_K', '_Kcal', '_MHz', '_MW', '_MeV', '_N', '_NA_', '_Ohm', '_P', '_PSun_', '_Pa', '_R', '_REarth_', '_RSun_', '_R_', '_Rankine', '_Rinfinity_', '_S', '_St', '_StdP_', '_StdT_', '_Sv', '_T', '_V', '_Vm_', '_W', '_Wb', '_Wh', '_a', '_a0_', '_acre', '_alpha_', '_angl_', '_arcmin', '_arcs', '_atm', '_au', '_b', '_bar', '_bbl', '_bblep', '_bu', '_buUS', '_c3_', '_c_', '_cal', '_cd', '_chain', '_cm', '_cm^2', '_cm^3', '_ct', '_cu', '_d', '_dB', '_deg', '_degreeF', '_dyn', '_eV', '_epsilon0_', '_epsilon0q_', '_epsilonox_', '_epsilonsi_', '_erg', '_f0_', '_fath', '_fbm', '_fc', '_fermi', '_flam', '_fm', '_ft', '_ft*lb', '_ftUS', '_ft^2', '_ft^3', '_g', '_g_', '_ga', '_galC', '_galUK', '_galUS', '_gf', '_gmol', '_gon', '_grad', '_grain', '_h', '_h_', '_ha', '_hbar_', '_hp', '_in', '_inH20', '_inHg', '_in^2', '_in^3', '_j', '_kWh', '_k_', '_kg', '_kip', '_km', '_km^2', '_knot', '_kph', '_kq_', '_l', '_lam', '_lambda0_', '_lambdac_', '_lb', '_lbf', '_lbmol', '_lbt', '_lep', '_liqpt', '_lm', '_lx', '_lyr', '_m', '_mEarth_', '_m^2', '_m^3', '_me_', '_mho', '_miUS', '_miUS^2', '_mi^2', '_mil', '_mile', '_mille', '_ml', '_mm', '_mmHg', '_mn', '_mol', '_mp_', '_mph', '_mpme_', '_mu0_', '_muB_', '_muN_', '_oz', '_ozUK', '_ozfl', '_ozt', '_pc', '_pdl', '_ph', '_phi_', '_pk', '_psi', '_ptUK', '_q_', '_qe_', '_qepsilon0_', '_qme_', '_qt', '_rad', '_rad_', '_rd', '_rem', '_rod', '_rpm', '_s', '_sb', '_sd_', '_sigma_', '_slug', '_sr', '_st', '_syr_', '_t', '_tbsp', '_tec', '_tep', '_tex', '_therm', '_ton', '_tonUK', '_torr', '_tr', '_tsp', '_twopi_', '_u', '_yd', '_yd^2', '_yd^3', '_yr', '_\xc2\xb5', '_µ', 'assert', 'affichage', 'alors', 'animate', 'animate3d', 'animation', 'approx_mode', 'archive', 'args', 'as_function_of', 'asc', 'asec', 'assign', 'backquote', 'begin', 'black', 'blanc', 'bleu', 'bloc', 'blue', 'breakpoint', 'by', 'c1oc2', 'c1op2', 'cache_tortue', 'cap', 'cap_flat_line', 'cap_round_line', 'cap_square_line', 'case', 'cat', 'catch', 'cd', 'choosebox', 'click', 'close', 'complex_mode', 'de', 'del', 'debug', 'default', 'div', 'double', 'ecris', 'efface', 'elif', 'end', 'end_for', 'end_if', 'end_while', 'epaisseur', 'epaisseur_ligne_1', 'epaisseur_ligne_2', 'epaisseur_ligne_3', 'epaisseur_ligne_4', 'epaisseur_ligne_5', 'epaisseur_ligne_6', 'epaisseur_ligne_7', 'epaisseur_point_1', 'epaisseur_point_2', 'epaisseur_point_3', 'epaisseur_point_4', 'epaisseur_point_5', 'epaisseur_point_6', 'epaisseur_point_7', 'erase', 'erase3d', 'est_cocyclique', 'est_inclus', 'et', 'faire', 'faux', 'feuille', 'ffaire', 'ffonction', 'fi', 'filled', 'fin_enregistrement', 'float', 'fonction', 'fopen', 'format', 'fpour', 'frame_3d', 'frames', 'fsi', 'ftantque', 'func', 'function', 'gauche', 'gl_ortho', 'gl_quaternion', 'gl_rotation', 'gl_shownames', 'gl_texture', 'gl_x', 'gl_x_axis_color', 'gl_x_axis_name', 'gl_x_axis_unit', 'gl_xtick', 'gl_y', 'gl_y_axis_color', 'gl_y_axis_name', 'gl_y_axis_unit', 'gl_ytick', 'gl_z', 'gl_z_axis_color', 'gl_z_axis_name', 'gl_z_axis_unit', 'gl_ztick', 'gnuplot', 'goto', 'graph2tex', 'graph3d2tex', 'graphe', 'graphe3d', 'graphe_probabiliste', 'graphe_suite', 'green', 'grid_paper', 'hidden_name', 'identifier', 'ifft', 'ifte', 'inputform', 'intersect', 'is_included', 'jusqu_a', 'jusqua', 'jusque', 'keep_algext', 'kill', 'label', 'labels', 'len', 'leve_crayon', 'line_width_1', 'line_width_2', 'line_width_3', 'line_width_4', 'line_width_5', 'line_width_6', 'line_width_7', 'lis', 'local', 'minus', 'mod', 'noir', 'nom_cache', 'non', 'od', 'option', 'otherwise', 'ou', 'pas', 'point_arret', 'point_carre', 'point_croix', 'point_div', 'point_etoile', 'point_invisible', 'point_losange', 'point_milieu', 'point_plus', 'point_point', 'point_polaire', 'point_triangle', 'point_width_1', 'point_width_2', 'point_width_3', 'point_width_4', 'point_width_5', 'point_width_6', 'point_width_7', 'pour', 'proc', 'program', 'quadrant1', 'quadrant2', 'quadrant3', 'quadrant4', 'range', 'redim', 'repeat', 'repete', 'repeter', 'replace', 'restart', 'rouge', 'saisir', 'saisir_chaine', 'sauve', 'save_history', 'scale', 'scaleadd', 'si', 'sinon', 'size', 'stack', 'step', 'switch', 'tantque', 'test', 'textinput', 'then', 'thiele', 'time', 'to', 'union', 'until', 'var', 'vector', 'vers', 'vert', 'vrai', 'watch', 'when', 'white', 'with_sqrt', 'write', 'wz_certificate', 'xor', 'yellow', '{}', '|', '||','expression'] +toremove = ['!', '!=', '#', '$', '%', '/%', '%/', '%{%}', '&&', '&*', '&^', "'", '()', '*', '*=', '+', '-', '+&', '+=', '+infinity', '-<', '-=', '->', '-infinity', '.*', '.+', '.-', './', '.^', '/=', ':=', '<', '<=', '=', '=<', '==', '=>', '>', '>=', '?', '@', '@@', 'ACOSH', 'ACOT', 'ACSC', 'ASEC', 'ASIN', 'ASINH', 'ATAN', 'ATANH', 'COND', 'COS', 'COSH', 'COT', 'CSC', 'CST', 'Celsius2Fahrenheit', 'ClrDraw', 'ClrGraph', 'ClrIO', 'CyclePic', 'DIGITS', 'DOM_COMPLEX', 'DOM_FLOAT', 'DOM_FUNC', 'DOM_IDENT', 'DOM_INT', 'DOM_LIST', 'DOM_RAT', 'DOM_STRING', 'DOM_SYMBOLIC', 'DOM_int', 'DelFold', 'DelVar', 'Det', 'Dialog', 'Digits', 'Disp', 'DispG', 'DispHome', 'DrawFunc', 'DrawInv', 'DrawParm', 'DrawPol', 'DrawSlp', 'DropDown', 'DrwCtour', 'ERROR', 'EXP', 'EndDlog', 'FALSE', 'False', 'Fahrenheit2Celsius', 'Fill', 'Gcd', 'GetFold', 'Graph', 'IFTE', 'Input', 'InputStr', 'Int', 'Inverse', 'LN', 'LQ', 'LSQ', 'NORMALD', 'NewFold', 'NewPic', 'Nullspace', 'Output', 'Ox_2d_unit_vector', 'Ox_3d_unit_vector', 'Oy_2d_unit_vector', 'Oy_3d_unit_vector', 'Oz_3d_unit_vector', 'Pause', 'PopUp', 'Quo', 'REDIM', 'REPLACE', 'RclPic', 'Rem', 'Resultant', 'RplcPic', 'Rref', 'SCALE', 'SCALEADD', 'SCHUR', 'SIN', 'SVD', 'SVL', 'SWAPCOL', 'SWAPROW', 'SetFold', 'Si', 'StoPic', 'Store', 'TAN', 'TRUE', 'True', 'TeX', 'Text', 'Title', 'Unarchiv', 'WAIT', '^', '_(cm/s)', '_(ft/s)', '_(ft*lb)', '_(m/s)', '_(m/s^2)', '_(rad/s)', '_(rad/s^2)', '_(tr/min)', '_(tr/s)', '_A', '_Angstrom', '_Bq', '_Btu', '_Ci', '_F', '_F_', '_Fdy', '_G_', '_Gal', '_Gy', '_H', '_Hz', '_I0_', '_J', '_K', '_Kcal', '_MHz', '_MW', '_MeV', '_N', '_NA_', '_Ohm', '_P', '_PSun_', '_Pa', '_R', '_REarth_', '_RSun_', '_R_', '_Rankine', '_Rinfinity_', '_S', '_St', '_StdP_', '_StdT_', '_Sv', '_T', '_V', '_Vm_', '_W', '_Wb', '_Wh', '_a', '_a0_', '_acre', '_alpha_', '_angl_', '_arcmin', '_arcs', '_atm', '_au', '_b', '_bar', '_bbl', '_bblep', '_bu', '_buUS', '_c3_', '_c_', '_cal', '_cd', '_chain', '_cm', '_cm^2', '_cm^3', '_ct', '_cu', '_d', '_dB', '_deg', '_degreeF', '_dyn', '_eV', '_epsilon0_', '_epsilon0q_', '_epsilonox_', '_epsilonsi_', '_erg', '_f0_', '_fath', '_fbm', '_fc', '_fermi', '_flam', '_fm', '_ft', '_ft*lb', '_ftUS', '_ft^2', '_ft^3', '_g', '_g_', '_ga', '_galC', '_galUK', '_galUS', '_gf', '_gmol', '_gon', '_grad', '_grain', '_h', '_h_', '_ha', '_hbar_', '_hp', '_in', '_inH20', '_inHg', '_in^2', '_in^3', '_j', '_kWh', '_k_', '_kg', '_kip', '_km', '_km^2', '_knot', '_kph', '_kq_', '_l', '_lam', '_lambda0_', '_lambdac_', '_lb', '_lbf', '_lbmol', '_lbt', '_lep', '_liqpt', '_lm', '_lx', '_lyr', '_m', '_mEarth_', '_m^2', '_m^3', '_me_', '_mho', '_miUS', '_miUS^2', '_mi^2', '_mil', '_mile', '_mille', '_ml', '_mm', '_mmHg', '_mn', '_mol', '_mp_', '_mph', '_mpme_', '_mu0_', '_muB_', '_muN_', '_oz', '_ozUK', '_ozfl', '_ozt', '_pc', '_pdl', '_ph', '_phi_', '_pk', '_psi', '_ptUK', '_q_', '_qe_', '_qepsilon0_', '_qme_', '_qt', '_rad', '_rad_', '_rd', '_rem', '_rod', '_rpm', '_s', '_sb', '_sd_', '_sigma_', '_slug', '_sr', '_st', '_syr_', '_t', '_tbsp', '_tec', '_tep', '_tex', '_therm', '_ton', '_tonUK', '_torr', '_tr', '_tsp', '_twopi_', '_u', '_yd', '_yd^2', '_yd^3', '_yr', '_\xc2\xb5', '_µ', 'assert', 'affichage', 'alors', 'animate', 'animate3d', 'animation', 'approx_mode', 'archive', 'args', 'as_function_of', 'asc', 'asec', 'assign', 'backquote', 'begin', 'black', 'blanc', 'bleu', 'bloc', 'blue', 'breakpoint', 'by', 'c1oc2', 'c1op2', 'cache_tortue', 'cap', 'cap_flat_line', 'cap_round_line', 'cap_square_line', 'case', 'cat', 'catch', 'cd', 'choosebox', 'click', 'close', 'complex_mode', 'de', 'del', 'debug', 'default', 'div', 'double', 'ecris', 'efface', 'elif', 'end', 'end_for', 'end_if', 'end_while', 'epaisseur', 'epaisseur_ligne_1', 'epaisseur_ligne_2', 'epaisseur_ligne_3', 'epaisseur_ligne_4', 'epaisseur_ligne_5', 'epaisseur_ligne_6', 'epaisseur_ligne_7', 'epaisseur_point_1', 'epaisseur_point_2', 'epaisseur_point_3', 'epaisseur_point_4', 'epaisseur_point_5', 'epaisseur_point_6', 'epaisseur_point_7', 'erase', 'erase3d', 'est_cocyclique', 'est_inclus', 'et', 'faire', 'faux', 'feuille', 'ffaire', 'ffonction', 'fi', 'filled', 'fin_enregistrement', 'float', 'fonction', 'fopen', 'format', 'fpour', 'frame_3d', 'frames', 'fsi', 'ftantque', 'func', 'function', 'gauche', 'gl_ortho', 'gl_quaternion', 'gl_rotation', 'gl_shownames', 'gl_texture', 'gl_x', 'gl_x_axis_color', 'gl_x_axis_name', 'gl_x_axis_unit', 'gl_xtick', 'gl_y', 'gl_y_axis_color', 'gl_y_axis_name', 'gl_y_axis_unit', 'gl_ytick', 'gl_z', 'gl_z_axis_color', 'gl_z_axis_name', 'gl_z_axis_unit', 'gl_ztick', 'gnuplot', 'goto', 'graph2tex', 'graph3d2tex', 'graphe', 'graphe3d', 'graphe_probabiliste', 'graphe_suite', 'green', 'grid_paper', 'hidden_name', 'identifier', 'ifft', 'ifte', 'inputform', 'intersect', 'is_included', 'jusqu_a', 'jusqua', 'jusque', 'keep_algext', 'kill', 'label', 'labels', 'len', 'leve_crayon', 'line_width_1', 'line_width_2', 'line_width_3', 'line_width_4', 'line_width_5', 'line_width_6', 'line_width_7', 'lis', 'local', 'minus', 'mod', 'noir', 'nom_cache', 'non', 'od', 'option', 'otherwise', 'ou', 'pas', 'point_arret', 'point_carre', 'point_croix', 'point_div', 'point_etoile', 'point_invisible', 'point_losange', 'point_milieu', 'point_plus', 'point_point', 'point_polaire', 'point_triangle', 'point_width_1', 'point_width_2', 'point_width_3', 'point_width_4', 'point_width_5', 'point_width_6', 'point_width_7', 'pour', 'proc', 'program', 'quadrant1', 'quadrant2', 'quadrant3', 'quadrant4', 'range', 'redim', 'repeat', 'repete', 'repeter', 'replace', 'restart', 'rouge', 'saisir', 'saisir_chaine', 'sauve', 'save_history', 'scale', 'scaleadd', 'si', 'sinon', 'size', 'stack', 'step', 'switch', 'tantque', 'test', 'textinput', 'then', 'thiele', 'time', 'to', 'union', 'until', 'var', 'vector', 'vers', 'vert', 'vrai', 'watch', 'when', 'white', 'with_sqrt', 'write', 'wz_certificate', 'xor', 'yellow', '{}', '|', '||','expression'] @@ -89,7 +90,7 @@ Mi.write(s) Mi.close() - + # building keywords.pxi with open("keywords.pxi", "w") as Fi: Fi.write("# file auto generated by mkkeywords.py\n") diff --git a/src/sage_setup/autogen/interpreters/specs/base.py b/src/sage_setup/autogen/interpreters/specs/base.py index 263b22ea2d6..67d75b9bf45 100644 --- a/src/sage_setup/autogen/interpreters/specs/base.py +++ b/src/sage_setup/autogen/interpreters/specs/base.py @@ -112,18 +112,18 @@ def __init__(self, type, mc_retval=None): Initializes the fields described in the documentation for InterpreterSpec.__init__, as well as the following: - mc_args, mc_constants, mc_stack -- MemoryChunk values - return_type -- the type returned by the C interpreter (None for int, - where 1 means success and 0 means error) - mc_retval -- None, or the MemoryChunk to use as a return value - ipow_range -- the range of exponents supported by the ipow - instruction (default is False, meaning never use ipow) - adjust_retval -- None, or a string naming a function to call - in the wrapper's __call__ to modify the return - value of the interpreter - implement_call_c -- True if the wrapper should have a fast cdef call_c - method (that bypasses the Python call overhead) - (default True) + - mc_args, mc_constants, mc_stack -- MemoryChunk values + - return_type -- the type returned by the C interpreter (None for int, + where 1 means success and 0 means error) + - mc_retval -- None, or the MemoryChunk to use as a return value + - ipow_range -- the range of exponents supported by the ipow + instruction (default is False, meaning never use ipow) + - adjust_retval -- None, or a string naming a function to call + in the wrapper's __call__ to modify the return + value of the interpreter + - implement_call_c -- True if the wrapper should have a fast cdef call_c + method (that bypasses the Python call overhead) + (default True) EXAMPLES:: diff --git a/src/sage_setup/clean.py b/src/sage_setup/clean.py index 4ed286cdac6..3cf0d08a353 100644 --- a/src/sage_setup/clean.py +++ b/src/sage_setup/clean.py @@ -2,7 +2,10 @@ Clean the Install Dir """ #***************************************************************************** -# Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com> +# Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com> +# 2015 Jeroen Demeyer +# 2017 Erik M. Bray +# 2020-2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,8 +18,8 @@ import os import importlib.util -from sage_setup.find import installed_files_by_module, get_extensions, read_distribution - +from sage.misc.package_dir import SourceDistributionFilter +from sage_setup.find import installed_files_by_module, get_extensions def _remove(file_set, module_base, to_remove): @@ -83,8 +86,14 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module sage: from sage_setup.find import find_python_sources, find_extra_files sage: python_packages, python_modules, cython_modules = find_python_sources( ....: SAGE_SRC, ['sage', 'sage_setup']) - sage: extra_files = list(find_extra_files(SAGE_SRC, - ....: ['sage', 'sage_setup'], cythonized_dir, []).items()) + sage: extra_files = find_extra_files(SAGE_SRC, + ....: ['sage', 'sage_setup'], cythonized_dir, []) + sage: from importlib.metadata import files + sage: for f in files('sagemath-standard'): + ....: dir = os.path.dirname(str(f)) + ....: extra_files[dir] = extra_files.get(dir, []) + ....: extra_files[dir].append(str(f)) + sage: extra_files = list(extra_files.items()) sage: from sage_setup.clean import _find_stale_files TODO: Also check extension modules:: @@ -141,7 +150,7 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module def clean_install_dir(site_packages, python_packages, python_modules, ext_modules, data_files, nobase_data_files, *, - distributions=None): + distributions=None, exclude_distributions=None): """ Delete all modules that are **not** being installed @@ -178,10 +187,11 @@ def clean_install_dir(site_packages, python_packages, python_modules, ext_module ``distribution`` (from a ``# sage_setup: distribution = PACKAGE`` directive in the file) is an element of ``distributions``. """ + distribution_filter = SourceDistributionFilter(distributions, exclude_distributions) stale_file_iter = _find_stale_files( site_packages, python_packages, python_modules, ext_modules, data_files, nobase_data_files) for f in stale_file_iter: f = os.path.join(site_packages, f) - if distributions is None or read_distribution(f) in distributions: + if f in distribution_filter: print('Cleaning up stale file: {0}'.format(f)) os.unlink(f) diff --git a/src/sage_setup/find.py b/src/sage_setup/find.py index 5cec2c4ee44..61d91abc2eb 100644 --- a/src/sage_setup/find.py +++ b/src/sage_setup/find.py @@ -2,7 +2,12 @@ Recursive Directory Contents """ # **************************************************************************** -# Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com> +# Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com> +# 2014 R. Andrew Ohana +# 2015-2018 Jeroen Demeyer +# 2017 Erik M. Bray +# 2021 Tobias Diez +# 2020-2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,49 +24,13 @@ from collections import defaultdict from sage.misc.package_dir import is_package_or_sage_namespace_package_dir as is_package_or_namespace_package_dir +from sage.misc.package_dir import read_distribution, SourceDistributionFilter +assert read_distribution # unused in this file, re-export for compatibility -def read_distribution(src_file): - """ - Parse ``src_file`` for a ``# sage_setup: distribution = PKG`` directive. - - INPUT: - - - ``src_file`` -- file name of a Python or Cython source file - - OUTPUT: - - - a string, the name of the distribution package (``PKG``); or the empty - string if no directive was found. - - EXAMPLES:: - sage: from sage.env import SAGE_SRC - sage: from sage_setup.find import read_distribution - sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'tdlib.pyx')) - 'sagemath-tdlib' - sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py')) - '' - """ - from Cython.Utils import open_source_file - with open_source_file(src_file, error_handling='ignore') as fh: - for line in fh: - # Adapted from Cython's Build/Dependencies.py - line = line.lstrip() - if not line: - continue - if line[0] != '#': - break - line = line[1:].lstrip() - kind = "sage_setup:" - if line.startswith(kind): - key, _, value = [s.strip() for s in line[len(kind):].partition('=')] - if key == "distribution": - return value - return '' - - -def find_python_sources(src_dir, modules=['sage'], distributions=None): +def find_python_sources(src_dir, modules=['sage'], distributions=None, + exclude_distributions=None): """ Find all Python packages and Python/Cython modules in the sources. @@ -78,6 +47,11 @@ def find_python_sources(src_dir, modules=['sage'], distributions=None): directive in the module source file) is an element of ``distributions``. + - ``exclude_distributions`` -- (default: ``None``) if not ``None``, + should be a sequence or set of strings: exclude modules whose + ``distribution`` (from a ``# sage_setup: distribution = PACKAGE`` + directive in the module source file) is in ``exclude_distributions``. + OUTPUT: Triple consisting of - the list of package names (corresponding to ordinary packages @@ -164,6 +138,8 @@ def find_python_sources(src_dir, modules=['sage'], distributions=None): python_modules = [] cython_modules = [] + distribution_filter = SourceDistributionFilter(distributions, exclude_distributions) + cwd = os.getcwd() try: os.chdir(src_dir) @@ -171,24 +147,21 @@ def find_python_sources(src_dir, modules=['sage'], distributions=None): for dirpath, dirnames, filenames in os.walk(module): package = dirpath.replace(os.path.sep, '.') if not is_package_or_namespace_package_dir(dirpath): + # Skip any subdirectories + dirnames[:] = [] continue # Ordinary package or namespace package. if distributions is None or '' in distributions: python_packages.append(package) - def is_in_distributions(filename): - if distributions is None: - return True - distribution = read_distribution(os.path.join(dirpath, filename)) - return distribution in distributions - for filename in filenames: base, ext = os.path.splitext(filename) + filepath = os.path.join(dirpath, filename) if ext == PYMOD_EXT and base != '__init__': - if is_in_distributions(filename): + if filepath in distribution_filter: python_modules.append(package + '.' + base) if ext == '.pyx': - if is_in_distributions(filename): + if filepath in distribution_filter: cython_modules.append(Extension(package + '.' + base, sources=[os.path.join(dirpath, filename)])) @@ -196,7 +169,7 @@ def is_in_distributions(filename): os.chdir(cwd) return python_packages, python_modules, cython_modules -def filter_cython_sources(src_dir, distributions): +def filter_cython_sources(src_dir, distributions, exclude_distributions=None): """ Find all Cython modules in the given source directory that belong to the given distributions. @@ -235,12 +208,12 @@ def filter_cython_sources(src_dir, distributions): 1 loops, best of 1: 850 ms per loop """ files: list[str] = [] - + distribution_filter = SourceDistributionFilter(distributions, exclude_distributions) for dirpath, dirnames, filenames in os.walk(src_dir): for filename in filenames: filepath = os.path.join(dirpath, filename) base, ext = os.path.splitext(filename) - if ext == '.pyx' and read_distribution(filepath) in distributions: + if ext == '.pyx' and filepath in distribution_filter: files.append(filepath) return files diff --git a/src/sage_setup/library_order.py b/src/sage_setup/library_order.py index 0b450b37239..f40690f8d22 100644 --- a/src/sage_setup/library_order.py +++ b/src/sage_setup/library_order.py @@ -32,7 +32,7 @@ "brial_groebner", "m4rie", ] + aliases.get("M4RI_LIBRARIES", []) + [ - "zn_poly", "gap", + "gap", ] + aliases.get("GDLIB_LIBRARIES", []) + aliases.get("LIBPNG_LIBRARIES", []) + [ "m", "readline", "Lfunction", ] + aliases.get("CBLAS_LIBRARIES", []) + aliases.get("ZLIB_LIBRARIES", []) diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index a69b69924c5..9f69af385e9 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -22,11 +22,12 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.8, <3.11 +python_requires = >=3.8, <3.12 install_requires = esyscmd(`sage-get-system-packages install-requires \ sage_conf \ @@ -68,7 +69,6 @@ dnl From Makefile.in: DOC_DEPENDENCIES | sed "2,\$s/^/ /;"')dnl' dnl Other Python packages that are standard spkg, used in doctests esyscmd(`sage-get-system-packages install-requires \ - rpy2 \ fpylll \ | sed "2,\$s/^/ /;"')dnl' dnl pycryptosat # Sage distribution installs it as part of cryptominisat. According to its README on https://pypi.org/project/pycryptosat/: "The pycryptosat python package compiles while compiling CryptoMiniSat. It cannot be compiled on its own, it must be compiled at the same time as CryptoMiniSat." @@ -160,3 +160,8 @@ sage = ext_data/magma/sage/* ext_data/valgrind/* ext_data/threejs/* + +[options.extras_require] +R = esyscmd(`sage-get-system-packages install-requires \ + rpy2 \ + | sed "2,\$s/^/ /;"')dnl' diff --git a/src/setup.py b/src/setup.py index 948e4754513..f0dd4054bb7 100755 --- a/src/setup.py +++ b/src/setup.py @@ -80,7 +80,7 @@ # Exclude a few files if the corresponding distribution is not loaded optional_packages = ['mcqd', 'bliss', 'tdlib', - 'coxeter3', 'fes', 'sirocco', 'meataxe'] + 'coxeter3', 'sirocco', 'meataxe'] not_installed_packages = [package for package in optional_packages if not is_package_installed_and_updated(package)] diff --git a/src/tox.ini b/src/tox.ini index b54938f74e3..ccf3141a9a7 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -25,6 +25,10 @@ envlist = doctest, coverage, startuptime, pycodestyle-minimal, relint, codespell # When adding environments above, also update the delegations in SAGE_ROOT/tox.ini skipsdist = true +requires = + # For the renamed "allowlist_externals" keyword + tox>=3.18 + [sagedirect] # Base for tox environments that bypass the virtual environment set up by tox, # calling sage directly. @@ -33,6 +37,8 @@ passenv = setenv = SAGE={toxinidir}/../sage envdir={toxworkdir}/sagedirect +allowlist_externals = + {env:SAGE} [testenv:doctest] description = @@ -41,6 +47,7 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} +allowlist_externals = {[sagedirect]allowlist_externals} commands = {env:SAGE} -t -p 0 {posargs:--all} @@ -52,6 +59,7 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} +allowlist_externals = {[sagedirect]allowlist_externals} commands = {env:SAGE} --coverage {posargs:--all} @@ -63,6 +71,7 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} +allowlist_externals = {[sagedirect]allowlist_externals} commands = {env:SAGE} --startuptime {posargs} @@ -71,9 +80,11 @@ description = run the static typing checker pyright deps = pyright setenv = + {[sagedirect]setenv} HOME={envdir} # Fix version, see .github/workflows/build.yml PYRIGHT_PYTHON_FORCE_VERSION=1.1.232 +allowlist_externals = {[sagedirect]allowlist_externals} ## We run pyright from within the sage-env so that SAGE_LOCAL is available. ## pyright is already configured via SAGE_ROOT/pyrightconfig.json to use our venv. ## @@ -81,7 +92,7 @@ setenv = ## and may run out of memory. When no files/directories are given, just run it ## on the packages that already have typing annotations. commands = - {toxinidir}/../sage -sh -c 'pyright {posargs:{toxinidir}/sage/combinat {toxinidir}/sage/manifolds}' + {env:SAGE} -sh -c 'pyright {posargs:{toxinidir}/sage/combinat {toxinidir}/sage/manifolds}' [testenv:pycodestyle] description = @@ -96,6 +107,7 @@ description = check against Sage's minimal style conventions # Check for the following issues: # E111: indentation is not a multiple of four + # E211: whitespace before '(' # E306: expected 1 blank line before a nested definition, found 0 # E401: multiple imports on one line # E701: multiple statements on one line (colon) @@ -103,14 +115,16 @@ description = # E703: statement ends with a semicolon # E711: comparison to None should be ‘if cond is None:’ # E712: comparison to True should be ‘if cond is True:’ or ‘if cond:’ - # E713 test for membership should be 'not in' + # E713 test for membership should be ’not in’ # E721: do not compare types, use isinstance() # E722: do not use bare except, specify exception instead + # W291: trailing whitespace + # W391: blank line at end of file # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E401,E703,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E211,E306,E401,E701,E702,E703,W291,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 @@ -123,9 +137,9 @@ description = # https://github.com/codingjoe/relint # The patterns are in .relint.yml deps = relint -whitelist_externals = find +allowlist_externals = find commands = find {posargs:{toxinidir}/sage/} \ - -name "*#*" -prune -o \ + -name "*\#*" -prune -o \ -name "*.a" -prune -o \ -name "*.bak" -prune -o \ -name "*.bz2" -prune -o \ @@ -162,7 +176,7 @@ description = # https://pypi.org/project/codespell/ deps = codespell commands = codespell \ - --skip="*#*,*.a,*.bak,*.bz2,*.dia,*.gz,*.ico,*.inv,*.JPEG,*.jpeg" \ + --skip="*\#*,*.a,*.bak,*.bz2,*.dia,*.gz,*.ico,*.inv,*.JPEG,*.jpeg" \ --skip="*.JPG,*.jpg,*.log,*.o,*.orig,*.PDF,*.pdf,*.PNG,*.png,*.pyc" \ --skip="*.so,*.sobj,*.sws,*.tar,*.tgz,*.xz,*.zip,*~*,.DS_Store" \ --skip="doc/ca,doc/de,doc/es,doc/fr,doc/hu,doc/it,doc/ja,doc/pt,doc/ru,doc/tr" \ @@ -196,6 +210,7 @@ rst-roles = data, exc, func, + kbd, meth, mod, obj, diff --git a/tox.ini b/tox.ini index 0106e693891..5eb3e79633b 100644 --- a/tox.ini +++ b/tox.ini @@ -128,6 +128,9 @@ envlist = # # pycodestyle +requires = + # For repaired numerical factors in tox 4: + tox>=4.2.7 skipsdist = true @@ -181,6 +184,7 @@ setenv = # What system packages should be installed. Default: All standard packages with spkg-configure. SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard: recommended: EXTRA_SAGE_PACKAGES_3=_recommended $(head -n 1 build/pkgs/_recommended/dependencies) + develop: EXTRA_SAGE_PACKAGES_4=_develop $(head -n 1 build/pkgs/_develop/dependencies) minimal: SAGE_PACKAGE_LIST_ARGS=_prereq maximal: SAGE_PACKAGE_LIST_ARGS=:standard: :optional: conda-environment: SAGE_PACKAGE_LIST_ARGS=_prereq @@ -197,13 +201,15 @@ setenv = docker: BASE_TAG=latest # # https://hub.docker.com/_/ubuntu?tab=description - # as of 2022-05, latest=rolling=jammy=22.04, devel=kinetic=22.10 + # as of 2023-01, latest=jammy=22.04, rolling=kinetic=22.10, devel=lunar=23.04 # ubuntu: SYSTEM=debian ubuntu: BASE_IMAGE=ubuntu - ubuntu-toolchain: EXTRA_REPOSITORY=ppa:ubuntu-toolchain-r/test + ubuntu-toolchain: EXTRA_REPOSITORY_1=ppa:ubuntu-toolchain-r/test ubuntu-toolchain-trusty: EXTRA_SYSTEM_PACKAGES=binutils-2.26 ubuntu-toolchain-trusty: EXTRA_PATH=/usr/lib/binutils-2.26/bin + ubuntu-deadsnakes: EXTRA_REPOSITORY_2=ppa:deadsnakes/ppa + ubuntu: EXTRA_REPOSITORIES={env:EXTRA_REPOSITORY_1:} {env:EXTRA_REPOSITORY_2:} ubuntu-trusty: BASE_TAG=trusty ubuntu-trusty: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-xenial: BASE_TAG=xenial @@ -214,6 +220,8 @@ setenv = ubuntu-jammy: BASE_TAG=jammy ubuntu-kinetic: BASE_TAG=kinetic ubuntu-kinetic: IGNORE_MISSING_SYSTEM_PACKAGES=yes + ubuntu-lunar: BASE_TAG=lunar + ubuntu-lunar: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/_/debian # debian-bullseye does not have libgiac-dev @@ -245,9 +253,10 @@ setenv = linuxmint-20.2: BASE_IMAGE=linuxmintd/mint20.2 linuxmint-20.3: BASE_IMAGE=linuxmintd/mint20.3 linuxmint-21: BASE_IMAGE=linuxmintd/mint21 + linuxmint-21.1: BASE_IMAGE=linuxmintd/mint21.1 # # https://hub.docker.com/_/fedora - # as of 2021-11, latest=35, rawhide=37 + # as of 2023-01, latest=37, rawhide=38 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora-26: BASE_TAG=26 @@ -272,6 +281,8 @@ setenv = fedora-36: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-37: BASE_TAG=37 fedora-37: IGNORE_MISSING_SYSTEM_PACKAGES=yes + fedora-38: BASE_TAG=38 + fedora-38: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/scientificlinux/sl # @@ -299,6 +310,7 @@ setenv = gentoo: BASE_IMAGE=sheerluck/sage-on-gentoo-stage4 gentoo-python3.9: BASE_TAG=latest-py39 gentoo-python3.10: BASE_TAG=latest-py10 + gentoo-python3.11: BASE_TAG=latest-py11 gentoo: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/archlinux/ @@ -507,17 +519,20 @@ setenv = python3.9: PYTHON_MINOR=9 python3.10: PYTHON_MINOR=10 python3.11: PYTHON_MINOR=11 + python3.12: PYTHON_MINOR=12 CONFIG_CONFIGURE_ARGS_1=--with-system-python3=yes python3_spkg: CONFIG_CONFIGURE_ARGS_1=--without-system-python3 + python3.8,python3.9,python3.10,python3.11,python3.12: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} + python3.8,python3.9,python3.10,python3.11,python3.12: EXTRA_SAGE_PACKAGES_5=_python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} _bootstrap liblzma bzip2 libffi libpng macos-python3_xcode: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/usr/bin/python3 macos-{python3_xcode,nohomebrew}-{python3.8}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # Homebrew keg installs - homebrew-{python3.8,python3.9,python3.10,python3.11}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python={env:HOMEBREW}/opt/python@{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 + homebrew-{python3.8,python3.9,python3.10,python3.11,python3.12}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python={env:HOMEBREW}/opt/python@{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # Installers from https://www.python.org/downloads/macos/ (must manually download and install) macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Frameworks/Python.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}-cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}/bin/python3 - manylinux-{python3.8,python3.9,python3.10,python3.11}: EXTRA_SAGE_PACKAGES=_bootstrap xz bzip2 libffi libpng + manylinux-{python3.8,python3.9,python3.10,python3.11,python3.12}: EXTRA_SAGE_PACKAGES_5=_bootstrap liblzma bzip2 libffi libpng conda: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python3 # # - toolchain @@ -570,13 +585,13 @@ setenv = # # Resulting EXTRA_SAGE_PACKAGES # - ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES:} + ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES_4:} {env:EXTRA_SAGE_PACKAGES_5:} {env:EXTRA_SAGE_PACKAGES:} # environment will be skipped if regular expression does not match against the sys.platform string platform = local-macos: darwin -whitelist_externals = +allowlist_externals = bash docker: docker homebrew: brew @@ -678,7 +693,7 @@ commands = [testenv:check_configure] ## Test that configure behaves properly -whitelist_externals = +allowlist_externals = bash setenv = HOME = {envdir} @@ -691,7 +706,7 @@ commands = passenv = HOME envdir = {toxworkdir}/src -whitelist_externals = tox +allowlist_externals = tox commands = tox -c {toxinidir}/src/tox.ini -e {envname} -- {posargs} [testenv:doctest] @@ -700,7 +715,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:coverage] description = @@ -708,7 +723,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:startuptime] description = @@ -716,7 +731,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:pycodestyle] description = @@ -724,7 +739,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:pycodestyle-minimal] description = @@ -732,7 +747,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:relint] description = @@ -740,14 +755,14 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:codespell] description = check for misspelled words in source code (use -w -i to fix) passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} # Run on the whole project, not just src/ by default, if invoked directly at top level commands = tox -c {toxinidir}/src/tox.ini -e {envname} -- {posargs:{toxinidir}} @@ -757,7 +772,7 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals} [testenv:rst] description = @@ -765,4 +780,4 @@ description = passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} -whitelist_externals = {[sage_src]whitelist_externals} +allowlist_externals = {[sage_src]allowlist_externals}