diff --git a/.github/workflows/ci-cygwin-minimal.yml b/.github/workflows/ci-cygwin-minimal.yml index f14c683a058..04299199b49 100644 --- a/.github/workflows/ci-cygwin-minimal.yml +++ b/.github/workflows/ci-cygwin-minimal.yml @@ -17,6 +17,7 @@ env: CYGWIN: winsymlinks:native CONFIGURE_ARGS: --enable-experimental-packages --enable-download-from-upstream-url SAGE_FAT_BINARY: yes + SAGE_LOCAL: /opt/sage-${{ github.sha }} jobs: @@ -41,31 +42,20 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - name: tox run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) - choco install $PACKAGES --source cygwin - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -81,9 +71,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + 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. @@ -111,31 +101,20 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + - name: tox run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -151,9 +130,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + 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. @@ -169,7 +148,7 @@ jobs: env: STAGE: ii-a PREVIOUS_STAGES: i-* - TARGETS: cvxopt rpy2 networkx + TARGETS: cvxopt rpy2 LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} @@ -186,20 +165,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -207,17 +177,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -233,9 +201,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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. @@ -266,20 +234,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -287,17 +246,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -313,9 +270,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -344,20 +301,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -365,17 +313,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -391,9 +337,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -422,20 +368,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -443,17 +380,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -469,9 +404,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -500,20 +435,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -521,17 +447,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -547,9 +471,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -559,11 +483,11 @@ jobs: ############################################## stage-iii ########################################## - cygwin-stage-iii: + cygwin-stage-iii-a: env: - STAGE: iii + STAGE: iii-a PREVIOUS_STAGES: ii-* - TARGETS: build + TARGETS: sagelib LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} @@ -571,6 +495,8 @@ jobs: runs-on: windows-latest + continue-on-error: true + strategy: fail-fast: false matrix: @@ -580,20 +506,82 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + C:\\tools\\cygwin\\bin\\bash -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 '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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -601,17 +589,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -627,9 +613,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -639,18 +625,20 @@ jobs: ############################################## stage-iv ########################################## - cygwin-stage-iv-a: + cygwin-stage-iv: env: - STAGE: iv-a - PREVIOUS_STAGES: iii - TARGETS: ptest + 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] + needs: [cygwin-stage-iii-a, cygwin-stage-iii-b] runs-on: windows-latest + continue-on-error: true + strategy: fail-fast: false matrix: @@ -660,20 +648,80 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + 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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -681,17 +729,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -707,9 +753,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -717,15 +763,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-b: + cygwin-stage-v-b: env: - STAGE: iv-b - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -738,20 +784,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -759,17 +796,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -785,9 +820,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -795,15 +830,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-c: + cygwin-stage-v-c: env: - STAGE: iv-c - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -816,20 +851,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -837,17 +863,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -863,9 +887,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -873,15 +897,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-d: + cygwin-stage-v-d: env: - STAGE: iv-d - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -894,20 +918,78 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -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 '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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + 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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -915,17 +997,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -943,7 +1023,7 @@ jobs: # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: diff --git a/.github/workflows/ci-cygwin-standard.yml b/.github/workflows/ci-cygwin-standard.yml index 09e3517485f..7ee83ebcb6d 100644 --- a/.github/workflows/ci-cygwin-standard.yml +++ b/.github/workflows/ci-cygwin-standard.yml @@ -17,6 +17,7 @@ env: CYGWIN: winsymlinks:native CONFIGURE_ARGS: --enable-experimental-packages --enable-download-from-upstream-url SAGE_FAT_BINARY: yes + SAGE_LOCAL: /opt/sage-${{ github.sha }} jobs: @@ -41,31 +42,20 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - name: tox run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) - choco install $PACKAGES --source cygwin - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -81,9 +71,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + 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. @@ -111,31 +101,20 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + - name: tox run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -151,9 +130,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + 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. @@ -169,7 +148,7 @@ jobs: env: STAGE: ii-a PREVIOUS_STAGES: i-* - TARGETS: cvxopt rpy2 networkx + TARGETS: cvxopt rpy2 LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} @@ -186,20 +165,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -207,17 +177,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -233,9 +201,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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. @@ -266,20 +234,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -287,17 +246,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -313,9 +270,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -344,20 +301,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -365,17 +313,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -391,9 +337,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -422,20 +368,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -443,17 +380,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -469,9 +404,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -500,20 +435,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -521,17 +447,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 base-toolchain && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -547,9 +471,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -559,11 +483,11 @@ jobs: ############################################## stage-iii ########################################## - cygwin-stage-iii: + cygwin-stage-iii-a: env: - STAGE: iii + STAGE: iii-a PREVIOUS_STAGES: ii-* - TARGETS: build + TARGETS: sagelib LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} @@ -571,6 +495,8 @@ jobs: runs-on: windows-latest + continue-on-error: true + strategy: fail-fast: false matrix: @@ -580,20 +506,82 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + C:\\tools\\cygwin\\bin\\bash -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 '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: [standard] + 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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -601,17 +589,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -627,9 +613,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -639,18 +625,20 @@ jobs: ############################################## stage-iv ########################################## - cygwin-stage-iv-a: + cygwin-stage-iv: env: - STAGE: iv-a - PREVIOUS_STAGES: iii - TARGETS: ptest + 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] + needs: [cygwin-stage-iii-a, cygwin-stage-iii-b] runs-on: windows-latest + continue-on-error: true + strategy: fail-fast: false matrix: @@ -660,20 +648,80 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + 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: [standard] + 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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -681,17 +729,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure + C:\\tools\\cygwin\\bin\\bash -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 -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make - run: | - C:\\tools\\cygwin\\bin\\bash -l -x -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -707,9 +753,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -717,15 +763,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-b: + cygwin-stage-v-b: env: - STAGE: iv-b - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -738,20 +784,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -759,17 +796,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -785,9 +820,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -795,15 +830,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-c: + cygwin-stage-v-c: env: - STAGE: iv-c - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -816,20 +851,11 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) - choco install $PACKAGES --source cygwin - - name: bootstrap - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') - shell: bash {0} - run: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -837,17 +863,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -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 local' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -863,9 +887,9 @@ jobs: 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. + # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: @@ -873,15 +897,15 @@ jobs: name: ${{ env.LOCAL_ARTIFACT_NAME }} if: always() - cygwin-stage-iv-d: + cygwin-stage-v-d: env: - STAGE: iv-d - PREVIOUS_STAGES: iii + 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-iii] + needs: [cygwin-stage-iv] runs-on: windows-latest @@ -894,20 +918,78 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true - uses: actions/checkout@v1 - - name: install cygwin and minimal prerequisites with choco + - name: install cygwin and test prerequisites with choco shell: bash {0} run: | choco --version - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/_prereq/distros/cygwin.txt ./build/pkgs/_prereq/distros/cygwin-bootstrap.txt) + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - - name: bootstrap + - 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\\bash -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 '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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && env && ./bootstrap' - - name: install additional cygwin packages with choco - if: contains(matrix.pkgs, 'standard') + 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: [standard] + 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: | - PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) + choco --version + PACKAGES="python38 python38-pip" choco install $PACKAGES --source cygwin - uses: actions/download-artifact@v2 with: @@ -915,17 +997,15 @@ jobs: path: C:\\tools\\cygwin\\tmp - name: Extract sage-local artifact run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh' - - name: configure - run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure $CONFIGURE_ARGS' - - name: make + C:\\tools\\cygwin\\bin\\bash -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 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && make -k -w V=0 $TARGETS' + 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 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" + 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: @@ -943,7 +1023,7 @@ jobs: # 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 local/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar local' + 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: diff --git a/.github/workflows/extract-sage-local.sh b/.github/workflows/extract-sage-local.sh index 5c4be3afb1c..3bc7601cbe9 100755 --- a/.github/workflows/extract-sage-local.sh +++ b/.github/workflows/extract-sage-local.sh @@ -1,26 +1,27 @@ #!/usr/bin/env bash # to be run from $SAGE_ROOT, with arguments sage-local-${{ env.PREVIOUS_STAGES }}.tar +if [ -z "$SAGE_LOCAL" ]; then + SAGE_LOCAL=$(pwd)/local +fi + # Show all tar files ls -l $* # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. for a in $*; do echo Extracting $a - tar xf $a + (cd / && tar xf -) < $a rm -f $a done -# Also get rid of the stages that were not extracted -rm -f sage-local-*.tar - # We set the installation records to the same mtime so that no rebuilds due to dependencies # among these packages are triggered. -(cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *) +(cd "$SAGE_LOCAL"/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *) # Show what has been built already. -ls -l local local/var/lib/sage/installed/ +ls -l "$SAGE_LOCAL" "$SAGE_LOCAL"/var/lib/sage/installed/ df -h # Rebase! -src/bin/sage-rebase.sh local +src/bin/sage-rebase.sh "$SAGE_LOCAL" diff --git a/.github/workflows/tox-experimental.yml b/.github/workflows/tox-experimental.yml index 0001c03ec3b..bb314fa403e 100644 --- a/.github/workflows/tox-experimental.yml +++ b/.github/workflows/tox-experimental.yml @@ -62,7 +62,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - name: Try to login to docker.pkg.github.com # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable run: | diff --git a/.github/workflows/tox-gcc_spkg.yml b/.github/workflows/tox-gcc_spkg.yml index b8cda7704a1..7a865cd42b8 100644 --- a/.github/workflows/tox-gcc_spkg.yml +++ b/.github/workflows/tox-gcc_spkg.yml @@ -60,7 +60,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - name: Try to login to docker.pkg.github.com # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable run: | @@ -114,7 +114,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - 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. diff --git a/.github/workflows/tox-optional.yml b/.github/workflows/tox-optional.yml index 1de68d3232d..08b807a9098 100644 --- a/.github/workflows/tox-optional.yml +++ b/.github/workflows/tox-optional.yml @@ -64,7 +64,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - name: Try to login to docker.pkg.github.com # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable run: | diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 64b8861e4f8..ee28e8c6e07 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -60,7 +60,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - name: Try to login to docker.pkg.github.com # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable run: | @@ -107,7 +107,16 @@ jobs: os: [ macos-10.15, macos-11.0 ] tox_system_factor: [homebrew-macos, homebrew-macos-python3_xcode, homebrew-macos-python3_xcode-nokegonly, homebrew-macos-python3_pythonorg, homebrew-macos-python3_xcode-gcc_spkg, conda-forge-macos] tox_packages_factor: [minimal, standard] - xcode_version_factor: [11.7, default, 12.3] + xcode_version_factor: [11.7, 12.2, default, 12.4] + include: + - tox_system_factor: conda-forge-macos + tox_packages_factor: environment + xcode_version_factor: default + os: macos-11.0 + - tox_system_factor: conda-forge-macos + tox_packages_factor: environment-optional + xcode_version_factor: default + os: macos-11.0 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 }}-${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} @@ -154,7 +163,7 @@ jobs: max-parallel: 1 matrix: tox_system_factor: [conda-forge-ubuntu] - tox_packages_factor: [minimal, standard] + tox_packages_factor: [minimal, standard, environment, environment-optional] 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 }} @@ -163,7 +172,7 @@ jobs: - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - 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. @@ -196,7 +205,7 @@ jobs: - name: Install bootstrap prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install $(sed "s/#.*//;" build/pkgs/debian-bootstrap.txt) + sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) - name: Bootstrap with sage-update-version # We set SAGE_ROOT and SAGE_SRC by hand # because 'sage -sh' does not work with an unconfigured tree, @@ -225,7 +234,7 @@ jobs: os: [ macos-10.15, macos-11.0 ] tox_system_factor: [macos-nobootstrap, macos-nobootstrap-python3_pythonorg] tox_packages_factor: [minimal] - xcode_version_factor: [default, 12] + xcode_version_factor: [11.7, 12.2, default, 12.4] 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 }} diff --git a/.gitignore b/.gitignore index 8d639d60891..8d2e8d86240 100644 --- a/.gitignore +++ b/.gitignore @@ -101,7 +101,7 @@ src/sage/modular/arithgroup/farey_symbol.h !src/sage/cpython/debugimpl.c !src/sage/graphs/base/boost_interface.cpp !src/sage/graphs/cliquer/cl.c -!src/sage/graphs/graph_decompositions/tdlib/sage_tdlib.cpp +!src/sage/graphs/graph_decompositions/sage_tdlib.cpp !src/sage/libs/eclib/wrap.cpp !src/sage/misc/inherit_comparison_impl.c !src/sage/modular/arithgroup/farey.cpp @@ -118,6 +118,7 @@ src/sage/modular/arithgroup/farey_symbol.h !src/sage/stats/distributions/dgs_bern.c !src/sage/stats/distributions/dgs_gauss_dp.c !src/sage/stats/distributions/dgs_gauss_mp.c +/src/cython_debug # Temporary build files build/temp.*/ diff --git a/.homebrew-build-env b/.homebrew-build-env index 01035ebe536..a3d58b57ba9 100644 --- a/.homebrew-build-env +++ b/.homebrew-build-env @@ -23,7 +23,7 @@ export PKG_CONFIG_PATH LIBRARY_PATH="$HOMEBREW/lib$LIBRARY_PATH" [ -z "$CPATH" ] || CPATH=":${CPATH}" CPATH="$HOMEBREW/include$CPATH" -for l in readline bzip2; do +for l in readline bzip2 ntl; do if [ -d "$HOMEBREW/opt/$l/lib" ]; then LIBRARY_PATH="$HOMEBREW/opt/$l/lib:$LIBRARY_PATH" fi diff --git a/.zenodo.json b/.zenodo.json index 25d2eb45af6..f93ddfead54 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.3.beta7", - "version": "9.3.beta7", + "title": "sagemath/sage: 9.3.beta9", + "version": "9.3.beta9", "upload_type": "software", - "publication_date": "2021-02-07", + "publication_date": "2021-03-14", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.3.beta7", + "identifier": "https://github.com/sagemath/sage/tree/9.3.beta9", "relation": "isSupplementTo" }, { diff --git a/COPYING.txt b/COPYING.txt index c85f5b35b87..2da3a8e492c 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -130,7 +130,6 @@ symmetrica MIT-like License (see below) sympow Modified BSD sympy Modified BSD tachyon Modified BSD -termcap GPLv2+ threejs MIT License tornado Apache License zlib Custom (Modified BSD) diff --git a/README.md b/README.md index 458c55f8b76..e48e6f1730b 100644 --- a/README.md +++ b/README.md @@ -219,9 +219,9 @@ Guide](https://doc.sagemath.org/html/en/installation). * Build tools: GNU `make`, GNU `m4`, `perl` (including ``ExtUtils::MakeMaker``), `ranlib`, `git`, `tar`, `bc` - * Any version of `python` (full installation including `urllib`), - but ideally version 3.7.x or 3.8.x, which will avoid having to build Sage's - own copy of Python 3. + * Python 3.4 or later, or Python 2.7, a full installation including + `urllib`; but ideally version 3.7.x, 3.8.x, or 3.9.x, which will + avoid having to build Sage's own copy of Python 3. We have collected lists of system packages that provide these build prerequisites. See [build/pkgs/arch.txt](build/pkgs/arch.txt), diff --git a/VERSION.txt b/VERSION.txt index 25976cc1ac5..4177b802ab7 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.3.beta7, Release Date: 2021-02-07 +SageMath version 9.3.beta9, Release Date: 2021-03-14 diff --git a/build/bin/sage-bootstrap-python b/build/bin/sage-bootstrap-python index 62777bfa23f..faee444e8dc 100755 --- a/build/bin/sage-bootstrap-python +++ b/build/bin/sage-bootstrap-python @@ -17,6 +17,9 @@ fi # So it needs to find a python that has the urllib module. # For example, on Debian buster, the python3-minimal package does NOT provide it. # +# Also, Trac #20023 removed the vendored argparse library from sage_bootstrap, +# so we test that python is new enough (>= 2.7) to run it. +# # See https://trac.sagemath.org/ticket/29090 # Trac #29890: Our first choice is "python", not "python3". This is to avoid @@ -41,7 +44,7 @@ PYTHONS="python python3 python3.8 python3.7 python2.7 python3.6 python2" for PY in $PYTHONS; do PYTHON="$(PATH="$SAGE_ORIG_PATH" command -v $PY)" if [ -n "$PYTHON" ]; then - if "$PYTHON" -c "import urllib; from hashlib import sha1; from os import listdir; listdir(\"$(pwd)\");" 2>/dev/null; then + if "$PYTHON" -c "import argparse; import urllib; from hashlib import sha1; from os import listdir; listdir(\"$(pwd)\");" 2>/dev/null; then exec "$PYTHON" "$@" fi fi diff --git a/build/bin/sage-build-env b/build/bin/sage-build-env index e09b4437533..21a383e57b3 100644 --- a/build/bin/sage-build-env +++ b/build/bin/sage-build-env @@ -24,34 +24,34 @@ # The compiler flags are set in order of priority by # 1) environment variables # 2) flags set at configuration time -if [ "x$CFLAGS" == "x" ]; then +if [ "x$CFLAGS" = "x" ]; then export ORIGINAL_CFLAGS="$CONFIGURED_CFLAGS" else export ORIGINAL_CFLAGS="$CFLAGS" fi -if [ "x$CXXFLAGS" == "x" ]; then +if [ "x$CXXFLAGS" = "x" ]; then export ORIGINAL_CXXFLAGS="$CONFIGURED_CXXFLAGS" else export ORIGINAL_CXXFLAGS="$CXXFLAGS" fi -if [ "x$FCFLAGS" == "x" ]; then +if [ "x$FCFLAGS" = "x" ]; then export ORIGINAL_FCFLAGS="$CONFIGURED_FCFLAGS" else export ORIGINAL_FCFLAGS="$FCFLAGS" fi -if [ "x$F77FLAGS" == "x" ]; then +if [ "x$F77FLAGS" = "x" ]; then export ORIGINAL_F77FLAGS="$CONFIGURED_F77FLAGS" else export ORIGINAL_F77FLAGS="$F77FLAGS" fi # We optimize according to $SAGE_DEBUG. -if [ "x$ORIGINAL_CFLAGS" == "x" ]; then +if [ "x$ORIGINAL_CFLAGS" = "x" ]; then # Evaluate SAGE_DEBUG: - if [ "x$SAGE_DEBUG" == "xyes" ]; then + if [ "x$SAGE_DEBUG" = "xyes" ]; then export CFLAGS_NON_NATIVE="-Og -g" export CFLAGS_O3_NON_NATIVE="-Og -g" - elif [ "x$SAGE_DEBUG" == "xno" ]; then + elif [ "x$SAGE_DEBUG" = "xno" ]; then export CFLAGS_NON_NATIVE="-O2" export CFLAGS_O3_NON_NATIVE="-O3" else @@ -69,7 +69,7 @@ else fi # Copy to CXXFLAGS if this is not set. -if [ "x$ORIGINAL_CXXFLAGS" == "x" ]; then +if [ "x$ORIGINAL_CXXFLAGS" = "x" ]; then export CXXFLAGS="$CFLAGS" export CXXFLAGS_O3="$CFLAGS_O3" export CXXFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" @@ -82,7 +82,7 @@ else fi # Copy CFLAGS to FCFLAGS if this is not set. -if [ "x$ORIGINAL_FCFLAGS" == "x" ]; then +if [ "x$ORIGINAL_FCFLAGS" = "x" ]; then export FCFLAGS="$CFLAGS" export FCFLAGS_O3="$CFLAGS_O3" export FCFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" @@ -95,7 +95,7 @@ else fi # Copy FCFLAGS to F77FLAGS if this is not set. -if [ "x$ORIGINAL_F77FLAGS" == "x" ]; then +if [ "x$ORIGINAL_F77FLAGS" = "x" ]; then export F77FLAGS="$FCFLAGS" export F77FLAGS_O3="$FCFLAGS_O3" export F77FLAGS_NON_NATIVE="$FCFLAGS_NON_NATIVE" diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 0ad0572f91f..d727e7db7d8 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -27,7 +27,7 @@ export SAGE_FAT_BINARY="@SAGE_FAT_BINARY@" # Export SAGE_DEBUG if this was enabled during configure, # but be respectful of the current settings. -if [ "x$SAGE_DEBUG" == "x" ]; then +if [ "x$SAGE_DEBUG" = "x" ]; then export SAGE_DEBUG="@SAGE_DEBUG@" fi @@ -106,3 +106,5 @@ export SAGE_FREETYPE_PREFIX="@SAGE_FREETYPE_PREFIX@" export SAGE_SUITESPARSE_PREFIX="@SAGE_SUITESPARSE_PREFIX@" export SAGE_CONFIGURE_FFLAS_FFPACK="@SAGE_CONFIGURE_FFLAS_FFPACK@" + +export SAGE_EDITABLE="@SAGE_EDITABLE@" diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 215e6f3f06a..990bc81e180 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -227,17 +227,14 @@ sdh_pip_install() { sdh_store_and_pip_install_wheel . } -sdh_store_and_pip_install_wheel() { +sdh_store_wheel() { if [ -n "$SAGE_DESTDIR" ]; then local sudo="" - # --no-warn-script-location: Suppress a warning caused by --root - local root="--root=$SAGE_DESTDIR --no-warn-script-location" else local sudo="$SAGE_SUDO" - local root="" fi if [ "$*" != "." ]; then - sdh_die "Error: sdh_store_and_pip_install_wheel requires . as only argument" + sdh_die "Error: sdh_store_wheel requires . as only argument" fi wheel="" for w in dist/*.whl; do @@ -252,11 +249,24 @@ sdh_store_and_pip_install_wheel() { sdh_die "Error: no wheel found after building" fi - $sudo sage-pip-install $root "$wheel" || \ - sdh_die "Error installing $wheel" mkdir -p "${SAGE_DESTDIR}${SAGE_SPKG_WHEELS}" && \ $sudo mv "$wheel" "${SAGE_DESTDIR}${SAGE_SPKG_WHEELS}/" || \ sdh_die "Error storing $wheel" + wheel="${SAGE_DESTDIR}${SAGE_SPKG_WHEELS}/${wheel##*/}" +} + +sdh_store_and_pip_install_wheel() { + sdh_store_wheel "$@" + if [ -n "$SAGE_DESTDIR" ]; then + local sudo="" + # --no-warn-script-location: Suppress a warning caused by --root + local root="--root=$SAGE_DESTDIR --no-warn-script-location" + else + local sudo="$SAGE_SUDO" + local root="" + fi + $sudo sage-pip-install $root "$wheel" || \ + sdh_die "Error installing ${wheel##*/}" } sdh_cmake() { diff --git a/build/bin/sage-print-system-package-command b/build/bin/sage-print-system-package-command index 113b9576c3a..f5ec9b3e1ce 100755 --- a/build/bin/sage-print-system-package-command +++ b/build/bin/sage-print-system-package-command @@ -5,7 +5,11 @@ shift IF_VERBOSE=: SUDO= PROMPT= -COMMENT="# " +if [ -n "$OUTPUT_RST" ]; then + COMMENT="" +else + COMMENT="# " +fi while : do case "$1" in @@ -51,6 +55,25 @@ system_packages="$*" options= env= shopt -s extglob + +function print_shell_command() +{ + if [ -n "$OUTPUT_RST" ]; then + echo + echo ".. CODE-BLOCK:: bash" + echo + fi + echo "${PROMPT}$1" + if [ -n "$OUTPUT_RST" ]; then + echo + fi +} + +function print_comment() +{ + echo "${COMMENT}$1" +} + case $system:$command in homebrew*:setup-build-env) $IF_VERBOSE echo "${COMMENT}" @@ -70,66 +93,79 @@ case $system:$command in # Verbs handled above are our own inventions. Verbs handled below are apt-get verbs. # @(debian*|ubuntu*):update) - echo "${PROMPT}${SUDO}apt-get $command $system_packages" + print_shell_command "${SUDO}apt-get $command $system_packages" ;; @(debian*|ubuntu*):*) [ "$NO_INSTALL_RECOMMENDS" = yes ] && options="$options --no-install-recommends" [ "$YES" = yes ] && options="$options --yes" env="DEBIAN_FRONTEND=noninteractive " - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}${env}apt-get $command $options $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}${env}apt-get $command $options $system_packages" ;; @(fedora*|redhat*|centos*):install) [ "$YES" = yes ] && options="$options -y" - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}yum install $options $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}yum install $options $system_packages" ;; gentoo*:install) - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}emerge $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}emerge $system_packages" ;; arch*:update) - echo "${PROMPT}${SUDO}pacman -Sy" + print_shell_command "${SUDO}pacman -Sy" ;; arch*:install) [ "$YES" = yes ] && options="$options --noconfirm" - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}pacman -S $options $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}pacman -S $options $system_packages" ;; void*:update) - echo "${PROMPT}${SUDO}xbps-install -Su" + print_shell_command "${SUDO}xbps-install -Su" ;; void*:install) [ "$YES" = yes ] && options="$options --yes" - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}xbps-install $options $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}xbps-install $options $system_packages" ;; opensuse*:install) - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}zypper install $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}zypper install $system_packages" ;; *conda*:install) [ "$YES" = yes ] && options="$options --yes" - [ -n "$system_packages" ] && echo "${PROMPT}conda install $system_packages" + [ -n "$system_packages" ] && print_shell_command "conda install $system_packages" ;; homebrew*:install) - [ -n "$system_packages" ] && echo "${PROMPT}brew install $system_packages" + [ -n "$system_packages" ] && print_shell_command "brew install $system_packages" ;; slackware*:install) - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}slackpkg install $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}slackpkg install $system_packages" ;; cygwin*:update) - echo "# first install apt-cyg from https://github.com/transcode-open/apt-cyg" + print_commment "first install apt-cyg from https://github.com/transcode-open/apt-cyg" ;; cygwin*:install) - [ -n "$system_packages" ] && echo "${PROMPT}apt-cyg install $system_packages" + [ -n "$system_packages" ] && print_shell_command "apt-cyg install $system_packages" ;; freebsd*:install) - [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}pkg install $system_packages" + [ -n "$system_packages" ] && print_shell_command "${SUDO}pkg install $system_packages" ;; nix*:install) - [ -n "$system_packages" ] && echo "${PROMPT}nix-env --install $system_packages" + [ -n "$system_packages" ] && print_shell_command "nix-env --install $system_packages" ;; pip:install) - [ -n "$system_packages" ] && echo "${PROMPT}sage -pip install $system_packages" + [ -n "$system_packages" ] && print_shell_command "sage -pip install $system_packages" + ;; + repology:install) + if [ -n "$system_packages" ]; then + links="" + for pkg in $system_packages; do + link="https://repology.org/project/$pkg/versions" + if [ -n "$links" ]; then + links="$links, " + fi + links="$links$link" + done + print_comment "See ${links}" + fi ;; *:update) # Nothing needed ;; *) - echo "# $command the following packages: $system_packages" + print_comment "$command the following packages: $system_packages" ;; esac diff --git a/build/bin/sage-site b/build/bin/sage-site index 317a588a8ad..400782164b3 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -162,7 +162,7 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then export OMP_NUM_THREADS=1 fi - exec sage-python -m sage_setup.docbuild "$@" -Date: Wed, 6 May 2020 20:58:20 +0200 -Subject: [PATCH 1/3] unicode: fix ecl_string_case for non-ascii characters - -Problem reported and fix provided by Vladimir Sedach on the ecl-devel -mailing list. ---- - src/c/character.d | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) - -diff --git a/src/c/character.d b/src/c/character.d -index a69b6e4b..c3ef328a 100644 ---- a/src/c/character.d -+++ b/src/c/character.d -@@ -99,6 +99,7 @@ cl_both_case_p(cl_object c) - int - ecl_string_case(cl_object s) - { -+ /* Returns 1 if string is all uppercase, -1 if all lowercase, and 0 if mixed case */ - int upcase; - cl_index i; - ecl_base_char *text; -@@ -106,16 +107,16 @@ ecl_string_case(cl_object s) - switch (ecl_t_of(s)) { - #ifdef ECL_UNICODE - case t_string: -- s = si_coerce_to_base_string(s); - #endif - case t_base_string: -- text = (ecl_base_char*)s->base_string.self; - for (i = 0, upcase = 0; i < s->base_string.dim; i++) { -- if (ecl_upper_case_p(text[i])) { -+ ecl_character c = ecl_char(s, i); -+ -+ if (ecl_upper_case_p(c)) { - if (upcase < 0) - return 0; - upcase = +1; -- } else if (ecl_lower_case_p(text[i])) { -+ } else if (ecl_lower_case_p(c)) { - if (upcase > 0) - return 0; - upcase = -1; --- -2.26.2 - diff --git a/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch b/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch deleted file mode 100644 index 9ebcb224707..00000000000 --- a/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch +++ /dev/null @@ -1,941 +0,0 @@ -From 1a835fa016fc65e7d19beaa416afc574bd79f7e6 Mon Sep 17 00:00:00 2001 -From: Marius Gerbershagen -Date: Sun, 26 Apr 2020 18:45:40 +0200 -Subject: [PATCH 2/3] cosmetic: fix some compiler warnings - ---- - contrib/bytecmp/bytecmp.lsp | 2 +- - src/c/alloc_2.d | 4 ++++ - src/c/clos/cache.d | 4 ++-- - src/c/compiler.d | 12 ++++++------ - src/c/disassembler.d | 2 -- - src/c/ffi/mmap.d | 2 +- - src/c/interpreter.d | 1 - - src/c/number.d | 2 +- - src/c/numbers/number_equalp.d | 1 - - src/c/pathname.d | 2 ++ - src/c/printer/float_to_digits.d | 2 -- - src/c/printer/write_symbol.d | 22 ++++++++++++---------- - src/c/printer/write_ugly.d | 2 +- - src/c/sequence.d | 2 +- - src/c/threads/queue.d | 2 ++ - src/c/unixfsys.d | 4 +++- - src/clos/combin.lsp | 1 + - src/clos/generic.lsp | 2 +- - src/clos/kernel.lsp | 4 ++-- - src/clos/method.lsp | 3 +++ - src/clos/print.lsp | 3 +++ - src/clos/std-accessors.lsp | 1 + - src/cmp/cmpc-machine.lsp | 4 ---- - src/cmp/cmpenv-declaim.lsp | 1 + - src/cmp/cmpenv-declare.lsp | 6 +++--- - src/cmp/cmpenv-fun.lsp | 2 +- - src/cmp/cmpinline.lsp | 2 +- - src/cmp/cmplam.lsp | 1 + - src/cmp/cmplet.lsp | 2 +- - src/cmp/cmploc.lsp | 1 + - src/cmp/cmpmain.lsp | 4 ++++ - src/cmp/cmpmulti.lsp | 2 +- - src/cmp/cmpname.lsp | 2 +- - src/cmp/cmpnum.lsp | 2 ++ - src/cmp/cmpopt-printer.lsp | 6 +++--- - src/cmp/cmpopt-sequence.lsp | 3 +++ - src/cmp/cmpopt.lsp | 4 ++-- - src/cmp/cmpprop.lsp | 2 ++ - src/cmp/cmptop.lsp | 1 + - src/cmp/cmptype-assert.lsp | 6 +++--- - src/cmp/cmpvar.lsp | 15 ++++++++------- - src/cmp/proclamations.lsp | 2 +- - src/h/bytecodes.h | 1 + - src/lsp/defstruct.lsp | 4 ++-- - src/lsp/ffi.lsp | 6 ++++-- - src/lsp/setf.lsp | 1 - - 46 files changed, 94 insertions(+), 66 deletions(-) - -diff --git a/contrib/bytecmp/bytecmp.lsp b/contrib/bytecmp/bytecmp.lsp -index c71b7d8c..1a233b4d 100755 ---- a/contrib/bytecmp/bytecmp.lsp -+++ b/contrib/bytecmp/bytecmp.lsp -@@ -81,7 +81,7 @@ - (defun bc-compile-file-pathname (name &key (output-file name) (type :fasl) - verbose print c-file h-file data-file - shared-data-file system-p load external-format) -- (declare (ignore load c-file h-file data-file shared-data-file system-p verbose print)) -+ (declare (ignore load c-file h-file data-file shared-data-file system-p verbose print external-format)) - (let ((extension "fasc")) - (case type - ((:fasl :fas) (setf extension "fasc")) -diff --git a/src/c/alloc_2.d b/src/c/alloc_2.d -index dbeb1ead..a2223334 100644 ---- a/src/c/alloc_2.d -+++ b/src/c/alloc_2.d -@@ -191,11 +191,13 @@ static struct ecl_type_information { - size_t t; - } type_info[t_end]; - -+#ifdef GBC_BOEHM_PRECISE - static void - error_wrong_tag(cl_type t) - { - ecl_internal_error("Collector called with invalid tag number."); - } -+#endif - - cl_index - ecl_object_byte_size(cl_type t) -@@ -764,6 +766,7 @@ extern void (*GC_push_other_roots)(); - static void (*old_GC_push_other_roots)(); - static void stacks_scanner(); - -+#ifdef GBC_BOEHM_PRECISE - static cl_index - to_bitmap(void *x, void *y) - { -@@ -773,6 +776,7 @@ to_bitmap(void *x, void *y) - n /= sizeof(void*); - return 1 << n; - } -+#endif - - void - init_alloc(void) -diff --git a/src/c/clos/cache.d b/src/c/clos/cache.d -index 0f66f2db..9f8a59fd 100644 ---- a/src/c/clos/cache.d -+++ b/src/c/clos/cache.d -@@ -36,6 +36,7 @@ empty_cache(ecl_cache_ptr cache) - #endif - } - -+#ifndef ECL_THREADS - static void - clear_one_from_cache(ecl_cache_ptr cache, cl_object target) - { -@@ -51,8 +52,7 @@ clear_one_from_cache(ecl_cache_ptr cache, cl_object target) - } - } - } -- --#ifdef ECL_THREADS -+#else - static void - clear_list_from_cache(ecl_cache_ptr cache) - { -diff --git a/src/c/compiler.d b/src/c/compiler.d -index e67cd2b2..a4d02806 100644 ---- a/src/c/compiler.d -+++ b/src/c/compiler.d -@@ -847,7 +847,7 @@ c_block(cl_env_ptr env, cl_object body, int old_flags) { - struct cl_compiler_env old_env; - cl_object name = pop(&body); - cl_object block_record; -- cl_index labelz, pc, loc, constants; -+ cl_index labelz, pc, constants; - int flags; - - if (!ECL_SYMBOLP(name)) -@@ -858,7 +858,7 @@ c_block(cl_env_ptr env, cl_object body, int old_flags) { - pc = current_pc(env); - - flags = maybe_values_or_reg0(old_flags); -- loc = c_register_block(env, name); -+ c_register_block(env, name); - block_record = ECL_CONS_CAR(env->c_env->variables); - if (Null(name)) { - asm_op(env, OP_DO); -@@ -1063,7 +1063,7 @@ c_case(cl_env_ptr env, cl_object clause, int flags) { - - static int - c_catch(cl_env_ptr env, cl_object args, int flags) { -- cl_index labelz, loc; -+ cl_index labelz; - cl_object old_env; - - /* Compile evaluation of tag */ -@@ -1071,7 +1071,7 @@ c_catch(cl_env_ptr env, cl_object args, int flags) { - - /* Compile binding of tag */ - old_env = env->c_env->variables; -- loc = c_register_block(env, ecl_make_fixnum(0)); -+ c_register_block(env, ecl_make_fixnum(0)); - asm_op(env, OP_CATCH); - - /* Compile jump point */ -@@ -3039,7 +3039,7 @@ c_default(cl_env_ptr env, cl_object var, cl_object stmt, cl_object flag, cl_obje - cl_object - ecl_make_lambda(cl_env_ptr env, cl_object name, cl_object lambda) { - cl_object reqs, opts, rest, key, keys, auxs, allow_other_keys; -- cl_object specials, doc, decl, body, output; -+ cl_object specials, decl, body, output; - cl_index handle; - struct cl_compiler_env *old_c_env, new_c_env; - -@@ -3057,7 +3057,7 @@ ecl_make_lambda(cl_env_ptr env, cl_object name, cl_object lambda) { - keys = env->values[4]; - allow_other_keys = env->values[5]; - auxs = env->values[6]; -- doc = env->values[7]; -+ /* doc = env->values[7]; unused */; - specials = env->values[8]; - decl = env->values[9]; - body = env->values[10]; -diff --git a/src/c/disassembler.d b/src/c/disassembler.d -index b134c56c..46c78259 100644 ---- a/src/c/disassembler.d -+++ b/src/c/disassembler.d -@@ -53,13 +53,11 @@ print_oparg_arg(const char *s, cl_fixnum n, cl_object x) { - static void - disassemble_lambda(cl_object bytecodes) { - const cl_env_ptr env = ecl_process_env(); -- cl_object *data; - cl_opcode *vector; - - ecl_bds_bind(env, @'*print-pretty*', ECL_NIL); - - /* Print required arguments */ -- data = bytecodes->bytecodes.data->vector.self.t; - cl_print(1,bytecodes->bytecodes.data); - - /* Name of LAMBDA */ -diff --git a/src/c/ffi/mmap.d b/src/c/ffi/mmap.d -index ee1f49b6..23ed9e4b 100644 ---- a/src/c/ffi/mmap.d -+++ b/src/c/ffi/mmap.d -@@ -53,7 +53,7 @@ - @':element-type', element_type, - @':if-exists', if_exists, - @':if-does-not-exist', if_does_not_exist, -- @':external-format', @':default', -+ @':external-format', external_format, - @':cstream', ECL_NIL); - fd = ecl_to_int(si_file_stream_fd(stream)); - if (Null(length)) -diff --git a/src/c/interpreter.d b/src/c/interpreter.d -index 22b78af6..34fd2a1c 100644 ---- a/src/c/interpreter.d -+++ b/src/c/interpreter.d -@@ -314,7 +314,6 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) - frame_aux.stack = frame_aux.base = 0; - frame_aux.size = 0; - frame_aux.env = the_env; -- BEGIN: - BEGIN_SWITCH { - CASE(OP_NOP); { - reg0 = ECL_NIL; -diff --git a/src/c/number.d b/src/c/number.d -index ba28cfe5..c6511af6 100644 ---- a/src/c/number.d -+++ b/src/c/number.d -@@ -611,7 +611,7 @@ si_complex_float(cl_object r, cl_object i) - { - cl_type tr = ecl_t_of(r); - cl_type ti = ecl_t_of(i); -- cl_object result; -+ cl_object result = OBJNULL; - switch (tr) { - case t_singlefloat: - if (ti != tr) { ecl_type_error(@'si::complex-float',"imag part", i, @'single-float'); } -diff --git a/src/c/numbers/number_equalp.d b/src/c/numbers/number_equalp.d -index 678f509e..2add2691 100644 ---- a/src/c/numbers/number_equalp.d -+++ b/src/c/numbers/number_equalp.d -@@ -36,7 +36,6 @@ - int - ecl_number_equalp(cl_object x, cl_object y) - { -- double dx; - /* INV: (= fixnum bignum) => 0 */ - /* INV: (= fixnum ratio) => 0 */ - /* INV: (= bignum ratio) => 0 */ -diff --git a/src/c/pathname.d b/src/c/pathname.d -index 3617ce93..0507e606 100644 ---- a/src/c/pathname.d -+++ b/src/c/pathname.d -@@ -659,7 +659,9 @@ ecl_parse_namestring(cl_object s, cl_index start, cl_index end, cl_index *ep, - if (!ecl_stringp(device)) { - return ECL_NIL; - } -+#if defined(ECL_MS_WINDOWS_HOST) - maybe_parse_host: -+#endif - /* Files have no effective device. */ - if (@string-equal(2, device, @':file') == ECL_T) - device = ECL_NIL; -diff --git a/src/c/printer/float_to_digits.d b/src/c/printer/float_to_digits.d -index 2d9fdd8a..384dceb6 100644 ---- a/src/c/printer/float_to_digits.d -+++ b/src/c/printer/float_to_digits.d -@@ -161,10 +161,8 @@ generate(cl_object digits, float_approx *approx) - static void - change_precision(float_approx *approx, cl_object position, cl_object relativep) - { -- cl_fixnum pos; - if (Null(position)) - return; -- pos = ecl_fixnum(position); - if (!Null(relativep)) { - cl_object k = ecl_make_fixnum(0); - cl_object l = ecl_make_fixnum(1); -diff --git a/src/c/printer/write_symbol.d b/src/c/printer/write_symbol.d -index 1e1d4e2b..a39bab97 100644 ---- a/src/c/printer/write_symbol.d -+++ b/src/c/printer/write_symbol.d -@@ -102,13 +102,15 @@ needs_to_be_escaped(cl_object s, cl_object readtable, cl_object print_case) - return 0; - } - --#define buffer_write_char(c, buffer, stream, buffer_ndx, buffer_size) \ -- ecl_char_set(buffer, buffer_ndx++, c); \ -- if (buffer_ndx >= buffer_size) { \ -- si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); \ -- si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL);\ -- buffer_ndx = 0; \ -+static inline void -+buffer_write_char(char c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { -+ ecl_char_set(buffer, (*buffer_ndx)++, c); -+ if (*buffer_ndx >= buffer_size) { -+ si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); -+ si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL); -+ *buffer_ndx = 0; - } -+} - - static void - write_symbol_string(cl_object s, int action, cl_object print_case, -@@ -124,13 +126,13 @@ write_symbol_string(cl_object s, int action, cl_object print_case, - cl_index buffer_size = ecl_fixnum(cl_array_total_size(buffer)); - cl_index buffer_ndx = 0; - if (escape) -- buffer_write_char('|', buffer, stream, buffer_ndx, buffer_size); -+ buffer_write_char('|', buffer, stream, &buffer_ndx, buffer_size); - capitalize = 1; - for (i = 0; i < s->base_string.fillp; i++) { - ecl_character c = ecl_char(s, i); - if (escape) { - if (c == '|' || c == '\\') { -- buffer_write_char('\\', buffer, stream, buffer_ndx, buffer_size); -+ buffer_write_char('\\', buffer, stream, &buffer_ndx, buffer_size); - } - } else if (action != ecl_case_preserve) { - if (ecl_upper_case_p(c)) { -@@ -155,10 +157,10 @@ write_symbol_string(cl_object s, int action, cl_object print_case, - capitalize = !ecl_alphanumericp(c); - } - } -- buffer_write_char(c, buffer, stream, buffer_ndx, buffer_size); -+ buffer_write_char(c, buffer, stream, &buffer_ndx, buffer_size); - } - if (escape) -- buffer_write_char('|', buffer, stream, buffer_ndx, buffer_size); -+ buffer_write_char('|', buffer, stream, &buffer_ndx, buffer_size); - si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_ndx)); - si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL); - si_put_buffer_string(buffer); -diff --git a/src/c/printer/write_ugly.d b/src/c/printer/write_ugly.d -index ae07a388..d99672ee 100644 ---- a/src/c/printer/write_ugly.d -+++ b/src/c/printer/write_ugly.d -@@ -120,7 +120,7 @@ write_float(cl_object f, cl_object stream) - static void /* XXX: do not cons new floats here! */ - write_complex_float(cl_object f, cl_object stream) - { -- cl_object real, imag; -+ cl_object real = OBJNULL, imag = OBJNULL; - switch (ecl_t_of(f)) { - case t_csfloat: - real = ecl_make_single_float(crealf(ecl_csfloat(f))); -diff --git a/src/c/sequence.d b/src/c/sequence.d -index a39d2760..fa77b7c9 100644 ---- a/src/c/sequence.d -+++ b/src/c/sequence.d -@@ -189,7 +189,7 @@ ecl_copy_seq(cl_object sequence) - return ecl_subseq(sequence, 0, MOST_POSITIVE_FIXNUM); - } - --@(defun subseq (sequence start &optional end &aux x) -+@(defun subseq (sequence start &optional end) - cl_index_pair p; - @ - p = ecl_sequence_start_end(@[subseq], sequence, start, end); -diff --git a/src/c/threads/queue.d b/src/c/threads/queue.d -index 727e5c32..81ee4337 100755 ---- a/src/c/threads/queue.d -+++ b/src/c/threads/queue.d -@@ -84,6 +84,7 @@ wait_queue_delete(cl_env_ptr the_env, cl_object q, cl_object item) - * THREAD SCHEDULER & WAITING - */ - -+#if !defined(HAVE_SIGPROCMASK) - static cl_object - bignum_set_time(cl_object bignum, struct ecl_timeval *time) - { -@@ -194,6 +195,7 @@ ecl_wait_on_timed(cl_env_ptr env, cl_object (*condition)(cl_env_ptr, cl_object), - ecl_bds_unwind1(the_env); - return output; - } -+#endif - - /********************************************************************** - * BLOCKING WAIT QUEUE ALGORITHM -diff --git a/src/c/unixfsys.d b/src/c/unixfsys.d -index 9e4ecbab..3d169db3 100644 ---- a/src/c/unixfsys.d -+++ b/src/c/unixfsys.d -@@ -351,7 +351,7 @@ file_truename(cl_object pathname, cl_object filename, int flags) - * the other hand, if the link is broken – return file - * truename "as is". */ - struct stat filestatus; -- if (safe_stat(filename->base_string.self, &filestatus) < 0) { -+ if (safe_stat((char*) filename->base_string.self, &filestatus) < 0) { - @(return pathname kind); - } - filename = si_readlink(filename); -@@ -560,7 +560,9 @@ ecl_file_len(int f) - } - #endif - } -+#if defined(ECL_MS_WINDOWS_HOST) - FAILURE_CLOBBER: -+#endif - ecl_enable_interrupts(); - { - cl_object c_error = _ecl_strerror(errno); -diff --git a/src/clos/combin.lsp b/src/clos/combin.lsp -index b4cead1d..fccb5e4d 100644 ---- a/src/clos/combin.lsp -+++ b/src/clos/combin.lsp -@@ -211,6 +211,7 @@ - o)) - - (defun find-method-combination (gf method-combination-type-name method-combination-options) -+ (declare (ignore gf)) - (make-method-combination method-combination-type-name - (search-method-combination method-combination-type-name) - method-combination-options -diff --git a/src/clos/generic.lsp b/src/clos/generic.lsp -index dba4b531..9993ebf4 100644 ---- a/src/clos/generic.lsp -+++ b/src/clos/generic.lsp -@@ -181,7 +181,7 @@ - - (defmethod shared-initialize ((gfun standard-generic-function) slot-names - &rest initargs) -- (declare (ignore initargs slot-names)) -+ (declare (ignore slot-names)) - (call-next-method) - (when (generic-function-methods gfun) - (compute-g-f-spec-list gfun)) -diff --git a/src/clos/kernel.lsp b/src/clos/kernel.lsp -index a0b9b2ed..31594f9d 100644 ---- a/src/clos/kernel.lsp -+++ b/src/clos/kernel.lsp -@@ -355,7 +355,7 @@ - (with-early-accessors (+standard-generic-function-slots+ - +eql-specializer-slots+ - +standard-method-slots+) -- (flet ((nupdate-spec-how-list (spec-how-list specializers gf) -+ (flet ((nupdate-spec-how-list (spec-how-list specializers) - ;; update the spec-how of the gfun - ;; computing the or of the previous value and the new one - (setf spec-how-list (or spec-how-list -@@ -379,7 +379,7 @@ - (a-p-o (generic-function-argument-precedence-order gf))) - (dolist (method (generic-function-methods gf)) - (setf spec-how-list -- (nupdate-spec-how-list spec-how-list (method-specializers method) gf))) -+ (nupdate-spec-how-list spec-how-list (method-specializers method)))) - (setf (generic-function-spec-list gf) - (loop for type in spec-how-list - for i from 0 -diff --git a/src/clos/method.lsp b/src/clos/method.lsp -index 3e607b5e..2866e0a8 100644 ---- a/src/clos/method.lsp -+++ b/src/clos/method.lsp -@@ -102,6 +102,7 @@ - (when (eq (first method-lambda) 'lambda) - (multiple-value-bind (declarations body documentation) - (si::find-declarations (cddr method-lambda)) -+ (declare (ignore documentation)) - (let (block) - (when (and (null (rest body)) - (listp (setf block (first body))) -@@ -177,6 +178,7 @@ - (values method-lambda declarations documentation)))) - - (defun make-method-lambda (gf method method-lambda env) -+ (declare (ignore method gf)) - (multiple-value-bind (call-next-method-p next-method-p-p in-closure-p) - (walk-method-lambda method-lambda env) - (values `(lambda (.combined-method-args. *next-methods*) -@@ -190,6 +192,7 @@ - (defun add-call-next-method-closure (method-lambda) - (multiple-value-bind (declarations real-body documentation) - (si::find-declarations (cddr method-lambda)) -+ (declare (ignore documentation)) - `(lambda ,(second method-lambda) - ,@declarations - (let* ((.closed-combined-method-args. -diff --git a/src/clos/print.lsp b/src/clos/print.lsp -index ea392516..afa7d83b 100644 ---- a/src/clos/print.lsp -+++ b/src/clos/print.lsp -@@ -112,12 +112,15 @@ - (no-make-load-form object))))) - - (defmethod make-load-form ((object standard-object) &optional environment) -+ (declare (ignore environment)) - (no-make-load-form object)) - - (defmethod make-load-form ((object structure-object) &optional environment) -+ (declare (ignore environment)) - (no-make-load-form object)) - - (defmethod make-load-form ((object condition) &optional environment) -+ (declare (ignore environment)) - (no-make-load-form object)) - - (defun no-make-load-form (object) -diff --git a/src/clos/std-accessors.lsp b/src/clos/std-accessors.lsp -index fd8431f0..ff83156e 100644 ---- a/src/clos/std-accessors.lsp -+++ b/src/clos/std-accessors.lsp -@@ -23,6 +23,7 @@ - ;;; - - (defun safe-slot-definition-location (slotd &optional default) -+ (declare (ignore default)) - (cond ((listp slotd) - (error "List instead of a slot definition object")) - (t -diff --git a/src/cmp/cmpc-machine.lsp b/src/cmp/cmpc-machine.lsp -index 95e9d024..eff6d6fb 100644 ---- a/src/cmp/cmpc-machine.lsp -+++ b/src/cmp/cmpc-machine.lsp -@@ -130,10 +130,6 @@ - :from-lisp from-lisp - :from-lisp-unsafe from-lisp-unsafe)))) - --(defun make-rep-type-hash (all-c-types) -- (let ((table (make-hash-table :size 128 :test 'eq))) -- table)) -- - (defun default-machine () - (let* ((all-c-types (append +this-machine-c-types+ +all-machines-c-types+)) - (table (make-hash-table :size 128 :test 'eq)) -diff --git a/src/cmp/cmpenv-declaim.lsp b/src/cmp/cmpenv-declaim.lsp -index 4428915d..c93efbe0 100644 ---- a/src/cmp/cmpenv-declaim.lsp -+++ b/src/cmp/cmpenv-declaim.lsp -@@ -36,6 +36,7 @@ - env)) - (multiple-value-bind (body specials types ignored others doc all) - (c1body `((DECLARE ,@args)) nil) -+ (declare (ignore body doc all)) - (when ignored - (cmpwarn-style "IGNORE/IGNORABLE declarations in DECLAIM are ignored")) - (reduce #'add-one-declaration others -diff --git a/src/cmp/cmpenv-declare.lsp b/src/cmp/cmpenv-declare.lsp -index 2996e739..25de42bc 100644 ---- a/src/cmp/cmpenv-declare.lsp -+++ b/src/cmp/cmpenv-declare.lsp -@@ -33,10 +33,10 @@ - (defun validate-alien-declaration (names-list error) - (dolist (new-declaration names-list) - (unless (symbolp new-declaration) -- (cmperr "The declaration ~s is not a symbol" new-declaration)) -+ (funcall error "The declaration ~s is not a symbol" new-declaration)) - (when (type-name-p new-declaration) -- (cmperr "Symbol name ~S cannot be both the name of a type and of a declaration" -- new-declaration)))) -+ (funcall error "Symbol name ~S cannot be both the name of a type and of a declaration" -+ new-declaration)))) - - (defun alien-declaration-p (name &optional (env *cmp-env*)) - (and (symbolp name) -diff --git a/src/cmp/cmpenv-fun.lsp b/src/cmp/cmpenv-fun.lsp -index 7bc1e9a8..681602d6 100644 ---- a/src/cmp/cmpenv-fun.lsp -+++ b/src/cmp/cmpenv-fun.lsp -@@ -91,7 +91,7 @@ - (values nil nil)))) - - (defun get-local-return-type (fun &optional (env *cmp-env*)) -- (let ((x (cmp-env-search-ftype (fun-name fun)))) -+ (let ((x (cmp-env-search-ftype (fun-name fun) env))) - (if x - (values (second x) t) - (values nil nil)))) -diff --git a/src/cmp/cmpinline.lsp b/src/cmp/cmpinline.lsp -index a44a0a10..b0ff9596 100644 ---- a/src/cmp/cmpinline.lsp -+++ b/src/cmp/cmpinline.lsp -@@ -182,7 +182,7 @@ - (wt-nl-open-brace) - (incf *inline-blocks*)) - --(defun close-inline-blocks (&optional new-line) -+(defun close-inline-blocks () - (loop for i of-type fixnum from 0 below *inline-blocks* - do (wt-nl-close-brace))) - -diff --git a/src/cmp/cmplam.lsp b/src/cmp/cmplam.lsp -index 29f02ed6..c6e313ff 100644 ---- a/src/cmp/cmplam.lsp -+++ b/src/cmp/cmplam.lsp -@@ -335,6 +335,7 @@ The function thus belongs to the type of functions that ecl_make_cfun accepts." - (maxarg call-arguments-limit)) - (destructuring-bind (requireds optionals rest key-flag keywords a-o-k) - (c1form-arg 0 lambda) -+ (declare (ignore keywords)) - (setf minarg (length requireds)) - (when (and (null rest) (not key-flag) (not a-o-k)) - (setf maxarg (+ minarg (/ (length optionals) 3))))) -diff --git a/src/cmp/cmplet.lsp b/src/cmp/cmplet.lsp -index 08694a51..cdd789cd 100644 ---- a/src/cmp/cmplet.lsp -+++ b/src/cmp/cmplet.lsp -@@ -311,7 +311,7 @@ - (when env (pop-debug-lexical-env)))) - (c2expr body)) - -- (close-inline-blocks :line)) -+ (close-inline-blocks)) - - (defun discarded (var form body &aux last) - (labels ((last-form (x &aux (args (c1form-args x))) -diff --git a/src/cmp/cmploc.lsp b/src/cmp/cmploc.lsp -index b2d2115c..c6ec0a66 100644 ---- a/src/cmp/cmploc.lsp -+++ b/src/cmp/cmploc.lsp -@@ -218,6 +218,7 @@ - ;;; - - (defun set-unknown-loc (loc) -+ (declare (ignore loc)) - (unknown-location 'set-loc *destination*)) - - (defun set-loc (loc &aux fd) -diff --git a/src/cmp/cmpmain.lsp b/src/cmp/cmpmain.lsp -index a9858ef5..dd2a4be4 100755 ---- a/src/cmp/cmpmain.lsp -+++ b/src/cmp/cmpmain.lsp -@@ -48,6 +48,8 @@ the environment variable TMPDIR to a different value." template)) - verbose print c-file h-file data-file - system-p load external-format source-truename - source-offset) -+ (declare (ignore verbose print c-file h-file data-file load -+ external-format source-truename source-offset)) - (let* ((format '()) - (extension '())) - (unless type-supplied-p -@@ -145,6 +147,7 @@ the environment variable TMPDIR to a different value." template)) - (defun linker-cc (o-pathname object-files &key - (type :program) - (ld-flags (split-program-options *ld-flags*))) -+ (declare (ignore type)) - (safe-run-program - *ld* - `("-o" ,(brief-namestring o-pathname) -@@ -995,6 +998,7 @@ from the C language code. NIL means \"do not create the file\"." - *safety* *space* *speed* *debug*)) - - (defmacro with-compilation-unit (options &rest body) -+ (declare (ignore options)) - `(progn ,@body)) - - (ext:package-lock "CL" t) -diff --git a/src/cmp/cmpmulti.lsp b/src/cmp/cmpmulti.lsp -index f8cb77c8..a27c6693 100644 ---- a/src/cmp/cmpmulti.lsp -+++ b/src/cmp/cmpmulti.lsp -@@ -166,7 +166,7 @@ - (declare (si::c-local)) - (if (plusp i) (values-loc i) 'VALUE0)) - --(defun do-m-v-setq (vars form use-bind &aux min-values max-values) -+(defun do-m-v-setq (vars form use-bind) - ;; This routine moves values from the multiple-value stack into the - ;; variables VARS. The amount of values is not known (or at least we only - ;; know that there is some number between MIN-VALUES and MAX-VALUES). If -diff --git a/src/cmp/cmpname.lsp b/src/cmp/cmpname.lsp -index dbd34091..4774816a 100644 ---- a/src/cmp/cmpname.lsp -+++ b/src/cmp/cmpname.lsp -@@ -161,7 +161,7 @@ initialization from the C code which wants to use it." - c) - (t - #\p))) -- (disambiguation (c) -+ (disambiguation (kind) - (case kind - ((:object :c) "") - ((:fasl :fas) "fas_") -diff --git a/src/cmp/cmpnum.lsp b/src/cmp/cmpnum.lsp -index 78ae3260..fab4e0b6 100644 ---- a/src/cmp/cmpnum.lsp -+++ b/src/cmp/cmpnum.lsp -@@ -254,11 +254,13 @@ - (def-type-propagator acos (fname op1-type) - (multiple-value-bind (output-type op1-type) - (ensure-nonrational-type op1-type) -+ (declare (ignore output-type)) - (values (list op1-type) 'NUMBER))) - - (def-type-propagator atan (fname op1-type &optional (op2-type t op2-p)) - (multiple-value-bind (float-t1 t1) - (ensure-nonrational-type op1-type) -+ (declare (ignore float-t1)) - (if op2-p - (multiple-value-bind (result t1 t2) - (maximum-number-type t1 op2-type :only-real t) -diff --git a/src/cmp/cmpopt-printer.lsp b/src/cmp/cmpopt-printer.lsp -index 015ac1e2..fb140c51 100644 ---- a/src/cmp/cmpopt-printer.lsp -+++ b/src/cmp/cmpopt-printer.lsp -@@ -65,19 +65,19 @@ - "ecl_princ(#0,#1)" - :one-liner t))) - --(define-compiler-macro terpri (&optional stream &environment env) -+(define-compiler-macro terpri (&optional stream) - `(ffi:c-inline (,stream) - (:object) :object - "ecl_terpri(#0)" - :one-liner t)) - --(define-compiler-macro print (value &optional stream &environment env) -+(define-compiler-macro print (value &optional stream) - `(ffi:c-inline (,value ,stream) - (:object :object) :object - "ecl_print(#0,#1)" - :one-liner t)) - --(define-compiler-macro prin1 (value &optional stream &environment env) -+(define-compiler-macro prin1 (value &optional stream) - `(ffi:c-inline (,value ,stream) - (:object :object) :object - "ecl_prin1(#0,#1)" -diff --git a/src/cmp/cmpopt-sequence.lsp b/src/cmp/cmpopt-sequence.lsp -index 7443f317..e07ed90f 100644 ---- a/src/cmp/cmpopt-sequence.lsp -+++ b/src/cmp/cmpopt-sequence.lsp -@@ -220,6 +220,7 @@ - (return ,%sublist))))))) - - (define-compiler-macro member (&whole whole value list &rest sequence-args) -+ (declare (value list sequence-args)) - (if (policy-inline-sequence-functions) - (or (apply #'expand-member (rest whole)) - whole) -@@ -264,6 +265,7 @@ - (return ,%elt)))))))))) - - (define-compiler-macro assoc (&whole whole value list &rest sequence-args) -+ (declare (ignore value list sequence-args)) - (if (policy-inline-sequence-functions) - (or (apply #'expand-assoc (rest whole)) - whole) -@@ -287,6 +289,7 @@ - (return ,%elt)))))))) - - (define-compiler-macro find (&whole whole value sequence &rest sequence-args) -+ (declare (ignore value sequence sequence-args)) - (if (policy-inline-sequence-functions) - (or (apply #'expand-find (rest whole)) - whole) -diff --git a/src/cmp/cmpopt.lsp b/src/cmp/cmpopt.lsp -index e4de366f..b591d0cc 100644 ---- a/src/cmp/cmpopt.lsp -+++ b/src/cmp/cmpopt.lsp -@@ -160,6 +160,7 @@ - form)))) - - (define-compiler-macro typep (&whole form object type &optional e &environment env) -+ (declare (ignore e)) - (expand-typep form object type env)) - - ;;; -@@ -346,8 +347,7 @@ - (multiple-value-bind (constant-p float) - (constant-value-p float env) - (when (and constant-p (floatp float)) -- (let* ((aux (gentemp)) -- (float (type-of float)) -+ (let* ((float (type-of float)) - (c-type (lisp-type->rep-type float))) - `(let ((value ,value)) - (declare (:read-only value)) -diff --git a/src/cmp/cmpprop.lsp b/src/cmp/cmpprop.lsp -index b6e6f727..e9ba7c6f 100644 ---- a/src/cmp/cmpprop.lsp -+++ b/src/cmp/cmpprop.lsp -@@ -379,12 +379,14 @@ compute it. This version only handles the simplest cases." - elt-type))) - - (def-type-propagator si::row-major-aset (fname array-type index obj) -+ (declare (ignore index obj)) - (multiple-value-bind (elt-type array-type) - (type-from-array-elt array-type) - (values (list array-type 'si::index elt-type) - elt-type))) - - (def-type-propagator row-major-aref (fname array-type index) -+ (declare (ignore index)) - (multiple-value-bind (elt-type array-type) - (type-from-array-elt array-type) - (values (list array-type 'si::index) elt-type))) -diff --git a/src/cmp/cmptop.lsp b/src/cmp/cmptop.lsp -index 072329e4..e41f4f0a 100644 ---- a/src/cmp/cmptop.lsp -+++ b/src/cmp/cmptop.lsp -@@ -477,6 +477,7 @@ - args - (multiple-value-bind (function pprint doc-string) - (sys::expand-defmacro name lambda-list body) -+ (declare (ignore pprint doc-string)) - (let ((fn (cmp-eval function *cmp-env*))) - (cmp-env-register-global-macro name fn)) - (t1expr* (macroexpand `(DEFMACRO ,@args)))))) -diff --git a/src/cmp/cmptype-assert.lsp b/src/cmp/cmptype-assert.lsp -index d485d651..f979997d 100644 ---- a/src/cmp/cmptype-assert.lsp -+++ b/src/cmp/cmptype-assert.lsp -@@ -58,7 +58,7 @@ - FEtype_error_sequence(#0);") - (vector . "if (ecl_unlikely(!ECL_VECTORP(#0))) FEtype_error_vector(#0);"))) - --(defun simple-type-assertion (value type env) -+(defun simple-type-assertion (value type) - (let ((simple-form (cdr (assoc type +simple-type-assertions+)))) - (if simple-form - `(ffi:c-inline (,value) (:object) :void ,simple-form -@@ -82,13 +82,13 @@ - (compulsory - ;; The check has to be produced, independent of the declared - ;; value of the variable (for instance, in LAMBDA arguments). -- (simple-type-assertion value type env)) -+ (simple-type-assertion value type)) - (t - ;; We may rely on the compiler to choose the appropriate - ;; branch once type propagation has happened. - `(ext:compiler-typecase ,value - (,type) -- (t ,(simple-type-assertion value type env)))))) -+ (t ,(simple-type-assertion value type)))))) - - (defun c1checked-value (args) - (let* ((type (pop args)) -diff --git a/src/cmp/cmpvar.lsp b/src/cmp/cmpvar.lsp -index d5009e35..590b3081 100644 ---- a/src/cmp/cmpvar.lsp -+++ b/src/cmp/cmpvar.lsp -@@ -74,13 +74,13 @@ - (mapcar #'first (var-read-nodes var))) - - (defun assert-var-ref-value (var) -- #+debug-compiler -- (unless (let ((ref (var-ref var))) -- (or (> ref (/ most-positive-fixnum 2)) -- (= (var-ref var) (+ (length (var-read-nodes var)) -- (length (var-set-nodes var)))))) -- (baboon :format-control "Number of references in VAR ~A unequal to references list" -- :format-arguments (list var)))) -+ (when *debug-compiler* -+ (unless (let ((ref (var-ref var))) -+ (or (> ref (/ most-positive-fixnum 2)) -+ (= (var-ref var) (+ (length (var-read-nodes var)) -+ (length (var-set-nodes var)))))) -+ (baboon :format-control "Number of references in VAR ~A unequal to references list" -+ :format-arguments (list var))))) - - (defun assert-var-not-ignored (var) - (when (let ((x (var-ignorable var))) (and x (minusp x))) -@@ -229,6 +229,7 @@ - (defun c1vref (name) - (multiple-value-bind (var cfb unw) - (cmp-env-search-var name) -+ (declare (ignore unw)) - (cond ((null var) - (c1make-global-variable name :warn t - :type (or (si:get-sysprop name 'CMP-TYPE) t))) -diff --git a/src/cmp/proclamations.lsp b/src/cmp/proclamations.lsp -index 7f9d607a..54839d2a 100644 ---- a/src/cmp/proclamations.lsp -+++ b/src/cmp/proclamations.lsp -@@ -1189,7 +1189,7 @@ - (proclamation si:open-unix-socket-stream (string) stream) - #+wants-sockets - (proclamation si:lookup-host-entry (t) (values (or null string) list list)) --(proclamation si:copy-stream (stream stream wait) t) -+(proclamation si:copy-stream (stream stream gen-bool) t) - (proclamation si:make-encoding (t) hash-table) - (proclamation si:load-encoding (t) t) - -diff --git a/src/h/bytecodes.h b/src/h/bytecodes.h -index 8b106ea7..235a1a97 100644 ---- a/src/h/bytecodes.h -+++ b/src/h/bytecodes.h -@@ -152,6 +152,7 @@ typedef int16_t cl_opcode; - goto *(&&LBL_OP_NOP + offsets[GET_OPCODE(vector)]) - #else - #define BEGIN_SWITCH \ -+ BEGIN: \ - switch (GET_OPCODE(vector)) - #define THREAD_NEXT \ - goto BEGIN -diff --git a/src/lsp/defstruct.lsp b/src/lsp/defstruct.lsp -index 3b568b8d..f3848a25 100644 ---- a/src/lsp/defstruct.lsp -+++ b/src/lsp/defstruct.lsp -@@ -313,10 +313,10 @@ - (or (equal old-def new-def) - (destructuring-bind (old-slot-name old-init old-type old-read-only old-offset old-ac) - old-def -- (declare (ignore old-init old-read-only old-ac)) -+ (declare (ignore old-slot-name old-init old-read-only old-ac)) - (destructuring-bind (new-slot-name new-init new-type new-read-only new-offset new-ac) - new-def -- (declare (ignore new-init new-read-only new-ac)) -+ (declare (ignore new-slot-name new-init new-read-only new-ac)) - ;; Name EQL is not enforced because structures may be - ;; constructed by code generators and it is likely they - ;; will have gensymed names. -- jd 2019-05-22 -diff --git a/src/lsp/ffi.lsp b/src/lsp/ffi.lsp -index 7e2b12f3..017aaaa9 100644 ---- a/src/lsp/ffi.lsp -+++ b/src/lsp/ffi.lsp -@@ -834,7 +834,8 @@ reference the arguments of the function as \"#0\", \"#1\", etc. - - The interpreter ignores this form. ARG-TYPES are argument types of - the defined Lisp function and RESULT-TYPE is its return type." -- (let ((args (mapcar #'(lambda (x) (gensym)) arg-types))) -+ (let ((args (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) -+ arg-types))) - `(defun ,name ,args - (c-inline ,args ,arg-types ,result-type - ,c-expression :one-liner t)))) -@@ -850,7 +851,8 @@ FUNCTION-NAME. - The interpreter ignores this form. ARG-TYPES are argument types of - the C function and RESULT-TYPE is its return type." - (let ((output-type :object) -- (args (mapcar #'(lambda (x) (gensym)) arg-types))) -+ (args (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) -+ arg-types))) - (if (consp c-name) - (setf output-type (first c-name) - c-name (second c-name))) -diff --git a/src/lsp/setf.lsp b/src/lsp/setf.lsp -index 6575701b..6b9ffbb6 100644 ---- a/src/lsp/setf.lsp -+++ b/src/lsp/setf.lsp -@@ -31,7 +31,6 @@ - (push item vars)) - (push item all)) - (dotimes (i stores-no) -- (declare (ignore i)) - (push (gensym) stores)) - (let* ((all (nreverse all))) - (values (nreverse vars) --- -2.26.2 - diff --git a/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch b/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch deleted file mode 100644 index 3cbe157cfbc..00000000000 --- a/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch +++ /dev/null @@ -1,29 +0,0 @@ -From d0a454a36e1e552beb15d9b09e8d16658489f4d5 Mon Sep 17 00:00:00 2001 -From: Marius Gerbershagen -Date: Wed, 6 May 2020 21:03:18 +0200 -Subject: [PATCH 3/3] printer: fix printing of symbols with non-ascii names - -Bug was introduced in commit c6b4296bb886ad70b83c5cc0f472f6855783e2f9 -in converting buffer_write_char from a macro to an inline -function. Problem reported by Vladimir Sedach on the ecl-devel mailing -list. ---- - src/c/printer/write_symbol.d | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/c/printer/write_symbol.d b/src/c/printer/write_symbol.d -index a39bab97..49ef5d31 100644 ---- a/src/c/printer/write_symbol.d -+++ b/src/c/printer/write_symbol.d -@@ -103,7 +103,7 @@ needs_to_be_escaped(cl_object s, cl_object readtable, cl_object print_case) - } - - static inline void --buffer_write_char(char c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { -+buffer_write_char(ecl_character c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { - ecl_char_set(buffer, (*buffer_ndx)++, c); - if (*buffer_ndx >= buffer_size) { - si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); --- -2.26.2 - diff --git a/build/pkgs/ecl/patches/214.patch b/build/pkgs/ecl/patches/214.patch deleted file mode 100644 index b466b3fe685..00000000000 --- a/build/pkgs/ecl/patches/214.patch +++ /dev/null @@ -1,202 +0,0 @@ -diff --git a/src/configure b/src/configure -index 103f4102be537c5521889ad14f7d4151245bd4b2..5c7cfc2b8b4822a7477c0ba2883993a8ef185d09 100755 ---- a/src/configure -+++ b/src/configure -@@ -3932,6 +3932,184 @@ 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 - # sets variable CC -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 -+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } -+if ${ac_cv_prog_cc_c99+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_cv_prog_cc_c99=no -+ac_save_CC=$CC -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+#include -+#include -+#include -+#include -+#include -+ -+// Check varargs macros. These examples are taken from C99 6.10.3.5. -+#define debug(...) fprintf (stderr, __VA_ARGS__) -+#define showlist(...) puts (#__VA_ARGS__) -+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -+static void -+test_varargs_macros (void) -+{ -+ int x = 1234; -+ int y = 5678; -+ debug ("Flag"); -+ debug ("X = %d\n", x); -+ showlist (The first, second, and third items.); -+ report (x>y, "x is %d but y is %d", x, y); -+} -+ -+// Check long long types. -+#define BIG64 18446744073709551615ull -+#define BIG32 4294967295ul -+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -+#if !BIG_OK -+ your preprocessor is broken; -+#endif -+#if BIG_OK -+#else -+ your preprocessor is broken; -+#endif -+static long long int bignum = -9223372036854775807LL; -+static unsigned long long int ubignum = BIG64; -+ -+struct incomplete_array -+{ -+ int datasize; -+ double data[]; -+}; -+ -+struct named_init { -+ int number; -+ const wchar_t *name; -+ double average; -+}; -+ -+typedef const char *ccp; -+ -+static inline int -+test_restrict (ccp restrict text) -+{ -+ // See if C++-style comments work. -+ // Iterate through items via the restricted pointer. -+ // Also check for declarations in for loops. -+ for (unsigned int i = 0; *(text+i) != '\0'; ++i) -+ continue; -+ return 0; -+} -+ -+// Check varargs and va_copy. -+static void -+test_varargs (const char *format, ...) -+{ -+ va_list args; -+ va_start (args, format); -+ va_list args_copy; -+ va_copy (args_copy, args); -+ -+ const char *str; -+ int number; -+ float fnumber; -+ -+ while (*format) -+ { -+ switch (*format++) -+ { -+ case 's': // string -+ str = va_arg (args_copy, const char *); -+ break; -+ case 'd': // int -+ number = va_arg (args_copy, int); -+ break; -+ case 'f': // float -+ fnumber = va_arg (args_copy, double); -+ break; -+ default: -+ break; -+ } -+ } -+ va_end (args_copy); -+ va_end (args); -+} -+ -+int -+main () -+{ -+ -+ // Check bool. -+ _Bool success = false; -+ -+ // Check restrict. -+ if (test_restrict ("String literal") == 0) -+ success = true; -+ char *restrict newvar = "Another string"; -+ -+ // Check varargs. -+ test_varargs ("s, d' f .", "string", 65, 34.234); -+ test_varargs_macros (); -+ -+ // Check flexible array members. -+ struct incomplete_array *ia = -+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); -+ ia->datasize = 10; -+ for (int i = 0; i < ia->datasize; ++i) -+ ia->data[i] = i * 1.234; -+ -+ // Check named initializers. -+ struct named_init ni = { -+ .number = 34, -+ .name = L"Test wide string", -+ .average = 543.34343, -+ }; -+ -+ ni.number = 58; -+ -+ int dynamic_array[ni.number]; -+ dynamic_array[ni.number - 1] = 543; -+ -+ // work around unused variable warnings -+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' -+ || dynamic_array[ni.number - 1] != 543); -+ -+ ; -+ return 0; -+} -+_ACEOF -+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 -+do -+ CC="$ac_save_CC $ac_arg" -+ if ac_fn_c_try_compile "$LINENO"; then : -+ ac_cv_prog_cc_c99=$ac_arg -+fi -+rm -f core conftest.err conftest.$ac_objext -+ test "x$ac_cv_prog_cc_c99" != "xno" && break -+done -+rm -f conftest.$ac_ext -+CC=$ac_save_CC -+ -+fi -+# AC_CACHE_VAL -+case "x$ac_cv_prog_cc_c99" in -+ x) -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -+$as_echo "none needed" >&6; } ;; -+ xno) -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -+$as_echo "unsupported" >&6; } ;; -+ *) -+ CC="$CC $ac_cv_prog_cc_c99" -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; -+esac -+if test "x$ac_cv_prog_cc_c99" != xno; then : -+ -+fi -+ -+ # checks that CC by default accepts C99 code, if not, -+ # tries adding -std=gnu-99, if this has no effect, throws an error. - ac_ext=cpp - ac_cpp='$CXXCPP $CPPFLAGS' - ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -diff --git a/src/configure.ac b/src/configure.ac -index 4e615408e7b336329b4228cb40ca820f12adacba..df7a90c84538a32ca45f2dbc4f398c4c51bec3ae 100644 ---- a/src/configure.ac -+++ b/src/configure.ac -@@ -334,6 +334,8 @@ AC_CANONICAL_HOST - dnl ===================================================================== - dnl Checks for programs - AC_PROG_CC # sets variable CC -+AC_PROG_CC_C99 # checks that CC by default accepts C99 code, if not, -+ # tries adding -std=gnu-99, if this has no effect, throws an error. - AC_PROG_CXX # sets variable CXX - AC_PROG_CPP # sets variable CPP - AC_PROG_RANLIB # sets variable RANLIB diff --git a/build/pkgs/ecl/patches/215.patch b/build/pkgs/ecl/patches/215.patch deleted file mode 100644 index a0f18f2b22f..00000000000 --- a/build/pkgs/ecl/patches/215.patch +++ /dev/null @@ -1,175 +0,0 @@ -diff --git a/src/aclocal.m4 b/src/aclocal.m4 -index 6adc5c63d7c60cf34ac7cee372158d617143191b..cf011efd016a20d5685c12bbac7d1d731eaa755e 100644 ---- a/src/aclocal.m4 -+++ b/src/aclocal.m4 -@@ -665,6 +665,21 @@ case "${ECL_STACK_DIR}" in - up|UP) AC_MSG_RESULT(no) ;; - *) AC_MSG_ERROR(Unable to determine stack growth direction) - esac]) -+ -+dnl -+dnl -------------------------------------------------------------- -+dnl Check if we can determine the stack size at runtime -+dnl -+AC_DEFUN(ECL_STACK_SIZE,[ -+AC_CHECK_HEADER([sys/resource.h], -+ [AC_DEFINE([HAVE_SYS_RESOURCE_H], [], [Define to 1 if you have the header file.]) -+ AC_CHECK_DECL([RLIMIT_STACK], -+ [AC_DEFINE([ECL_CAN_SET_STACK_SIZE], [], [Define to 1 if we can set the stack size at runtime.])], -+ [], -+ [#include ])], -+ [],[]) -+]) -+ - dnl - dnl ------------------------------------------------------------ - dnl Find out a setjmp() that does not save signals. It is called -diff --git a/src/c/stacks.d b/src/c/stacks.d -index d985766ba5e03b07d6a25e31e9880576250038aa..c0125ed2bc8ec9d1712e5e71f3c91edb1bc7c12a 100644 ---- a/src/c/stacks.d -+++ b/src/c/stacks.d -@@ -29,7 +29,7 @@ cs_set_size(cl_env_ptr env, cl_index new_size) - { - volatile char foo = 0; - cl_index margin = ecl_option_values[ECL_OPT_C_STACK_SAFETY_AREA]; --#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_STACK) && !defined(NACL) -+#if defined(ECL_CAN_SET_STACK_SIZE) - { - struct rlimit rl; - -@@ -40,13 +40,22 @@ cs_set_size(cl_env_ptr env, cl_index new_size) - if (setrlimit(RLIMIT_STACK, &rl)) - ecl_internal_error("Can't set the size of the C stack"); - } -+ } else { -+ rl.rlim_cur = new_size; -+ } -+ if (rl.rlim_cur == 0 || rl.rlim_cur == RLIM_INFINITY || rl.rlim_cur > (cl_index)(-1)) { -+ /* Either getrlimit failed or returned nonsense, either way we -+ * don't know the stack size. Use a default of 1 MB and hope for -+ * the best. */ -+ new_size = 1048576; -+ } else { - new_size = rl.rlim_cur; -+ } - #ifdef ECL_DOWN_STACK -- env->cs_barrier = env->cs_org - new_size; -+ env->cs_barrier = env->cs_org - new_size; - #else -- env->cs_barrier = env->cs_org + new_size; -+ env->cs_barrier = env->cs_org + new_size; - #endif -- } - } - #endif - env->cs_limit_size = new_size - (2*margin); -@@ -64,7 +73,7 @@ cs_set_size(cl_env_ptr env, cl_index new_size) - } - #endif - else -- ecl_internal_error("Can't set the size of the C stack"); -+ ecl_internal_error("Can't set the size of the C stack: sanity check failed"); - env->cs_size = new_size; - } - -diff --git a/src/configure b/src/configure -index 103f4102be537c5521889ad14f7d4151245bd4b2..b2e7608887d1d0648c80578dea26a50ff9a945c0 100755 ---- a/src/configure -+++ b/src/configure -@@ -7125,7 +7125,7 @@ fi - done - - --for ac_header in sys/resource.h sys/utsname.h float.h pwd.h dlfcn.h link.h \ -+for ac_header in sys/utsname.h float.h pwd.h dlfcn.h link.h \ - mach-o/dyld.h dirent.h sys/ioctl.h sys/select.h \ - sys/wait.h semaphore.h - do : -@@ -8345,6 +8345,24 @@ $as_echo "no" >&6; } ;; - *) as_fn_error $? "Unable to determine stack growth direction" "$LINENO" 5 - esac - -+ac_fn_c_check_header_mongrel "$LINENO" "sys/resource.h" "ac_cv_header_sys_resource_h" "$ac_includes_default" -+if test "x$ac_cv_header_sys_resource_h" = xyes; then : -+ -+$as_echo "#define HAVE_SYS_RESOURCE_H /**/" >>confdefs.h -+ -+ ac_fn_c_check_decl "$LINENO" "RLIMIT_STACK" "ac_cv_have_decl_RLIMIT_STACK" "#include -+" -+if test "x$ac_cv_have_decl_RLIMIT_STACK" = xyes; then : -+ -+$as_echo "#define ECL_CAN_SET_STACK_SIZE /**/" >>confdefs.h -+ -+fi -+ -+fi -+ -+ -+ -+ - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether closedir returns void" >&5 - $as_echo_n "checking whether closedir returns void... " >&6; } -@@ -8997,8 +9015,6 @@ main () - if (*(data + i) != *(data3 + i)) - return 14; - close (fd); -- free (data); -- free (data3); - return 0; - } - _ACEOF -diff --git a/src/configure.ac b/src/configure.ac -index 4e615408e7b336329b4228cb40ca820f12adacba..8103d88bc9b2abe24ebef83db4ea36355c947fc6 100644 ---- a/src/configure.ac -+++ b/src/configure.ac -@@ -658,7 +658,7 @@ AC_CHECK_HEADERS( [fcntl.h limits.h netdb.h netinet/in.h] \ - [sched.h] ) - dnl !!! end autoscan - --AC_CHECK_HEADERS( [sys/resource.h sys/utsname.h float.h pwd.h dlfcn.h link.h] \ -+AC_CHECK_HEADERS( [sys/utsname.h float.h pwd.h dlfcn.h link.h] \ - [mach-o/dyld.h dirent.h sys/ioctl.h sys/select.h] \ - [sys/wait.h semaphore.h] ) - -@@ -711,8 +711,9 @@ ECL_SSE - ECL_COMPLEX_C99 - - dnl ----------------------------------------------------------------------- --dnl Study the call conventions -+dnl Stack size and growth direction - ECL_STACK_DIRECTION -+ECL_STACK_SIZE - - dnl ===================================================================== - dnl Checks for library functions -diff --git a/src/ecl/configpre.h b/src/ecl/configpre.h -index 98f96bd653052014ef612cfcfcd87a08557979fd..aeda058e059d07d07d9c903f647e74f8dfe30c7a 100644 ---- a/src/ecl/configpre.h -+++ b/src/ecl/configpre.h -@@ -9,6 +9,9 @@ - /* ECL_AVOID_FPE_H */ - #undef ECL_AVOID_FPE_H - -+/* Define to 1 if we can set the stack size at runtime. */ -+#undef ECL_CAN_SET_STACK_SIZE -+ - /* Allow STREAM operations to work on arbitrary objects */ - #undef ECL_CLOS_STREAMS - -diff --git a/src/h/config-internal.h.in b/src/h/config-internal.h.in -index dd7a4f8ad0e86fb735333c4794665d4520fbf830..6b4438ad5953971952ac26328c5c2a3e0f898eff 100644 ---- a/src/h/config-internal.h.in -+++ b/src/h/config-internal.h.in -@@ -240,7 +240,10 @@ - #include "@ECL_LIBFFI_HEADER@" - #endif - --#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_STACK) && !defined(NACL) -+/* Can we determine and set the stack size at runtime? */ -+#undef ECL_CAN_SET_STACK_SIZE -+ -+#if defined(ECL_CAN_SET_STACK_SIZE) - #define ECL_DEFAULT_C_STACK_SIZE 0 /* Use the stack size provided by the OS */ - #else - #define ECL_DEFAULT_C_STACK_SIZE @ECL_DEFAULT_C_STACK_SIZE@ diff --git a/build/pkgs/ecl/patches/216.patch b/build/pkgs/ecl/patches/216.patch deleted file mode 100644 index 055a2636e7c..00000000000 --- a/build/pkgs/ecl/patches/216.patch +++ /dev/null @@ -1,42 +0,0 @@ -diff --git a/src/cmp/cmpos-run.lsp b/src/cmp/cmpos-run.lsp -index 418751c3d188c1ba6042d965fe1424eb6d4c7ca4..4008b3a6ad7bac846f50d1c4d03b45cf751a451e 100755 ---- a/src/cmp/cmpos-run.lsp -+++ b/src/cmp/cmpos-run.lsp -@@ -51,18 +51,25 @@ - (program (car program))) - (with-current-directory - ;; when compiling ECL itself, we only have low-level functions -- ;; available, otherwise we can use run-program and get proper -- ;; quoting of arguments -- #+ecl-min (multiple-value-bind (output-stream return-status pid) -- (si:run-program-inner program args :default nil) -- (setf output (collect-lines output-stream)) -- (multiple-value-setq (return-status result) -- (si:waitpid pid t))) -- #-ecl-min (multiple-value-bind (output-stream return-status process-obj) -- (ext:run-program program args :wait nil) -- (setf output (collect-lines output-stream)) -- (multiple-value-setq (return-status result) -- (ext:external-process-wait process-obj t))))) -+ ;; available ... -+ #+(and ecl-min (not cygwin)) -+ (multiple-value-bind (output-stream return-status pid) -+ (si:run-program-inner program args :default nil) -+ (setf output (collect-lines output-stream)) -+ (multiple-value-setq (return-status result) -+ (si:waitpid pid t))) -+ ;; ... otherwise we can use run-program and get proper -+ ;; quoting of arguments ... -+ #+(and (not ecl-min) (not cygwin)) -+ (multiple-value-bind (output-stream return-status process-obj) -+ (ext:run-program program args :wait nil) -+ (setf output (collect-lines output-stream)) -+ (multiple-value-setq (return-status result) -+ (ext:external-process-wait process-obj t))) -+ ;; ... unless we're running on cygwin which has problems with -+ ;; forking so we have to use si:system -+ #+cygwin -+ (setf result (si:system (format nil "~A~{ ~A~}" program args))))) - (cond ((null result) - (cerror "Continues anyway." - "Unable to execute:~%(EXT:RUN-PROGRAM ~S ~S)" diff --git a/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch b/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch deleted file mode 100644 index ce87f79ab58..00000000000 --- a/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 75877dd8f0d534552284ba4380ba65baa74f028f Mon Sep 17 00:00:00 2001 -From: Marius Gerbershagen -Date: Sun, 28 Jun 2020 11:02:15 +0200 -Subject: [PATCH] fpe: fix ECL_WITH_LISP_FPE macro - -We can't use ecl_process_env_unsafe() == NULL to check if ECL has -booted because the return value of ecl_process_env_unsafe is -unpredictable before ECL has booted. The reason is that -ecl_process_env_unsafe calls pthread_getspecific with an uninitialized -key stored in cl_env_key. But another call to pthread_setspecific -might have already registered a key which happens to be the same as -the not yet initialized cl_env_key, yielding a non-NULL value. ---- - src/h/impl/math_fenv.h | 17 ++++++++--------- - 1 file changed, 8 insertions(+), 9 deletions(-) - -diff --git a/src/h/impl/math_fenv.h b/src/h/impl/math_fenv.h -index 0a93c8e0a..9630f4c6c 100644 ---- a/src/h/impl/math_fenv.h -+++ b/src/h/impl/math_fenv.h -@@ -72,15 +72,14 @@ - - #if defined(HAVE_FENV_H) && !defined(ECL_AVOID_FPE_H) - # if defined(HAVE_FEENABLEEXCEPT) --# define ECL_WITH_LISP_FPE_BEGIN do { \ -- fenv_t __fenv; \ -- fegetenv(&__fenv); \ -- cl_env_ptr __the_env = ecl_process_env_unsafe(); \ -- if (__the_env) { \ -- int bits = __the_env->trap_fpe_bits; \ -- fedisableexcept(FE_ALL_EXCEPT & ~bits); \ -- feenableexcept(FE_ALL_EXCEPT & bits); \ -- } \ -+# define ECL_WITH_LISP_FPE_BEGIN do { \ -+ fenv_t __fenv; \ -+ fegetenv(&__fenv); \ -+ if (ecl_get_option(ECL_OPT_BOOTED) > 0) { \ -+ int bits = ecl_process_env()->trap_fpe_bits; \ -+ fedisableexcept(FE_ALL_EXCEPT & ~bits); \ -+ feenableexcept(FE_ALL_EXCEPT & bits); \ -+ } \ - feclearexcept(FE_ALL_EXCEPT); - # else - # define ECL_WITH_LISP_FPE_BEGIN do { \ --- -GitLab - diff --git a/build/pkgs/ecl/patches/ecl-configure-include-stdlib-h.patch b/build/pkgs/ecl/patches/ecl-configure-include-stdlib-h.patch deleted file mode 100644 index 725521ce074..00000000000 --- a/build/pkgs/ecl/patches/ecl-configure-include-stdlib-h.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- a/src/configure 2020-04-24 03:54:52.000000000 -0700 -+++ b/src/configure 2020-09-17 14:47:48.000000000 -0700 -@@ -2046,6 +2046,7 @@ - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - $4 -+#include - int - main () - { -@@ -2059,6 +2060,7 @@ - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - $4 -+#include - int - main () - { -@@ -7421,6 +7423,7 @@ - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - #include -+#include - int main() { - const char *int_type; - int bits; -@@ -7709,6 +7712,7 @@ - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - #include -+#include - int main() { - const char *int_type; - int bits; -@@ -8004,6 +8008,7 @@ - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - #include -+#include - int main() { - FILE *f = fopen("conftestval","w"); - int c1, c2; diff --git a/build/pkgs/ecl/patches/ffi_abi_libffi33.oldpatch b/build/pkgs/ecl/patches/ffi_abi_libffi33.oldpatch deleted file mode 100644 index 28dd7d0805b..00000000000 --- a/build/pkgs/ecl/patches/ffi_abi_libffi33.oldpatch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/src/c/ffi.d b/src/c/ffi.d -index 8861303e..8a959c23 100644 ---- a/src/c/ffi.d -+++ b/src/c/ffi.d -@@ -145,8 +145,8 @@ static struct { - #elif defined(X86_WIN64) - {@':win64', FFI_WIN64}, - #elif defined(X86_ANY) || defined(X86) || defined(X86_64) -- {@':cdecl', FFI_SYSV}, -- {@':sysv', FFI_SYSV}, -+ {@':cdecl', FFI_UNIX64}, -+ {@':sysv', FFI_UNIX64}, - {@':unix64', FFI_UNIX64}, - #endif - }; diff --git a/build/pkgs/ecl/patches/skip_makeinfo_test.patch b/build/pkgs/ecl/patches/skip_makeinfo_test.patch deleted file mode 100644 index 15387feac49..00000000000 --- a/build/pkgs/ecl/patches/skip_makeinfo_test.patch +++ /dev/null @@ -1,59 +0,0 @@ -diff --git a/src/configure b/src/configure -index beca5e5b..103f4102 100755 ---- a/src/configure -+++ b/src/configure -@@ -5304,8 +5304,8 @@ fi - elif test "${enable_manual}" = "info"; then - as_fn_error $? "Unable to build the manual: install-info not found." "$LINENO" 5 - fi -- fi -- # Extract the first word of "makeinfo", so it can be a program name with args. -+ else -+ # Extract the first word of "makeinfo", so it can be a program name with args. - set dummy makeinfo; ac_word=$2 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 - $as_echo_n "checking for $ac_word... " >&6; } -@@ -5345,11 +5345,12 @@ $as_echo "no" >&6; } - fi - - -- if test "x${MAKEINFO}" = "x"; then -- if test "${enable_manual}" = "auto"; then -- enable_manual=no -- else -- as_fn_error $? "Unable to build the manual: makeinfo not found." "$LINENO" 5 -+ if test "x${MAKEINFO}" = "x"; then -+ if test "${enable_manual}" = "auto"; then -+ enable_manual=no -+ else -+ as_fn_error $? "Unable to build the manual: makeinfo not found." "$LINENO" 5 -+ fi - fi - fi - fi -diff --git a/src/configure.ac b/src/configure.ac -index 0184a182..4e615408 100644 ---- a/src/configure.ac -+++ b/src/configure.ac -@@ -359,13 +359,14 @@ if test "${enable_manual}" != "no"; then - elif test "${enable_manual}" = "info"; then - AC_MSG_ERROR([Unable to build the manual: install-info not found.]) - fi -- fi -- AC_PATH_PROG([MAKEINFO], [makeinfo], []) -- if test "x${MAKEINFO}" = "x"; then -- if test "${enable_manual}" = "auto"; then -- enable_manual=no -- else -- AC_MSG_ERROR([Unable to build the manual: makeinfo not found.]) -+ else -+ AC_PATH_PROG([MAKEINFO], [makeinfo], []) -+ if test "x${MAKEINFO}" = "x"; then -+ if test "${enable_manual}" = "auto"; then -+ enable_manual=no -+ else -+ AC_MSG_ERROR([Unable to build the manual: makeinfo not found.]) -+ fi - fi - fi - fi diff --git a/build/pkgs/eclib/distros/repology.txt b/build/pkgs/eclib/distros/repology.txt new file mode 100644 index 00000000000..1fa444a63e6 --- /dev/null +++ b/build/pkgs/eclib/distros/repology.txt @@ -0,0 +1 @@ +eclib diff --git a/build/pkgs/ecm/distros/repology.txt b/build/pkgs/ecm/distros/repology.txt new file mode 100644 index 00000000000..71377f447e3 --- /dev/null +++ b/build/pkgs/ecm/distros/repology.txt @@ -0,0 +1 @@ +gmp-ecm diff --git a/build/pkgs/elliptic_curves/distros/repology.txt b/build/pkgs/elliptic_curves/distros/repology.txt new file mode 100644 index 00000000000..1c665b3ace4 --- /dev/null +++ b/build/pkgs/elliptic_curves/distros/repology.txt @@ -0,0 +1 @@ +sagemath-elliptic-curves diff --git a/build/pkgs/entrypoints/distros/repology.txt b/build/pkgs/entrypoints/distros/repology.txt new file mode 100644 index 00000000000..c3e98c2e882 --- /dev/null +++ b/build/pkgs/entrypoints/distros/repology.txt @@ -0,0 +1,2 @@ +entrypoints +python:entrypoints diff --git a/build/pkgs/fflas_ffpack/distros/repology.txt b/build/pkgs/fflas_ffpack/distros/repology.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/repology.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/package-version.txt b/build/pkgs/fflas_ffpack/package-version.txt index 35cee72dcbf..e0af9dd584a 100644 --- a/build/pkgs/fflas_ffpack/package-version.txt +++ b/build/pkgs/fflas_ffpack/package-version.txt @@ -1 +1 @@ -2.4.3 +2.4.3.p0 diff --git a/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch b/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch new file mode 100644 index 00000000000..9351b03a941 --- /dev/null +++ b/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch @@ -0,0 +1,93 @@ +From d72a7643b7f8a1dedd12eadf89690c07ff6eed6e Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Mon, 1 Mar 2021 09:23:50 -0800 +Subject: [PATCH] Do not use variable names B0, B1 to avoid clash with + sys/termio.h macros (again) + +--- + .../fflas/fflas_igemm/igemm_kernels.inl | 50 +++++++++---------- + 1 file changed, 25 insertions(+), 25 deletions(-) + +diff --git a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl +index c69d32c6..0ca12110 100644 +--- a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl ++++ b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl +@@ -403,21 +403,21 @@ namespace FFLAS { namespace details { /* kernels */ + vect_t R0; + R0 = simd::set(r0[0], r1[0], r2[0], r3[0]); // could be done with a gather (marginally faster?) + for(k=0;k= ]GIAC_MIN_VERSION[, <= ]GIAC_MAX_VERSION, [ac_cv_path_GIAC], [ AC_PATH_PROGS_FEATURE_CHECK([GIAC], [giac], [ giac_version=$($ac_path_GIAC --version 2> /dev/null | tail -1) diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in index 66db3fcc08a..6a56720e503 100644 --- a/build/pkgs/giac/spkg-install.in +++ b/build/pkgs/giac/spkg-install.in @@ -51,7 +51,7 @@ if [ "$UNAME" = "CYGWIN" ]; then export ac_cv_header_nauty_naututil_h=no fi -sdh_configure --disable-gui --disable-ao "$DISABLENLS" --enable-png=no --disable-samplerate --disable-static +sdh_configure --disable-gui --disable-ao "$DISABLENLS" --enable-png=no --disable-samplerate --disable-static --disable-micropy ############################################################# # Build diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index bbdeb8b2f92..3da68c43db7 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -13,8 +13,8 @@ fi # Exit on failure set -e -VERSION="1.5.0" -VERSIONREV="87" +VERSION="1.6.0" +VERSIONREV="47" PATCHSUFFIX="p2" # The upstream tarball name is: giac"$SOURCEORIG".tar.gz @@ -31,6 +31,7 @@ fi # Build a temporary working dir. (preferably, a subdir of SAGE_DISTFILES) TARGET=$(mktemp -d -p"$SAGE_DISTFILES" 2>/dev/null || mktemp -d) +echo "Working in $TARGET" ORIGDIR=`pwd` cd "$TARGET" @@ -70,6 +71,10 @@ touch html_vall # building giac source tarball for the spkg cd ../../ +# get rid of micropython +rm -rf micropython* +sed -i.bak 's/micropython-[^ ]*//' Makefile.am configure.ac + for a in "$SAGE_ROOT"/build/pkgs/giac/patches/autotools/*.patch; do patch -p1 < $a done @@ -80,7 +85,7 @@ tar -cjf "$OUTPUTFILEBASENAME".tar.bz2 src # cleaning extracted dir. cd .. -rm -rf "$TARGET" +### rm -rf "$TARGET" # going back to starting dir cd "$ORIGDIR" diff --git a/build/pkgs/git/distros/repology.txt b/build/pkgs/git/distros/repology.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/repology.txt @@ -0,0 +1 @@ +git diff --git a/build/pkgs/givaro/distros/repology.txt b/build/pkgs/givaro/distros/repology.txt new file mode 100644 index 00000000000..54f01b4305f --- /dev/null +++ b/build/pkgs/givaro/distros/repology.txt @@ -0,0 +1 @@ +givaro diff --git a/build/pkgs/glpk/distros/repology.txt b/build/pkgs/glpk/distros/repology.txt new file mode 100644 index 00000000000..aca7917cfa1 --- /dev/null +++ b/build/pkgs/glpk/distros/repology.txt @@ -0,0 +1 @@ +glpk diff --git a/build/pkgs/glucose/distros/repology.txt b/build/pkgs/glucose/distros/repology.txt new file mode 100644 index 00000000000..90cba4351c8 --- /dev/null +++ b/build/pkgs/glucose/distros/repology.txt @@ -0,0 +1 @@ +glucose diff --git a/build/pkgs/gmp/distros/repology.txt b/build/pkgs/gmp/distros/repology.txt new file mode 100644 index 00000000000..a0a04787c06 --- /dev/null +++ b/build/pkgs/gmp/distros/repology.txt @@ -0,0 +1 @@ +gmp diff --git a/build/pkgs/gmpy2/distros/repology.txt b/build/pkgs/gmpy2/distros/repology.txt new file mode 100644 index 00000000000..3a2e328fdb3 --- /dev/null +++ b/build/pkgs/gmpy2/distros/repology.txt @@ -0,0 +1,2 @@ +python:gmpy2 +python:gmpy2-devel diff --git a/build/pkgs/gp2c/distros/repology.txt b/build/pkgs/gp2c/distros/repology.txt new file mode 100644 index 00000000000..f4ab6d425f1 --- /dev/null +++ b/build/pkgs/gp2c/distros/repology.txt @@ -0,0 +1 @@ +gp2c diff --git a/build/pkgs/graphs/distros/repology.txt b/build/pkgs/graphs/distros/repology.txt new file mode 100644 index 00000000000..7d793f63f0b --- /dev/null +++ b/build/pkgs/graphs/distros/repology.txt @@ -0,0 +1 @@ +sagemath-graphs diff --git a/build/pkgs/graphviz/SPKG.rst b/build/pkgs/graphviz/SPKG.rst new file mode 100644 index 00000000000..e897b8cbddd --- /dev/null +++ b/build/pkgs/graphviz/SPKG.rst @@ -0,0 +1,18 @@ +graphviz: Graph visualization software +====================================== + +Description +----------- + +Graphviz is open source graph visualization software. It has several main graph layout programs. +They take descriptions of graphs in a simple text language, and make diagrams in several useful formats. + +License +------- + +Eclipse Public License 1.0 + +Upstream Contact +---------------- + +https://graphviz.org/about/ diff --git a/build/pkgs/graphviz/distros/alpine.txt b/build/pkgs/graphviz/distros/alpine.txt new file mode 100644 index 00000000000..52aa6f38376 --- /dev/null +++ b/build/pkgs/graphviz/distros/alpine.txt @@ -0,0 +1 @@ +graphviz-dev diff --git a/build/pkgs/graphviz/distros/arch.txt b/build/pkgs/graphviz/distros/arch.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/arch.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/conda.txt b/build/pkgs/graphviz/distros/conda.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/conda.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/cygwin.txt b/build/pkgs/graphviz/distros/cygwin.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/cygwin.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/debian.txt b/build/pkgs/graphviz/distros/debian.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/debian.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/fedora.txt b/build/pkgs/graphviz/distros/fedora.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/fedora.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/freebsd.txt b/build/pkgs/graphviz/distros/freebsd.txt new file mode 100644 index 00000000000..f76a7c14a97 --- /dev/null +++ b/build/pkgs/graphviz/distros/freebsd.txt @@ -0,0 +1 @@ +graphics/graphviz diff --git a/build/pkgs/graphviz/distros/homebrew.txt b/build/pkgs/graphviz/distros/homebrew.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/homebrew.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/nix.txt b/build/pkgs/graphviz/distros/nix.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/nix.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/opensuse.txt b/build/pkgs/graphviz/distros/opensuse.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/opensuse.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/repology.txt b/build/pkgs/graphviz/distros/repology.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/graphviz/distros/repology.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/graphviz/distros/void.txt b/build/pkgs/graphviz/distros/void.txt new file mode 100644 index 00000000000..f137846bc26 --- /dev/null +++ b/build/pkgs/graphviz/distros/void.txt @@ -0,0 +1 @@ +graphviz-devel diff --git a/build/pkgs/graphviz/spkg-configure.m4 b/build/pkgs/graphviz/spkg-configure.m4 new file mode 100644 index 00000000000..5e673b1c617 --- /dev/null +++ b/build/pkgs/graphviz/spkg-configure.m4 @@ -0,0 +1,9 @@ +SAGE_SPKG_CONFIGURE([graphviz], [ + dnl We check all executables that are tested by sage.features.graphviz + AC_CHECK_PROGS([DOT], [dot]) + AS_IF([test x$DOT = x], [sage_spkg_install_graphviz=yes]) + AC_CHECK_PROGS([NEATO], [neato]) + AS_IF([test x$NEATO = x], [sage_spkg_install_graphviz=yes]) + AC_CHECK_PROGS([TWOPI], [twopi]) + AS_IF([test x$TWOPI = x], [sage_spkg_install_graphviz=yes]) +]) diff --git a/build/pkgs/graphviz/spkg-install b/build/pkgs/graphviz/spkg-install new file mode 100755 index 00000000000..aeb51456c5d --- /dev/null +++ b/build/pkgs/graphviz/spkg-install @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +echo Error: graphviz is not installed as a system package. +echo Please install the system package recommended by ./configure +exit 1 diff --git a/build/pkgs/termcap/type b/build/pkgs/graphviz/type similarity index 100% rename from build/pkgs/termcap/type rename to build/pkgs/graphviz/type diff --git a/build/pkgs/gsl/distros/repology.txt b/build/pkgs/gsl/distros/repology.txt new file mode 100644 index 00000000000..bd0d9198bf3 --- /dev/null +++ b/build/pkgs/gsl/distros/repology.txt @@ -0,0 +1 @@ +gsl diff --git a/build/pkgs/html5lib/distros/repology.txt b/build/pkgs/html5lib/distros/repology.txt new file mode 100644 index 00000000000..52080964da2 --- /dev/null +++ b/build/pkgs/html5lib/distros/repology.txt @@ -0,0 +1,2 @@ +html5lib +python:html5lib diff --git a/build/pkgs/iconv/distros/repology.txt b/build/pkgs/iconv/distros/repology.txt new file mode 100644 index 00000000000..788f2359abe --- /dev/null +++ b/build/pkgs/iconv/distros/repology.txt @@ -0,0 +1 @@ +libiconv diff --git a/build/pkgs/igraph/distros/repology.txt b/build/pkgs/igraph/distros/repology.txt new file mode 100644 index 00000000000..4e17d64ff49 --- /dev/null +++ b/build/pkgs/igraph/distros/repology.txt @@ -0,0 +1 @@ +igraph diff --git a/build/pkgs/imagesize/distros/repology.txt b/build/pkgs/imagesize/distros/repology.txt new file mode 100644 index 00000000000..703401f0c89 --- /dev/null +++ b/build/pkgs/imagesize/distros/repology.txt @@ -0,0 +1 @@ +python:imagesize diff --git a/build/pkgs/iml/distros/repology.txt b/build/pkgs/iml/distros/repology.txt new file mode 100644 index 00000000000..c1773871ebc --- /dev/null +++ b/build/pkgs/iml/distros/repology.txt @@ -0,0 +1 @@ +iml diff --git a/build/pkgs/importlib_metadata/distros/repology.txt b/build/pkgs/importlib_metadata/distros/repology.txt new file mode 100644 index 00000000000..8094f89dabb --- /dev/null +++ b/build/pkgs/importlib_metadata/distros/repology.txt @@ -0,0 +1 @@ +python:importlib-metadata diff --git a/build/pkgs/ipykernel/distros/repology.txt b/build/pkgs/ipykernel/distros/repology.txt new file mode 100644 index 00000000000..43e7baed6f9 --- /dev/null +++ b/build/pkgs/ipykernel/distros/repology.txt @@ -0,0 +1 @@ +python:ipykernel diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index 2979434cf9b..045ed1cd0ee 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,5 +1,5 @@ tarball=ipython-VERSION.tar.gz -sha1=8e2751e3365bbfd97277760b110fb2f290e0d3d3 -md5=de97012f2496dfcc6f005b68ca0eda4a -cksum=1236566613 +sha1=a4e6d466eac2703dfcfa541e375d349c2033776c +md5=bfa1f54283f34d3004a32cb04a29fdbd +cksum=2991277678 upstream_url=https://pypi.io/packages/source/i/ipython/ipython-VERSION.tar.gz diff --git a/build/pkgs/ipython/distros/repology.txt b/build/pkgs/ipython/distros/repology.txt new file mode 100644 index 00000000000..49a7ffe2a95 --- /dev/null +++ b/build/pkgs/ipython/distros/repology.txt @@ -0,0 +1 @@ +ipython diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index eb1dc6a51a0..0973ae3f415 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -7.13.0 +7.16.1 diff --git a/build/pkgs/ipython_genutils/distros/repology.txt b/build/pkgs/ipython_genutils/distros/repology.txt new file mode 100644 index 00000000000..1ff4b81d8ec --- /dev/null +++ b/build/pkgs/ipython_genutils/distros/repology.txt @@ -0,0 +1 @@ +python:ipython-genutils diff --git a/build/pkgs/ipywidgets/distros/repology.txt b/build/pkgs/ipywidgets/distros/repology.txt new file mode 100644 index 00000000000..11732fd922d --- /dev/null +++ b/build/pkgs/ipywidgets/distros/repology.txt @@ -0,0 +1 @@ +python:ipywidgets diff --git a/build/pkgs/isl/distros/repology.txt b/build/pkgs/isl/distros/repology.txt new file mode 100644 index 00000000000..ca114727532 --- /dev/null +++ b/build/pkgs/isl/distros/repology.txt @@ -0,0 +1 @@ +isl diff --git a/build/pkgs/jedi/checksums.ini b/build/pkgs/jedi/checksums.ini index 0aa980641c6..250e2bacbd6 100644 --- a/build/pkgs/jedi/checksums.ini +++ b/build/pkgs/jedi/checksums.ini @@ -1,5 +1,5 @@ tarball=jedi-VERSION.tar.gz -sha1=87408742e4bc7a0cea9512757388ed58587c3956 -md5=d6a8e5832939c51dceda474b720696f6 -cksum=783840182 +sha1=882249ea674ef7d500ddbd694d9bcbf0475f7f54 +md5=f012668907d76cebe9c4766f3b806fcf +cksum=2221704486 upstream_url=https://pypi.io/packages/source/j/jedi/jedi-VERSION.tar.gz diff --git a/build/pkgs/jedi/distros/repology.txt b/build/pkgs/jedi/distros/repology.txt new file mode 100644 index 00000000000..bc72c30dfd6 --- /dev/null +++ b/build/pkgs/jedi/distros/repology.txt @@ -0,0 +1,2 @@ +jedi +python:jedi \ No newline at end of file diff --git a/build/pkgs/jedi/package-version.txt b/build/pkgs/jedi/package-version.txt index c5523bd09b1..c3d16c1646b 100644 --- a/build/pkgs/jedi/package-version.txt +++ b/build/pkgs/jedi/package-version.txt @@ -1 +1 @@ -0.17.0 +0.17.2 diff --git a/build/pkgs/jinja2/distros/repology.txt b/build/pkgs/jinja2/distros/repology.txt new file mode 100644 index 00000000000..0db4d3636e3 --- /dev/null +++ b/build/pkgs/jinja2/distros/repology.txt @@ -0,0 +1 @@ +python:jinja2 diff --git a/build/pkgs/jmol/distros/repology.txt b/build/pkgs/jmol/distros/repology.txt new file mode 100644 index 00000000000..f07a1f4e035 --- /dev/null +++ b/build/pkgs/jmol/distros/repology.txt @@ -0,0 +1 @@ +jmol diff --git a/build/pkgs/jsonschema/distros/repology.txt b/build/pkgs/jsonschema/distros/repology.txt new file mode 100644 index 00000000000..5b3b9df9c12 --- /dev/null +++ b/build/pkgs/jsonschema/distros/repology.txt @@ -0,0 +1 @@ +python:jsonschema diff --git a/build/pkgs/jupymake/distros/repology.txt b/build/pkgs/jupymake/distros/repology.txt new file mode 100644 index 00000000000..7642fd127d3 --- /dev/null +++ b/build/pkgs/jupymake/distros/repology.txt @@ -0,0 +1,2 @@ +jupymake +python:jupymake diff --git a/build/pkgs/jupyter_client/distros/repology.txt b/build/pkgs/jupyter_client/distros/repology.txt new file mode 100644 index 00000000000..ca78cc1c855 --- /dev/null +++ b/build/pkgs/jupyter_client/distros/repology.txt @@ -0,0 +1,2 @@ +jupyter-client +python:jupyter-client diff --git a/build/pkgs/jupyter_core/distros/repology.txt b/build/pkgs/jupyter_core/distros/repology.txt new file mode 100644 index 00000000000..f4febf3574f --- /dev/null +++ b/build/pkgs/jupyter_core/distros/repology.txt @@ -0,0 +1,2 @@ +jupyter-core +python:jupyter-core diff --git a/build/pkgs/jupyter_jsmol/distros/repology.txt b/build/pkgs/jupyter_jsmol/distros/repology.txt new file mode 100644 index 00000000000..fe7400f9de7 --- /dev/null +++ b/build/pkgs/jupyter_jsmol/distros/repology.txt @@ -0,0 +1,2 @@ +jupyter-jsmol +python:jupyter-jsmol diff --git a/build/pkgs/jupyterlab/distros/repology.txt b/build/pkgs/jupyterlab/distros/repology.txt new file mode 100644 index 00000000000..c9356a72837 --- /dev/null +++ b/build/pkgs/jupyterlab/distros/repology.txt @@ -0,0 +1 @@ +jupyterlab diff --git a/build/pkgs/jupyterlab_widgets/distros/repology.txt b/build/pkgs/jupyterlab_widgets/distros/repology.txt new file mode 100644 index 00000000000..a635e31b8fc --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/distros/repology.txt @@ -0,0 +1 @@ +jupyterlab-widgets \ No newline at end of file diff --git a/build/pkgs/kiwisolver/distros/repology.txt b/build/pkgs/kiwisolver/distros/repology.txt new file mode 100644 index 00000000000..7b3e2f4f438 --- /dev/null +++ b/build/pkgs/kiwisolver/distros/repology.txt @@ -0,0 +1 @@ +python:kiwisolver diff --git a/build/pkgs/latte_int/distros/repology.txt b/build/pkgs/latte_int/distros/repology.txt new file mode 100644 index 00000000000..2c444214ec4 --- /dev/null +++ b/build/pkgs/latte_int/distros/repology.txt @@ -0,0 +1 @@ +latte-integrale diff --git a/build/pkgs/lcalc/distros/repology.txt b/build/pkgs/lcalc/distros/repology.txt new file mode 100644 index 00000000000..421591fc62d --- /dev/null +++ b/build/pkgs/lcalc/distros/repology.txt @@ -0,0 +1 @@ +lcalc diff --git a/build/pkgs/libatomic_ops/distros/repology.txt b/build/pkgs/libatomic_ops/distros/repology.txt new file mode 100644 index 00000000000..46bd44d2589 --- /dev/null +++ b/build/pkgs/libatomic_ops/distros/repology.txt @@ -0,0 +1 @@ +libatomic-ops diff --git a/build/pkgs/libbraiding/checksums.ini b/build/pkgs/libbraiding/checksums.ini index 9e726e3a7fb..bb76fa5b604 100644 --- a/build/pkgs/libbraiding/checksums.ini +++ b/build/pkgs/libbraiding/checksums.ini @@ -1,4 +1,5 @@ tarball=libbraiding-VERSION.tar.gz -sha1=849d146abc03f0773a74e41351711b56b6bfc5d3 -md5=969f2f1f412c60e8ff1ea107f00a25b1 -cksum=357804794 +sha1=06610e47eb243b27aea0ad399b41614fcdb179c9 +md5=5466605026b90bdca7ca20852f88b5c5 +cksum=704753563 +upstream_url=https://github.com/miguelmarco/libbraiding/releases/download/1.1/libbraiding-1.1.tar.gz diff --git a/build/pkgs/libbraiding/distros/repology.txt b/build/pkgs/libbraiding/distros/repology.txt new file mode 100644 index 00000000000..3767599b368 --- /dev/null +++ b/build/pkgs/libbraiding/distros/repology.txt @@ -0,0 +1 @@ +libbraiding diff --git a/build/pkgs/libbraiding/package-version.txt b/build/pkgs/libbraiding/package-version.txt index f9c24f5d4ec..9459d4ba2a0 100644 --- a/build/pkgs/libbraiding/package-version.txt +++ b/build/pkgs/libbraiding/package-version.txt @@ -1 +1 @@ -1.0.p0 +1.1 diff --git a/build/pkgs/libffi/distros/repology.txt b/build/pkgs/libffi/distros/repology.txt new file mode 100644 index 00000000000..eb88b305fdc --- /dev/null +++ b/build/pkgs/libffi/distros/repology.txt @@ -0,0 +1 @@ +libffi diff --git a/build/pkgs/libgd/distros/repology.txt b/build/pkgs/libgd/distros/repology.txt new file mode 100644 index 00000000000..3f310cfdeb0 --- /dev/null +++ b/build/pkgs/libgd/distros/repology.txt @@ -0,0 +1 @@ +gd diff --git a/build/pkgs/libhomfly/distros/repology.txt b/build/pkgs/libhomfly/distros/repology.txt new file mode 100644 index 00000000000..f13362db8cb --- /dev/null +++ b/build/pkgs/libhomfly/distros/repology.txt @@ -0,0 +1,2 @@ +libhomfly +llibhomfly diff --git a/build/pkgs/libnauty/distros/repology.txt b/build/pkgs/libnauty/distros/repology.txt new file mode 100644 index 00000000000..21c67b1e856 --- /dev/null +++ b/build/pkgs/libnauty/distros/repology.txt @@ -0,0 +1 @@ +nauty diff --git a/build/pkgs/libogg/distros/repology.txt b/build/pkgs/libogg/distros/repology.txt new file mode 100644 index 00000000000..b6a6854a477 --- /dev/null +++ b/build/pkgs/libogg/distros/repology.txt @@ -0,0 +1 @@ +libogg diff --git a/build/pkgs/libpng/distros/repology.txt b/build/pkgs/libpng/distros/repology.txt new file mode 100644 index 00000000000..30c33ac62a1 --- /dev/null +++ b/build/pkgs/libpng/distros/repology.txt @@ -0,0 +1 @@ +libpng diff --git a/build/pkgs/libsemigroups/distros/repology.txt b/build/pkgs/libsemigroups/distros/repology.txt new file mode 100644 index 00000000000..34c9d7b610d --- /dev/null +++ b/build/pkgs/libsemigroups/distros/repology.txt @@ -0,0 +1 @@ +libsemigroups diff --git a/build/pkgs/libtheora/distros/repology.txt b/build/pkgs/libtheora/distros/repology.txt new file mode 100644 index 00000000000..944587f5d50 --- /dev/null +++ b/build/pkgs/libtheora/distros/repology.txt @@ -0,0 +1 @@ +libtheora diff --git a/build/pkgs/libxml2/distros/repology.txt b/build/pkgs/libxml2/distros/repology.txt new file mode 100644 index 00000000000..eb89cd5895d --- /dev/null +++ b/build/pkgs/libxml2/distros/repology.txt @@ -0,0 +1 @@ +libxml2 diff --git a/build/pkgs/lidia/distros/repology.txt b/build/pkgs/lidia/distros/repology.txt new file mode 100644 index 00000000000..0a7fc432b24 --- /dev/null +++ b/build/pkgs/lidia/distros/repology.txt @@ -0,0 +1 @@ +lidia diff --git a/build/pkgs/lie/distros/repology.txt b/build/pkgs/lie/distros/repology.txt new file mode 100644 index 00000000000..22c15b0a6cb --- /dev/null +++ b/build/pkgs/lie/distros/repology.txt @@ -0,0 +1 @@ +lie diff --git a/build/pkgs/linbox/distros/repology.txt b/build/pkgs/linbox/distros/repology.txt new file mode 100644 index 00000000000..891a35cb224 --- /dev/null +++ b/build/pkgs/linbox/distros/repology.txt @@ -0,0 +1 @@ +linbox diff --git a/build/pkgs/lrcalc/distros/repology.txt b/build/pkgs/lrcalc/distros/repology.txt new file mode 100644 index 00000000000..b854d5c73dd --- /dev/null +++ b/build/pkgs/lrcalc/distros/repology.txt @@ -0,0 +1 @@ +lrcalc diff --git a/build/pkgs/lrslib/distros/repology.txt b/build/pkgs/lrslib/distros/repology.txt new file mode 100644 index 00000000000..c762c018aa3 --- /dev/null +++ b/build/pkgs/lrslib/distros/repology.txt @@ -0,0 +1 @@ +lrslib diff --git a/build/pkgs/m4ri/distros/repology.txt b/build/pkgs/m4ri/distros/repology.txt new file mode 100644 index 00000000000..b6b04790806 --- /dev/null +++ b/build/pkgs/m4ri/distros/repology.txt @@ -0,0 +1 @@ +libm4ri diff --git a/build/pkgs/m4rie/distros/repology.txt b/build/pkgs/m4rie/distros/repology.txt new file mode 100644 index 00000000000..4d38322c367 --- /dev/null +++ b/build/pkgs/m4rie/distros/repology.txt @@ -0,0 +1 @@ +libm4rie diff --git a/build/pkgs/markupsafe/distros/repology.txt b/build/pkgs/markupsafe/distros/repology.txt new file mode 100644 index 00000000000..5e8e65f6c40 --- /dev/null +++ b/build/pkgs/markupsafe/distros/repology.txt @@ -0,0 +1 @@ +python:markupsafe diff --git a/build/pkgs/mathjax/distros/repology.txt b/build/pkgs/mathjax/distros/repology.txt new file mode 100644 index 00000000000..37aaaac759c --- /dev/null +++ b/build/pkgs/mathjax/distros/repology.txt @@ -0,0 +1 @@ +mathjax diff --git a/build/pkgs/matplotlib/distros/repology.txt b/build/pkgs/matplotlib/distros/repology.txt new file mode 100644 index 00000000000..5cca573a065 --- /dev/null +++ b/build/pkgs/matplotlib/distros/repology.txt @@ -0,0 +1 @@ +python:matplotlib diff --git a/build/pkgs/maxima/distros/repology.txt b/build/pkgs/maxima/distros/repology.txt new file mode 100644 index 00000000000..02bb5d767c4 --- /dev/null +++ b/build/pkgs/maxima/distros/repology.txt @@ -0,0 +1,3 @@ +maxima +maxima-ecl +maxima-sage diff --git a/build/pkgs/mcqd/distros/repology.txt b/build/pkgs/mcqd/distros/repology.txt new file mode 100644 index 00000000000..36c7102c81a --- /dev/null +++ b/build/pkgs/mcqd/distros/repology.txt @@ -0,0 +1 @@ +mcqd diff --git a/build/pkgs/meataxe/checksums.ini b/build/pkgs/meataxe/checksums.ini index 24d43b09467..ada4cb16f11 100644 --- a/build/pkgs/meataxe/checksums.ini +++ b/build/pkgs/meataxe/checksums.ini @@ -1,4 +1,5 @@ -tarball=shared_meataxe-VERSION.tar.gz -sha1=1a84840fb928ca2684f16dc630c854ce579b0c2e -md5=5e1aab41b19c9fec967d02ead5077eca -cksum=2618257012 +tarball=shared_meataxe-VERSION.tar.bz2 +sha1=6764f72fab8b4472660cff6605087356ab91d3b7 +md5=c9af9efa686e120612455778d64a71ca +cksum=1648865875 +upstream_url=https://github.com/simon-king-jena/SharedMeatAxe/releases/download/vVERSION/shared_meataxe-VERSION.tar.bz2 diff --git a/build/pkgs/meataxe/distros/fedora.txt b/build/pkgs/meataxe/distros/fedora.txt new file mode 100644 index 00000000000..532525e3d04 --- /dev/null +++ b/build/pkgs/meataxe/distros/fedora.txt @@ -0,0 +1,2 @@ +# Old Fedora package meataxe was replaced by sharedmeataxe +sharedmeataxe diff --git a/build/pkgs/meataxe/distros/repology.txt b/build/pkgs/meataxe/distros/repology.txt new file mode 100644 index 00000000000..d0e7634fd4f --- /dev/null +++ b/build/pkgs/meataxe/distros/repology.txt @@ -0,0 +1,2 @@ +shared-meataxe +sharedmeataxe diff --git a/build/pkgs/meataxe/package-version.txt b/build/pkgs/meataxe/package-version.txt index f9c24f5d4ec..7dea76edb3d 100644 --- a/build/pkgs/meataxe/package-version.txt +++ b/build/pkgs/meataxe/package-version.txt @@ -1 +1 @@ -1.0.p0 +1.0.1 diff --git a/build/pkgs/mistune/distros/repology.txt b/build/pkgs/mistune/distros/repology.txt new file mode 100644 index 00000000000..b63466dc48c --- /dev/null +++ b/build/pkgs/mistune/distros/repology.txt @@ -0,0 +1,2 @@ +mistune +python:mistune diff --git a/build/pkgs/modular_decomposition/distros/repology.txt b/build/pkgs/modular_decomposition/distros/repology.txt new file mode 100644 index 00000000000..63136a9369a --- /dev/null +++ b/build/pkgs/modular_decomposition/distros/repology.txt @@ -0,0 +1 @@ +modular-decomposition \ No newline at end of file diff --git a/build/pkgs/mpc/distros/repology.txt b/build/pkgs/mpc/distros/repology.txt new file mode 100644 index 00000000000..098b049316b --- /dev/null +++ b/build/pkgs/mpc/distros/repology.txt @@ -0,0 +1 @@ +libmpc diff --git a/build/pkgs/mpfi/distros/repology.txt b/build/pkgs/mpfi/distros/repology.txt new file mode 100644 index 00000000000..0508439baac --- /dev/null +++ b/build/pkgs/mpfi/distros/repology.txt @@ -0,0 +1 @@ +mpfi diff --git a/build/pkgs/mpfr/distros/repology.txt b/build/pkgs/mpfr/distros/repology.txt new file mode 100644 index 00000000000..5bcf2cdfb19 --- /dev/null +++ b/build/pkgs/mpfr/distros/repology.txt @@ -0,0 +1 @@ +mpfr diff --git a/build/pkgs/mpfrcx/distros/repology.txt b/build/pkgs/mpfrcx/distros/repology.txt new file mode 100644 index 00000000000..b9e80b872fc --- /dev/null +++ b/build/pkgs/mpfrcx/distros/repology.txt @@ -0,0 +1 @@ +mpfrcx diff --git a/build/pkgs/mpir/distros/repology.txt b/build/pkgs/mpir/distros/repology.txt new file mode 100644 index 00000000000..bef94785573 --- /dev/null +++ b/build/pkgs/mpir/distros/repology.txt @@ -0,0 +1 @@ +mpir diff --git a/build/pkgs/mpmath/distros/repology.txt b/build/pkgs/mpmath/distros/repology.txt new file mode 100644 index 00000000000..f71d48c841f --- /dev/null +++ b/build/pkgs/mpmath/distros/repology.txt @@ -0,0 +1,2 @@ +mpmath +python:mpmath diff --git a/build/pkgs/nauty/distros/repology.txt b/build/pkgs/nauty/distros/repology.txt new file mode 100644 index 00000000000..21c67b1e856 --- /dev/null +++ b/build/pkgs/nauty/distros/repology.txt @@ -0,0 +1 @@ +nauty diff --git a/build/pkgs/nbconvert/distros/repology.txt b/build/pkgs/nbconvert/distros/repology.txt new file mode 100644 index 00000000000..acf649a7200 --- /dev/null +++ b/build/pkgs/nbconvert/distros/repology.txt @@ -0,0 +1,4 @@ +nbconvert +python:nbconvert +jupyter-nbconvert +python:jupyter-nbconvert diff --git a/build/pkgs/nbformat/distros/repology.txt b/build/pkgs/nbformat/distros/repology.txt new file mode 100644 index 00000000000..513d8a8e60e --- /dev/null +++ b/build/pkgs/nbformat/distros/repology.txt @@ -0,0 +1,4 @@ +nbformat +python:nbformat +jupyter-nbformat +python:jupyter-nbformat diff --git a/build/pkgs/ncurses/distros/repology.txt b/build/pkgs/ncurses/distros/repology.txt new file mode 100644 index 00000000000..6a470ffa9e3 --- /dev/null +++ b/build/pkgs/ncurses/distros/repology.txt @@ -0,0 +1 @@ +ncurses diff --git a/build/pkgs/networkx/distros/repology.txt b/build/pkgs/networkx/distros/repology.txt new file mode 100644 index 00000000000..1e54a2446b2 --- /dev/null +++ b/build/pkgs/networkx/distros/repology.txt @@ -0,0 +1 @@ +python:networkx diff --git a/build/pkgs/nibabel/distros/repology.txt b/build/pkgs/nibabel/distros/repology.txt new file mode 100644 index 00000000000..7722709aae5 --- /dev/null +++ b/build/pkgs/nibabel/distros/repology.txt @@ -0,0 +1,2 @@ +nibabel +python:nibabel diff --git a/build/pkgs/ninja_build/distros/repology.txt b/build/pkgs/ninja_build/distros/repology.txt new file mode 100644 index 00000000000..63730036fd3 --- /dev/null +++ b/build/pkgs/ninja_build/distros/repology.txt @@ -0,0 +1 @@ +ninja diff --git a/build/pkgs/nodeenv/distros/repology.txt b/build/pkgs/nodeenv/distros/repology.txt new file mode 100644 index 00000000000..a3202e13f17 --- /dev/null +++ b/build/pkgs/nodeenv/distros/repology.txt @@ -0,0 +1,2 @@ +nodeenv +python:nodeenv diff --git a/build/pkgs/nodejs/distros/repology.txt b/build/pkgs/nodejs/distros/repology.txt new file mode 100644 index 00000000000..e36de65c4cc --- /dev/null +++ b/build/pkgs/nodejs/distros/repology.txt @@ -0,0 +1 @@ +nodejs diff --git a/build/pkgs/normaliz/distros/repology.txt b/build/pkgs/normaliz/distros/repology.txt new file mode 100644 index 00000000000..e0851491edb --- /dev/null +++ b/build/pkgs/normaliz/distros/repology.txt @@ -0,0 +1,2 @@ +normaliz +libnormaliz diff --git a/build/pkgs/nose/distros/repology.txt b/build/pkgs/nose/distros/repology.txt new file mode 100644 index 00000000000..f3c7e8e6ffb --- /dev/null +++ b/build/pkgs/nose/distros/repology.txt @@ -0,0 +1 @@ +nose diff --git a/build/pkgs/notebook/dependencies b/build/pkgs/notebook/dependencies index 76a05899142..ba4f335e19a 100644 --- a/build/pkgs/notebook/dependencies +++ b/build/pkgs/notebook/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado send2trash prometheus_client +$(PYTHON) | $(PYTHON_TOOLCHAIN) ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado send2trash prometheus_client argon2_cffi ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/notebook/distros/repology.txt b/build/pkgs/notebook/distros/repology.txt new file mode 100644 index 00000000000..86de8605518 --- /dev/null +++ b/build/pkgs/notebook/distros/repology.txt @@ -0,0 +1 @@ +python:notebook diff --git a/build/pkgs/notedown/distros/repology.txt b/build/pkgs/notedown/distros/repology.txt new file mode 100644 index 00000000000..e50016c4d8e --- /dev/null +++ b/build/pkgs/notedown/distros/repology.txt @@ -0,0 +1 @@ +python:notedown diff --git a/build/pkgs/ntl/distros/repology.txt b/build/pkgs/ntl/distros/repology.txt new file mode 100644 index 00000000000..9f4d4f8fdb6 --- /dev/null +++ b/build/pkgs/ntl/distros/repology.txt @@ -0,0 +1 @@ +ntl diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index fbf142a3e94..5f153f19600 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -49,6 +49,12 @@ SAGE_SPKG_CONFIGURE([ntl], [ AC_SUBST(SAGE_NTL_PREFIX, ['$SAGE_LOCAL']) else AC_SUBST(SAGE_NTL_PREFIX, ['']) + AX_ABSOLUTE_HEADER([NTL/ZZ.h]) + ntl_inc_ntl_dir=`AS_DIRNAME(["$gl_cv_absolute_NTL_ZZ_h"])` + ntl_inc_dir=`AS_DIRNAME(["$ntl_inc_ntl_dir"])` + ntl_prefix=`AS_DIRNAME(["$ntl_inc_dir"])` + AC_SUBST(NTL_INCDIR, [$ntl_inc_dir]) + AC_SUBST(NTL_LIBDIR, [$ntl_prefix/lib]) fi ]) diff --git a/build/pkgs/numpy/distros/repology.txt b/build/pkgs/numpy/distros/repology.txt new file mode 100644 index 00000000000..ef13028c43c --- /dev/null +++ b/build/pkgs/numpy/distros/repology.txt @@ -0,0 +1 @@ +python:numpy diff --git a/build/pkgs/openblas/distros/repology.txt b/build/pkgs/openblas/distros/repology.txt new file mode 100644 index 00000000000..dcc5d064110 --- /dev/null +++ b/build/pkgs/openblas/distros/repology.txt @@ -0,0 +1 @@ +openblas diff --git a/build/pkgs/openssl/SPKG.rst b/build/pkgs/openssl/SPKG.rst index 730e57940b3..7aea563b9da 100644 --- a/build/pkgs/openssl/SPKG.rst +++ b/build/pkgs/openssl/SPKG.rst @@ -13,16 +13,10 @@ variety of computer languages are available. License ------- -- Custom GPL-incompatible license +- Apache License v2 (considered compatible with GPL v3) Upstream Contact ---------------- - http://openssl.org/ -- http://openssl.org/support/community.html - -Patches -~~~~~~~ - -- src/config: patched to fix a problem on Solaris. diff --git a/build/pkgs/openssl/checksums.ini b/build/pkgs/openssl/checksums.ini index 6b0c42fb18f..9ec6926de2a 100644 --- a/build/pkgs/openssl/checksums.ini +++ b/build/pkgs/openssl/checksums.ini @@ -1,5 +1,5 @@ tarball=openssl-VERSION.tar.gz -sha1=eb684ba4ed31fe2c48062aead75233ecd36882a6 -md5=08987c3cf125202e2b0840035efb392c -cksum=815486537 +sha1=fbcb255c1bf11928f4bd52b8cf68ab8341238d4f +md5=26581d6856356698414d71f50ece668d +cksum=1374793308 upstream_url=https://www.openssl.org/source/openssl-VERSION.tar.gz diff --git a/build/pkgs/openssl/distros/repology.txt b/build/pkgs/openssl/distros/repology.txt new file mode 100644 index 00000000000..fa963ae15cb --- /dev/null +++ b/build/pkgs/openssl/distros/repology.txt @@ -0,0 +1 @@ +openssl diff --git a/build/pkgs/openssl/package-version.txt b/build/pkgs/openssl/package-version.txt index 46f67be97c7..28ed36c8a75 100644 --- a/build/pkgs/openssl/package-version.txt +++ b/build/pkgs/openssl/package-version.txt @@ -1 +1 @@ -1.1.1i +3.0.0-alpha12 diff --git a/build/pkgs/openssl/patches/config.patch b/build/pkgs/openssl/patches/config.patch deleted file mode 100644 index 4cb5be0a2da..00000000000 --- a/build/pkgs/openssl/patches/config.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -ru src/config b/config ---- src/config 2019-02-26 14:15:30.000000000 +0000 -+++ b/config 2019-03-05 10:53:04.281408906 +0000 -@@ -371,8 +371,10 @@ - # this is where the translation occurs into SSLeay terms - # --------------------------------------------------------------------------- - --# Only set CC if not supplied already --if [ -z "$CROSS_COMPILE$CC" ]; then -+# Save given CC -+userCC="$CC" -+ -+if true; then - GCCVER=`sh -c "gcc -dumpversion" 2>/dev/null` - if [ "$GCCVER" != "" ]; then - # then strip off whatever prefix egcs prepends the number with... -@@ -433,6 +435,11 @@ - - CCVER=${CCVER:-0} - -+# Restore given $CC -+if [ -n "$userCC" ]; then -+ CC="$userCC" -+fi -+ - # read the output of the embedded GuessOS - read GUESSOS - diff --git a/build/pkgs/openssl/spkg-configure.m4 b/build/pkgs/openssl/spkg-configure.m4 index e6554422398..76158601461 100644 --- a/build/pkgs/openssl/spkg-configure.m4 +++ b/build/pkgs/openssl/spkg-configure.m4 @@ -1,3 +1,11 @@ SAGE_SPKG_CONFIGURE([openssl], [ - AX_CHECK_OPENSSL([], [sage_spkg_install_openssl=yes]) + AX_CHECK_OPENSSL([], [ + sage_spkg_install_openssl=yes + AC_MSG_WARN([Because your system does not have a suitable OpenSSL library, +Sage will install a prerelease version of OpenSSL from the 3.0 alpha series. +The OpenSSL Project Team indicates that this prerelease version has been provided +for testing ONLY. It should NOT be used for security critical purposes. +We strongly recommend to install OpenSSL using the system package manager and +to re-run configure.]) + ]) ]) diff --git a/build/pkgs/openssl/spkg-install.in b/build/pkgs/openssl/spkg-install.in index 6e3712a168d..328833ac5b7 100644 --- a/build/pkgs/openssl/spkg-install.in +++ b/build/pkgs/openssl/spkg-install.in @@ -15,27 +15,8 @@ unset BUILD # Building on OS X Lion 64-bit seems to fail unless we build a 64-bit library. echo echo "Configuring openssl..." -if [ "$UNAME" = "Darwin" ]; then - # This check for 64-bit is taken from src/config. - ISA64=`(sysctl -n hw.optional.x86_64) 2>/dev/null` - if [ "$ISA64" = "1" ]; then - # 64-bit - ./Configure darwin64-x86_64-cc --libdir=lib --prefix="$SAGE_LOCAL" --openssldir="$SAGE_LOCAL"/openssl shared - else - ./config --libdir=lib --prefix="$SAGE_LOCAL" --openssldir="$SAGE_LOCAL"/openssl shared - fi -else - ./config --libdir=lib --prefix="$SAGE_LOCAL" --openssldir="$SAGE_LOCAL"/openssl shared -fi - -if [ $? -ne 0 ]; then - echo >&2 "Error configuring openssl." - exit 1 -fi -echo +./config --prefix="$SAGE_LOCAL" --openssldir="$SAGE_LOCAL"/openssl shared -# Do not build in parallel. -MAKE="$MAKE -j1" echo "Building openssl..." $MAKE @@ -54,11 +35,3 @@ if [ $? -ne 0 ]; then echo >&2 "Error installing openssl." exit 1 fi - - -# Openssl installs itself readonly, which will break attempts to -# rewrite library paths later -chmod u+w \ - "$SAGE_LOCAL"/lib/engines-1.1/* \ - "$SAGE_LOCAL"/lib/libssl* \ - "$SAGE_LOCAL"/lib/libcrypto* diff --git a/build/pkgs/openssl/type b/build/pkgs/openssl/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/openssl/type +++ b/build/pkgs/openssl/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/p_group_cohomology/distros/repology.txt b/build/pkgs/p_group_cohomology/distros/repology.txt new file mode 100644 index 00000000000..c6ea8c5c26c --- /dev/null +++ b/build/pkgs/p_group_cohomology/distros/repology.txt @@ -0,0 +1 @@ +sagemath-p-group-cohomology diff --git a/build/pkgs/packaging/distros/repology.txt b/build/pkgs/packaging/distros/repology.txt new file mode 100644 index 00000000000..ea45b3d6e70 --- /dev/null +++ b/build/pkgs/packaging/distros/repology.txt @@ -0,0 +1,2 @@ +packaging +python:packaging diff --git a/build/pkgs/palp/distros/repology.txt b/build/pkgs/palp/distros/repology.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/repology.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/pandoc/distros/repology.txt b/build/pkgs/pandoc/distros/repology.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/repology.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc_attributes/distros/repology.txt b/build/pkgs/pandoc_attributes/distros/repology.txt new file mode 100644 index 00000000000..d46a120acff --- /dev/null +++ b/build/pkgs/pandoc_attributes/distros/repology.txt @@ -0,0 +1,2 @@ +pandoc-attributes +python:pandoc-attributes diff --git a/build/pkgs/pandocfilters/distros/repology.txt b/build/pkgs/pandocfilters/distros/repology.txt new file mode 100644 index 00000000000..06110c19b2a --- /dev/null +++ b/build/pkgs/pandocfilters/distros/repology.txt @@ -0,0 +1 @@ +python:pandocfilters diff --git a/build/pkgs/pari/distros/repology.txt b/build/pkgs/pari/distros/repology.txt new file mode 100644 index 00000000000..a069ef1e5dc --- /dev/null +++ b/build/pkgs/pari/distros/repology.txt @@ -0,0 +1,9 @@ +pari +pari-gp +pari-data +pari-elldata +pari-galdata +pari-galpol +pari-nftables +pari-seadata +pari-seadata-big diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index a07f650f820..25cce6b3556 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.11.4.p0 +2.11.4.p1 diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index de2c6089517..9a1b0c7db8c 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -134,9 +134,18 @@ SAGE_SPKG_CONFIGURE([pari], [ [t=strcmp(paricfg_datadir,$gp_datadir);] [pari_close()]; [return t;]])], - [AC_MSG_RESULT([libpari's and GP's datadirs match. Good])], - [AC_MSG_RESULT([libpari's datadir does not match GP's datadir. Not good]) - sage_spkg_install_pari=yes]) + [AC_MSG_RESULT([libpari's and GP's datadirs match. Good]) + AC_MSG_CHECKING([whether pari is configured with pthreads]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([ + [#include + #include ]], + [[return strcmp(PARI_MT_ENGINE, "pthread") != 0]])], + [AC_MSG_RESULT([yes. Good])], + [AC_MSG_RESULT([no. Not good]) + sage_spkg_install_pari=yes]) + ], + [AC_MSG_RESULT([libpari's datadir does not match GP's datadir. Not good]) + sage_spkg_install_pari=yes]) ], [ AC_MSG_RESULT([no]) sage_spkg_install_pari=yes]) diff --git a/build/pkgs/pari/spkg-install.in b/build/pkgs/pari/spkg-install.in index 4e6861efcaf..2da3fa54e4e 100644 --- a/build/pkgs/pari/spkg-install.in +++ b/build/pkgs/pari/spkg-install.in @@ -135,6 +135,7 @@ fi bash ./Configure --prefix="$SAGE_LOCAL" \ --with-readline="$SAGE_LOCAL" $SAGE_CONFIGURE_GMP \ --with-runtime-perl="/usr/bin/env perl" \ + --mt=pthread \ --kernel=gmp $PARI_CONFIGURE || \ sdh_die "Error configuring PARI with readline and GMP kernel failed." diff --git a/build/pkgs/pari_elldata/distros/repology.txt b/build/pkgs/pari_elldata/distros/repology.txt new file mode 100644 index 00000000000..540f0b1ab86 --- /dev/null +++ b/build/pkgs/pari_elldata/distros/repology.txt @@ -0,0 +1 @@ +pari-elldata diff --git a/build/pkgs/pari_galdata/distros/repology.txt b/build/pkgs/pari_galdata/distros/repology.txt new file mode 100644 index 00000000000..38ad64bac82 --- /dev/null +++ b/build/pkgs/pari_galdata/distros/repology.txt @@ -0,0 +1 @@ +pari-galdata diff --git a/build/pkgs/pari_galpol/distros/repology.txt b/build/pkgs/pari_galpol/distros/repology.txt new file mode 100644 index 00000000000..e3b41380f41 --- /dev/null +++ b/build/pkgs/pari_galpol/distros/repology.txt @@ -0,0 +1 @@ +pari-galpol diff --git a/build/pkgs/pari_jupyter/distros/repology.txt b/build/pkgs/pari_jupyter/distros/repology.txt new file mode 100644 index 00000000000..ecc482ece6d --- /dev/null +++ b/build/pkgs/pari_jupyter/distros/repology.txt @@ -0,0 +1 @@ +pari-jupyter diff --git a/build/pkgs/pari_nftables/distros/repology.txt b/build/pkgs/pari_nftables/distros/repology.txt new file mode 100644 index 00000000000..a9f2eb4a474 --- /dev/null +++ b/build/pkgs/pari_nftables/distros/repology.txt @@ -0,0 +1 @@ +pari-nftables diff --git a/build/pkgs/pari_seadata/distros/repology.txt b/build/pkgs/pari_seadata/distros/repology.txt new file mode 100644 index 00000000000..fae71eb9078 --- /dev/null +++ b/build/pkgs/pari_seadata/distros/repology.txt @@ -0,0 +1,2 @@ +pari-seadata +pari-seadata-big diff --git a/build/pkgs/pari_seadata_small/distros/repology.txt b/build/pkgs/pari_seadata_small/distros/repology.txt new file mode 100644 index 00000000000..68d650832dc --- /dev/null +++ b/build/pkgs/pari_seadata_small/distros/repology.txt @@ -0,0 +1 @@ +pari-seadata-small diff --git a/build/pkgs/parso/distros/repology.txt b/build/pkgs/parso/distros/repology.txt new file mode 100644 index 00000000000..220a860af62 --- /dev/null +++ b/build/pkgs/parso/distros/repology.txt @@ -0,0 +1 @@ +python:parso diff --git a/build/pkgs/patch/distros/repology.txt b/build/pkgs/patch/distros/repology.txt new file mode 100644 index 00000000000..9eb7b90ed50 --- /dev/null +++ b/build/pkgs/patch/distros/repology.txt @@ -0,0 +1 @@ +patch diff --git a/build/pkgs/pcre/distros/repology.txt b/build/pkgs/pcre/distros/repology.txt new file mode 100644 index 00000000000..abd501ce241 --- /dev/null +++ b/build/pkgs/pcre/distros/repology.txt @@ -0,0 +1 @@ +pcre diff --git a/build/pkgs/perl_term_readline_gnu/distros/repology.txt b/build/pkgs/perl_term_readline_gnu/distros/repology.txt new file mode 100644 index 00000000000..d606d4dbb57 --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/repology.txt @@ -0,0 +1,2 @@ +perl:term-readline-gnu +perl:termreadline-gnu diff --git a/build/pkgs/pexpect/distros/repology.txt b/build/pkgs/pexpect/distros/repology.txt new file mode 100644 index 00000000000..07cc486b382 --- /dev/null +++ b/build/pkgs/pexpect/distros/repology.txt @@ -0,0 +1,2 @@ +pexpect +python:pexpect diff --git a/build/pkgs/pickleshare/distros/repology.txt b/build/pkgs/pickleshare/distros/repology.txt new file mode 100644 index 00000000000..373595719f2 --- /dev/null +++ b/build/pkgs/pickleshare/distros/repology.txt @@ -0,0 +1,2 @@ +pickleshare +python:pickleshare diff --git a/build/pkgs/pillow/distros/repology.txt b/build/pkgs/pillow/distros/repology.txt new file mode 100644 index 00000000000..d8df470a29e --- /dev/null +++ b/build/pkgs/pillow/distros/repology.txt @@ -0,0 +1 @@ +python:pillow diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index c252259a7ae..a89f768a51a 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=a8c85abfdc0bf15dd7360865f59e4234be00d1e2 -md5=3d5d0639042c829bba411d3735267546 -cksum=4219824548 +sha1=f74aa5460852ab99f4433212af87949168a8181c +md5=246523bd34dd356e7506adf54d206b12 +cksum=3347074384 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/distros/repology.txt b/build/pkgs/pip/distros/repology.txt new file mode 100644 index 00000000000..adb97839453 --- /dev/null +++ b/build/pkgs/pip/distros/repology.txt @@ -0,0 +1,3 @@ +pip3 +python:pip +python3x-pip diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index bbcf7fc16a6..a8f5438c0a5 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -20.3.3 +21.0.1 diff --git a/build/pkgs/pip/spkg-install.in b/build/pkgs/pip/spkg-install.in index df71edf7d2e..9db95c4b8cd 100644 --- a/build/pkgs/pip/spkg-install.in +++ b/build/pkgs/pip/spkg-install.in @@ -1,17 +1,9 @@ cd src +sdh_setup_bdist_wheel -# pip can install itself! But first we need to ensure that the pip +# pip can install its own wheel! But first we need to ensure that the pip # source directory in on the PYTHONPATH export PYTHONPATH="$(pwd)/src" -# need to use --upgrade or --ignore-installed; Otherwise pip, which is -# importing itself, will think itself is already installed -# -versions=3 - -for vers in $versions; do - python${vers} -m pip install --verbose --no-index --ignore-installed \ - --no-build-isolation --isolated --root="$SAGE_DESTDIR" . || \ - sdh_die "Error building / installing pip${vers}" -done +sdh_store_and_pip_install_wheel . diff --git a/build/pkgs/pkgconf/distros/repology.txt b/build/pkgs/pkgconf/distros/repology.txt new file mode 100644 index 00000000000..6e82c010c4a --- /dev/null +++ b/build/pkgs/pkgconf/distros/repology.txt @@ -0,0 +1,2 @@ +pkgconf +pkg-config diff --git a/build/pkgs/pkgconfig/distros/repology.txt b/build/pkgs/pkgconfig/distros/repology.txt new file mode 100644 index 00000000000..451c7fc24b7 --- /dev/null +++ b/build/pkgs/pkgconfig/distros/repology.txt @@ -0,0 +1 @@ +python:pkgconfig diff --git a/build/pkgs/planarity/distros/repology.txt b/build/pkgs/planarity/distros/repology.txt new file mode 100644 index 00000000000..1556d0f7767 --- /dev/null +++ b/build/pkgs/planarity/distros/repology.txt @@ -0,0 +1 @@ +planarity diff --git a/build/pkgs/plantri/distros/repology.txt b/build/pkgs/plantri/distros/repology.txt new file mode 100644 index 00000000000..290ff321e46 --- /dev/null +++ b/build/pkgs/plantri/distros/repology.txt @@ -0,0 +1 @@ +plantri diff --git a/build/pkgs/polylib/distros/repology.txt b/build/pkgs/polylib/distros/repology.txt new file mode 100644 index 00000000000..766a4492247 --- /dev/null +++ b/build/pkgs/polylib/distros/repology.txt @@ -0,0 +1 @@ +polylib diff --git a/build/pkgs/polymake/distros/repology.txt b/build/pkgs/polymake/distros/repology.txt new file mode 100644 index 00000000000..2a9cc70b2f5 --- /dev/null +++ b/build/pkgs/polymake/distros/repology.txt @@ -0,0 +1 @@ +polymake diff --git a/build/pkgs/polytopes_db/distros/repology.txt b/build/pkgs/polytopes_db/distros/repology.txt new file mode 100644 index 00000000000..3d362556c3e --- /dev/null +++ b/build/pkgs/polytopes_db/distros/repology.txt @@ -0,0 +1 @@ +sagemath-polytopes-db diff --git a/build/pkgs/ppl/distros/repology.txt b/build/pkgs/ppl/distros/repology.txt new file mode 100644 index 00000000000..0efaae6634f --- /dev/null +++ b/build/pkgs/ppl/distros/repology.txt @@ -0,0 +1 @@ +ppl diff --git a/build/pkgs/pplpy/distros/repology.txt b/build/pkgs/pplpy/distros/repology.txt new file mode 100644 index 00000000000..b4423c8ecaf --- /dev/null +++ b/build/pkgs/pplpy/distros/repology.txt @@ -0,0 +1,2 @@ +pplpy +python:pplpy diff --git a/build/pkgs/primecount/distros/repology.txt b/build/pkgs/primecount/distros/repology.txt new file mode 100644 index 00000000000..f67843baa2b --- /dev/null +++ b/build/pkgs/primecount/distros/repology.txt @@ -0,0 +1 @@ +primecount diff --git a/build/pkgs/prometheus_client/distros/repology.txt b/build/pkgs/prometheus_client/distros/repology.txt new file mode 100644 index 00000000000..f5d086196c7 --- /dev/null +++ b/build/pkgs/prometheus_client/distros/repology.txt @@ -0,0 +1 @@ +python:prometheus-client diff --git a/build/pkgs/prompt_toolkit/distros/repology.txt b/build/pkgs/prompt_toolkit/distros/repology.txt new file mode 100644 index 00000000000..7d95f1766d8 --- /dev/null +++ b/build/pkgs/prompt_toolkit/distros/repology.txt @@ -0,0 +1 @@ +python:prompt-toolkit diff --git a/build/pkgs/psutil/distros/repology.txt b/build/pkgs/psutil/distros/repology.txt new file mode 100644 index 00000000000..a4d92cc08db --- /dev/null +++ b/build/pkgs/psutil/distros/repology.txt @@ -0,0 +1 @@ +psutil diff --git a/build/pkgs/ptyprocess/distros/repology.txt b/build/pkgs/ptyprocess/distros/repology.txt new file mode 100644 index 00000000000..65d24c88f8c --- /dev/null +++ b/build/pkgs/ptyprocess/distros/repology.txt @@ -0,0 +1,2 @@ +ptyprocess +python:ptyprocess diff --git a/build/pkgs/pybind11/distros/repology.txt b/build/pkgs/pybind11/distros/repology.txt new file mode 100644 index 00000000000..2904b11c470 --- /dev/null +++ b/build/pkgs/pybind11/distros/repology.txt @@ -0,0 +1 @@ +python:pybind11 diff --git a/build/pkgs/pybtex/distros/repology.txt b/build/pkgs/pybtex/distros/repology.txt new file mode 100644 index 00000000000..f8d3de9acf6 --- /dev/null +++ b/build/pkgs/pybtex/distros/repology.txt @@ -0,0 +1 @@ +python:pybtex diff --git a/build/pkgs/pycosat/distros/repology.txt b/build/pkgs/pycosat/distros/repology.txt new file mode 100644 index 00000000000..e62bd6061dc --- /dev/null +++ b/build/pkgs/pycosat/distros/repology.txt @@ -0,0 +1,2 @@ +pycosat +python:pycosat diff --git a/build/pkgs/pycparser/distros/repology.txt b/build/pkgs/pycparser/distros/repology.txt new file mode 100644 index 00000000000..1ec25a37d32 --- /dev/null +++ b/build/pkgs/pycparser/distros/repology.txt @@ -0,0 +1,2 @@ +pycparser +python:pycparser diff --git a/build/pkgs/pyflakes/distros/repology.txt b/build/pkgs/pyflakes/distros/repology.txt new file mode 100644 index 00000000000..9990e60b2eb --- /dev/null +++ b/build/pkgs/pyflakes/distros/repology.txt @@ -0,0 +1,2 @@ +pyflakes +python:pyflakes diff --git a/build/pkgs/pygments/distros/repology.txt b/build/pkgs/pygments/distros/repology.txt new file mode 100644 index 00000000000..b0831661524 --- /dev/null +++ b/build/pkgs/pygments/distros/repology.txt @@ -0,0 +1,2 @@ +pygments +python:pygments diff --git a/build/pkgs/pygraphviz/SPKG.rst b/build/pkgs/pygraphviz/SPKG.rst new file mode 100644 index 00000000000..df6a5025303 --- /dev/null +++ b/build/pkgs/pygraphviz/SPKG.rst @@ -0,0 +1,18 @@ +pygraphviz: Python interface to Graphviz +======================================== + +Description +----------- + +Python interface to Graphviz + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/pygraphviz/ + diff --git a/build/pkgs/pygraphviz/dependencies b/build/pkgs/pygraphviz/dependencies new file mode 100644 index 00000000000..4910f651546 --- /dev/null +++ b/build/pkgs/pygraphviz/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) graphviz | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pygraphviz/distros/conda.txt b/build/pkgs/pygraphviz/distros/conda.txt new file mode 100644 index 00000000000..7d3252d02e6 --- /dev/null +++ b/build/pkgs/pygraphviz/distros/conda.txt @@ -0,0 +1 @@ +pygraphviz diff --git a/build/pkgs/pygraphviz/install-requires.txt b/build/pkgs/pygraphviz/install-requires.txt new file mode 100644 index 00000000000..7d3252d02e6 --- /dev/null +++ b/build/pkgs/pygraphviz/install-requires.txt @@ -0,0 +1 @@ +pygraphviz diff --git a/build/pkgs/pygraphviz/requirements.txt b/build/pkgs/pygraphviz/requirements.txt new file mode 100644 index 00000000000..7d3252d02e6 --- /dev/null +++ b/build/pkgs/pygraphviz/requirements.txt @@ -0,0 +1 @@ +pygraphviz diff --git a/build/pkgs/pygraphviz/type b/build/pkgs/pygraphviz/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pygraphviz/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pynac/SPKG.rst b/build/pkgs/pynac/SPKG.rst index 9be4161d11b..61486cae128 100644 --- a/build/pkgs/pynac/SPKG.rst +++ b/build/pkgs/pynac/SPKG.rst @@ -16,6 +16,7 @@ GPL V2+ Upstream Contact ---------------- +- https://github.com/pynac/pynac - http://pynac.org - Burcin Erocal - burcin spam.erocal.org - William Stein - wstein spam.gmail.com @@ -25,13 +26,3 @@ Dependencies ------------ Python - - -Special Update/Build Instructions ---------------------------------- - -If build fails trying to run autoheader, run - - autoreconf -i --force - -in the src directory. diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index 1f24040868d..ec1e4b8ca05 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,5 +1,5 @@ tarball=pynac-VERSION.tar.bz2 -sha1=4913173e8bbb3d79bb4ee1faf631b154c9b8ef8c -md5=b38234ffedc018e7f31217dcc2879035 -cksum=881228674 -upstream_url=https://github.com/mkoeppe/pynac/releases/download/pynac-VERSION/pynac-VERSION.tar.bz2 +sha1=948ab5d8f81abd87669cd0b48916cfeffd772868 +md5=22275a875cd14bf55689987fb6bb846f +cksum=3697405663 +upstream_url=https://github.com/pynac/pynac/releases/download/pynac-VERSION/pynac-VERSION.tar.bz2 diff --git a/build/pkgs/pynac/distros/repology.txt b/build/pkgs/pynac/distros/repology.txt new file mode 100644 index 00000000000..f28f855f90a --- /dev/null +++ b/build/pkgs/pynac/distros/repology.txt @@ -0,0 +1 @@ +pynac diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index 2268e754e75..36905e8533b 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.26.sage-2020-04-03.p0 +0.7.27.p1 diff --git a/build/pkgs/pynac/patches/power_inf_loop.patch b/build/pkgs/pynac/patches/power_inf_loop.patch new file mode 100644 index 00000000000..dd5fc6aefe1 --- /dev/null +++ b/build/pkgs/pynac/patches/power_inf_loop.patch @@ -0,0 +1,21 @@ +diff --git a/ginac/power.cpp b/ginac/power.cpp +--- a/ginac/power.cpp ++++ b/ginac/power.cpp +@@ -920,10 +920,14 @@ + const ex& e = m.recombine_pair_to_ex(elem); + if (e.is_positive()) + prodseq.push_back(pow(e, exponent).expand(options)); +- else if (e.info(info_flags::negative)) { +- prodseq.push_back(pow(-e, exponent).expand(options)); +- possign = !possign; +- } else ++// we delete the following 'else if' clause because it can lead to ++// an infinite loop (see sagemath :trac:`30688`) ++// TODO: find a bug-free treatment of negative factors ++// else if (e.info(info_flags::negative)) { ++// prodseq.push_back(pow(-e, exponent).expand(options)); ++// possign = !possign; ++// } ++ else + powseq.push_back(elem); + } diff --git a/build/pkgs/pynac/patches/py_ssize_t_clean.patch b/build/pkgs/pynac/patches/py_ssize_t_clean.patch deleted file mode 100644 index c781ddd3082..00000000000 --- a/build/pkgs/pynac/patches/py_ssize_t_clean.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 37f3233e7eead521c25f798cab1df5746b9e8708 Mon Sep 17 00:00:00 2001 -From: Antonio Rojas -Date: Sun, 26 Jul 2020 20:04:35 +0200 -Subject: [PATCH 1/2] define PY_SSIZE_T_CLEAN - -As required by python 3.8 ---- - ginac/function.cpp | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ginac/function.cpp b/ginac/function.cpp -index c158723..689e2b8 100644 ---- a/ginac/function.cpp -+++ b/ginac/function.cpp -@@ -21,6 +21,7 @@ - */ - - #define register -+#define PY_SSIZE_T_CLEAN - #include - #include "py_funcs.h" - #include "function.h" - -From 0869189faf3899d6aeb07501e16719159f13cb2f Mon Sep 17 00:00:00 2001 -From: Antonio Rojas -Date: Sun, 26 Jul 2020 20:05:19 +0200 -Subject: [PATCH 2/2] define PY_SSIZE_T_CLEAN - -As required by Python 3.8 ---- - ginac/numeric.cpp | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ginac/numeric.cpp b/ginac/numeric.cpp -index 276d86c..b463806 100644 ---- a/ginac/numeric.cpp -+++ b/ginac/numeric.cpp -@@ -50,6 +50,7 @@ - */ - - #define register -+#define PY_SSIZE_T_CLEAN - #include - #include - #include "flint/fmpz.h" diff --git a/build/pkgs/pynormaliz/distros/repology.txt b/build/pkgs/pynormaliz/distros/repology.txt new file mode 100644 index 00000000000..2ec66cd96d3 --- /dev/null +++ b/build/pkgs/pynormaliz/distros/repology.txt @@ -0,0 +1,2 @@ +pynormaliz +python:pynormaliz diff --git a/build/pkgs/pyopenssl/distros/repology.txt b/build/pkgs/pyopenssl/distros/repology.txt new file mode 100644 index 00000000000..b3288b190ea --- /dev/null +++ b/build/pkgs/pyopenssl/distros/repology.txt @@ -0,0 +1,2 @@ +pyopenssl +python:pyopenssl diff --git a/build/pkgs/pyparsing/distros/repology.txt b/build/pkgs/pyparsing/distros/repology.txt new file mode 100644 index 00000000000..4739b941a07 --- /dev/null +++ b/build/pkgs/pyparsing/distros/repology.txt @@ -0,0 +1,2 @@ +pyparsing +python:pyparsing diff --git a/build/pkgs/pyrsistent/distros/repology.txt b/build/pkgs/pyrsistent/distros/repology.txt new file mode 100644 index 00000000000..4b8b8a25979 --- /dev/null +++ b/build/pkgs/pyrsistent/distros/repology.txt @@ -0,0 +1,2 @@ +pyrsistent +python:pyrsistent diff --git a/build/pkgs/pysingular/distros/repology.txt b/build/pkgs/pysingular/distros/repology.txt new file mode 100644 index 00000000000..2d9aac0523e --- /dev/null +++ b/build/pkgs/pysingular/distros/repology.txt @@ -0,0 +1,2 @@ +pysingular +python:pysingular diff --git a/build/pkgs/pytest/distros/repology.txt b/build/pkgs/pytest/distros/repology.txt new file mode 100644 index 00000000000..fe049edcb75 --- /dev/null +++ b/build/pkgs/pytest/distros/repology.txt @@ -0,0 +1 @@ +python:pytest diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 941fc771f79..825afb53913 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,5 +1,5 @@ tarball=Python-VERSION.tar.xz -sha1=77f4105846f6740297e50d7535a42c02d6b8e7db -md5=61981498e75ac8f00adcb908281fadb6 -cksum=4032849512 +sha1=110ca5bca7989f9558a54ee6762e6774a4b9644a +md5=f0dc9000312abeb16de4eccce9a870ab +cksum=1344509533 upstream_url=https://www.python.org/ftp/python/VERSION/Python-VERSION.tar.xz diff --git a/build/pkgs/python3/dependencies b/build/pkgs/python3/dependencies index 5af9e3f5891..4dd1e381802 100644 --- a/build/pkgs/python3/dependencies +++ b/build/pkgs/python3/dependencies @@ -1,4 +1,4 @@ -zlib readline sqlite bzip2 xz libffi $(findstring openssl,$(OPTIONAL_INSTALLED_PACKAGES)) +zlib readline sqlite libpng bzip2 xz libffi openssl ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/python3/distros/repology.txt b/build/pkgs/python3/distros/repology.txt new file mode 100644 index 00000000000..fdc793e786a --- /dev/null +++ b/build/pkgs/python3/distros/repology.txt @@ -0,0 +1 @@ +python diff --git a/build/pkgs/python3/package-version.txt b/build/pkgs/python3/package-version.txt index 6bd10744ae8..2009c7dfad9 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.9.1 +3.9.2 diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index c645a66db19..637d73b74f2 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,4 +1,7 @@ SAGE_SPKG_CONFIGURE([python3], [ + m4_pushdef([MIN_VERSION], [3.6.0]) + m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.7.0]) + m4_pushdef([LT_VERSION], [3.10.0]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python=PYTHON3], [Python 3 executable to use for the Sage venv; default: python3])]) @@ -20,8 +23,6 @@ SAGE_SPKG_CONFIGURE([python3], [ dnl Check if we can do venv with a system python3 dnl instead of building our own copy. check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" - m4_pushdef([MIN_VERSION], [3.6.0]) - m4_pushdef([LT_VERSION], [3.10.0]) AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python AC_MSG_RESULT([]) @@ -29,7 +30,7 @@ SAGE_SPKG_CONFIGURE([python3], [ SAGE_CHECK_PYTHON_FOR_VENV([$ac_path_PYTHON3], MIN_VERSION, LT_VERSION, $check_modules, [ - AS_IF([[conftest_venv/bin/python3 -m sysconfig | grep '^\sw*\(C\|LD\)FLAGS *=.*[" ]-[IL]' ]] [>& AS_MESSAGE_LOG_FD 2>&1 ], [ + AS_IF([[conftest_venv/bin/python3 -m sysconfig | grep '^\sw*\(C\|LD\)FLAGS *=.*[" ]-[IL] *[^.]' ]] [>& AS_MESSAGE_LOG_FD 2>&1 ], [ AC_MSG_WARN([this is a misconfigured Python whose sysconfig compiler/linker flags contain -I or -L options, which may cause wrong versions of libraries to leak into the build of Python packages - see https://trac.sagemath.org/ticket/31132]) ]) dnl It is good @@ -48,7 +49,7 @@ SAGE_SPKG_CONFIGURE([python3], [ SAGE_CHECK_PYTHON_FOR_VENV([$ac_path_PYTHON3], MIN_VERSION, LT_VERSION, $check_modules, [ - AS_IF([[conftest_venv/bin/python3 -m sysconfig | grep '^\sw*\(C\|LD\)FLAGS *=.*[" ]-[IL]' ]] [>& AS_MESSAGE_LOG_FD 2>&1 ], [ + AS_IF([[conftest_venv/bin/python3 -m sysconfig | grep '^\sw*\(C\|LD\)FLAGS *=.*[" ]-[IL] *[^.]' ]] [>& AS_MESSAGE_LOG_FD 2>&1 ], [ AC_MSG_RESULT([no, this is a misconfigured Python whose sysconfig compiler/linker flags contain -I or -L options, which may cause wrong versions of libraries to leak into the build of Python packages - see https://trac.sagemath.org/ticket/31132; to use it anyway, use ./configure --with-python=$ac_path_PYTHON3]) ], [ dnl It is good @@ -66,16 +67,45 @@ SAGE_SPKG_CONFIGURE([python3], [ AC_MSG_NOTICE([to try to use a different system python, use ./configure --with-python=/path/to/python]) sage_spkg_install_python3=yes ]) - m4_popdef([MIN_VERSION]) - m4_popdef([LT_VERSION]) ]) ],, [ dnl PRE ], [ dnl POST - AS_IF([test x$sage_spkg_install_python3 = xno], - [PYTHON_FOR_VENV="$ac_cv_path_PYTHON3"], - [SAGE_MACOSX_DEPLOYMENT_TARGET=legacy]) + AS_IF([test x$sage_spkg_install_python3 = xno], [ + PYTHON_FOR_VENV="$ac_cv_path_PYTHON3" + AS_IF([test "$SAGE_ARCHFLAGS" = "unset"], [ + AC_MSG_CHECKING([whether $PYTHON_FOR_VENV is configured to build multiarch extensions]) + AS_IF([[CC="$CC" CXX="$CXX" conftest_venv/bin/python3 -m sysconfig | grep '^\sw*\(C\|LD\)FLAGS *=.*[" ]-arch.* -arch' ]] [>& AS_MESSAGE_LOG_FD 2>&1 ], [ + AC_MSG_RESULT([yes; disabling it by setting ARCHFLAGS]) + SAGE_ARCHFLAGS="" + ], [ + AC_MSG_RESULT([no]) + ]) + ]) + AS_IF([test "$SAGE_ARCHFLAGS" != "unset"], [ + ARCHFLAGS="$SAGE_ARCHFLAGS" + export ARCHFLAGS + ]) + AS_IF([test -n "$CFLAGS_MARCH"], [ + dnl Trac #31228 + AC_MSG_CHECKING([whether "$CFLAGS_MARCH" works with the C/C++ compilers configured for building extensions for $PYTHON_FOR_VENV]) + SAGE_PYTHON_CHECK_DISTUTILS([CC="$CC" CXX="$CXX" CFLAGS="$CFLAGS_MARCH" conftest_venv/bin/python3], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no, with these flags, $reason; disabling use of "$CFLAGS_MARCH"]) + CFLAGS_MARCH="" + ]) + ]) + AX_COMPARE_VERSION([$python3_version], [lt], MIN_NONDEPRECATED_VERSION, [ + AC_MSG_NOTICE([deprecation notice: Support for system python < MIN_NONDEPRECATED_VERSION is deprecated +and will be removed in the next development cycle. Consider using a newer version of Python +that may be available on your system or can be installed using the system package manager. +To build Sage with a different system python, use ./configure --with-python=/path/to/python]) + ]) + ], [ + SAGE_MACOSX_DEPLOYMENT_TARGET=legacy + ]) AC_SUBST([PYTHON_FOR_VENV]) AC_SUBST([SAGE_MACOSX_DEPLOYMENT_TARGET]) @@ -84,4 +114,8 @@ SAGE_SPKG_CONFIGURE([python3], [ dnl (that a bunch of other checks do) from emitting warnings about dnl conftest.dir and conftest_venv being directories. rm -rf conftest.dir conftest_venv + + m4_popdef([MIN_VERSION]) + m4_popdef([MIN_NONDEPRECATED_VERSION]) + m4_popdef([LT_VERSION]) ]) diff --git a/build/pkgs/python_igraph/distros/repology.txt b/build/pkgs/python_igraph/distros/repology.txt new file mode 100644 index 00000000000..8fb57f30813 --- /dev/null +++ b/build/pkgs/python_igraph/distros/repology.txt @@ -0,0 +1,2 @@ +python:igraph +python:python-igraph diff --git a/build/pkgs/pytz/distros/repology.txt b/build/pkgs/pytz/distros/repology.txt new file mode 100644 index 00000000000..ec209f0004f --- /dev/null +++ b/build/pkgs/pytz/distros/repology.txt @@ -0,0 +1 @@ +python:pytz diff --git a/build/pkgs/pyx/distros/repology.txt b/build/pkgs/pyx/distros/repology.txt new file mode 100644 index 00000000000..e49f797a4e3 --- /dev/null +++ b/build/pkgs/pyx/distros/repology.txt @@ -0,0 +1 @@ +python:pyx diff --git a/build/pkgs/pyzmq/distros/repology.txt b/build/pkgs/pyzmq/distros/repology.txt new file mode 100644 index 00000000000..cb446ff4e05 --- /dev/null +++ b/build/pkgs/pyzmq/distros/repology.txt @@ -0,0 +1,2 @@ +pyzmq +python:pyzmq diff --git a/build/pkgs/qepcad/distros/repology.txt b/build/pkgs/qepcad/distros/repology.txt new file mode 100644 index 00000000000..f90850e3c41 --- /dev/null +++ b/build/pkgs/qepcad/distros/repology.txt @@ -0,0 +1 @@ +qepcad-b \ No newline at end of file diff --git a/build/pkgs/qhull/distros/repology.txt b/build/pkgs/qhull/distros/repology.txt new file mode 100644 index 00000000000..95d316779cf --- /dev/null +++ b/build/pkgs/qhull/distros/repology.txt @@ -0,0 +1 @@ +qhull diff --git a/build/pkgs/r/distros/repology.txt b/build/pkgs/r/distros/repology.txt new file mode 100644 index 00000000000..4286f428e3b --- /dev/null +++ b/build/pkgs/r/distros/repology.txt @@ -0,0 +1 @@ +r diff --git a/build/pkgs/r_jupyter/distros/repology.txt b/build/pkgs/r_jupyter/distros/repology.txt new file mode 100644 index 00000000000..58091c9a446 --- /dev/null +++ b/build/pkgs/r_jupyter/distros/repology.txt @@ -0,0 +1 @@ +r:irkernel \ No newline at end of file diff --git a/build/pkgs/ratpoints/distros/repology.txt b/build/pkgs/ratpoints/distros/repology.txt new file mode 100644 index 00000000000..e137758627f --- /dev/null +++ b/build/pkgs/ratpoints/distros/repology.txt @@ -0,0 +1 @@ +ratpoints diff --git a/build/pkgs/readline/distros/repology.txt b/build/pkgs/readline/distros/repology.txt new file mode 100644 index 00000000000..0b5a58e278a --- /dev/null +++ b/build/pkgs/readline/distros/repology.txt @@ -0,0 +1 @@ +readline diff --git a/build/pkgs/requests/distros/repology.txt b/build/pkgs/requests/distros/repology.txt new file mode 100644 index 00000000000..f2293605cf1 --- /dev/null +++ b/build/pkgs/requests/distros/repology.txt @@ -0,0 +1 @@ +requests diff --git a/build/pkgs/rpy2/distros/repology.txt b/build/pkgs/rpy2/distros/repology.txt new file mode 100644 index 00000000000..8f389862688 --- /dev/null +++ b/build/pkgs/rpy2/distros/repology.txt @@ -0,0 +1 @@ +rpy2 diff --git a/build/pkgs/rubiks/distros/repology.txt b/build/pkgs/rubiks/distros/repology.txt new file mode 100644 index 00000000000..9991b8e7aa6 --- /dev/null +++ b/build/pkgs/rubiks/distros/repology.txt @@ -0,0 +1 @@ +rubiks diff --git a/build/pkgs/rw/SPKG.rst b/build/pkgs/rw/SPKG.rst index 634c5e4fc61..265310be498 100644 --- a/build/pkgs/rw/SPKG.rst +++ b/build/pkgs/rw/SPKG.rst @@ -6,7 +6,7 @@ Description rw is a program that calculates rank-width and rank-decompositions. -http://pholia.tdi.informatik.uni-frankfurt.de/~philipp/software/rw.shtml +https://sourceforge.net/projects/rankwidth/ License ------- diff --git a/build/pkgs/rw/checksums.ini b/build/pkgs/rw/checksums.ini index 756713fd910..eaf5817e53c 100644 --- a/build/pkgs/rw/checksums.ini +++ b/build/pkgs/rw/checksums.ini @@ -1,4 +1,5 @@ tarball=rw-VERSION.tar.gz -sha1=4f2b92af13848c70b834c330cd751b22c03738aa -md5=58695835a1b5aaa3935143feabc8619a -cksum=3270113719 +sha1=5c9c8a5c2eda798f6b0fe5218ba08ef429e0aa9f +md5=829612ea322d64bc529ffbb6be42d97e +cksum=93091513 +upstream_url=https://sourceforge.net/projects/rankwidth/files/rw-VERSION.tar.gz diff --git a/build/pkgs/rw/distros/repology.txt b/build/pkgs/rw/distros/repology.txt new file mode 100644 index 00000000000..615dd1998af --- /dev/null +++ b/build/pkgs/rw/distros/repology.txt @@ -0,0 +1 @@ +rankwidth diff --git a/build/pkgs/rw/package-version.txt b/build/pkgs/rw/package-version.txt index 1100d95a42e..b63ba696b7a 100644 --- a/build/pkgs/rw/package-version.txt +++ b/build/pkgs/rw/package-version.txt @@ -1 +1 @@ -0.7.p0 +0.9 diff --git a/build/pkgs/saclib/distros/repology.txt b/build/pkgs/saclib/distros/repology.txt new file mode 100644 index 00000000000..84222a463fa --- /dev/null +++ b/build/pkgs/saclib/distros/repology.txt @@ -0,0 +1 @@ +saclib diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 4e38838700b..6ac91e68018 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -6,6 +6,13 @@ MAXIMA = "@prefix@/bin/maxima" ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" +NTL_INCDIR = "@NTL_INCDIR@" +NTL_LIBDIR = "@NTL_LIBDIR@" + +# Path to the ecl-config script +# TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. +ECL_CONFIG = "@prefix@/bin/ecl-config" + SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" # Colon-separated list of pkg-config modules to search for cblas functionality. diff --git a/build/pkgs/sage_docbuild/dependencies b/build/pkgs/sage_docbuild/dependencies new file mode 100644 index 00000000000..d3920d653a9 --- /dev/null +++ b/build/pkgs/sage_docbuild/dependencies @@ -0,0 +1 @@ +$(PYTHON) sphinx ../pkgs/sage_docbuild/src/sage_docbuild/*.py ../pkgs/sage_docbuild/src/sage_docbuild/ext/*.py | $(PYTHON_TOOLCHAIN) sagelib diff --git a/build/pkgs/sage_docbuild/package-version.txt b/build/pkgs/sage_docbuild/package-version.txt new file mode 120000 index 00000000000..cf10fe4b4e4 --- /dev/null +++ b/build/pkgs/sage_docbuild/package-version.txt @@ -0,0 +1 @@ +../sagelib/package-version.txt \ No newline at end of file diff --git a/build/pkgs/sage_docbuild/spkg-install b/build/pkgs/sage_docbuild/spkg-install new file mode 100755 index 00000000000..99a95cf9cd5 --- /dev/null +++ b/build/pkgs/sage_docbuild/spkg-install @@ -0,0 +1,14 @@ +#!/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 +# 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 . diff --git a/build/pkgs/sage_docbuild/spkg-src b/build/pkgs/sage_docbuild/spkg-src new file mode 100755 index 00000000000..dc1ba829b3a --- /dev/null +++ b/build/pkgs/sage_docbuild/spkg-src @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sage_docbuild +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sage_docbuild/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sage_docbuild + +cd src +python3 -u setup.py --no-user-cfg sdist --dist-dir "$SAGE_DISTFILES" diff --git a/build/pkgs/sage_docbuild/src/MANIFEST.in b/build/pkgs/sage_docbuild/src/MANIFEST.in new file mode 100644 index 00000000000..74282fceee2 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/MANIFEST.in @@ -0,0 +1 @@ +include VERSION.txt diff --git a/build/pkgs/sage_docbuild/src/README.rst b/build/pkgs/sage_docbuild/src/README.rst new file mode 100644 index 00000000000..16ed52cdd52 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/README.rst @@ -0,0 +1,23 @@ +================================================================================ + Sage: Open Source Mathematics Software: Build system of the Sage documentation +================================================================================ + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2020 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). + +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 pip-installable source distribution +---------------------------------------------- + +This is the build system of the Sage documentation, based on Sphinx. diff --git a/build/pkgs/sage_docbuild/src/VERSION.txt b/build/pkgs/sage_docbuild/src/VERSION.txt new file mode 120000 index 00000000000..9207ddd5144 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/VERSION.txt @@ -0,0 +1 @@ +../package-version.txt \ No newline at end of file diff --git a/build/pkgs/sage_docbuild/src/requirements.txt b/build/pkgs/sage_docbuild/src/requirements.txt new file mode 100644 index 00000000000..4e5dd84d0e8 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/requirements.txt @@ -0,0 +1,2 @@ +#sage +sphinx diff --git a/build/pkgs/sage_docbuild/src/sage_docbuild b/build/pkgs/sage_docbuild/src/sage_docbuild new file mode 120000 index 00000000000..a9114dd4f0b --- /dev/null +++ b/build/pkgs/sage_docbuild/src/sage_docbuild @@ -0,0 +1 @@ +../../../../src/sage_docbuild \ No newline at end of file diff --git a/build/pkgs/sage_docbuild/src/setup.cfg b/build/pkgs/sage_docbuild/src/setup.cfg new file mode 100644 index 00000000000..99b54e9c310 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/setup.cfg @@ -0,0 +1,33 @@ +[metadata] +name = sage_docbuild +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Build system of the Sage documentation +long_description = file: README.rst +long_description_content_type = text/x-rst +license = GNU General Public License (GPL) v2 or later +author = The Sage Developers +author_email = sage-support@googlegroups.com +url = https://www.sagemath.org + +classifiers = + Development Status :: 6 - Mature + Intended Audience :: Education + Intended Audience :: Science/Research + License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) + Operating System :: POSIX + Operating System :: MacOS :: MacOS X + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Topic :: Scientific/Engineering :: Mathematics + +[options] +packages = + sage_docbuild + sage_docbuild.ext + +install_requires = + sphinx diff --git a/build/pkgs/sage_docbuild/src/setup.py b/build/pkgs/sage_docbuild/src/setup.py new file mode 100644 index 00000000000..beda28e8216 --- /dev/null +++ b/build/pkgs/sage_docbuild/src/setup.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +from setuptools import setup + +setup() diff --git a/build/pkgs/sage_docbuild/src/tox.ini b/build/pkgs/sage_docbuild/src/tox.ini new file mode 100644 index 00000000000..de900475e2a --- /dev/null +++ b/build/pkgs/sage_docbuild/src/tox.ini @@ -0,0 +1,30 @@ +# First pip-install tox: +# +# ./sage -pip install tox +# +# To build and test in the tox environment: +# +# ./sage -sh -c '(cd build/pkgs/sage_docbuild/src && tox)' +# +# To test interactively: +# +# build/pkgs/sage_docbuild/src/.tox/python/bin/python +# +[tox] + +[testenv] +deps = -rrequirements.txt + +setenv = + # Sage scripts like to use $HOME/.sage + HOME={envdir} + +whitelist_externals = + bash + +commands = + # Beware of the treacherous non-src layout. + #python -c 'import sys; "" in sys.path and sys.path.remove(""); import sage_docbuild' + +# TODO: Add tests after adding the dependency on sagelib to +# requirements.txt diff --git a/build/pkgs/sage_docbuild/type b/build/pkgs/sage_docbuild/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/sage_docbuild/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index bffd9045cd8..9efdd7fc203 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.3.beta7 +9.3.beta9 diff --git a/build/pkgs/sagelib/spkg-configure.m4 b/build/pkgs/sagelib/spkg-configure.m4 new file mode 100644 index 00000000000..21f7ee7ce08 --- /dev/null +++ b/build/pkgs/sagelib/spkg-configure.m4 @@ -0,0 +1,4 @@ +AC_ARG_ENABLE([editable], + [AS_HELP_STRING([--enable-editable], + [use an editable install of the Sage library])], + [AC_SUBST([SAGE_EDITABLE], [yes])]) diff --git a/build/pkgs/sagelib/spkg-install b/build/pkgs/sagelib/spkg-install index 09b3108585a..19687a45fcd 100755 --- a/build/pkgs/sagelib/spkg-install +++ b/build/pkgs/sagelib/spkg-install @@ -1,5 +1,9 @@ #!/usr/bin/env bash -cd src +if [ "$SAGE_EDITABLE" = yes ]; then + cd "$SAGE_SRC" +else + cd src +fi ## All sagelib-building is done by setup.py. ## This is so that sagelib can be installed by standard Python procedures, ## such as "./setup.py install" or "pip install ." @@ -39,7 +43,20 @@ export SAGE_SHARE=/doesnotexist # spec, which includes setting a symlink to the installed documentation. # export SAGE_DOC=/doesnotexist -time "$PYTHON" -u setup.py --no-user-cfg build install || exit 1 +SITEPACKAGESDIR=$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') +if [ "$SAGE_EDITABLE" = yes ]; then + # In an incremental build, we may need to uninstall old versions installed by distutils + # 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 +else + # 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 +fi + if [ "$UNAME" = "CYGWIN" ]; then sage-rebase.sh "$SAGE_LOCAL" 2>/dev/null; fi diff --git a/build/pkgs/sagelib/src/MANIFEST.in b/build/pkgs/sagelib/src/MANIFEST.in index 7123d149222..e149951fe4e 100644 --- a/build/pkgs/sagelib/src/MANIFEST.in +++ b/build/pkgs/sagelib/src/MANIFEST.in @@ -6,3 +6,5 @@ prune .tox graft sage/libs/gap/test 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_docbuild # Shipped by sage_docbuild diff --git a/build/pkgs/sagelib/src/VERSION.txt b/build/pkgs/sagelib/src/VERSION.txt index 9207ddd5144..bcb9ebfb52b 120000 --- a/build/pkgs/sagelib/src/VERSION.txt +++ b/build/pkgs/sagelib/src/VERSION.txt @@ -1 +1 @@ -../package-version.txt \ No newline at end of file +../../../../src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagelib/src/setup.py b/build/pkgs/sagelib/src/setup.py index d4a6591ec5c..0d7c29d7463 100755 --- a/build/pkgs/sagelib/src/setup.py +++ b/build/pkgs/sagelib/src/setup.py @@ -125,7 +125,6 @@ 'bin/sage-runtests', # because it is useful for doctesting user scripts too 'bin/sage-fixdoctests', # likewise 'bin/sage-coverage', # because it is useful for coverage-testing user scripts too - 'bin/sage-coverageall', # likewise 'bin/sage-cython', # deprecated, might be used in user package install scripts ## Helper scripts invoked by sage script ## (they would actually belong to something like libexec) diff --git a/build/pkgs/sagenb_export/distros/repology.txt b/build/pkgs/sagenb_export/distros/repology.txt new file mode 100644 index 00000000000..9b3625851da --- /dev/null +++ b/build/pkgs/sagenb_export/distros/repology.txt @@ -0,0 +1 @@ +sagenb-export diff --git a/build/pkgs/sagetex/distros/repology.txt b/build/pkgs/sagetex/distros/repology.txt new file mode 100644 index 00000000000..455cf465069 --- /dev/null +++ b/build/pkgs/sagetex/distros/repology.txt @@ -0,0 +1 @@ +sagetex diff --git a/build/pkgs/scandir/distros/repology.txt b/build/pkgs/scandir/distros/repology.txt new file mode 100644 index 00000000000..e098979afc8 --- /dev/null +++ b/build/pkgs/scandir/distros/repology.txt @@ -0,0 +1 @@ +python:scandir diff --git a/build/pkgs/scipoptsuite/distros/repology.txt b/build/pkgs/scipoptsuite/distros/repology.txt new file mode 100644 index 00000000000..9a9aa4002ac --- /dev/null +++ b/build/pkgs/scipoptsuite/distros/repology.txt @@ -0,0 +1 @@ +scipoptsuite diff --git a/build/pkgs/scipy/distros/repology.txt b/build/pkgs/scipy/distros/repology.txt new file mode 100644 index 00000000000..4ea9535375a --- /dev/null +++ b/build/pkgs/scipy/distros/repology.txt @@ -0,0 +1 @@ +python:scipy diff --git a/build/pkgs/send2trash/distros/repology.txt b/build/pkgs/send2trash/distros/repology.txt new file mode 100644 index 00000000000..082d6c270dc --- /dev/null +++ b/build/pkgs/send2trash/distros/repology.txt @@ -0,0 +1,2 @@ +send2trash +python:send2trash diff --git a/build/pkgs/setuptools/distros/repology.txt b/build/pkgs/setuptools/distros/repology.txt new file mode 100644 index 00000000000..845a263c318 --- /dev/null +++ b/build/pkgs/setuptools/distros/repology.txt @@ -0,0 +1 @@ +python:setuptools diff --git a/build/pkgs/setuptools_scm/distros/repology.txt b/build/pkgs/setuptools_scm/distros/repology.txt new file mode 100644 index 00000000000..343b53a138f --- /dev/null +++ b/build/pkgs/setuptools_scm/distros/repology.txt @@ -0,0 +1 @@ +python:setuptools-scm diff --git a/build/pkgs/setuptools_wheel/SPKG.rst b/build/pkgs/setuptools_wheel/SPKG.rst new file mode 100644 index 00000000000..c78602a296a --- /dev/null +++ b/build/pkgs/setuptools_wheel/SPKG.rst @@ -0,0 +1,5 @@ +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. diff --git a/build/pkgs/setuptools_wheel/checksums.ini b/build/pkgs/setuptools_wheel/checksums.ini new file mode 120000 index 00000000000..4f64d3ce107 --- /dev/null +++ b/build/pkgs/setuptools_wheel/checksums.ini @@ -0,0 +1 @@ +../setuptools/checksums.ini \ No newline at end of file diff --git a/build/pkgs/termcap/dependencies b/build/pkgs/setuptools_wheel/dependencies similarity index 84% rename from build/pkgs/termcap/dependencies rename to build/pkgs/setuptools_wheel/dependencies index 3546cda4614..6f2aa240c02 100644 --- a/build/pkgs/termcap/dependencies +++ b/build/pkgs/setuptools_wheel/dependencies @@ -1,4 +1,4 @@ -# no dependencies +$(PYTHON) setuptools wheel ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/setuptools_wheel/install-requires.txt b/build/pkgs/setuptools_wheel/install-requires.txt new file mode 100644 index 00000000000..d1dc532ceab --- /dev/null +++ b/build/pkgs/setuptools_wheel/install-requires.txt @@ -0,0 +1 @@ +# We use this file to mark the package as a Python package diff --git a/build/pkgs/setuptools_wheel/package-version.txt b/build/pkgs/setuptools_wheel/package-version.txt new file mode 120000 index 00000000000..5268dbec8f6 --- /dev/null +++ b/build/pkgs/setuptools_wheel/package-version.txt @@ -0,0 +1 @@ +../setuptools/package-version.txt \ No newline at end of file diff --git a/build/pkgs/setuptools_wheel/spkg-install.in b/build/pkgs/setuptools_wheel/spkg-install.in new file mode 100644 index 00000000000..7fdd493b5aa --- /dev/null +++ b/build/pkgs/setuptools_wheel/spkg-install.in @@ -0,0 +1,3 @@ +cd src +sdh_setup_bdist_wheel +sdh_store_wheel . diff --git a/build/pkgs/setuptools_wheel/type b/build/pkgs/setuptools_wheel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/setuptools_wheel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/simplegeneric/distros/repology.txt b/build/pkgs/simplegeneric/distros/repology.txt new file mode 100644 index 00000000000..52baf78b41b --- /dev/null +++ b/build/pkgs/simplegeneric/distros/repology.txt @@ -0,0 +1,2 @@ +simplegeneric +python:simplegeneric diff --git a/build/pkgs/singular/distros/repology.txt b/build/pkgs/singular/distros/repology.txt new file mode 100644 index 00000000000..5f0dc01955f --- /dev/null +++ b/build/pkgs/singular/distros/repology.txt @@ -0,0 +1 @@ +singular diff --git a/build/pkgs/singular_jupyter/distros/repology.txt b/build/pkgs/singular_jupyter/distros/repology.txt new file mode 100644 index 00000000000..2e4c4258a6d --- /dev/null +++ b/build/pkgs/singular_jupyter/distros/repology.txt @@ -0,0 +1,2 @@ +jupyter-singular +python:jupyter-kernel-singular diff --git a/build/pkgs/sip/distros/repology.txt b/build/pkgs/sip/distros/repology.txt new file mode 100644 index 00000000000..d2bfd2d7b27 --- /dev/null +++ b/build/pkgs/sip/distros/repology.txt @@ -0,0 +1 @@ +python:sip diff --git a/build/pkgs/sirocco/distros/repology.txt b/build/pkgs/sirocco/distros/repology.txt new file mode 100644 index 00000000000..f2baa26096c --- /dev/null +++ b/build/pkgs/sirocco/distros/repology.txt @@ -0,0 +1 @@ +sirocco diff --git a/build/pkgs/six/distros/repology.txt b/build/pkgs/six/distros/repology.txt new file mode 100644 index 00000000000..4b112612966 --- /dev/null +++ b/build/pkgs/six/distros/repology.txt @@ -0,0 +1 @@ +python:six diff --git a/build/pkgs/snappy/SPKG.rst b/build/pkgs/snappy/SPKG.rst new file mode 100644 index 00000000000..738f86f073f --- /dev/null +++ b/build/pkgs/snappy/SPKG.rst @@ -0,0 +1,18 @@ +snappy: Topology and geometry of 3-manifolds, with a focus on hyperbolic structures +=================================================================================== + +Description +----------- + +Studying the topology and geometry of 3-manifolds, with a focus on hyperbolic structures. + +License +------- + +GPLv2+ + +Upstream Contact +---------------- + +https://pypi.org/project/snappy/ + diff --git a/build/pkgs/snappy/dependencies b/build/pkgs/snappy/dependencies new file mode 100644 index 00000000000..8e4fca87caa --- /dev/null +++ b/build/pkgs/snappy/dependencies @@ -0,0 +1,10 @@ +$(PYTHON) decorator ipython cypari | $(PYTHON_TOOLCHAIN) sagelib + +---------- +All lines of this file are ignored except the first. + +The dependency cypari above is actually cypari2. + +An installed sagelib is needed when snappy is installed from source (instead of a wheel) +because snappy's setup.py tests its presence to adjust dependencies. +https://trac.sagemath.org/ticket/31180 diff --git a/build/pkgs/snappy/requirements.txt b/build/pkgs/snappy/requirements.txt new file mode 100644 index 00000000000..33e93e66a1a --- /dev/null +++ b/build/pkgs/snappy/requirements.txt @@ -0,0 +1,10 @@ +# Note: As of 2021-01, snappy will pull in cypari (!= cypari2) as a dependency +# if installed as a wheel but will actually use Sage's cypari2. +# cypari contains a statically linked copy of pari and other libraries +# and will remain completely unused (wastes 30M). Snappy is about 165M. +# See https://trac.sagemath.org/ticket/31180 +snappy +# cypari 2.4.0 has a broken sdist, https://trac.sagemath.org/ticket/31180 +cypari !=2.4.0 +# An optional database (110M uncompressed) +snappy_15_knots diff --git a/build/pkgs/snappy/type b/build/pkgs/snappy/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/snappy/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/snowballstemmer/distros/repology.txt b/build/pkgs/snowballstemmer/distros/repology.txt new file mode 100644 index 00000000000..603dfa3bfe4 --- /dev/null +++ b/build/pkgs/snowballstemmer/distros/repology.txt @@ -0,0 +1 @@ +python:snowballstemmer diff --git a/build/pkgs/speaklater/distros/repology.txt b/build/pkgs/speaklater/distros/repology.txt new file mode 100644 index 00000000000..5ecb1edfeb0 --- /dev/null +++ b/build/pkgs/speaklater/distros/repology.txt @@ -0,0 +1,2 @@ +speaklater +python:speaklater diff --git a/build/pkgs/sphinx/distros/repology.txt b/build/pkgs/sphinx/distros/repology.txt new file mode 100644 index 00000000000..536ee133feb --- /dev/null +++ b/build/pkgs/sphinx/distros/repology.txt @@ -0,0 +1 @@ +python:sphinx diff --git a/build/pkgs/sphinxcontrib_applehelp/distros/repology.txt b/build/pkgs/sphinxcontrib_applehelp/distros/repology.txt new file mode 100644 index 00000000000..ec9a737af5d --- /dev/null +++ b/build/pkgs/sphinxcontrib_applehelp/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-applehelp diff --git a/build/pkgs/sphinxcontrib_devhelp/distros/repology.txt b/build/pkgs/sphinxcontrib_devhelp/distros/repology.txt new file mode 100644 index 00000000000..82718af9921 --- /dev/null +++ b/build/pkgs/sphinxcontrib_devhelp/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-devhelp diff --git a/build/pkgs/sphinxcontrib_htmlhelp/distros/repology.txt b/build/pkgs/sphinxcontrib_htmlhelp/distros/repology.txt new file mode 100644 index 00000000000..8407c7bca60 --- /dev/null +++ b/build/pkgs/sphinxcontrib_htmlhelp/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-htmlhelp diff --git a/build/pkgs/sphinxcontrib_jsmath/distros/repology.txt b/build/pkgs/sphinxcontrib_jsmath/distros/repology.txt new file mode 100644 index 00000000000..71bbb574826 --- /dev/null +++ b/build/pkgs/sphinxcontrib_jsmath/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-jsmath diff --git a/build/pkgs/sphinxcontrib_qthelp/distros/repology.txt b/build/pkgs/sphinxcontrib_qthelp/distros/repology.txt new file mode 100644 index 00000000000..270284b15bc --- /dev/null +++ b/build/pkgs/sphinxcontrib_qthelp/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-qthelp diff --git a/build/pkgs/sphinxcontrib_serializinghtml/distros/repology.txt b/build/pkgs/sphinxcontrib_serializinghtml/distros/repology.txt new file mode 100644 index 00000000000..e4112e85ad0 --- /dev/null +++ b/build/pkgs/sphinxcontrib_serializinghtml/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-serializinghtml diff --git a/build/pkgs/sphinxcontrib_websupport/distros/repology.txt b/build/pkgs/sphinxcontrib_websupport/distros/repology.txt new file mode 100644 index 00000000000..afa8a811123 --- /dev/null +++ b/build/pkgs/sphinxcontrib_websupport/distros/repology.txt @@ -0,0 +1 @@ +python:sphinxcontrib-websupport diff --git a/build/pkgs/sqlalchemy/distros/repology.txt b/build/pkgs/sqlalchemy/distros/repology.txt new file mode 100644 index 00000000000..e5de6e8fbd1 --- /dev/null +++ b/build/pkgs/sqlalchemy/distros/repology.txt @@ -0,0 +1 @@ +python:sqlalchemy diff --git a/build/pkgs/sqlite/distros/repology.txt b/build/pkgs/sqlite/distros/repology.txt new file mode 100644 index 00000000000..532c6c608dd --- /dev/null +++ b/build/pkgs/sqlite/distros/repology.txt @@ -0,0 +1 @@ +sqlite diff --git a/build/pkgs/suitesparse/distros/repology.txt b/build/pkgs/suitesparse/distros/repology.txt new file mode 100644 index 00000000000..8309d12f520 --- /dev/null +++ b/build/pkgs/suitesparse/distros/repology.txt @@ -0,0 +1 @@ +suitesparse diff --git a/build/pkgs/surf/distros/repology.txt b/build/pkgs/surf/distros/repology.txt new file mode 100644 index 00000000000..e33671ee0e3 --- /dev/null +++ b/build/pkgs/surf/distros/repology.txt @@ -0,0 +1 @@ +surf-alggeo diff --git a/build/pkgs/symengine/SPKG.rst b/build/pkgs/symengine/SPKG.rst new file mode 100644 index 00000000000..2eeff9e352f --- /dev/null +++ b/build/pkgs/symengine/SPKG.rst @@ -0,0 +1,17 @@ +symengine: A C++ symbolic manipulation library +============================================== + +Description +----------- + +SymEngine is a standalone fast C++ symbolic manipulation library. + +License +------- + +BSD 3-clause + +Upstream Contact +---------------- + +https://github.com/symengine/symengine diff --git a/build/pkgs/symengine/checksums.ini b/build/pkgs/symengine/checksums.ini new file mode 100644 index 00000000000..e41647436f2 --- /dev/null +++ b/build/pkgs/symengine/checksums.ini @@ -0,0 +1,5 @@ +tarball=symengine-VERSION.tar.gz +sha1=abd7d39b1b724f47bcdc5b1d811cf33f2c413aea +md5=3c0df2b14310467c6d45bc26a557324b +cksum=100973134 +upstream_url=https://github.com/symengine/symengine/releases/download/vVERSION/symengine-VERSION.tar.gz diff --git a/build/pkgs/symengine/dependencies b/build/pkgs/symengine/dependencies new file mode 100644 index 00000000000..77248f3a86e --- /dev/null +++ b/build/pkgs/symengine/dependencies @@ -0,0 +1,5 @@ +$(MP_LIBRARY) arb ecm flint mpc mpfr | cmake + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/symengine/distros/conda.txt b/build/pkgs/symengine/distros/conda.txt new file mode 100644 index 00000000000..7bcf459b746 --- /dev/null +++ b/build/pkgs/symengine/distros/conda.txt @@ -0,0 +1 @@ +symengine diff --git a/build/pkgs/symengine/distros/freebsd.txt b/build/pkgs/symengine/distros/freebsd.txt new file mode 100644 index 00000000000..d03b55dff4c --- /dev/null +++ b/build/pkgs/symengine/distros/freebsd.txt @@ -0,0 +1 @@ +math/symengine diff --git a/build/pkgs/symengine/distros/gentoo.txt b/build/pkgs/symengine/distros/gentoo.txt new file mode 100644 index 00000000000..c9abf4739e8 --- /dev/null +++ b/build/pkgs/symengine/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/symengine diff --git a/build/pkgs/symengine/distros/nix.txt b/build/pkgs/symengine/distros/nix.txt new file mode 100644 index 00000000000..7bcf459b746 --- /dev/null +++ b/build/pkgs/symengine/distros/nix.txt @@ -0,0 +1 @@ +symengine diff --git a/build/pkgs/symengine/distros/opensuse.txt b/build/pkgs/symengine/distros/opensuse.txt new file mode 100644 index 00000000000..7bcf459b746 --- /dev/null +++ b/build/pkgs/symengine/distros/opensuse.txt @@ -0,0 +1 @@ +symengine diff --git a/build/pkgs/symengine/distros/repology.txt b/build/pkgs/symengine/distros/repology.txt new file mode 100644 index 00000000000..7bcf459b746 --- /dev/null +++ b/build/pkgs/symengine/distros/repology.txt @@ -0,0 +1 @@ +symengine diff --git a/build/pkgs/symengine/package-version.txt b/build/pkgs/symengine/package-version.txt new file mode 100644 index 00000000000..a918a2aa18d --- /dev/null +++ b/build/pkgs/symengine/package-version.txt @@ -0,0 +1 @@ +0.6.0 diff --git a/build/pkgs/symengine/spkg-check.in b/build/pkgs/symengine/spkg-check.in new file mode 100644 index 00000000000..b2ec9936595 --- /dev/null +++ b/build/pkgs/symengine/spkg-check.in @@ -0,0 +1,2 @@ +cd src/build +ctest diff --git a/build/pkgs/symengine/spkg-install.in b/build/pkgs/symengine/spkg-install.in new file mode 100644 index 00000000000..36856e9d5b3 --- /dev/null +++ b/build/pkgs/symengine/spkg-install.in @@ -0,0 +1,20 @@ +cd src +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX="$SAGE_LOCAL" \ + -DCMAKE_PREFIX_PATH="$SAGE_LOCAL" \ + -DWITH_SYMENGINE_THREAD_SAFE=yes \ + -DWITH_ECM=yes \ + -DWITH_FLINT=yes \ + -DWITH_ARB=yes \ + -DWITH_MPFR=yes \ + -DWITH_MPC=yes \ + -DWITH_LLVM=no \ + -DINTEGER_CLASS="flint" \ + -DBUILD_BENCHMARKS=no \ + -DBUILD_SHARED_LIBS=yes \ + -DBUILD_TESTS=yes \ + .. + +sdh_make +sdh_make install diff --git a/build/pkgs/symengine/type b/build/pkgs/symengine/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/symengine/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/symengine_py/SPKG.rst b/build/pkgs/symengine_py/SPKG.rst new file mode 100644 index 00000000000..e361acd73b7 --- /dev/null +++ b/build/pkgs/symengine_py/SPKG.rst @@ -0,0 +1,18 @@ +symengine_py: Python wrappers for SymEngine +=========================================== + +Description +----------- + +Python wrappers for SymEngine + +License +------- + +symengine.py is MIT licensed and uses several LGPL, BSD-3 and MIT +licensed libraries + +Upstream Contact +---------------- + +https://github.com/symengine/symengine.py diff --git a/build/pkgs/symengine_py/checksums.ini b/build/pkgs/symengine_py/checksums.ini new file mode 100644 index 00000000000..489d040bcc7 --- /dev/null +++ b/build/pkgs/symengine_py/checksums.ini @@ -0,0 +1,5 @@ +tarball=symengine.py-VERSION.tar.gz +sha1=40df2b8f406b6ac4a86c83a82d31593b5738a470 +md5=52b035da7851414d74f3bde83b6c2976 +cksum=2829876300 +upstream_url=https://github.com/symengine/symengine.py/archive/vVERSION.tar.gz diff --git a/build/pkgs/symengine_py/dependencies b/build/pkgs/symengine_py/dependencies new file mode 100644 index 00000000000..1b773295978 --- /dev/null +++ b/build/pkgs/symengine_py/dependencies @@ -0,0 +1,5 @@ +symengine $(PYTHON) | cmake cython $(PYTHON_TOOLCHAIN) $(and $(filter-out no,$(SAGE_CHECK_symengine_py)), nose) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/symengine_py/distros/conda.txt b/build/pkgs/symengine_py/distros/conda.txt new file mode 100644 index 00000000000..16ff7effe29 --- /dev/null +++ b/build/pkgs/symengine_py/distros/conda.txt @@ -0,0 +1 @@ +python-symengine diff --git a/build/pkgs/symengine_py/distros/repology.txt b/build/pkgs/symengine_py/distros/repology.txt new file mode 100644 index 00000000000..10308af4f31 --- /dev/null +++ b/build/pkgs/symengine_py/distros/repology.txt @@ -0,0 +1 @@ +python:symengine diff --git a/build/pkgs/symengine_py/install-requires.txt b/build/pkgs/symengine_py/install-requires.txt new file mode 100644 index 00000000000..9718edce40c --- /dev/null +++ b/build/pkgs/symengine_py/install-requires.txt @@ -0,0 +1 @@ +symengine.py >= 0.6.1 diff --git a/build/pkgs/symengine_py/package-version.txt b/build/pkgs/symengine_py/package-version.txt new file mode 100644 index 00000000000..ee6cdce3c29 --- /dev/null +++ b/build/pkgs/symengine_py/package-version.txt @@ -0,0 +1 @@ +0.6.1 diff --git a/build/pkgs/symengine_py/spkg-check.in b/build/pkgs/symengine_py/spkg-check.in new file mode 100644 index 00000000000..fba8e388486 --- /dev/null +++ b/build/pkgs/symengine_py/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +nosetests symengine/tests -v diff --git a/build/pkgs/symengine_py/spkg-install.in b/build/pkgs/symengine_py/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/symengine_py/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/symengine_py/type b/build/pkgs/symengine_py/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/symengine_py/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/symmetrica/distros/repology.txt b/build/pkgs/symmetrica/distros/repology.txt new file mode 100644 index 00000000000..27c7a2636ab --- /dev/null +++ b/build/pkgs/symmetrica/distros/repology.txt @@ -0,0 +1 @@ +symmetrica diff --git a/build/pkgs/sympow/distros/repology.txt b/build/pkgs/sympow/distros/repology.txt new file mode 100644 index 00000000000..a2ae7a8a59c --- /dev/null +++ b/build/pkgs/sympow/distros/repology.txt @@ -0,0 +1 @@ +sympow diff --git a/build/pkgs/sympy/distros/repology.txt b/build/pkgs/sympy/distros/repology.txt new file mode 100644 index 00000000000..078f604676f --- /dev/null +++ b/build/pkgs/sympy/distros/repology.txt @@ -0,0 +1 @@ +python:sympy diff --git a/build/pkgs/tachyon/distros/repology.txt b/build/pkgs/tachyon/distros/repology.txt new file mode 100644 index 00000000000..e379e7e2d4e --- /dev/null +++ b/build/pkgs/tachyon/distros/repology.txt @@ -0,0 +1,2 @@ +tachyon +tachyon-opengl diff --git a/build/pkgs/tdlib/distros/repology.txt b/build/pkgs/tdlib/distros/repology.txt new file mode 100644 index 00000000000..b57463d09d6 --- /dev/null +++ b/build/pkgs/tdlib/distros/repology.txt @@ -0,0 +1 @@ +python:tdlib diff --git a/build/pkgs/termcap/SPKG.rst b/build/pkgs/termcap/SPKG.rst deleted file mode 100644 index 0acea46ad6c..00000000000 --- a/build/pkgs/termcap/SPKG.rst +++ /dev/null @@ -1,30 +0,0 @@ -termcap: Terminal control library -================================= - -Description ------------ - -A library of C functions that enable programs to send control strings to -terminals in a way independent of the terminal type. - -License -------- - -GPL version 2 - - -Upstream Contact ----------------- - -Please report any bugs in this library to bug-gnu-emacs@prep.ai.mit.edu - -Dependencies ------------- - -- GNU patch - - -Special Update/Build Instructions ---------------------------------- - -None diff --git a/build/pkgs/termcap/checksums.ini b/build/pkgs/termcap/checksums.ini deleted file mode 100644 index 07aeae6f762..00000000000 --- a/build/pkgs/termcap/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=termcap-VERSION.tar.bz2 -sha1=8747ab1dfc349304fa320f1a49c765829bad8ec5 -md5=b7ed7b1e377ba8a427f230dc25b665f6 -cksum=2089505110 diff --git a/build/pkgs/termcap/distros/repology.txt b/build/pkgs/termcap/distros/repology.txt new file mode 100644 index 00000000000..4809e2a5027 --- /dev/null +++ b/build/pkgs/termcap/distros/repology.txt @@ -0,0 +1 @@ +termcap diff --git a/build/pkgs/termcap/package-version.txt b/build/pkgs/termcap/package-version.txt deleted file mode 100644 index d77b1f67603..00000000000 --- a/build/pkgs/termcap/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.3.1.p3 diff --git a/build/pkgs/termcap/patches/Makefile.in.patch b/build/pkgs/termcap/patches/Makefile.in.patch deleted file mode 100644 index e04b2943857..00000000000 --- a/build/pkgs/termcap/patches/Makefile.in.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -ur src/Makefile.in src.spkg/Makefile.in ---- src/Makefile.in 1995-08-17 02:54:29.000000000 +0200 -+++ src.spkg/Makefile.in 2005-12-12 05:39:42.000000000 +0100 -@@ -46,7 +46,10 @@ - # so compilers besides gcc can find it by default. - # If it is empty or not defined, termcap.h will only be installed in - # includedir. --oldincludedir = /usr/include -+ -+# I commented this, since a Sage install should work as non-root. -+# -- William Stein -+#oldincludedir = /usr/include - - # Directory in which to install the documentation info files. - infodir = $(prefix)/info -@@ -66,7 +69,7 @@ - termcap.src termcap.texi termcap.info* \ - texinfo.tex Makefile.in configure configure.in mkinstalldirs install-sh - --all: libtermcap.a info -+all: libtermcap.a - - .c.o: - $(CC) -c $(CPPFLAGS) $(DEFS) -I. -I$(srcdir) $(CFLAGS) $< -@@ -77,8 +80,6 @@ - cd $(srcdir); $(INSTALL_DATA) termcap.h $(includedir)/termcap.h - -cd $(srcdir); test -z "$(oldincludedir)" || \ - $(INSTALL_DATA) termcap.h $(oldincludedir)/termcap.h -- cd $(srcdir); for f in termcap.info*; \ -- do $(INSTALL_DATA) $$f $(infodir)/$$f; done - - uninstall: @uninstalldata@ - rm -f $(libdir)/libtermcap.a $(includedir)/termcap.h diff --git a/build/pkgs/termcap/patches/strcmp_NULL.patch b/build/pkgs/termcap/patches/strcmp_NULL.patch deleted file mode 100644 index 7733a89d7fd..00000000000 --- a/build/pkgs/termcap/patches/strcmp_NULL.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -ru src/termcap.c src.fixed/termcap.c ---- src/termcap.c 2002-02-25 18:59:21.000000000 +0100 -+++ src.fixed/termcap.c 2012-01-09 11:04:54.000000000 +0100 -@@ -460,6 +460,7 @@ - char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */ - char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ - int filep; -+ char *term_name; - - #ifdef INTERNAL_TERMINAL - /* For the internal terminal we don't want to read any termcap file, -@@ -500,7 +501,8 @@ - it is the entry itself, but only if - the name the caller requested matches the TERM variable. */ - -- if (termcap_name && !filep && !strcmp (name, getenv ("TERM"))) -+ term_name = getenv("TERM"); -+ if (termcap_name && !filep && term_name && !strcmp (name, term_name)) - { - indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0); - if (!indirect) diff --git a/build/pkgs/termcap/spkg-install.in b/build/pkgs/termcap/spkg-install.in deleted file mode 100644 index deafef924ee..00000000000 --- a/build/pkgs/termcap/spkg-install.in +++ /dev/null @@ -1,45 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -export CFLAGS="-O2 -g -fPIC $CFLAGS" - -cd src/ - - -build() -{ - ./configure --prefix="$SAGE_LOCAL" - if [ $? -ne 0 ]; then - echo >&2 "Error configuring termcap" - exit 1 - fi - - $MAKE CFLAGS="$CFLAGS" install - if [ $? -ne 0 ]; then - echo >&2 "Error building termcap" - exit 1 - fi -} - -build - -# If we cannot link programs against -lncurses, then symlink -# libtermcap.a to libncurses.a. This is to support the PARI and -# Python build scripts when /usr/lib/libncurses.so exists but -# cannot be linked (e.g. because we are cross-compiling). -# See #12725. -if testcflags.sh -lncurses >/dev/null; then - echo "Good, we can link against libncurses on your system." -else - echo "We cannot link against libncurses on your system," - echo "making a symbolic link libncurses.a -> libtermcap.a" - echo "(to work around bugs in the PARI and Python build scripts)" - cd "$SAGE_LOCAL/lib" && ln -s libtermcap.a libncurses.a - if [ $? -ne 0 ]; then - echo >&2 "Error making symbolic link libncurses.a -> libtermcap.a" - exit 1 - fi -fi diff --git a/build/pkgs/terminado/distros/repology.txt b/build/pkgs/terminado/distros/repology.txt new file mode 100644 index 00000000000..98af16755b5 --- /dev/null +++ b/build/pkgs/terminado/distros/repology.txt @@ -0,0 +1,2 @@ +terminado +python:terminado diff --git a/build/pkgs/testpath/distros/repology.txt b/build/pkgs/testpath/distros/repology.txt new file mode 100644 index 00000000000..5d3e402eadd --- /dev/null +++ b/build/pkgs/testpath/distros/repology.txt @@ -0,0 +1,2 @@ +testpath +python:testpath diff --git a/build/pkgs/texlive/distros/repology.txt b/build/pkgs/texlive/distros/repology.txt new file mode 100644 index 00000000000..ba0ee3a029f --- /dev/null +++ b/build/pkgs/texlive/distros/repology.txt @@ -0,0 +1 @@ +texlive diff --git a/build/pkgs/texttable/distros/repology.txt b/build/pkgs/texttable/distros/repology.txt new file mode 100644 index 00000000000..a075cc0aa2d --- /dev/null +++ b/build/pkgs/texttable/distros/repology.txt @@ -0,0 +1,2 @@ +texttable +python:texttable diff --git a/build/pkgs/texttable/install-requires.txt b/build/pkgs/texttable/install-requires.txt new file mode 100644 index 00000000000..094296b2ec3 --- /dev/null +++ b/build/pkgs/texttable/install-requires.txt @@ -0,0 +1 @@ +texttable >=1.6.3 diff --git a/build/pkgs/thebe/distros/repology.txt b/build/pkgs/thebe/distros/repology.txt new file mode 100644 index 00000000000..71905dc408c --- /dev/null +++ b/build/pkgs/thebe/distros/repology.txt @@ -0,0 +1 @@ +thebe \ No newline at end of file diff --git a/build/pkgs/threejs/distros/repology.txt b/build/pkgs/threejs/distros/repology.txt new file mode 100644 index 00000000000..a6a450f79f0 --- /dev/null +++ b/build/pkgs/threejs/distros/repology.txt @@ -0,0 +1,2 @@ +threejs +threejs-sage diff --git a/build/pkgs/topcom/distros/repology.txt b/build/pkgs/topcom/distros/repology.txt new file mode 100644 index 00000000000..fffddc135ba --- /dev/null +++ b/build/pkgs/topcom/distros/repology.txt @@ -0,0 +1 @@ +topcom diff --git a/build/pkgs/tornado/distros/repology.txt b/build/pkgs/tornado/distros/repology.txt new file mode 100644 index 00000000000..5639f05ddb4 --- /dev/null +++ b/build/pkgs/tornado/distros/repology.txt @@ -0,0 +1 @@ +python:tornado diff --git a/build/pkgs/tox/distros/repology.txt b/build/pkgs/tox/distros/repology.txt new file mode 100644 index 00000000000..a75cee527ae --- /dev/null +++ b/build/pkgs/tox/distros/repology.txt @@ -0,0 +1 @@ +python:tox diff --git a/build/pkgs/traitlets/distros/repology.txt b/build/pkgs/traitlets/distros/repology.txt new file mode 100644 index 00000000000..b12b5a01a40 --- /dev/null +++ b/build/pkgs/traitlets/distros/repology.txt @@ -0,0 +1,2 @@ +traitlets +python:traitlets diff --git a/build/pkgs/tzlocal/distros/repology.txt b/build/pkgs/tzlocal/distros/repology.txt new file mode 100644 index 00000000000..a38ebf0989e --- /dev/null +++ b/build/pkgs/tzlocal/distros/repology.txt @@ -0,0 +1,2 @@ +tzlocal +python:tzlocal diff --git a/build/pkgs/valgrind/distros/repology.txt b/build/pkgs/valgrind/distros/repology.txt new file mode 100644 index 00000000000..e7af4129194 --- /dev/null +++ b/build/pkgs/valgrind/distros/repology.txt @@ -0,0 +1 @@ +valgrind diff --git a/build/pkgs/vcversioner/distros/repology.txt b/build/pkgs/vcversioner/distros/repology.txt new file mode 100644 index 00000000000..1cf127b4f86 --- /dev/null +++ b/build/pkgs/vcversioner/distros/repology.txt @@ -0,0 +1,2 @@ +vcversioner +python:vcversioner diff --git a/build/pkgs/wcwidth/distros/repology.txt b/build/pkgs/wcwidth/distros/repology.txt new file mode 100644 index 00000000000..2ce937ebbd9 --- /dev/null +++ b/build/pkgs/wcwidth/distros/repology.txt @@ -0,0 +1,2 @@ +wcwidth +python:wcwidth diff --git a/build/pkgs/webencodings/distros/repology.txt b/build/pkgs/webencodings/distros/repology.txt new file mode 100644 index 00000000000..a0b35f8ade3 --- /dev/null +++ b/build/pkgs/webencodings/distros/repology.txt @@ -0,0 +1 @@ +python:webencodings diff --git a/build/pkgs/wheel/distros/repology.txt b/build/pkgs/wheel/distros/repology.txt new file mode 100644 index 00000000000..b4c5576a110 --- /dev/null +++ b/build/pkgs/wheel/distros/repology.txt @@ -0,0 +1,2 @@ +wheel +python:wheel diff --git a/build/pkgs/wheel/install-requires.txt b/build/pkgs/wheel/install-requires.txt new file mode 100644 index 00000000000..fb07166d42c --- /dev/null +++ b/build/pkgs/wheel/install-requires.txt @@ -0,0 +1,2 @@ +# https://trac.sagemath.org/ticket/31050 - version constraint for macOS Big Sur support +wheel >=0.36.2 diff --git a/build/pkgs/wheel/spkg-install.in b/build/pkgs/wheel/spkg-install.in index 9c0353a87ef..5ad5c08fe0e 100644 --- a/build/pkgs/wheel/spkg-install.in +++ b/build/pkgs/wheel/spkg-install.in @@ -1,9 +1,13 @@ cd src -versions=3 +python3 setup.py --no-user-cfg install \ + --single-version-externally-managed --root="$SAGE_DESTDIR" || \ + sdh_die "Error building / installing package 'wheel'" -for vers in $versions; do - python${vers} setup.py --no-user-cfg install \ - --single-version-externally-managed --root="$SAGE_DESTDIR" || \ - sdh_die "Error building / installing wheel for Python ${vers}" -done +# Now build a wheel so that we have a complete set of wheels. + +# Because of staging, we cannot use the installed 'wheel' package yet. +export PYTHONPATH="$(pwd)/src" + +sdh_setup_bdist_wheel +sdh_store_wheel . diff --git a/build/pkgs/widgetsnbextension/distros/repology.txt b/build/pkgs/widgetsnbextension/distros/repology.txt new file mode 100644 index 00000000000..dec19021e8f --- /dev/null +++ b/build/pkgs/widgetsnbextension/distros/repology.txt @@ -0,0 +1,3 @@ +python:widgetsnbextension +jupyter-widgetsnbextension +python:jupyter-widgetsnbextension diff --git a/build/pkgs/xz/distros/repology.txt b/build/pkgs/xz/distros/repology.txt new file mode 100644 index 00000000000..d66e95ca507 --- /dev/null +++ b/build/pkgs/xz/distros/repology.txt @@ -0,0 +1 @@ +xz diff --git a/build/pkgs/yasm/distros/repology.txt b/build/pkgs/yasm/distros/repology.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/repology.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/zeromq/distros/repology.txt b/build/pkgs/zeromq/distros/repology.txt new file mode 100644 index 00000000000..bd4caa9e83d --- /dev/null +++ b/build/pkgs/zeromq/distros/repology.txt @@ -0,0 +1 @@ +zeromq diff --git a/build/pkgs/zipp/distros/repology.txt b/build/pkgs/zipp/distros/repology.txt new file mode 100644 index 00000000000..c23522a7379 --- /dev/null +++ b/build/pkgs/zipp/distros/repology.txt @@ -0,0 +1 @@ +python:zipp diff --git a/build/pkgs/zlib/distros/repology.txt b/build/pkgs/zlib/distros/repology.txt new file mode 100644 index 00000000000..f22003e83c1 --- /dev/null +++ b/build/pkgs/zlib/distros/repology.txt @@ -0,0 +1 @@ +zlib diff --git a/build/pkgs/zn_poly/distros/repology.txt b/build/pkgs/zn_poly/distros/repology.txt new file mode 100644 index 00000000000..597d1f28efa --- /dev/null +++ b/build/pkgs/zn_poly/distros/repology.txt @@ -0,0 +1,2 @@ +zn-poly +libzn-poly diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 9fb8a6aefb2..3514da9200c 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -23,7 +23,7 @@ from sage_bootstrap.tarball import Tarball, FileNotMirroredError from sage_bootstrap.updater import ChecksumUpdater, PackageUpdater from sage_bootstrap.creator import PackageCreator -from sage_bootstrap.pypi import PyPiVersion, PyPiNotFound +from sage_bootstrap.pypi import PyPiVersion, PyPiNotFound, PyPiError from sage_bootstrap.fileserver import FileServer from sage_bootstrap.expand_class import PackageClass @@ -138,21 +138,24 @@ def update_latest(self, package_name): log.debug('%s is not a pypi package', package_name) return else: - pypi.update() + pypi.update(Package(package_name)) - def update_latest_all(self): - log.debug('Attempting to update all packages') + def update_latest_cls(self, package_name_or_class): exclude = [ - 'atlas', 'flint', 'bzip2', 'ecm', 'freetype', 'gap', 'glpk', 'graphs', - 'iconv', 'patch', 'r', 'configure', 'bliss', 'readline', 'decorator', - 'igraph', 'rw', 'planarity', 'gambit', + 'cypari' # Name conflict ] - pc = PackageClass(':standard:') - for package_name in pc.names: + # Restrict to normal Python packages + pc = PackageClass(package_name_or_class, has_files=['checksums.ini', 'install-requires.txt']) + if not pc.names: + log.warn('nothing to do (does not name a normal Python package)') + for package_name in sorted(pc.names): if package_name in exclude: log.debug('skipping %s because of pypi name collision', package_name) continue - self.update_latest(package_name) + try: + self.update_latest(package_name) + except PyPiError as e: + log.warn('updating %s failed: %s', package_name, e) def download(self, package_name, allow_upstream=False): """ @@ -210,22 +213,14 @@ def upload_cls(self, package_name_or_class): log.info('Publishing') fs.publish() - def fix_all_checksums(self): + def fix_checksum_cls(self, *package_classes): """ - Fix the checksum of a package + Fix the checksum of packages $ sage --package fix-checksum """ - for pkg in Package.all(): - if not os.path.exists(pkg.tarball.upstream_fqn): - log.debug('Ignoring {0} because tarball is not cached'.format(pkg.tarball_filename)) - continue - if pkg.tarball.checksum_verifies(): - log.debug('Checksum of {0} (tarball {1}) unchanged'.format(pkg.name, pkg.tarball_filename)) - continue - update = ChecksumUpdater(pkg.name) - print('Updating checksum of {0} (tarball {1})'.format(pkg.name, pkg.tarball_filename)) - update.fix_checksum() + pc = PackageClass(*package_classes, has_files=['checksums.ini']) + pc.apply(self.fix_checksum) def fix_checksum(self, package_name): """ @@ -237,12 +232,18 @@ def fix_checksum(self, package_name): log.debug('Correcting the checksum of %s', package_name) update = ChecksumUpdater(package_name) pkg = update.package + if not pkg.tarball_filename: + log.info('Ignoring {0} because it is not a normal package'.format(package_name)) + return + if not os.path.exists(pkg.tarball.upstream_fqn): + log.info('Ignoring {0} because tarball is not cached'.format(package_name)) + return if pkg.tarball.checksum_verifies(): - print('Checksum of {0} (tarball {1}) unchanged'.format(package_name, pkg.tarball_filename)) + log.info('Checksum of {0} (tarball {1}) unchanged'.format(package_name, pkg.tarball_filename)) else: - print('Updating checksum of {0} (tarball {1})'.format(package_name, pkg.tarball_filename)) + log.info('Updating checksum of {0} (tarball {1})'.format(package_name, pkg.tarball_filename)) update.fix_checksum() - + def create(self, package_name, version=None, tarball=None, pkg_type=None, upstream_url=None, description=None, license=None, upstream_contact=None, pypi=False, source='normal'): """ diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 1a816918ab7..b8e5f66f9d0 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -21,11 +21,7 @@ log = logging.getLogger() -# Note that argparse is not part of Python 2.6, so we bundle it -try: - import argparse -except ImportError: - from sage_bootstrap.compat import argparse +import argparse from sage_bootstrap.app import Application @@ -274,10 +270,13 @@ def make_parser(): parser_fix_checksum = subparsers.add_parser( 'fix-checksum', epilog=epilog_fix_checksum, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Fix the checksum of package.') + help='Fix the checksum of normal packages.') parser_fix_checksum.add_argument( - 'package_name', nargs='?', default=None, type=str, - help='Package name. Default: fix all packages.') + 'package_class', metavar='[package_name|:package_type:]', + type=str, default=[':all:'], nargs='*', + help=('package name or designator for all packages of a given type ' + '(one of :all:, :standard:, :optional:, :experimental:, and :huge:); ' + 'default: :all:')) parser_create = subparsers.add_parser( 'create', epilog=epilog_create, @@ -334,10 +333,7 @@ def run(): elif args.subcommand == 'update': app.update(args.package_name, args.new_version, url=args.url) elif args.subcommand == 'update-latest': - if args.package_name == ':all:': - app.update_latest_all() - else: - app.update_latest(args.package_name) + app.update_latest_cls(args.package_name) elif args.subcommand == 'download': app.download_cls(args.package_name, allow_upstream=args.allow_upstream, @@ -349,10 +345,7 @@ def run(): elif args.subcommand == 'upload': app.upload_cls(args.package_name) elif args.subcommand == 'fix-checksum': - if args.package_name is None: - app.fix_all_checksums() - else: - app.fix_checksum(args.package_name) + app.fix_checksum_cls(*args.package_class) else: raise RuntimeError('unknown subcommand: {0}'.format(args)) diff --git a/build/sage_bootstrap/compat/argparse.py b/build/sage_bootstrap/compat/argparse.py deleted file mode 100644 index e3ddc96dc15..00000000000 --- a/build/sage_bootstrap/compat/argparse.py +++ /dev/null @@ -1,2376 +0,0 @@ -# Author: Steven J. Bethard . -# Maintainer: Thomas Waldmann - -"""Command-line parsing library - -This module is an optparse-inspired command-line parsing library that: - - - handles both optional and positional arguments - - produces highly informative usage messages - - supports parsers that dispatch to sub-parsers - -The following is a simple usage example that sums integers from the -command-line and writes the result to a file:: - - parser = argparse.ArgumentParser( - description='sum the integers at the command line') - parser.add_argument( - 'integers', metavar='int', nargs='+', type=int, - help='an integer to be summed') - parser.add_argument( - '--log', default=sys.stdout, type=argparse.FileType('w'), - help='the file where the sum should be written') - args = parser.parse_args() - args.log.write('%s' % sum(args.integers)) - args.log.close() - -The module contains the following public classes: - - - ArgumentParser -- The main entry point for command-line parsing. As the - example above shows, the add_argument() method is used to populate - the parser with actions for optional and positional arguments. Then - the parse_args() method is invoked to convert the args at the - command-line into an object with attributes. - - - ArgumentError -- The exception raised by ArgumentParser objects when - there are errors with the parser's actions. Errors raised while - parsing the command-line are caught by ArgumentParser and emitted - as command-line messages. - - - FileType -- A factory for defining types of files to be created. As the - example above shows, instances of FileType are typically passed as - the type= argument of add_argument() calls. - - - Action -- The base class for parser actions. Typically actions are - selected by passing strings like 'store_true' or 'append_const' to - the action= argument of add_argument(). However, for greater - customization of ArgumentParser actions, subclasses of Action may - be defined and passed as the action= argument. - - - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, - ArgumentDefaultsHelpFormatter -- Formatter classes which - may be passed as the formatter_class= argument to the - ArgumentParser constructor. HelpFormatter is the default, - RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser - not to change the formatting for help text, and - ArgumentDefaultsHelpFormatter adds information about argument defaults - to the help. - -All other classes in this module are considered implementation details. -(Also note that HelpFormatter and RawDescriptionHelpFormatter are only -considered public as object names -- the API of the formatter objects is -still considered an implementation detail.) -""" - -__version__ = '1.4.0' # we use our own version number independant of the - # one in stdlib and we release this on pypi. - -__external_lib__ = True # to make sure the tests really test THIS lib, - # not the builtin one in Python stdlib - -__all__ = [ - 'ArgumentParser', - 'ArgumentError', - 'ArgumentTypeError', - 'FileType', - 'HelpFormatter', - 'ArgumentDefaultsHelpFormatter', - 'RawDescriptionHelpFormatter', - 'RawTextHelpFormatter', - 'Namespace', - 'Action', - 'ONE_OR_MORE', - 'OPTIONAL', - 'PARSER', - 'REMAINDER', - 'SUPPRESS', - 'ZERO_OR_MORE', -] - - -import copy as _copy -import os as _os -import re as _re -import sys as _sys -import textwrap as _textwrap - -from gettext import gettext as _ - - -try: - basestring -except NameError: - basestring = str - - -def _callable(obj): - return hasattr(obj, '__call__') or hasattr(obj, '__bases__') - - -SUPPRESS = '==SUPPRESS==' - -OPTIONAL = '?' -ZERO_OR_MORE = '*' -ONE_OR_MORE = '+' -PARSER = 'A...' -REMAINDER = '...' -_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' - -# ============================= -# Utility functions and classes -# ============================= - -class _AttributeHolder(object): - """Abstract base class that provides __repr__. - - The __repr__ method returns a string in the format:: - ClassName(attr=name, attr=name, ...) - The attributes are determined either by a class-level attribute, - '_kwarg_names', or by inspecting the instance __dict__. - """ - - def __repr__(self): - type_name = type(self).__name__ - arg_strings = [] - for arg in self._get_args(): - arg_strings.append(repr(arg)) - for name, value in self._get_kwargs(): - arg_strings.append('%s=%r' % (name, value)) - return '%s(%s)' % (type_name, ', '.join(arg_strings)) - - def _get_kwargs(self): - return sorted(self.__dict__.items()) - - def _get_args(self): - return [] - - -def _ensure_value(namespace, name, value): - if getattr(namespace, name, None) is None: - setattr(namespace, name, value) - return getattr(namespace, name) - - -# =============== -# Formatting Help -# =============== - -class HelpFormatter(object): - """Formatter for generating usage messages and argument help strings. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def __init__(self, - prog, - indent_increment=2, - max_help_position=24, - width=None): - - # default setting for width - if width is None: - try: - width = int(_os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - - self._prog = prog - self._indent_increment = indent_increment - self._max_help_position = max_help_position - self._width = width - - self._current_indent = 0 - self._level = 0 - self._action_max_length = 0 - - self._root_section = self._Section(self, None) - self._current_section = self._root_section - - self._whitespace_matcher = _re.compile(r'\s+') - self._long_break_matcher = _re.compile(r'\n\n\n+') - - # =============================== - # Section and indentation methods - # =============================== - def _indent(self): - self._current_indent += self._indent_increment - self._level += 1 - - def _dedent(self): - self._current_indent -= self._indent_increment - assert self._current_indent >= 0, 'Indent decreased below 0.' - self._level -= 1 - - class _Section(object): - - def __init__(self, formatter, parent, heading=None): - self.formatter = formatter - self.parent = parent - self.heading = heading - self.items = [] - - def format_help(self): - # format the indented section - if self.parent is not None: - self.formatter._indent() - join = self.formatter._join_parts - for func, args in self.items: - func(*args) - item_help = join([func(*args) for func, args in self.items]) - if self.parent is not None: - self.formatter._dedent() - - # return nothing if the section was empty - if not item_help: - return '' - - # add the heading if the section was non-empty - if self.heading is not SUPPRESS and self.heading is not None: - current_indent = self.formatter._current_indent - heading = '%*s%s:\n' % (current_indent, '', self.heading) - else: - heading = '' - - # join the section-initial newline, the heading and the help - return join(['\n', heading, item_help, '\n']) - - def _add_item(self, func, args): - self._current_section.items.append((func, args)) - - # ======================== - # Message building methods - # ======================== - def start_section(self, heading): - self._indent() - section = self._Section(self, self._current_section, heading) - self._add_item(section.format_help, []) - self._current_section = section - - def end_section(self): - self._current_section = self._current_section.parent - self._dedent() - - def add_text(self, text): - if text is not SUPPRESS and text is not None: - self._add_item(self._format_text, [text]) - - def add_usage(self, usage, actions, groups, prefix=None): - if usage is not SUPPRESS: - args = usage, actions, groups, prefix - self._add_item(self._format_usage, args) - - def add_argument(self, action): - if action.help is not SUPPRESS: - - # find all invocations - get_invocation = self._format_action_invocation - invocations = [get_invocation(action)] - for subaction in self._iter_indented_subactions(action): - invocations.append(get_invocation(subaction)) - - # update the maximum item length - invocation_length = max([len(s) for s in invocations]) - action_length = invocation_length + self._current_indent - self._action_max_length = max(self._action_max_length, - action_length) - - # add the item to the list - self._add_item(self._format_action, [action]) - - def add_arguments(self, actions): - for action in actions: - self.add_argument(action) - - # ======================= - # Help-formatting methods - # ======================= - def format_help(self): - help = self._root_section.format_help() - if help: - help = self._long_break_matcher.sub('\n\n', help) - help = help.strip('\n') + '\n' - return help - - def _join_parts(self, part_strings): - return ''.join([part - for part in part_strings - if part and part is not SUPPRESS]) - - def _format_usage(self, usage, actions, groups, prefix): - if prefix is None: - prefix = _('usage: ') - - # if usage is specified, use that - if usage is not None: - usage = usage % dict(prog=self._prog) - - # if no optionals or positionals are available, usage is just prog - elif usage is None and not actions: - usage = '%(prog)s' % dict(prog=self._prog) - - # if optionals and positionals are available, calculate usage - elif usage is None: - prog = '%(prog)s' % dict(prog=self._prog) - - # split optionals from positionals - optionals = [] - positionals = [] - for action in actions: - if action.option_strings: - optionals.append(action) - else: - positionals.append(action) - - # build full usage string - format = self._format_actions_usage - action_usage = format(optionals + positionals, groups) - usage = ' '.join([s for s in [prog, action_usage] if s]) - - # wrap the usage parts if it's too long - text_width = self._width - self._current_indent - if len(prefix) + len(usage) > text_width: - - # break usage into wrappable parts - part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' - opt_usage = format(optionals, groups) - pos_usage = format(positionals, groups) - opt_parts = _re.findall(part_regexp, opt_usage) - pos_parts = _re.findall(part_regexp, pos_usage) - assert ' '.join(opt_parts) == opt_usage - assert ' '.join(pos_parts) == pos_usage - - # helper for wrapping lines - def get_lines(parts, indent, prefix=None): - lines = [] - line = [] - if prefix is not None: - line_len = len(prefix) - 1 - else: - line_len = len(indent) - 1 - for part in parts: - if line_len + 1 + len(part) > text_width: - lines.append(indent + ' '.join(line)) - line = [] - line_len = len(indent) - 1 - line.append(part) - line_len += len(part) + 1 - if line: - lines.append(indent + ' '.join(line)) - if prefix is not None: - lines[0] = lines[0][len(indent):] - return lines - - # if prog is short, follow it with optionals or positionals - if len(prefix) + len(prog) <= 0.75 * text_width: - indent = ' ' * (len(prefix) + len(prog) + 1) - if opt_parts: - lines = get_lines([prog] + opt_parts, indent, prefix) - lines.extend(get_lines(pos_parts, indent)) - elif pos_parts: - lines = get_lines([prog] + pos_parts, indent, prefix) - else: - lines = [prog] - - # if prog is long, put it on its own line - else: - indent = ' ' * len(prefix) - parts = opt_parts + pos_parts - lines = get_lines(parts, indent) - if len(lines) > 1: - lines = [] - lines.extend(get_lines(opt_parts, indent)) - lines.extend(get_lines(pos_parts, indent)) - lines = [prog] + lines - - # join lines into usage - usage = '\n'.join(lines) - - # prefix with 'usage:' - return '%s%s\n\n' % (prefix, usage) - - def _format_actions_usage(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - try: - start = actions.index(group._group_actions[0]) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if actions[start:end] == group._group_actions: - for action in group._group_actions: - group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' - else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' - for i in range(start + 1, end): - inserts[i] = '|' - - # collect all actions format strings - parts = [] - for i, action in enumerate(actions): - - # suppressed arguments are marked with None - # remove | separators for suppressed arguments - if action.help is SUPPRESS: - parts.append(None) - if inserts.get(i) == '|': - inserts.pop(i) - elif inserts.get(i + 1) == '|': - inserts.pop(i + 1) - - # produce all arg strings - elif not action.option_strings: - part = self._format_args(action, action.dest) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # add the action string to the list - parts.append(part) - - # produce the first way to invoke the option in brackets - else: - option_string = action.option_strings[0] - - # if the Optional doesn't take a value, format is: - # -s or --long - if action.nargs == 0: - part = '%s' % option_string - - # if the Optional takes a value, format is: - # -s ARGS or --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - part = '%s %s' % (option_string, args_string) - - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: - part = '[%s]' % part - - # add the action string to the list - parts.append(part) - - # insert things at the necessary indices - for i in sorted(inserts, reverse=True): - parts[i:i] = [inserts[i]] - - # join all the action items with spaces - text = ' '.join([item for item in parts if item is not None]) - - # clean up separators for mutually exclusive groups - open = r'[\[(]' - close = r'[\])]' - text = _re.sub(r'(%s) ' % open, r'\1', text) - text = _re.sub(r' (%s)' % close, r'\1', text) - text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) - text = text.strip() - - # return the text - return text - - def _format_text(self, text): - if '%(prog)' in text: - text = text % dict(prog=self._prog) - text_width = self._width - self._current_indent - indent = ' ' * self._current_indent - return self._fill_text(text, text_width, indent) + '\n\n' - - def _format_action(self, action): - # determine the required width and the entry label - help_position = min(self._action_max_length + 2, - self._max_help_position) - help_width = self._width - help_position - action_width = help_position - self._current_indent - 2 - action_header = self._format_action_invocation(action) - - # ho nelp; start on same line and add a final newline - if not action.help: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - - # short action name; start on the same line and pad two spaces - elif len(action_header) <= action_width: - tup = self._current_indent, '', action_width, action_header - action_header = '%*s%-*s ' % tup - indent_first = 0 - - # long action name; start on the next line - else: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - indent_first = help_position - - # collect the pieces of the action help - parts = [action_header] - - # if there was help for the action, add lines of help text - if action.help: - help_text = self._expand_help(action) - help_lines = self._split_lines(help_text, help_width) - parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) - for line in help_lines[1:]: - parts.append('%*s%s\n' % (help_position, '', line)) - - # or add a newline if the description doesn't end with one - elif not action_header.endswith('\n'): - parts.append('\n') - - # if there are any sub-actions, add their help as well - for subaction in self._iter_indented_subactions(action): - parts.append(self._format_action(subaction)) - - # return a single string - return self._join_parts(parts) - - def _format_action_invocation(self, action): - if not action.option_strings: - metavar, = self._metavar_formatter(action, action.dest)(1) - return metavar - - else: - parts = [] - - # if the Optional doesn't take a value, format is: - # -s, --long - if action.nargs == 0: - parts.extend(action.option_strings) - - # if the Optional takes a value, format is: - # -s ARGS, --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - for option_string in action.option_strings: - parts.append('%s %s' % (option_string, args_string)) - - return ', '.join(parts) - - def _metavar_formatter(self, action, default_metavar): - if action.metavar is not None: - result = action.metavar - elif action.choices is not None: - choice_strs = [str(choice) for choice in action.choices] - result = '{%s}' % ','.join(choice_strs) - else: - result = default_metavar - - def format(tuple_size): - if isinstance(result, tuple): - return result - else: - return (result, ) * tuple_size - return format - - def _format_args(self, action, default_metavar): - get_metavar = self._metavar_formatter(action, default_metavar) - if action.nargs is None: - result = '%s' % get_metavar(1) - elif action.nargs == OPTIONAL: - result = '[%s]' % get_metavar(1) - elif action.nargs == ZERO_OR_MORE: - result = '[%s [%s ...]]' % get_metavar(2) - elif action.nargs == ONE_OR_MORE: - result = '%s [%s ...]' % get_metavar(2) - elif action.nargs == REMAINDER: - result = '...' - elif action.nargs == PARSER: - result = '%s ...' % get_metavar(1) - else: - formats = ['%s' for _ in range(action.nargs)] - result = ' '.join(formats) % get_metavar(action.nargs) - return result - - def _expand_help(self, action): - params = dict(vars(action), prog=self._prog) - for name in list(params): - if params[name] is SUPPRESS: - del params[name] - for name in list(params): - if hasattr(params[name], '__name__'): - params[name] = params[name].__name__ - if params.get('choices') is not None: - choices_str = ', '.join([str(c) for c in params['choices']]) - params['choices'] = choices_str - return self._get_help_string(action) % params - - def _iter_indented_subactions(self, action): - try: - get_subactions = action._get_subactions - except AttributeError: - pass - else: - self._indent() - for subaction in get_subactions(): - yield subaction - self._dedent() - - def _split_lines(self, text, width): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.wrap(text, width) - - def _fill_text(self, text, width, indent): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.fill(text, width, initial_indent=indent, - subsequent_indent=indent) - - def _get_help_string(self, action): - return action.help - - -class RawDescriptionHelpFormatter(HelpFormatter): - """Help message formatter which retains any formatting in descriptions. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _fill_text(self, text, width, indent): - return ''.join([indent + line for line in text.splitlines(True)]) - - -class RawTextHelpFormatter(RawDescriptionHelpFormatter): - """Help message formatter which retains formatting of all help text. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _split_lines(self, text, width): - return text.splitlines() - - -class ArgumentDefaultsHelpFormatter(HelpFormatter): - """Help message formatter which adds default values to argument help. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _get_help_string(self, action): - help = action.help - if '%(default)' not in action.help: - if action.default is not SUPPRESS: - defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] - if action.option_strings or action.nargs in defaulting_nargs: - help += ' (default: %(default)s)' - return help - - -# ===================== -# Options and Arguments -# ===================== - -def _get_action_name(argument): - if argument is None: - return None - elif argument.option_strings: - return '/'.join(argument.option_strings) - elif argument.metavar not in (None, SUPPRESS): - return argument.metavar - elif argument.dest not in (None, SUPPRESS): - return argument.dest - else: - return None - - -class ArgumentError(Exception): - """An error from creating or using an argument (optional or positional). - - The string value of this exception is the message, augmented with - information about the argument that caused it. - """ - - def __init__(self, argument, message): - self.argument_name = _get_action_name(argument) - self.message = message - - def __str__(self): - if self.argument_name is None: - format = '%(message)s' - else: - format = 'argument %(argument_name)s: %(message)s' - return format % dict(message=self.message, - argument_name=self.argument_name) - - -class ArgumentTypeError(Exception): - """An error from trying to convert a command line string to a type.""" - pass - - -# ============== -# Action classes -# ============== - -class Action(_AttributeHolder): - """Information about how to convert command line strings to Python objects. - - Action objects are used by an ArgumentParser to represent the information - needed to parse a single argument from one or more strings from the - command line. The keyword arguments to the Action constructor are also - all attributes of Action instances. - - Keyword Arguments: - - - option_strings -- A list of command-line option strings which - should be associated with this action. - - - dest -- The name of the attribute to hold the created object(s) - - - nargs -- The number of command-line arguments that should be - consumed. By default, one argument will be consumed and a single - value will be produced. Other values include: - - N (an integer) consumes N arguments (and produces a list) - - '?' consumes zero or one arguments - - '*' consumes zero or more arguments (and produces a list) - - '+' consumes one or more arguments (and produces a list) - Note that the difference between the default and nargs=1 is that - with the default, a single value will be produced, while with - nargs=1, a list containing a single value will be produced. - - - const -- The value to be produced if the option is specified and the - option uses an action that takes no values. - - - default -- The value to be produced if the option is not specified. - - - type -- The type which the command-line arguments should be converted - to, should be one of 'string', 'int', 'float', 'complex' or a - callable object that accepts a single string argument. If None, - 'string' is assumed. - - - choices -- A container of values that should be allowed. If not None, - after a command-line argument has been converted to the appropriate - type, an exception will be raised if it is not a member of this - collection. - - - required -- True if the action must always be specified at the - command line. This is only meaningful for optional command-line - arguments. - - - help -- The help string describing the argument. - - - metavar -- The name to be used for the option's argument with the - help string. If None, the 'dest' value will be used as the name. - """ - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - self.option_strings = option_strings - self.dest = dest - self.nargs = nargs - self.const = const - self.default = default - self.type = type - self.choices = choices - self.required = required - self.help = help - self.metavar = metavar - - def _get_kwargs(self): - names = [ - 'option_strings', - 'dest', - 'nargs', - 'const', - 'default', - 'type', - 'choices', - 'help', - 'metavar', - ] - return [(name, getattr(self, name)) for name in names] - - def __call__(self, parser, namespace, values, option_string=None): - raise NotImplementedError(_('.__call__() not defined')) - - -class _StoreAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for store actions must be > 0; if you ' - 'have nothing to store, actions such as store ' - 'true or store const may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_StoreAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values) - - -class _StoreConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_StoreConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, self.const) - - -class _StoreTrueAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=False, - required=False, - help=None): - super(_StoreTrueAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=True, - default=default, - required=required, - help=help) - - -class _StoreFalseAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=True, - required=False, - help=None): - super(_StoreFalseAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=False, - default=default, - required=required, - help=help) - - -class _AppendAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for append actions must be > 0; if arg ' - 'strings are not supplying the value to append, ' - 'the append const action may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_AppendAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(values) - setattr(namespace, self.dest, items) - - -class _AppendConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_AppendConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(self.const) - setattr(namespace, self.dest, items) - - -class _CountAction(Action): - - def __init__(self, - option_strings, - dest, - default=None, - required=False, - help=None): - super(_CountAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - new_count = _ensure_value(namespace, self.dest, 0) + 1 - setattr(namespace, self.dest, new_count) - - -class _HelpAction(Action): - - def __init__(self, - option_strings, - dest=SUPPRESS, - default=SUPPRESS, - help=None): - super(_HelpAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - parser.print_help() - parser.exit() - - -class _VersionAction(Action): - - def __init__(self, - option_strings, - version=None, - dest=SUPPRESS, - default=SUPPRESS, - help="show program's version number and exit"): - super(_VersionAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - self.version = version - - def __call__(self, parser, namespace, values, option_string=None): - version = self.version - if version is None: - version = parser.version - formatter = parser._get_formatter() - formatter.add_text(version) - parser.exit(message=formatter.format_help()) - - -class _SubParsersAction(Action): - - class _ChoicesPseudoAction(Action): - - def __init__(self, name, aliases, help): - metavar = dest = name - if aliases: - metavar += ' (%s)' % ', '.join(aliases) - sup = super(_SubParsersAction._ChoicesPseudoAction, self) - sup.__init__(option_strings=[], dest=dest, help=help, - metavar=metavar) - - def __init__(self, - option_strings, - prog, - parser_class, - dest=SUPPRESS, - help=None, - metavar=None): - - self._prog_prefix = prog - self._parser_class = parser_class - self._name_parser_map = {} - self._choices_actions = [] - - super(_SubParsersAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=PARSER, - choices=self._name_parser_map, - help=help, - metavar=metavar) - - def add_parser(self, name, **kwargs): - # set prog from the existing prefix - if kwargs.get('prog') is None: - kwargs['prog'] = '%s %s' % (self._prog_prefix, name) - - aliases = kwargs.pop('aliases', ()) - - # create a pseudo-action to hold the choice help - if 'help' in kwargs: - help = kwargs.pop('help') - choice_action = self._ChoicesPseudoAction(name, aliases, help) - self._choices_actions.append(choice_action) - - # create the parser and add it to the map - parser = self._parser_class(**kwargs) - self._name_parser_map[name] = parser - - # make parser available under aliases also - for alias in aliases: - self._name_parser_map[alias] = parser - - return parser - - def _get_subactions(self): - return self._choices_actions - - def __call__(self, parser, namespace, values, option_string=None): - parser_name = values[0] - arg_strings = values[1:] - - # set the parser name if requested - if self.dest is not SUPPRESS: - setattr(namespace, self.dest, parser_name) - - # select the parser - try: - parser = self._name_parser_map[parser_name] - except KeyError: - tup = parser_name, ', '.join(self._name_parser_map) - msg = _('unknown parser %r (choices: %s)' % tup) - raise ArgumentError(self, msg) - - # parse all the remaining options into the namespace - # store any unrecognized options on the object, so that the top - # level parser can decide what to do with them - namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) - if arg_strings: - vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) - getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) - - -# ============== -# Type classes -# ============== - -class FileType(object): - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - """ - - def __init__(self, mode='r', bufsize=None): - self._mode = mode - self._bufsize = bufsize - - def __call__(self, string): - # the special argument "-" means sys.std{in,out} - if string == '-': - if 'r' in self._mode: - return _sys.stdin - elif 'w' in self._mode: - return _sys.stdout - else: - msg = _('argument "-" with mode %r' % self._mode) - raise ValueError(msg) - - try: - # all other arguments are used as file names - if self._bufsize: - return open(string, self._mode, self._bufsize) - else: - return open(string, self._mode) - except IOError: - err = _sys.exc_info()[1] - message = _("can't open '%s': %s") - raise ArgumentTypeError(message % (string, err)) - - def __repr__(self): - args = [self._mode, self._bufsize] - args_str = ', '.join([repr(arg) for arg in args if arg is not None]) - return '%s(%s)' % (type(self).__name__, args_str) - -# =========================== -# Optional and Positional Parsing -# =========================== - -class Namespace(_AttributeHolder): - """Simple object for storing attributes. - - Implements equality by attribute names and values, and provides a simple - string representation. - """ - - def __init__(self, **kwargs): - for name in kwargs: - setattr(self, name, kwargs[name]) - - __hash__ = None - - def __eq__(self, other): - return vars(self) == vars(other) - - def __ne__(self, other): - return not (self == other) - - def __contains__(self, key): - return key in self.__dict__ - - -class _ActionsContainer(object): - - def __init__(self, - description, - prefix_chars, - argument_default, - conflict_handler): - super(_ActionsContainer, self).__init__() - - self.description = description - self.argument_default = argument_default - self.prefix_chars = prefix_chars - self.conflict_handler = conflict_handler - - # set up registries - self._registries = {} - - # register actions - self.register('action', None, _StoreAction) - self.register('action', 'store', _StoreAction) - self.register('action', 'store_const', _StoreConstAction) - self.register('action', 'store_true', _StoreTrueAction) - self.register('action', 'store_false', _StoreFalseAction) - self.register('action', 'append', _AppendAction) - self.register('action', 'append_const', _AppendConstAction) - self.register('action', 'count', _CountAction) - self.register('action', 'help', _HelpAction) - self.register('action', 'version', _VersionAction) - self.register('action', 'parsers', _SubParsersAction) - - # raise an exception if the conflict handler is invalid - self._get_handler() - - # action storage - self._actions = [] - self._option_string_actions = {} - - # groups - self._action_groups = [] - self._mutually_exclusive_groups = [] - - # defaults storage - self._defaults = {} - - # determines whether an "option" looks like a negative number - self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') - - # whether or not there are any optionals that look like negative - # numbers -- uses a list so it can be shared and edited - self._has_negative_number_optionals = [] - - # ==================== - # Registration methods - # ==================== - def register(self, registry_name, value, object): - registry = self._registries.setdefault(registry_name, {}) - registry[value] = object - - def _registry_get(self, registry_name, value, default=None): - return self._registries[registry_name].get(value, default) - - # ================================== - # Namespace default accessor methods - # ================================== - def set_defaults(self, **kwargs): - self._defaults.update(kwargs) - - # if these defaults match any existing arguments, replace - # the previous default on the object with the new one - for action in self._actions: - if action.dest in kwargs: - action.default = kwargs[action.dest] - - def get_default(self, dest): - for action in self._actions: - if action.dest == dest and action.default is not None: - return action.default - return self._defaults.get(dest, None) - - - # ======================= - # Adding argument actions - # ======================= - def add_argument(self, *args, **kwargs): - """ - add_argument(dest, ..., name=value, ...) - add_argument(option_string, option_string, ..., name=value, ...) - """ - - # if no positional args are supplied or only one is supplied and - # it doesn't look like an option string, parse a positional - # argument - chars = self.prefix_chars - if not args or len(args) == 1 and args[0][0] not in chars: - if args and 'dest' in kwargs: - raise ValueError('dest supplied twice for positional argument') - kwargs = self._get_positional_kwargs(*args, **kwargs) - - # otherwise, we're adding an optional argument - else: - kwargs = self._get_optional_kwargs(*args, **kwargs) - - # if no default was supplied, use the parser-level default - if 'default' not in kwargs: - dest = kwargs['dest'] - if dest in self._defaults: - kwargs['default'] = self._defaults[dest] - elif self.argument_default is not None: - kwargs['default'] = self.argument_default - - # create the action object, and add it to the parser - action_class = self._pop_action_class(kwargs) - if not _callable(action_class): - raise ValueError('unknown action "%s"' % action_class) - action = action_class(**kwargs) - - # raise an error if the action type is not callable - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - raise ValueError('%r is not callable' % type_func) - - return self._add_action(action) - - def add_argument_group(self, *args, **kwargs): - group = _ArgumentGroup(self, *args, **kwargs) - self._action_groups.append(group) - return group - - def add_mutually_exclusive_group(self, **kwargs): - group = _MutuallyExclusiveGroup(self, **kwargs) - self._mutually_exclusive_groups.append(group) - return group - - def _add_action(self, action): - # resolve any conflicts - self._check_conflict(action) - - # add to actions list - self._actions.append(action) - action.container = self - - # index the action by any option strings it has - for option_string in action.option_strings: - self._option_string_actions[option_string] = action - - # set the flag if any option strings look like negative numbers - for option_string in action.option_strings: - if self._negative_number_matcher.match(option_string): - if not self._has_negative_number_optionals: - self._has_negative_number_optionals.append(True) - - # return the created action - return action - - def _remove_action(self, action): - self._actions.remove(action) - - def _add_container_actions(self, container): - # collect groups by titles - title_group_map = {} - for group in self._action_groups: - if group.title in title_group_map: - msg = _('cannot merge actions - two groups are named %r') - raise ValueError(msg % (group.title)) - title_group_map[group.title] = group - - # map each action to its group - group_map = {} - for group in container._action_groups: - - # if a group with the title exists, use that, otherwise - # create a new group matching the container's group - if group.title not in title_group_map: - title_group_map[group.title] = self.add_argument_group( - title=group.title, - description=group.description, - conflict_handler=group.conflict_handler) - - # map the actions to their new group - for action in group._group_actions: - group_map[action] = title_group_map[group.title] - - # add container's mutually exclusive groups - # NOTE: if add_mutually_exclusive_group ever gains title= and - # description= then this code will need to be expanded as above - for group in container._mutually_exclusive_groups: - mutex_group = self.add_mutually_exclusive_group( - required=group.required) - - # map the actions to their new mutex group - for action in group._group_actions: - group_map[action] = mutex_group - - # add all actions to this container or their group - for action in container._actions: - group_map.get(action, self)._add_action(action) - - def _get_positional_kwargs(self, dest, **kwargs): - # make sure required is not specified - if 'required' in kwargs: - msg = _("'required' is an invalid argument for positionals") - raise TypeError(msg) - - # mark positional arguments as required if at least one is - # always required - if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: - kwargs['required'] = True - if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: - kwargs['required'] = True - - # return the keyword arguments with no option strings - return dict(kwargs, dest=dest, option_strings=[]) - - def _get_optional_kwargs(self, *args, **kwargs): - # determine short and long option strings - option_strings = [] - long_option_strings = [] - for option_string in args: - # error on strings that don't start with an appropriate prefix - if not option_string[0] in self.prefix_chars: - msg = _('invalid option string %r: ' - 'must start with a character %r') - tup = option_string, self.prefix_chars - raise ValueError(msg % tup) - - # strings starting with two prefix characters are long options - option_strings.append(option_string) - if option_string[0] in self.prefix_chars: - if len(option_string) > 1: - if option_string[1] in self.prefix_chars: - long_option_strings.append(option_string) - - # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' - dest = kwargs.pop('dest', None) - if dest is None: - if long_option_strings: - dest_option_string = long_option_strings[0] - else: - dest_option_string = option_strings[0] - dest = dest_option_string.lstrip(self.prefix_chars) - if not dest: - msg = _('dest= is required for options like %r') - raise ValueError(msg % option_string) - dest = dest.replace('-', '_') - - # return the updated keyword arguments - return dict(kwargs, dest=dest, option_strings=option_strings) - - def _pop_action_class(self, kwargs, default=None): - action = kwargs.pop('action', default) - return self._registry_get('action', action, action) - - def _get_handler(self): - # determine function from conflict handler string - handler_func_name = '_handle_conflict_%s' % self.conflict_handler - try: - return getattr(self, handler_func_name) - except AttributeError: - msg = _('invalid conflict_resolution value: %r') - raise ValueError(msg % self.conflict_handler) - - def _check_conflict(self, action): - - # find all options that conflict with this option - confl_optionals = [] - for option_string in action.option_strings: - if option_string in self._option_string_actions: - confl_optional = self._option_string_actions[option_string] - confl_optionals.append((option_string, confl_optional)) - - # resolve any conflicts - if confl_optionals: - conflict_handler = self._get_handler() - conflict_handler(action, confl_optionals) - - def _handle_conflict_error(self, action, conflicting_actions): - message = _('conflicting option string(s): %s') - conflict_string = ', '.join([option_string - for option_string, action - in conflicting_actions]) - raise ArgumentError(action, message % conflict_string) - - def _handle_conflict_resolve(self, action, conflicting_actions): - - # remove all conflicting options - for option_string, action in conflicting_actions: - - # remove the conflicting option - action.option_strings.remove(option_string) - self._option_string_actions.pop(option_string, None) - - # if the option now has no option string, remove it from the - # container holding it - if not action.option_strings: - action.container._remove_action(action) - - -class _ArgumentGroup(_ActionsContainer): - - def __init__(self, container, title=None, description=None, **kwargs): - # add any missing keyword arguments by checking the container - update = kwargs.setdefault - update('conflict_handler', container.conflict_handler) - update('prefix_chars', container.prefix_chars) - update('argument_default', container.argument_default) - super_init = super(_ArgumentGroup, self).__init__ - super_init(description=description, **kwargs) - - # group attributes - self.title = title - self._group_actions = [] - - # share most attributes with the container - self._registries = container._registries - self._actions = container._actions - self._option_string_actions = container._option_string_actions - self._defaults = container._defaults - self._has_negative_number_optionals = \ - container._has_negative_number_optionals - - def _add_action(self, action): - action = super(_ArgumentGroup, self)._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - super(_ArgumentGroup, self)._remove_action(action) - self._group_actions.remove(action) - - -class _MutuallyExclusiveGroup(_ArgumentGroup): - - def __init__(self, container, required=False): - super(_MutuallyExclusiveGroup, self).__init__(container) - self.required = required - self._container = container - - def _add_action(self, action): - if action.required: - msg = _('mutually exclusive arguments must be optional') - raise ValueError(msg) - action = self._container._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - self._container._remove_action(action) - self._group_actions.remove(action) - - -class ArgumentParser(_AttributeHolder, _ActionsContainer): - """Object for parsing command line strings into Python objects. - - Keyword Arguments: - - prog -- The name of the program (default: sys.argv[0]) - - usage -- A usage message (default: auto-generated from arguments) - - description -- A description of what the program does - - epilog -- Text following the argument descriptions - - parents -- Parsers whose arguments should be copied into this one - - formatter_class -- HelpFormatter class for printing help messages - - prefix_chars -- Characters that prefix optional arguments - - fromfile_prefix_chars -- Characters that prefix files containing - additional arguments - - argument_default -- The default value for all arguments - - conflict_handler -- String indicating how to handle conflicts - - add_help -- Add a -h/-help option - """ - - def __init__(self, - prog=None, - usage=None, - description=None, - epilog=None, - version=None, - parents=[], - formatter_class=HelpFormatter, - prefix_chars='-', - fromfile_prefix_chars=None, - argument_default=None, - conflict_handler='error', - add_help=True): - - if version is not None: - import warnings - warnings.warn( - """The "version" argument to ArgumentParser is deprecated. """ - """Please use """ - """"add_argument(..., action='version', version="N", ...)" """ - """instead""", DeprecationWarning) - - superinit = super(ArgumentParser, self).__init__ - superinit(description=description, - prefix_chars=prefix_chars, - argument_default=argument_default, - conflict_handler=conflict_handler) - - # default setting for prog - if prog is None: - prog = _os.path.basename(_sys.argv[0]) - - self.prog = prog - self.usage = usage - self.epilog = epilog - self.version = version - self.formatter_class = formatter_class - self.fromfile_prefix_chars = fromfile_prefix_chars - self.add_help = add_help - - add_group = self.add_argument_group - self._positionals = add_group(_('positional arguments')) - self._optionals = add_group(_('optional arguments')) - self._subparsers = None - - # register types - def identity(string): - return string - self.register('type', None, identity) - - # add help and version arguments if necessary - # (using explicit default to override global argument_default) - if '-' in prefix_chars: - default_prefix = '-' - else: - default_prefix = prefix_chars[0] - if self.add_help: - self.add_argument( - default_prefix+'h', default_prefix*2+'help', - action='help', default=SUPPRESS, - help=_('show this help message and exit')) - if self.version: - self.add_argument( - default_prefix+'v', default_prefix*2+'version', - action='version', default=SUPPRESS, - version=self.version, - help=_("show program's version number and exit")) - - # add parent arguments and defaults - for parent in parents: - self._add_container_actions(parent) - try: - defaults = parent._defaults - except AttributeError: - pass - else: - self._defaults.update(defaults) - - # ======================= - # Pretty __repr__ methods - # ======================= - def _get_kwargs(self): - names = [ - 'prog', - 'usage', - 'description', - 'version', - 'formatter_class', - 'conflict_handler', - 'add_help', - ] - return [(name, getattr(self, name)) for name in names] - - # ================================== - # Optional/Positional adding methods - # ================================== - def add_subparsers(self, **kwargs): - if self._subparsers is not None: - self.error(_('cannot have multiple subparser arguments')) - - # add the parser class to the arguments if it's not present - kwargs.setdefault('parser_class', type(self)) - - if 'title' in kwargs or 'description' in kwargs: - title = _(kwargs.pop('title', 'subcommands')) - description = _(kwargs.pop('description', None)) - self._subparsers = self.add_argument_group(title, description) - else: - self._subparsers = self._positionals - - # prog defaults to the usage message of this parser, skipping - # optional arguments and with no "usage:" prefix - if kwargs.get('prog') is None: - formatter = self._get_formatter() - positionals = self._get_positional_actions() - groups = self._mutually_exclusive_groups - formatter.add_usage(self.usage, positionals, groups, '') - kwargs['prog'] = formatter.format_help().strip() - - # create the parsers action and add it to the positionals list - parsers_class = self._pop_action_class(kwargs, 'parsers') - action = parsers_class(option_strings=[], **kwargs) - self._subparsers._add_action(action) - - # return the created parsers action - return action - - def _add_action(self, action): - if action.option_strings: - self._optionals._add_action(action) - else: - self._positionals._add_action(action) - return action - - def _get_optional_actions(self): - return [action - for action in self._actions - if action.option_strings] - - def _get_positional_actions(self): - return [action - for action in self._actions - if not action.option_strings] - - # ===================================== - # Command line argument parsing methods - # ===================================== - def parse_args(self, args=None, namespace=None): - args, argv = self.parse_known_args(args, namespace) - if argv: - msg = _('unrecognized arguments: %s') - self.error(msg % ' '.join(argv)) - return args - - def parse_known_args(self, args=None, namespace=None): - # args default to the system args - if args is None: - args = _sys.argv[1:] - - # default Namespace built from parser defaults - if namespace is None: - namespace = Namespace() - - # add any action defaults that aren't present - for action in self._actions: - if action.dest is not SUPPRESS: - if not hasattr(namespace, action.dest): - if action.default is not SUPPRESS: - setattr(namespace, action.dest, action.default) - - # add any parser defaults that aren't present - for dest in self._defaults: - if not hasattr(namespace, dest): - setattr(namespace, dest, self._defaults[dest]) - - # parse the arguments and exit if there are any errors - try: - namespace, args = self._parse_known_args(args, namespace) - if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): - args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) - delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) - return namespace, args - except ArgumentError: - err = _sys.exc_info()[1] - self.error(str(err)) - - def _parse_known_args(self, arg_strings, namespace): - # replace arg strings that are file references - if self.fromfile_prefix_chars is not None: - arg_strings = self._read_args_from_files(arg_strings) - - # map all mutually exclusive arguments to the other arguments - # they can't occur with - action_conflicts = {} - for mutex_group in self._mutually_exclusive_groups: - group_actions = mutex_group._group_actions - for i, mutex_action in enumerate(mutex_group._group_actions): - conflicts = action_conflicts.setdefault(mutex_action, []) - conflicts.extend(group_actions[:i]) - conflicts.extend(group_actions[i + 1:]) - - # find all option indices, and determine the arg_string_pattern - # which has an 'O' if there is an option at an index, - # an 'A' if there is an argument, or a '-' if there is a '--' - option_string_indices = {} - arg_string_pattern_parts = [] - arg_strings_iter = iter(arg_strings) - for i, arg_string in enumerate(arg_strings_iter): - - # all args after -- are non-options - if arg_string == '--': - arg_string_pattern_parts.append('-') - for arg_string in arg_strings_iter: - arg_string_pattern_parts.append('A') - - # otherwise, add the arg to the arg strings - # and note the index if it was an option - else: - option_tuple = self._parse_optional(arg_string) - if option_tuple is None: - pattern = 'A' - else: - option_string_indices[i] = option_tuple - pattern = 'O' - arg_string_pattern_parts.append(pattern) - - # join the pieces together to form the pattern - arg_strings_pattern = ''.join(arg_string_pattern_parts) - - # converts arg strings to the appropriate and then takes the action - seen_actions = set() - seen_non_default_actions = set() - - def take_action(action, argument_strings, option_string=None): - seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) - - # error if this argument is not allowed with other previously - # seen arguments, assuming that actions that use the default - # value don't really count as "present" - if argument_values is not action.default: - seen_non_default_actions.add(action) - for conflict_action in action_conflicts.get(action, []): - if conflict_action in seen_non_default_actions: - msg = _('not allowed with argument %s') - action_name = _get_action_name(conflict_action) - raise ArgumentError(action, msg % action_name) - - # take the action if we didn't receive a SUPPRESS value - # (e.g. from a default) - if argument_values is not SUPPRESS: - action(self, namespace, argument_values, option_string) - - # function to convert arg_strings into an optional action - def consume_optional(start_index): - - # get the optional identified at this index - option_tuple = option_string_indices[start_index] - action, option_string, explicit_arg = option_tuple - - # identify additional optionals in the same arg string - # (e.g. -xyz is the same as -x -y -z if no args are required) - match_argument = self._match_argument - action_tuples = [] - while True: - - # if we found no optional action, skip it - if action is None: - extras.append(arg_strings[start_index]) - return start_index + 1 - - # if there is an explicit argument, try to match the - # optional's string arguments to only this - if explicit_arg is not None: - arg_count = match_argument(action, 'A') - - # if the action is a single-dash option and takes no - # arguments, try to parse more single-dash options out - # of the tail of the option string - chars = self.prefix_chars - if arg_count == 0 and option_string[1] not in chars: - action_tuples.append((action, [], option_string)) - char = option_string[0] - option_string = char + explicit_arg[0] - new_explicit_arg = explicit_arg[1:] or None - optionals_map = self._option_string_actions - if option_string in optionals_map: - action = optionals_map[option_string] - explicit_arg = new_explicit_arg - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if the action expect exactly one argument, we've - # successfully matched the option; exit the loop - elif arg_count == 1: - stop = start_index + 1 - args = [explicit_arg] - action_tuples.append((action, args, option_string)) - break - - # error if a double-dash option did not use the - # explicit argument - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if there is no explicit argument, try to match the - # optional's string arguments with the following strings - # if successful, exit the loop - else: - start = start_index + 1 - selected_patterns = arg_strings_pattern[start:] - arg_count = match_argument(action, selected_patterns) - stop = start + arg_count - args = arg_strings[start:stop] - action_tuples.append((action, args, option_string)) - break - - # add the Optional to the list and return the index at which - # the Optional's string args stopped - assert action_tuples - for action, args, option_string in action_tuples: - take_action(action, args, option_string) - return stop - - # the list of Positionals left to be parsed; this is modified - # by consume_positionals() - positionals = self._get_positional_actions() - - # function to convert arg_strings into positional actions - def consume_positionals(start_index): - # match as many Positionals as possible - match_partial = self._match_arguments_partial - selected_pattern = arg_strings_pattern[start_index:] - arg_counts = match_partial(positionals, selected_pattern) - - # slice off the appropriate arg strings for each Positional - # and add the Positional and its args to the list - for action, arg_count in zip(positionals, arg_counts): - args = arg_strings[start_index: start_index + arg_count] - start_index += arg_count - take_action(action, args) - - # slice off the Positionals that we just parsed and return the - # index at which the Positionals' string args stopped - positionals[:] = positionals[len(arg_counts):] - return start_index - - # consume Positionals and Optionals alternately, until we have - # passed the last option string - extras = [] - start_index = 0 - if option_string_indices: - max_option_string_index = max(option_string_indices) - else: - max_option_string_index = -1 - while start_index <= max_option_string_index: - - # consume any Positionals preceding the next option - next_option_string_index = min([ - index - for index in option_string_indices - if index >= start_index]) - if start_index != next_option_string_index: - positionals_end_index = consume_positionals(start_index) - - # only try to parse the next optional if we didn't consume - # the option string during the positionals parsing - if positionals_end_index > start_index: - start_index = positionals_end_index - continue - else: - start_index = positionals_end_index - - # if we consumed all the positionals we could and we're not - # at the index of an option string, there were extra arguments - if start_index not in option_string_indices: - strings = arg_strings[start_index:next_option_string_index] - extras.extend(strings) - start_index = next_option_string_index - - # consume the next optional and any arguments for it - start_index = consume_optional(start_index) - - # consume any positionals following the last Optional - stop_index = consume_positionals(start_index) - - # if we didn't consume all the argument strings, there were extras - extras.extend(arg_strings[stop_index:]) - - # if we didn't use all the Positional objects, there were too few - # arg strings supplied. - if positionals: - self.error(_('too few arguments')) - - # make sure all required actions were present, and convert defaults. - for action in self._actions: - if action not in seen_actions: - if action.required: - name = _get_action_name(action) - self.error(_('argument %s is required') % name) - else: - # Convert action default now instead of doing it before - # parsing arguments to avoid calling convert functions - # twice (which may fail) if the argument was given, but - # only if it was defined already in the namespace - if (action.default is not None and - isinstance(action.default, basestring) and - hasattr(namespace, action.dest) and - action.default is getattr(namespace, action.dest)): - setattr(namespace, action.dest, - self._get_value(action, action.default)) - - # make sure all required groups had one option present - for group in self._mutually_exclusive_groups: - if group.required: - for action in group._group_actions: - if action in seen_non_default_actions: - break - - # if no actions were used, report the error - else: - names = [_get_action_name(action) - for action in group._group_actions - if action.help is not SUPPRESS] - msg = _('one of the arguments %s is required') - self.error(msg % ' '.join(names)) - - # return the updated namespace and the extra arguments - return namespace, extras - - def _read_args_from_files(self, arg_strings): - # expand arguments referencing files - new_arg_strings = [] - for arg_string in arg_strings: - - # for regular arguments, just add them back into the list - if not arg_string or arg_string[0] not in self.fromfile_prefix_chars: - new_arg_strings.append(arg_string) - - # replace arguments referencing files with the file content - else: - try: - args_file = open(arg_string[1:]) - try: - arg_strings = [] - for arg_line in args_file.read().splitlines(): - for arg in self.convert_arg_line_to_args(arg_line): - arg_strings.append(arg) - arg_strings = self._read_args_from_files(arg_strings) - new_arg_strings.extend(arg_strings) - finally: - args_file.close() - except IOError: - err = _sys.exc_info()[1] - self.error(str(err)) - - # return the modified argument list - return new_arg_strings - - def convert_arg_line_to_args(self, arg_line): - return [arg_line] - - def _match_argument(self, action, arg_strings_pattern): - # match the pattern for this action to the arg strings - nargs_pattern = self._get_nargs_pattern(action) - match = _re.match(nargs_pattern, arg_strings_pattern) - - # raise an exception if we weren't able to find a match - if match is None: - nargs_errors = { - None: _('expected one argument'), - OPTIONAL: _('expected at most one argument'), - ONE_OR_MORE: _('expected at least one argument'), - } - default = _('expected %s argument(s)') % action.nargs - msg = nargs_errors.get(action.nargs, default) - raise ArgumentError(action, msg) - - # return the number of arguments matched - return len(match.group(1)) - - def _match_arguments_partial(self, actions, arg_strings_pattern): - # progressively shorten the actions list by slicing off the - # final actions until we find a match - result = [] - for i in range(len(actions), 0, -1): - actions_slice = actions[:i] - pattern = ''.join([self._get_nargs_pattern(action) - for action in actions_slice]) - match = _re.match(pattern, arg_strings_pattern) - if match is not None: - result.extend([len(string) for string in match.groups()]) - break - - # return the list of arg string counts - return result - - def _parse_optional(self, arg_string): - # if it's an empty string, it was meant to be a positional - if not arg_string: - return None - - # if it doesn't start with a prefix, it was meant to be positional - if not arg_string[0] in self.prefix_chars: - return None - - # if the option string is present in the parser, return the action - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - - # if it's just a single character, it was meant to be positional - if len(arg_string) == 1: - return None - - # if the option string before the "=" is present, return the action - if '=' in arg_string: - option_string, explicit_arg = arg_string.split('=', 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - - # search through all possible prefixes of the option string - # and all actions in the parser for possible interpretations - option_tuples = self._get_option_tuples(arg_string) - - # if multiple actions match, the option string was ambiguous - if len(option_tuples) > 1: - options = ', '.join([option_string - for action, option_string, explicit_arg in option_tuples]) - tup = arg_string, options - self.error(_('ambiguous option: %s could match %s') % tup) - - # if exactly one action matched, this segmentation is good, - # so return the parsed action - elif len(option_tuples) == 1: - option_tuple, = option_tuples - return option_tuple - - # if it was not found as an option, but it looks like a negative - # number, it was meant to be positional - # unless there are negative-number-like options - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - - # if it contains a space, it was meant to be a positional - if ' ' in arg_string: - return None - - # it was meant to be an optional but there is no such option - # in this parser (though it might be a valid option in a subparser) - return None, arg_string, None - - def _get_option_tuples(self, option_string): - result = [] - - # option strings starting with two prefix characters are only - # split at the '=' - chars = self.prefix_chars - if option_string[0] in chars and option_string[1] in chars: - if '=' in option_string: - option_prefix, explicit_arg = option_string.split('=', 1) - else: - option_prefix = option_string - explicit_arg = None - for option_string in self._option_string_actions: - if option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # single character options can be concatenated with their arguments - # but multiple character options always have to have their argument - # separate - elif option_string[0] in chars and option_string[1] not in chars: - option_prefix = option_string - explicit_arg = None - short_option_prefix = option_string[:2] - short_explicit_arg = option_string[2:] - - for option_string in self._option_string_actions: - if option_string == short_option_prefix: - action = self._option_string_actions[option_string] - tup = action, option_string, short_explicit_arg - result.append(tup) - elif option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # shouldn't ever get here - else: - self.error(_('unexpected option string: %s') % option_string) - - # return the collected option tuples - return result - - def _get_nargs_pattern(self, action): - # in all examples below, we have to allow for '--' args - # which are represented as '-' in the pattern - nargs = action.nargs - - # the default (None) is assumed to be a single argument - if nargs is None: - nargs_pattern = '(-*A-*)' - - # allow zero or one arguments - elif nargs == OPTIONAL: - nargs_pattern = '(-*A?-*)' - - # allow zero or more arguments - elif nargs == ZERO_OR_MORE: - nargs_pattern = '(-*[A-]*)' - - # allow one or more arguments - elif nargs == ONE_OR_MORE: - nargs_pattern = '(-*A[A-]*)' - - # allow any number of options or arguments - elif nargs == REMAINDER: - nargs_pattern = '([-AO]*)' - - # allow one argument followed by any number of options or arguments - elif nargs == PARSER: - nargs_pattern = '(-*A[-AO]*)' - - # all others should be integers - else: - nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) - - # if this is an optional action, -- is not allowed - if action.option_strings: - nargs_pattern = nargs_pattern.replace('-*', '') - nargs_pattern = nargs_pattern.replace('-', '') - - # return the pattern - return nargs_pattern - - # ======================== - # Value conversion methods - # ======================== - def _get_values(self, action, arg_strings): - # for everything but PARSER args, strip out '--' - if action.nargs not in [PARSER, REMAINDER]: - arg_strings = [s for s in arg_strings if s != '--'] - - # optional argument produces a default when not present - if not arg_strings and action.nargs == OPTIONAL: - if action.option_strings: - value = action.const - else: - value = action.default - if isinstance(value, basestring): - value = self._get_value(action, value) - self._check_value(action, value) - - # when nargs='*' on a positional, if there were no command-line - # args, use the default if it is anything other than None - elif (not arg_strings and action.nargs == ZERO_OR_MORE and - not action.option_strings): - if action.default is not None: - value = action.default - else: - value = arg_strings - self._check_value(action, value) - - # single argument or optional argument produces a single value - elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: - arg_string, = arg_strings - value = self._get_value(action, arg_string) - self._check_value(action, value) - - # REMAINDER arguments convert all values, checking none - elif action.nargs == REMAINDER: - value = [self._get_value(action, v) for v in arg_strings] - - # PARSER arguments convert all values, but check only the first - elif action.nargs == PARSER: - value = [self._get_value(action, v) for v in arg_strings] - self._check_value(action, value[0]) - - # all other types of nargs produce a list - else: - value = [self._get_value(action, v) for v in arg_strings] - for v in value: - self._check_value(action, v) - - # return the converted value - return value - - def _get_value(self, action, arg_string): - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - msg = _('%r is not callable') - raise ArgumentError(action, msg % type_func) - - # convert the value to the appropriate type - try: - result = type_func(arg_string) - - # ArgumentTypeErrors indicate errors - except ArgumentTypeError: - name = getattr(action.type, '__name__', repr(action.type)) - msg = str(_sys.exc_info()[1]) - raise ArgumentError(action, msg) - - # TypeErrors or ValueErrors also indicate errors - except (TypeError, ValueError): - name = getattr(action.type, '__name__', repr(action.type)) - msg = _('invalid %s value: %r') - raise ArgumentError(action, msg % (name, arg_string)) - - # return the converted value - return result - - def _check_value(self, action, value): - # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: - tup = value, ', '.join(map(repr, action.choices)) - msg = _('invalid choice: %r (choose from %s)') % tup - raise ArgumentError(action, msg) - - # ======================= - # Help-formatting methods - # ======================= - def format_usage(self): - formatter = self._get_formatter() - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - return formatter.format_help() - - def format_help(self): - formatter = self._get_formatter() - - # usage - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - - # description - formatter.add_text(self.description) - - # positionals, optionals and user-defined groups - for action_group in self._action_groups: - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - - # epilog - formatter.add_text(self.epilog) - - # determine help from format above - return formatter.format_help() - - def format_version(self): - import warnings - warnings.warn( - 'The format_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - formatter = self._get_formatter() - formatter.add_text(self.version) - return formatter.format_help() - - def _get_formatter(self): - return self.formatter_class(prog=self.prog) - - # ===================== - # Help-printing methods - # ===================== - def print_usage(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_usage(), file) - - def print_help(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_help(), file) - - def print_version(self, file=None): - import warnings - warnings.warn( - 'The print_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - self._print_message(self.format_version(), file) - - def _print_message(self, message, file=None): - if message: - if file is None: - file = _sys.stderr - file.write(message) - - # =============== - # Exiting methods - # =============== - def exit(self, status=0, message=None): - if message: - self._print_message(message, _sys.stderr) - _sys.exit(status) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(_sys.stderr) - self.exit(2, _('%s: error: %s\n') % (self.prog, message)) diff --git a/build/sage_bootstrap/download/cmdline.py b/build/sage_bootstrap/download/cmdline.py index 85062ae7d67..a6afb4f92be 100644 --- a/build/sage_bootstrap/download/cmdline.py +++ b/build/sage_bootstrap/download/cmdline.py @@ -19,11 +19,7 @@ import logging log = logging.getLogger() -# Note that argparse is not part of Python 2.6, so we bundle it -try: - import argparse -except ImportError: - from sage_bootstrap.compat import argparse +import argparse from sage_bootstrap.download.app import Application from sage_bootstrap.env import SAGE_DISTFILES diff --git a/build/sage_bootstrap/flock.py b/build/sage_bootstrap/flock.py index 0d039c42bf6..788c0214371 100644 --- a/build/sage_bootstrap/flock.py +++ b/build/sage_bootstrap/flock.py @@ -14,13 +14,7 @@ import os import pipes import sys - -# Note that argparse is not part of Python 2.6, so we bundle it -try: - import argparse -except ImportError: - from sage_bootstrap.compat import argparse - +import argparse class FileType(argparse.FileType): """ diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py index 88bcdfcf3d7..0d5c60ab160 100644 --- a/build/sage_bootstrap/pypi.py +++ b/build/sage_bootstrap/pypi.py @@ -100,12 +100,13 @@ def summary(self): """ return self.json['info']['summary'] - def update(self): - package = Package(self.name) + def update(self, package=None): + if package is None: + package = Package(self.name) if package.version == self.version: log.info('%s is already at the latest version', self.name) return - log.info('Updating %s: %s -> %s', self.name, package.version, self.version) - update = PackageUpdater(self.name, self.version) + log.info('Updating %s: %s -> %s', package.name, package.version, self.version) + update = PackageUpdater(package.name, self.version) update.download_upstream(self.url) update.fix_checksum() diff --git a/build/sage_bootstrap/uncompress/cmdline.py b/build/sage_bootstrap/uncompress/cmdline.py index 19db240a3e5..b224c13a752 100644 --- a/build/sage_bootstrap/uncompress/cmdline.py +++ b/build/sage_bootstrap/uncompress/cmdline.py @@ -17,12 +17,7 @@ import os import sys - -# Note that argparse is not part of Python 2.6, so we bundle it -try: - import argparse -except ImportError: - from sage_bootstrap.compat import argparse +import argparse from sage_bootstrap.uncompress.action import ( open_archive, unpack_archive diff --git a/build/sage_bootstrap/uninstall.py b/build/sage_bootstrap/uninstall.py index 1fa80c2aae8..90bbb9927b4 100644 --- a/build/sage_bootstrap/uninstall.py +++ b/build/sage_bootstrap/uninstall.py @@ -44,10 +44,7 @@ from os import path as pth -try: - import argparse -except ImportError: - from sage_bootstrap.compat import argparse +import argparse from .env import SAGE_ROOT diff --git a/build/tox.ini b/build/tox.ini index 93c79bf7892..9078fe144c5 100644 --- a/build/tox.ini +++ b/build/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py33, py34, py35, py36, py37, py38 +envlist = py27, py34, py35, py36, py37, py38, py39, py310 skip_missing_interpreters=true [testenv] @@ -8,16 +8,9 @@ setenv = LC_ALL = C SAGE_ROOT = {toxinidir}/.. -[testenv:py26] -deps = unittest2 -commands = unit2 discover - [testenv:py27] commands=python2.7 -m unittest discover -[testenv:py33] -commands=python3.3 -m unittest discover - [testenv:py34] commands=python3.4 -m unittest discover @@ -32,3 +25,9 @@ commands=python3.7 -m unittest discover [testenv:py38] commands=python3.8 -m unittest discover + +[testenv:py39] +commands=python3.9 -m unittest discover + +[testenv:py310] +commands=python3.10 -m unittest discover diff --git a/docker/Dockerfile b/docker/Dockerfile index f98d98c17f2..980aac2da78 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -186,12 +186,9 @@ ENV SAGE_NUM_THREADS $SAGE_NUM_THREADS_DOCBUILD RUN make ################################################################################ -# Image with a full build of sage, ready to release, i.e., with stripped # -# binaries and some extras to run the jupyter notebook. # +# Image with a full build of sage, ready to release. # ################################################################################ FROM make-all as make-release -RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" -RUN sage -i gap_jupyter singular_jupyter pari_jupyter RUN make micro_release ################################################################################ diff --git a/m4/sage_check_python_for_venv.m4 b/m4/sage_check_python_for_venv.m4 index 80b3f7d154a..a6da1f84167 100644 --- a/m4/sage_check_python_for_venv.m4 +++ b/m4/sage_check_python_for_venv.m4 @@ -7,6 +7,8 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ m4_pushdef([REQUIRED_MODULES], [$4]) m4_pushdef([COMMANDS_IF_GOOD], [$5]) + AC_SUBST([SAGE_ARCHFLAGS]) + AC_MSG_CHECKING([... whether ]PYTHON_EXE[ is good]) python3_version=`"PYTHON_EXE" --version 2>&1 \ | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` @@ -19,6 +21,80 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ dnl m4_define([conftest_venv], [config-venv]) .... for debugging only rm -rf conftest_venv AS_IF(["]PYTHON_EXE[" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import ]REQUIRED_MODULES["], [ + AS_VAR_SET([python3_result], [yes]) + SAGE_PYTHON_CHECK_DISTUTILS([CC="$CC" CXX="$CXX" conftest_venv/bin/python3], [ + SAGE_ARCHFLAGS="unset" + ], [ + AS_CASE([$host], + [*-*-darwin*], [ + dnl #31227: Try if setting ARCHFLAGS to empty fixes it + SAGE_PYTHON_CHECK_DISTUTILS([CC="$CC" CXX="$CXX" ARCHFLAGS="" conftest_venv/bin/python3], [ + SAGE_ARCHFLAGS="" + ], [ + AS_VAR_SET([python3_result], + ["no, the version is in the supported range, and the modules can be imported, but $reason (even with ARCHFLAGS set to empty)"]) + ]) + ], [ + AS_VAR_SET([python3_result], + ["no, the version is in the supported range, and the modules can be imported, but $reason"]) + ]) + ]) + AS_VAR_IF([python3_result], [yes], [ + dnl these commands are expected to call AC_MSG_RESULT + COMMANDS_IF_GOOD + ], [ + AC_MSG_RESULT([$python3_result]) + ]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: ]REQUIRED_MODULES) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too recent]) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too old]) + ]) + ], [ + AC_MSG_RESULT([no, "]PYTHON_EXE[ --version" does not work]) + ]) + + + m4_popdef([PYTHON_EXE]) + m4_popdef([MIN_VERSION]) + m4_popdef([LT_VERSION]) + m4_popdef([REQUIRED_MODULES]) + m4_popdef([COMMANDS_IF_GOOD]) + +]) + +dnl distutils test +AC_DEFUN([SAGE_PYTHON_CHECK_DISTUTILS], [ + m4_pushdef([PYTHON_EXE], [$1]) + m4_pushdef([COMMANDS_IF_DISTUTILS_GOOD], [$2]) + m4_pushdef([COMMANDS_IF_DISTUTILS_NOT_GOOD], [$3]) + SAGE_PYTHON_DISTUTILS_C_CONFTEST + dnl (echo "***ENV***:"; env; echo "***SYSCONFIG***"; conftest_venv/bin/python3 -m sysconfig) >& AS_MESSAGE_LOG_FD + echo PYTHON_EXE conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD + AS_IF([PYTHON_EXE conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ + SAGE_PYTHON_DISTUTILS_CXX_CONFTEST + echo PYTHON_EXE conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 + AS_IF([PYTHON_EXE conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ + COMMANDS_IF_DISTUTILS_GOOD], [ + reason="distutils cannot build a C++ 11 extension" + COMMANDS_IF_DISTUTILS_NOT_GOOD + ]) + ], [ + reason="distutils cannot build a C extension" + COMMANDS_IF_DISTUTILS_NOT_GOOD + ]) + m4_popdef([PYTHON_EXE]) + m4_popdef([COMMANDS_IF_DISTUTILS_GOOD]) + m4_popdef([COMMANDS_IF_DISTUTILS_NOT_GOOD]) +]) + +dnl Write conftest.py and conftest.c +AC_DEFUN([SAGE_PYTHON_DISTUTILS_C_CONFTEST], [ + rm -rf conftest.* AC_LANG_PUSH([C]) AC_LANG_CONFTEST([ AC_LANG_SOURCE([[ @@ -54,9 +130,10 @@ modules = list((Extension("config_check_distutils", list(("conftest.c",))),)) setup(name="config_check_distutils", ext_modules=modules) exit(0) EOF - dnl (echo "***ENV***:"; env; echo "***SYSCONFIG***"; conftest_venv/bin/python3 -m sysconfig) >& AS_MESSAGE_LOG_FD - echo CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD - AS_IF([CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ +]) + +dnl Write conftest.py and conftest.cpp +AC_DEFUN([SAGE_PYTHON_DISTUTILS_CXX_CONFTEST], [ rm -rf conftest.* AC_LANG_PUSH([C++]) AC_LANG_CONFTEST([ @@ -107,31 +184,4 @@ modules = list((Extension("config_check_distutils_cxx", list(("conftest.cpp",)), setup(name="config_check_distutils_cxx", ext_modules=modules) exit(0) EOF - AS_IF([CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ - COMMANDS_IF_GOOD], [ - AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C++ 11 extension]) - ]) - ], [ - AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C extension]) - ]) - ], [ - AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: ]REQUIRED_MODULES) - ]) - ], [ - AC_MSG_RESULT([no, $python3_version is too recent]) - ]) - ], [ - AC_MSG_RESULT([no, $python3_version is too old]) - ]) - ], [ - AC_MSG_RESULT([no, "]PYTHON_EXE[ --version" does not work]) - ]) - - - m4_popdef([PYTHON_EXE]) - m4_popdef([MIN_VERSION]) - m4_popdef([LT_VERSION]) - m4_popdef([REQUIRED_MODULES]) - m4_popdef([COMMANDS_IF_GOOD]) - ]) diff --git a/src/VERSION.txt b/src/VERSION.txt deleted file mode 120000 index c892f30fef3..00000000000 --- a/src/VERSION.txt +++ /dev/null @@ -1 +0,0 @@ -../VERSION.txt \ No newline at end of file diff --git a/src/VERSION.txt b/src/VERSION.txt new file mode 100644 index 00000000000..9efdd7fc203 --- /dev/null +++ b/src/VERSION.txt @@ -0,0 +1 @@ +9.3.beta9 diff --git a/src/bin/sage b/src/bin/sage index 3b86b2db9fd..eeff9452cde 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -531,7 +531,7 @@ fi if [ "$1" = '-pip' -o "$1" = '--pip' -o "$1" = "--pip3" ]; then shift - exec python3 -m pip "$@" + exec python3 -m pip --disable-pip-version-check "$@" fi if [ "$1" = '-python' -o "$1" = '--python' -o "$1" = '-python3' -o "$1" = '--python3' ]; then @@ -871,18 +871,32 @@ if [ "$1" = '-ba' ]; then exit $? fi +exec-runtests() { + sage_setup + export PYTHONIOENCODING="utf-8" # Fix encoding for doctests + # Trac #31191: Some versions of Python 3.6 disable utf-8 even if + # the locale is just "POSIX". This leads to errors in both the + # doctest framework and in various individual doctests. + if locale | grep -q -E -i '^LC_CTYPE="(C|POSIX)"$'; then + # Get an UTF-8 locale if possible. + LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) + LANG=$LC_ALL + export LC_ALL + export LANG + fi + exec sage-runtests "$@" +} + if [ "$1" = '-t' -o "$1" = '-bt' -o "$1" = '-tp' -o "$1" = '-btp' ]; then if [ "$1" = '-bt' -o "$1" = '-btp' ]; then build_sage fi - sage_setup - export PYTHONIOENCODING="utf-8" # Fix encoding for doctests if [ "$1" = '-tp' -o "$1" = '-btp' ]; then shift - exec sage-runtests -p "$@" + exec-runtests -p "$@" else shift - exec sage-runtests "$@" + exec-runtests "$@" fi fi @@ -891,16 +905,12 @@ if [ "$1" = '-tnew' -o "$1" = '-btnew' ]; then build_sage fi shift - sage_setup - export PYTHONIOENCODING="utf-8" # Fix encoding for doctests - exec sage-runtests --new "$@" + exec-runtests --new "$@" fi if [ "$1" = '-testall' -o "$1" = "--testall" ]; then shift - sage_setup - export PYTHONIOENCODING="utf-8" # Fix encoding for doctests - exec sage-runtests -a "$@" + exec-runtests -a "$@" fi if [ "$1" = '-fixdoctests' -o "$1" = '--fixdoctests' ]; then @@ -915,7 +925,7 @@ fi if [ "$1" = "-coverageall" -o "$1" = "--coverageall" ]; then shift - exec sage-coverageall "$@" + exec sage-coverage --all "$@" fi if [ "$1" = '-startuptime' -o "$1" = '--startuptime' ]; then diff --git a/src/bin/sage-coverage b/src/bin/sage-coverage index 63fb9746074..80b3b0eca63 100755 --- a/src/bin/sage-coverage +++ b/src/bin/sage-coverage @@ -8,12 +8,68 @@ from tokenize import (NEWLINE, COMMENT, INDENT, DEDENT, STRING, NL, import argparse parser = argparse.ArgumentParser(description='Look into Sage files for wrong doctests.') -parser.add_argument('filename', type=str, nargs='+', help='filename or a directory') +parser.add_argument('filename', type=str, nargs='*', help='filename or a directory') +parser.add_argument('--all', action='store_true', help='give summary info about all files in the Sage library') parser.add_argument('--only-bad', action='store_true', help='only print info for bad formatted files') parser.add_argument('--summary', action='store_true', help='only print a short summary') args = parser.parse_args() +def coverage_all(directory): + os.chdir(directory) + r = os.popen('sage-coverage * | grep SCORE').readlines() + + s = [] + scr = 0 + total = 0 + for x in r: + y = x.lstrip('SCORE ') + i = y.rfind(' of ') + j = y.rfind(')') + n = int(y[i+4:j]) + + i = y.rfind(':') + j = y.rfind('%') + scr += float(y[i+1:j]) * float(n) + + total += n + + s.append(y) + + print(''.join(s)) + + # Trac #5859: Don't crash if there isn't anything to test. + score = 100.0 + if total != 0: + score = (float(scr) / total) + + print("Overall weighted coverage score: {:.1f}%".format(score)) + print("Total number of functions: {}".format(total)) + + # Print up to 3 doctest coverage goals. + i = 0 + for goal in [70, 75, 80, 85, 90, 95, 99]: + if score < goal: + i += 1 + if i > 3: break + need = int((goal*total - scr)/100.0) + print("We need {:>4} more function{} to get to {}% coverage." + .format(need, "" if (need == 1) else "s", goal)) + +if args.all: + if not args.filename: + coverage_all(os.path.join(os.environ["SAGE_SRC"], 'sage')) + elif len(args.filename) == 1: + coverage_all(args.filename[0]) + else: + print("sage-coverage: error: --all only accepts one filename argument") + sys.exit(1) + sys.exit(0) + +if not args.filename: + print("sage-coverage: error: if --all is not given, at least one filename argument is expected") + sys.exit(1) + # Collect coverage results for one file class CoverageResults: def __init__(self, filename=""): diff --git a/src/bin/sage-coverageall b/src/bin/sage-coverageall deleted file mode 100755 index 7c62e2a1850..00000000000 --- a/src/bin/sage-coverageall +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 - -import os, sys - -def coverage_all(directory): - os.chdir(directory) - r = os.popen('sage-coverage * | grep SCORE').readlines() - - s = [] - scr = 0 - total = 0 - for x in r: - y = x.lstrip('SCORE ') - i = y.rfind(' of ') - j = y.rfind(')') - n = int(y[i+4:j]) - - i = y.rfind(':') - j = y.rfind('%') - scr += float(y[i+1:j]) * float(n) - - total += n - - s.append(y) - - print(''.join(s)) - - # Trac #5859: Don't crash if there isn't anything to test. - score = 100.0 - if total != 0: - score = (float(scr) / total) - - print("Overall weighted coverage score: {:.1f}%".format(score)) - print("Total number of functions: {}".format(total)) - - # Print up to 3 doctest coverage goals. - i = 0 - for goal in [70, 75, 80, 85, 90, 95, 99]: - if score < goal: - i += 1 - if i > 3: break - need = int((goal*total - scr)/100.0) - print("We need {:>4} more function{} to get to {}% coverage." - .format(need, "" if (need == 1) else "s", goal)) - -if len(sys.argv) == 1: - coverage_all(os.path.join(os.environ["SAGE_SRC"], 'sage')) -else: - coverage_all(sys.argv[1]) diff --git a/src/bin/sage-env b/src/bin/sage-env index e027ffd3d35..4c617d8cd44 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -202,6 +202,16 @@ export F77="$FC" export F90="$FC" # Needed for SciPy export F95="$FC" +# For ARCHFLAGS (#31227) we need to distinguish unset and empty. +# If the environment defines ARCHFLAGS, even when empty, then take that. +# Otherwise, use the configured value; but if that is "unset", do not set +# the variable at all. +if [ "${ARCHFLAGS-unset}" = "unset" ]; then + if [ "${SAGE_ARCHFLAGS-unset}" != "unset" ]; then + export ARCHFLAGS="${SAGE_ARCHFLAGS}" + fi +fi + # Call with: contains_spaces X${VAR}X # i.e., WITHOUT quotes but some character(s) around the environment variable to test. # (This function does return false for empty/unset variables.) diff --git a/src/bin/sage-env-config.in b/src/bin/sage-env-config.in index 299eea7e394..7ab1995e4e8 100644 --- a/src/bin/sage-env-config.in +++ b/src/bin/sage-env-config.in @@ -50,5 +50,6 @@ CONFIGURED_OBJCXX="@OBJCXX@" ####################################### # Other configuration (not exported, only used in sage-env) ####################################### +SAGE_ARCHFLAGS="@SAGE_ARCHFLAGS@" SAGE_PKG_CONFIG_PATH="@SAGE_PKG_CONFIG_PATH@" SAGE_MACOSX_DEPLOYMENT_TARGET="@SAGE_MACOSX_DEPLOYMENT_TARGET@" diff --git a/src/bin/sage-fixdoctests b/src/bin/sage-fixdoctests index 7d7d0a20573..c0aaf1b94b3 100755 --- a/src/bin/sage-fixdoctests +++ b/src/bin/sage-fixdoctests @@ -62,15 +62,31 @@ for block in doctests: comma = block.find(', in ') # we try to extract the line number which gives the test failure if line == -1 or comma==-1: continue # but if something goes wrong we give up line_num=eval(block[line+5:comma]) + + # Take care of multiline examples. + if 'Expected' in block: + i1 = block.index('Failed example') + i2 = block.index('Expected') + example_len = block[i1:i2].count('\n') - 1 + line_num += example_len - 1 + if 'Expected nothing' in block: exp = block.find('Expected nothing') block='\n'+block[exp+17:] # so that split('\nGot:\n') does not fail below elif 'Expected:' in block: exp = block.find('Expected:\n') block=block[exp+10:] + elif 'Exception raised' in block: + exp = block.find('Exception raised') + block='\nGot:\n'+block[exp+17:] else: continue - if block[-21:]=='Got:\n \n': + # Error testing. + if 'Traceback (most recent call last):' in block: + expected, got = block.split('\nGot:\n') + got = got.splitlines() + got = [' Traceback (most recent call last):', ' ...', got[-1]] + elif block[-21:]=='Got:\n \n': expected=block[:-22] got=[''] else: diff --git a/src/bin/sage-startuptime.py b/src/bin/sage-startuptime.py index 370d2945340..6e7ed21445c 100755 --- a/src/bin/sage-startuptime.py +++ b/src/bin/sage-startuptime.py @@ -119,7 +119,7 @@ def print_table(module_list, limit): def guess_module_name(src): module = [] src, ext = os.path.splitext(src) - while src: + while src and src != '/': head, tail = os.path.split(os.path.abspath(src)) if (tail == 'src' or any(os.path.exists(os.path.join(head, tail, f)) @@ -127,7 +127,7 @@ def guess_module_name(src): return '.'.join(module) module.insert(0, tail) src = head - raise ValueError('"' + file_name + '" does not appear to be a Python module source file or package directory.') + return None if not have_cmdline_args: print('== Slowest module imports (excluding / including children) ==') @@ -140,8 +140,13 @@ def guess_module_name(src): matching_modules = [m for m in all_modules if m.__name__ == module_arg] if not matching_modules: if '/' in module_arg or any(module_arg.endswith(ext) for ext in ('.py', '.pyx')) or os.path.isdir(module_arg): - module_arg = guess_module_name(module_arg) - matching_modules = [m for m in all_modules if m.__name__.startswith(module_arg)] + file_name = module_arg + module_arg = guess_module_name(file_name) + if not module_arg: + print('Warning: "' + file_name + '" does not appear to be a Python module source file or package directory.') + continue + else: + matching_modules = [m for m in all_modules if m.__name__.startswith(module_arg)] else: matching_modules = [m for m in all_modules if m.__name__.endswith(module_arg)] if not matching_modules: diff --git a/src/bin/sage-update-version b/src/bin/sage-update-version index d972b782918..cdf165694f7 100755 --- a/src/bin/sage-update-version +++ b/src/bin/sage-update-version @@ -36,7 +36,7 @@ SAGE_VERSION=`echo "$1" | sed 's/^sage-//'` SAGE_RELEASE_DATE=`date -u +'%Y-%m-%d'` SAGE_VERSION_BANNER="SageMath version $SAGE_VERSION, Release Date: $SAGE_RELEASE_DATE" -echo $SAGE_VERSION > "$SAGE_ROOT/build/pkgs/sagelib/package-version.txt" +echo $SAGE_VERSION | tee "$SAGE_SRC/VERSION.txt" > "$SAGE_ROOT/build/pkgs/sagelib/package-version.txt" # Update Sage version file for Python in SAGE_SRC/sage cat < "$SAGE_SRC/sage/version.py" @@ -72,6 +72,7 @@ git commit -m "Updated SageMath version to $SAGE_VERSION" -- \ "$SAGE_ROOT/VERSION.txt" \ "$SAGE_ROOT/.zenodo.json" \ "$SAGE_SRC/sage/version.py" \ + "$SAGE_SRC/VERSION.txt" \ "$SAGE_SRC/bin/sage-version.sh" \ "$SAGE_ROOT/build/pkgs/configure/checksums.ini" \ "$SAGE_ROOT/build/pkgs/configure/package-version.txt" \ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 08a54cd5665..35c967d8fe9 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.3.beta7' -SAGE_RELEASE_DATE='2021-02-07' -SAGE_VERSION_BANNER='SageMath version 9.3.beta7, Release Date: 2021-02-07' +SAGE_VERSION='9.3.beta9' +SAGE_RELEASE_DATE='2021-03-14' +SAGE_VERSION_BANNER='SageMath version 9.3.beta9, Release Date: 2021-03-14' diff --git a/src/conftest.py b/src/conftest.py new file mode 100644 index 00000000000..33135c9e333 --- /dev/null +++ b/src/conftest.py @@ -0,0 +1,31 @@ +# pyright: strict + +"""Configuration and fixtures for pytest. + +This file configures pytest and provides some global fixtures. +See https://docs.pytest.org/en/latest/index.html for more details. + +At the moment, Sage is not yet using any tests based on pytest. +""" + +from __future__ import annotations +from typing import Any +import pytest + +# Ignore a few test files that are (not yet) using pytest +collect_ignore = [ + "sage/misc/nested_class_test.py", + "sage/repl/rich_output/backend_test.py", + "sage/tests/deprecation_test.py" +] + + +@pytest.fixture(autouse=True) +def add_imports(doctest_namespace: dict[str, Any]): + """ + Add global imports for doctests. + + See `pytest documentation `. + """ + import sage.all # type: ignore # implicitly used below by calling locals() + doctest_namespace.update(**locals()) diff --git a/src/doc/de/tutorial/tour_numtheory.rst b/src/doc/de/tutorial/tour_numtheory.rst index 9932561a6bf..a012234c99b 100644 --- a/src/doc/de/tutorial/tour_numtheory.rst +++ b/src/doc/de/tutorial/tour_numtheory.rst @@ -146,9 +146,8 @@ implementiert. :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 80e2110f60d..0fa04587099 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -545,9 +545,58 @@ Where ``sdh_pip_install`` is a function provided by ``sage-dist-helpers`` that points to the correct ``pip`` for the Python used by Sage, and includes some default flags needed for correct installation into Sage. -If pip will not work, you may use ``sdh_setup_bdist_wheel``, followed by +If pip will not work but a command like ``python3 setup.py install`` +will, you may use ``sdh_setup_bdist_wheel``, followed by ``sdh_store_and_pip_install_wheel .``. +For ``spkg-check.in`` script templates, make sure to call +``sage-python23`` rather than ``python``. This will ensure that the +correct version of Python is used to check the package. +The same holds for ; for example, the ``scipy`` ``spkg-check.in`` +file contains the line + +.. CODE-BLOCK:: bash + + exec sage-python23 spkg-check.py + +All normal Python packages must have a file ``install-requires.txt``. +If a Python package is available on PyPI, this file must contain the +name of the package as it is known to PyPI. Optionally, +``install-requires.txt`` can encode version constraints (such as lower +and upper bounds). The constraints are in the format of the +``install_requires`` key of `setup.cfg +`_ +or `setup.py +`_. + +The files may include comments (starting with ``#``) that explain why a particular lower +bound is warranted or why we wish to include or reject certain versions. + +For example: + +.. CODE-BLOCK:: bash + + $ cat build/pkgs/sphinx/package-version.txt + 3.1.2.p0 + $ cat build/pkgs/sphinx/install-requires.txt + # gentoo uses 3.2.1 + sphinx >=3, <3.3 + +The comments may include links to Trac tickets, as in the following example: + +.. CODE-BLOCK:: bash + + $ cat build/pkgs/packaging/install-requires.txt + packaging >=18.0 + # Trac #30975: packaging 20.5 is known to work but we have to silence "DeprecationWarning: Creating a LegacyVersion" + +The currently encoded version constraints are merely a starting point. +Developers and downstream packagers are invited to refine the version +constraints based on their experience and tests. When a package +update is made in order to pick up a critical bug fix from a newer +version, then the lower bound should be adjusted. + + .. _section-spkg-SPKG-txt: The SPKG.rst or SPKG.txt File diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index 38ca69e050d..6482700b0e0 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -176,7 +176,7 @@ Building the Manuals All of the Sage manuals are built using the ``sage --docbuild`` script. The content of the ``sage --docbuild`` script is defined in -``SAGE_ROOT/src/sage_setup/docbuild/__init__.py``. It is a thin wrapper around +``SAGE_ROOT/src/sage_docbuild/__init__.py``. It is a thin wrapper around the ``sphinx-build`` script which does all of the real work. It is designed to be a replacement for the default Makefiles generated by the ``sphinx-quickstart`` script. The general form of the command diff --git a/src/doc/en/developer/tools.rst b/src/doc/en/developer/tools.rst index ce16324a39b..0a7124177d3 100644 --- a/src/doc/en/developer/tools.rst +++ b/src/doc/en/developer/tools.rst @@ -8,14 +8,26 @@ Additional development and testing tools ======================================== +Pytest +=============================== +`Pytest `_ is a testing framework. +At the moment, Sage is not yet using any tests based on pytest. + +*Installation:* ``pip install -U pytest``, see `documentation `__ for details. +*Usage:* +- Manual: Run ``pytest path/to/the/test_file.py`` or ``pytest`` to run all tests (from a virtual environment with Sage installed) +- VS Code: Install the `Python `_ extension and follow the `offical VS Code documentation `__. +*Configuration:* ``conftest.py`` in the source folder +*Documentation:* https://docs.pytest.org/en/stable/index.html + Pyright =============================== -`Pyright ` is static type checker. +`Pyright `_ is static type checker. -*Installation:* ``npm install -g pyright``, see `documentation `_ for details. +*Installation:* ``npm install -g pyright``, see `documentation `__ for details. *Usage:* - Manual: Run ``pyright path/to/the/file.py`` -- VS Code: Install the `Pylance `_ extension. +- VS Code: Install the `Pylance `__ extension. *Configuration:* ``pyrightconfig.json`` in the root *Note*: Currently, only the manifolds package is checked. Further packages can be added in the ``pyrightconfig.json`` file. *Documentation:* https://github.com/microsoft/pyright#documentation @@ -27,7 +39,7 @@ Pycodestyle *Installation:* ``pip install -U pycodestyle --user`` *Usage:* - Manual: Run ``pycodestyle path/to/the/file.py`` -- VS Code: Activate by adding the setting ``"python.linting.pycodestyleEnabled": true``, see `official VS Code documentation `_ for details. +- VS Code: Activate by adding the setting ``"python.linting.pycodestyleEnabled": true``, see `official VS Code documentation `__ for details. *Configuration:* ``[pycodestyle]`` block in ``src/tox.ini`` *Documentation:* https://pycodestyle.pycqa.org/en/latest/index.html diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 665472c600a..13074527a07 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -88,7 +88,7 @@ computer: - **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.6 or 2.7. +- **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``.) @@ -178,6 +178,9 @@ 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.6.x to 3.9.x; however, support for system python 3.6.x +is deprecated and will be removed in the next development cycle. + 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, diff --git a/src/doc/en/reference/arithmetic_curves/index.rst b/src/doc/en/reference/arithmetic_curves/index.rst index 94ce3cbf359..b6469625dd3 100644 --- a/src/doc/en/reference/arithmetic_curves/index.rst +++ b/src/doc/en/reference/arithmetic_curves/index.rst @@ -42,6 +42,7 @@ Elliptic curves over number fields sage/schemes/elliptic_curves/sha_tate sage/schemes/elliptic_curves/cm + sage/schemes/elliptic_curves/Qcurves The following relate to elliptic curves over local nonarchimedean fields. diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py index 28c95c69e4c..eef8721bdd8 100644 --- a/src/doc/en/reference/conf_sub.py +++ b/src/doc/en/reference/conf_sub.py @@ -67,10 +67,8 @@ ] latex_elements['hyperref'] = r""" -\usepackage{xcite} -\usepackage{xr-hyper} +\usepackage{xr} \externaldocument[../references/]{../references/references} -\externalcitedocument[../references/]{../references/references} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ad49f9c38cb..630b1b9b831 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -144,6 +144,12 @@ REFERENCES: .. [AHK2015] Karim Adiprasito, June Huh, and Eric Katz. *Hodge theory for combinatorial geometries*. :arxiv:`1511.02888`. +.. [AHKOS2014] Aubin Arroyo, Isabel Hubard, Klavdija Kutnar, Eugenia + O’Reilly, and Primož Šparl. *Classification of + Symmetric Tabačjn Graphs*. Graphs and Combinatorics + 31:1137-1153, 2015. + :doi:`10.1007/s00373-014-1447-8` + .. [AHMP2008] \J.-P. Aumasson, L. Henzen, W. Meier, and R. C-W Phan, *Sha-3 proposal blake*; in Submission to NIST, (2008). @@ -464,6 +470,11 @@ 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 +.. [BH2012] \A. Brouwer and W. Haemers, + Spectra of graphs, + Springer, 2012, + http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf + .. [BPPSST2017] Banik, Pandey, Peyrin, Sasaki, Sim, and Todo, GIFT : A Small Present Towards Reaching the Limit of Lightweight Encryption. *Cryptographic Hardware and Embedded Systems - CHES 2017*, @@ -557,6 +568,9 @@ REFERENCES: Fast and Simple Computation of Top-k Closeness Centralities. :arxiv:`1507.01490`. +.. [BCMS1988] \I. Z. Bouwer, W. W. Chernoff, B. Monson, and Z. Star. + *The Foster Census*, Charles Babbage Research Centre, 1988. + .. [BCN1989] Andries E. Brouwer, Arjeh M. Cohen, and Arnold Neumaier. *Distance-Regular Graphs*, Springer, 1989. @@ -794,6 +808,11 @@ REFERENCES: 16. n 9. 1973, pages 575-577. ACM Press. [Online] Available: http://www.ram.org/computing/rambin/rambin.html +.. [BK1977] James R. Bunch and Linda Kaufman. + Some Stable Methods for Calculating Inertia and Solving + Symmetric Linear Systems. + Mathematics of Computation, 31(137):163-179, 1977. + .. [BK1992] \U. Brehm and W. Kuhnel, *15-vertex triangulations of an 8-manifold*, Math. Annalen 294 (1992), no. 1, 167-193. @@ -917,6 +936,11 @@ REFERENCES: .. [Bond2007] P. Bonderson, Nonabelian anyons and interferometry, Dissertation (2007). https://thesis.library.caltech.edu/2447/ +.. [BDGRTW2019] Bonderson, Delaney, Galindo, Rowell, Tran, and Wang, + On invariants of modular categories beyond modular data. + J. Pure Appl. Algebra 223 (2019), no. 9, 4065–4088. + :arXiv:`1805.05736`. + .. [BM2004] John M. Boyer and Wendy J. Myrvold, *On the Cutting Edge: *Simplified `O(n)` Planarity by Edge Addition*. Journal of Graph Algorithms and Applications, Vol. 8, No. 3, pp. 241-273, @@ -1232,6 +1256,10 @@ REFERENCES: .. [Car1972] \R. W. Carter. *Simple groups of Lie type*, volume 28 of Pure and Applied Mathematics. John Wiley and Sons, 1972. +.. [Cha2005] \F. Chapoton, *Une Base Symétrique de l'algèbre des + Coinvariants Quasi-Symétriques*, Electronic Journal of + Combinatorics Vol 12(1) (2005) N16. + .. [CQ2019] \A. Cassella and C. Quadrelli. *Right-angled Artin groups and enhanced Koszul properties*. Preprint, :arxiv:`1907.03824`, (2019). @@ -1522,6 +1550,8 @@ REFERENCES: "Orange" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/orange-spec.pdf +.. [CrNa2020] \J.E. Cremona and F. Najman, `\QQ`-curves over odd degree number fields, :arxiv:`2004.10054`. + .. [CoCo1] J.H. Conway, H.S.M. Coxeter *Triangulated polygons and frieze patterns*, The Mathematical Gazette (1973) 57 p.87-94 @@ -1626,6 +1656,11 @@ REFERENCES: *Counting smaller elements in the tamari and m-tamari lattices*. Journal of Combinatorial Theory, Series A. (2015). :arxiv:`1311.3922`. +.. [CP2016] \N. Cohen, D. Pasechnik, + *Implementing Brouwer's database of strongly regular graphs*, + Designs, Codes, and Cryptography, 2016 + :doi:`10.1007/s10623-016-0264-x` + .. [CPdA2014] Maria Chlouveraki and Loïc Poulain d'Andecy. *Representation theory of the Yokonuma-Hecke algebra*. (2014) :arxiv:`1302.6225v2`. @@ -1818,6 +1853,11 @@ REFERENCES: orderings II. The parabolic analogue of Kazhdan-Lusztig polynomials, J. Alg. 111 (1987) 483-506. +.. [DesignHandbook] Handbook of Combinatorial Designs (2ed) + Charles Colbourn, Jeffrey Dinitz + Chapman & Hall/CRC + 2012 + .. [DerZak1980] Nachum Dershowitz and Schmuel Zaks, *Enumerations of ordered trees*, Discrete Mathematics (1980), 31: 9-28. @@ -2744,6 +2784,10 @@ REFERENCES: In: Recent Developments in Algebra and Related Areas, ALM vol. 8, pp. 129--153. International Press, Somerville (2009). +.. [Hig2002] Nicholas J. Higham. Accuracy and Stability of Numerical Algorithms, + Second Edition. Society for Industrial and Applied Mathematics, + Philadelphia, 2002. + .. [Hig2008] \N. J. Higham, "Functions of matrices: theory and computation", Society for Industrial and Applied Mathematics (2008). @@ -2938,6 +2982,11 @@ REFERENCES: in Quantum graphs and their applications, 173-189, Contemp. Math., Vol. 415. +.. [HST2008] \F. Hivert, A. Schilling, N. Thiery, + *Hecke group algebras as quotients of affine Hecke algebras at level 0*, + Journal of Combinatorial Theory, Series A 116 (2009) 844-863 + (:arxiv:`0804.3781`) + .. [HSV2006] Hess, Smart, Vercauteren, "The Eta Pairing Revisited", IEEE Trans. Information Theory, 52(10): 4595-4602, 2006. @@ -2969,6 +3018,14 @@ REFERENCES: .. [Huy2005] \D. Huybrechts : *Complex Geometry*, Springer (Berlin) (2005). + +.. [HX2010] \W. Haemers and Q. Xiang, + Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` + exist for all `m>1`, + European Journal of Combinatorics, + Volume 31, Issue 6, August 2010, Pages 1553-1559, + :doi:`10.1016/j.ejc.2009.07.009` + .. [HZ1999] \C. Holton, L. Q. Zamboni, *Descendants of primitive substitutions*, Theory Comput. Syst. 32 (1999) 133-157. @@ -5358,6 +5415,10 @@ REFERENCES: Jennings. *A linear approximation method for the Shapley value.* Artificial Intelligence 172.14 (2008): 1673-1699. +.. [SWW1972] \A. Street, W. Wallis, J. Wallis, + Combinatorics: Room squares, sum-free sets, Hadamard matrices. + Lecture notes in Mathematics 292 (1972). + .. [Sys1987] Maciej M. SysŁo, *Minimizing the jump number for partially-ordered sets: a graph-theoretic approach, II*. @@ -5368,6 +5429,10 @@ REFERENCES: \J. Yajima, N. Torii, and H. Tanaka, *The block cipher SC2000*; in FSE, (2001), pp. 312-327. +.. [Sz1969] \G. Szekeres, + Tournaments and Hadamard matrices, + Enseignement Math. (2) 15(1969), 269-278 + .. [SZ1994] Bruno Salvy and Paul Zimmermann. Gfun: a Maple package for the manipulation of generating and holonomic functions in one variable. ACM transactions on mathematical software, @@ -5645,6 +5710,10 @@ REFERENCES: .. [Wie2000] \B. Wieland. *A large dihedral symmetry of the set of alternating sign matrices*. Electron. J. Combin. 7 (2000). +.. [Wilson2008] Steve Wilson. *Rose Window Graphs*. Ars Mathematica + Contemporanea 1(1):7-19, 2008. + :doi:`10.26493/1855-3974.13.5bb` + .. [Wilson2016] \A. T. Wilson. *An extension of MacMahon's Equidistribution Theorem to ordered multiset partitions*. Electron. J. Combin., **23** (1) (2016). @@ -5700,6 +5769,10 @@ REFERENCES: *Numerical modular symbols for elliptic curves*. Math. Comp. 87 (2018), no. 313, 2393–2423. +.. [WW1972] \J. Wallis and A.L. Whiteman, + Some classes of Hadamard matrices with constant diagonal, + 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 Theory, Series A 56.1 (1991): 27-46. @@ -5793,6 +5866,10 @@ REFERENCES: *"Hidden symmetry" of the Askey–Wilson polynomials*, Theoret. and Math. Phys. **89** (1991), 1146--1157. +.. [ZF2012] Jin-Xin Zhou and Yan-Quan Feng. *Cubic Vertex-Transitive + Non-Cayley Graphs of Order 8p*. The Electronic Journal of + Combinatorics, 19(1), P53, 2012. :doi:`10.37236/2087` + .. [Zie1998] \G. M. Ziegler. *Shelling polyhedral 3-balls and 4-polytopes*. Discrete Comput. Geom. 19 (1998), 159-174. diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst index 846065890cf..07e84de55ea 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst @@ -16,7 +16,7 @@ that use Sage). sage: K. = NumberField(x^6 + 40*x^3 + 1372) sage: G = K.galois_group() sage: G - Galois group of Number Field in alpha with defining polynomial x^6 + 40*x^3 + 1372 + Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372 Internally G is represented as a group of permutations, but we can also apply any element of G to any element of the field: @@ -40,19 +40,18 @@ Some more advanced number-theoretical tools are available via G: sage: P = K.primes_above(2)[0] sage: G.inertia_group(P) - Subgroup [(), (1,4,6)(2,5,3), (1,6,4)(2,3,5)] of Galois group of Number Field in alpha with defining polynomial x^6 + 40*x^3 + 1372 + Subgroup [(), (1,4,6)(2,5,3), (1,6,4)(2,3,5)] of Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372 sage: sorted([G.artin_symbol(Q) for Q in K.primes_above(5)]) # random order, see Trac #18308 [(1,3)(2,6)(4,5), (1,2)(3,4)(5,6), (1,5)(2,4)(3,6)] If the number field is not Galois over `\QQ`, then the ``galois_group`` -command will construct its Galois closure and return the Galois group of that; -you need to give it a variable name for the generator of the Galois closure: +command will construct its Galois closure and return the Galois group of that: :: sage: K. = NumberField(x^3 - 2) sage: G = K.galois_group(names='b'); G - Galois group of Galois closure in b of Number Field in a with defining polynomial x^3 - 2 + Galois group 3T2 (S3) with order 6 of x^3 - 2 sage: G.order() 6 @@ -62,18 +61,14 @@ Some more Galois groups We compute two more Galois groups of degree :math:`5` extensions, and see that one has Galois group :math:`S_5`, so is not solvable by radicals. For these -purposes we only want to know the structure of the Galois group as an abstract +purposes we only need to know the structure of the Galois group as an abstract group, rather than as an explicit group of automorphisms of the splitting -field; this is much quicker to calculate. PARI has a type for representing -"abstract Galois groups", and Sage can use this.:: - - sage: NumberField(x^5 - 2, 'a').galois_group(type="pari") - Galois group PARI group [20, -1, 3, "F(5) = 5:4"] of - degree 5 of the Number Field in a with defining - polynomial x^5 - 2 - sage: NumberField(x^5 - x + 2, 'a').galois_group(type="pari") - Galois group PARI group [120, -1, 5, "S5"] of degree 5 of - the Number Field in a with defining polynomial x^5 - x + 2 +field:: + + sage: NumberField(x^5 - 2, 'a').galois_group() + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: NumberField(x^5 - x + 2, 'a').galois_group() + Galois group 5T5 (S5) with order 120 of x^5 - x + 2 Magma's Galois group command diff --git a/src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst b/src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst index 16b35fcab15..43a706a0367 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst @@ -52,7 +52,7 @@ driver code .. CODE-BLOCK:: python import numpy - j=numpy.complex(0,1) + j=complex(0,1) num_points=50 u=numpy.zeros((num_points,num_points),dtype=float) pi_c=float(pi) diff --git a/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst b/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst index e50b2ea5d40..6e2bdbfb0c6 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst @@ -235,7 +235,7 @@ from 0 to 2. There is a useful command :meth:`numpy.r_` that is best explained b :: sage: from numpy import r_ - sage: j=numpy.complex(0,1) + sage: j=complex(0,1) sage: RealNumber=float sage: Integer=int sage: n=r_[0.0:5.0] @@ -280,7 +280,7 @@ an equally spaced grid with `\Delta x = \Delta y = .25` for :: sage: import numpy - sage: j=numpy.complex(0,1) + sage: j=complex(0,1) sage: def f(x,y): ....: return x**2+y**2 sage: from numpy import meshgrid diff --git a/src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst b/src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst index 19a200ec50c..388f3556fd0 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst @@ -24,7 +24,7 @@ this with the solver we wrote in the section on f2py. root=0 dx=1.0/(num_points-1) from numpy import r_ - j=numpy.complex(0,1) + j=complex(0,1) rows_per_process=num_points/size max_iter=5000 num_iter=0 diff --git a/src/doc/en/tutorial/tour_numtheory.rst b/src/doc/en/tutorial/tour_numtheory.rst index 7b608725efd..3064d100e23 100644 --- a/src/doc/en/tutorial/tour_numtheory.rst +++ b/src/doc/en/tutorial/tour_numtheory.rst @@ -146,9 +146,8 @@ NumberField class. :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/es/tutorial/tour_numtheory.rst b/src/doc/es/tutorial/tour_numtheory.rst index 23975916643..a1f7d1a87b9 100644 --- a/src/doc/es/tutorial/tour_numtheory.rst +++ b/src/doc/es/tutorial/tour_numtheory.rst @@ -129,8 +129,8 @@ Varios métodos relacionados están implementados en la clase ``NumberField``:: :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/fr/tutorial/tour_numtheory.rst b/src/doc/fr/tutorial/tour_numtheory.rst index e6364f11f98..871092f5fa5 100644 --- a/src/doc/fr/tutorial/tour_numtheory.rst +++ b/src/doc/fr/tutorial/tour_numtheory.rst @@ -148,9 +148,8 @@ dans la classe NumberField. :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/ja/tutorial/tour_numtheory.rst b/src/doc/ja/tutorial/tour_numtheory.rst index 51646b514f3..47af68c862c 100644 --- a/src/doc/ja/tutorial/tour_numtheory.rst +++ b/src/doc/ja/tutorial/tour_numtheory.rst @@ -150,9 +150,8 @@ Sageには :math:`p` \-進数体も組込まれている. :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/pt/tutorial/tour_numtheory.rst b/src/doc/pt/tutorial/tour_numtheory.rst index 496bc1f8392..6371b491eaf 100644 --- a/src/doc/pt/tutorial/tour_numtheory.rst +++ b/src/doc/pt/tutorial/tour_numtheory.rst @@ -146,9 +146,8 @@ NumberField. :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/doc/ru/tutorial/tour_numtheory.rst b/src/doc/ru/tutorial/tour_numtheory.rst index 617799dfcaa..652abfbc99e 100644 --- a/src/doc/ru/tutorial/tour_numtheory.rst +++ b/src/doc/ru/tutorial/tour_numtheory.rst @@ -139,9 +139,8 @@ Sage содержит стандартные функции теории чис :: - sage: K.galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field - in a with defining polynomial x^3 + x^2 - 2*x + 8 + sage: K.galois_group() + Galois group 3T2 (S3) with order 6 of x^3 + x^2 - 2*x + 8 .. link diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index f79dccc2eef..dae5f0650f7 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -518,7 +518,7 @@ def F_polynomial(self): """ if not self.is_homogeneous(): raise ValueError("this element is not homogeneous") - subs_dict = dict() + subs_dict = {} A = self.parent() for x in A.initial_cluster_variables(): subs_dict[x.lift()] = A._U(1) @@ -560,7 +560,7 @@ def homogeneous_components(self): """ deg_matrix = block_matrix([[identity_matrix(self.parent().rank()), -self.parent().b_matrix()]]) - components = dict() + components = {} x = self.lift() monomials = x.monomials() for m in monomials: @@ -1260,7 +1260,7 @@ def __classcall__(self, data, **kwargs): M0 = Q.b_matrix()[n:, :] m = M0.nrows() - B0 = block_matrix([[B0],[M0]]) + B0 = block_matrix([[B0], [M0]]) B0.set_immutable() # Determine the names of the initial cluster variables @@ -1285,7 +1285,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([-1] + [int(v) for u, _, v in splitnames + if u == '' and v.isdigit()]) kwargs.setdefault('next_free_index', nfi) # Determine scalars @@ -2336,7 +2337,7 @@ def mutate_initial(self, direction, **kwargs): tmp_path_dict[new_g_vect] = new_path # update storage - initial_g = (0,)*(k)+(1,)+(0,)*(n-k-1) + initial_g = (0,) * (k) + (1,) + (0,) * (n - k - 1) tmp_path_dict[initial_g] = [] path_dict = tmp_path_dict path_to_current = ([k] + path_to_current[:1] if path_to_current[:1] != [k] else []) + path_to_current[1:] @@ -2348,7 +2349,8 @@ def mutate_initial(self, direction, **kwargs): # create new algebra coeff_names = self.coefficient_names() scalars = self.scalars() - A = ClusterAlgebra(B0, cluster_variable_names=cv_names, next_free_index = nfi, + A = ClusterAlgebra(B0, cluster_variable_names=cv_names, + next_free_index=nfi, coefficient_names=coeff_names, scalars=scalars) # store computed data @@ -2409,8 +2411,8 @@ def greedy_element(self, d_vector): elif a2 < 0: return self.retract(((1 + x1 ** b) / x2) ** a1 * x2 ** (-a2)) output = 0 - for p in range(0, a2 + 1): - for q in range(0, a1 + 1): + for p in range(a2 + 1): + for q in range(a1 + 1): output += self._greedy_coefficient(d_vector, p, q) * x1 ** (b * p) * x2 ** (c * q) return self.retract(x1 ** (-a1) * x2 ** (-a2) * output) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 7fba0bf3fb3..d430f3d88df 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -953,7 +953,7 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None): else: # Deal with multigrading: convert lists and tuples to elements # of an additive abelian group. - if len(degrees) > 0: + if degrees: multigrade = False try: rank = len(list(degrees[0])) diff --git a/src/sage/algebras/hall_algebra.py b/src/sage/algebras/hall_algebra.py index 97a697f9993..20b8b9c624c 100644 --- a/src/sage/algebras/hall_algebra.py +++ b/src/sage/algebras/hall_algebra.py @@ -116,7 +116,7 @@ class HallAlgebra(CombinatorialFreeModule): `n(\lambda) = \sum_i (i - 1) \lambda_i`. See section 2.3 in [Sch2006]_, and sections II.2 and III.3 - in [Macdonald1995]_ (where our `I_{\lambda}` is called `u_{\lambda}`). + in [Mac1995]_ (where our `I_{\lambda}` is called `u_{\lambda}`). EXAMPLES:: diff --git a/src/sage/algebras/quantum_groups/fock_space.py b/src/sage/algebras/quantum_groups/fock_space.py index dc9c88e1cf4..f3703018e02 100644 --- a/src/sage/algebras/quantum_groups/fock_space.py +++ b/src/sage/algebras/quantum_groups/fock_space.py @@ -1146,7 +1146,7 @@ def _A_to_fock_basis(self, la): while any(c[1]*k + c[0] >= b for c in corners): power = 0 i = -b + r # This will be converted to a mod n number - for x in range(0, b // k + 1): + for x in range(b // k + 1): if (b-x*k, x) in cells: power += 1 cur = cur.f(i) @@ -1907,7 +1907,7 @@ def _LLT(self, la): while any(c[1]*k + c[0] >= b for c in corners): # While there is some cell left to count power = 0 i = -b + r # This will be converted to a mod n number - for x in range(0, b // k + 1): + for x in range(b // k + 1): if (b-x*k, x) in cells: power += 1 cur = cur.f(i) diff --git a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx index d0d291ccdd8..40c55c7a071 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx @@ -1,5 +1,9 @@ # distutils: language = c++ -# distutils: libraries = gmp m ntl +# distutils: libraries = gmp m NTL_LIBRARIES +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA """ Optimized Cython code needed by quaternion algebras diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 061d6f33c9d..95c2732c78a 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -1,5 +1,9 @@ # distutils: language = c++ -# distutils: libraries = gmp m ntl +# distutils: libraries = gmp m NTL_LIBRARIES +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA """ Elements of Quaternion Algebras diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index 2215c243520..3de17e9c138 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -449,7 +449,7 @@ def milnor_multiplication_odd(m1,m2,p): for mono in old_answer: if k not in mono[0]: q_mono = set(mono[0]) - if len(q_mono) > 0: + if q_mono: ind = len(q_mono.intersection(range(k,1+max(q_mono)))) else: ind = 0 @@ -465,7 +465,7 @@ def milnor_multiplication_odd(m1,m2,p): for i in range(1,1+len(mono[1])): if (k+i not in mono[0]) and (p**k <= mono[1][i-1]): q_mono = set(mono[0]) - if len(q_mono) > 0: + if q_mono: ind = len(q_mono.intersection(range(k+i,1+max(q_mono)))) else: ind = 0 @@ -765,11 +765,14 @@ def adem(a, b, c=0, p=2, generic=None): True """ if generic is None: - generic = False if p==2 else True + generic = (p != 2) if not generic: - if b == 0: return {(a,): 1} - elif a == 0: return {(b,): 1} - elif a >= 2*b: return {(a,b): 1} + if b == 0: + return {(a,): 1} + elif a == 0: + return {(b,): 1} + elif a >= 2*b: + return {(a,b): 1} result = {} for c in range(1 + a//2): if binomial_mod2(b-c-1, a-2*c) == 1: diff --git a/src/sage/all.py b/src/sage/all.py index 682d2c4349b..52d59fadf2e 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -65,7 +65,7 @@ ############ setup warning filters before importing Sage stuff #### import warnings -__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: # a debug build does not install the default warning filters. Sadly, this breaks doctests so we # have to re-add them: @@ -204,7 +204,7 @@ from sage.ext.fast_callable import fast_callable from sage.ext.fast_eval import fast_float -sage.misc.lazy_import.lazy_import('sage.sandpiles.all', '*', globals()) +from sage.sandpiles.all import * from sage.tensor.all import * @@ -218,8 +218,7 @@ from cysignals.alarm import alarm, cancel_alarm -# Lazily import notebook functions and interacts (#15335) -lazy_import('sage.interacts.debugger', 'debug') +# Lazily import interacts (#15335) lazy_import('sage.interacts', 'all', 'interacts') from copy import copy, deepcopy diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 53d9683279b..173baa3b793 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -5599,7 +5599,7 @@ def _key_complex_for_display(a): ai = a.imag() if not ai: return (0, ar) - epsilon = 1e-10 + epsilon = ar.parent()(1e-10) if ar.abs() < epsilon: ar_truncated = 0 elif ar.prec() < 34: diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 461a0038fe9..b9f9801fd1f 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -420,7 +420,7 @@ from sage.libs.pynac.pynac import symbol_table from sage.misc.lazy_import import lazy_import -lazy_import('sage.interfaces.maxima_lib','maxima') +lazy_import('sage.interfaces.maxima_lib', 'maxima') ######################################################## @@ -663,6 +663,7 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima', hold=False): else: raise ValueError("unknown algorithm: %s" % algorithm) + def nintegral(ex, x, a, b, desired_relative_error='1e-8', maximum_num_subintervals=200): @@ -1089,7 +1090,7 @@ def minpoly(ex, var='x', algorithm=None, bits=None, degree=None, epsilon=0): for degree in degree_list: - f = QQ[var](algdep(a, degree)) # TODO: use the known_bits parameter? + f = QQ[var](algdep(a, degree)) # TODO: use the known_bits parameter? # If indeed we have found a minimal polynomial, # it should be accurate to a much higher precision. error = abs(f(aa)) @@ -1436,9 +1437,11 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): raise ValueError("Unknown algorithm: %s" % algorithm) return ex.parent()(l) + # lim is alias for limit lim = limit + ################################################################### # Laplace transform ################################################################### @@ -1820,6 +1823,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): else: raise ValueError("Unknown algorithm: %s" % algorithm) + ################################################################### # symbolic evaluation "at" a point ################################################################### @@ -1926,6 +1930,7 @@ def dummy_diff(*args): args[i] = Integer(args[i]) return f.diff(*args) + def dummy_integrate(*args): """ This function is called to create formal wrappers of integrals that @@ -1946,6 +1951,7 @@ def dummy_integrate(*args): else: return indefinite_integral(*args, hold=True) + def dummy_laplace(*args): """ This function is called to create formal wrappers of laplace transforms @@ -1961,6 +1967,7 @@ def dummy_laplace(*args): """ return _laplace(args[0], var(repr(args[1])), var(repr(args[2]))) + def dummy_inverse_laplace(*args): """ This function is called to create formal wrappers of inverse laplace @@ -1976,6 +1983,7 @@ def dummy_inverse_laplace(*args): """ return _inverse_laplace(args[0], var(repr(args[1])), var(repr(args[2]))) + ####################################################### # # Helper functions for printing latex expression @@ -2000,6 +2008,7 @@ def _laplace_latex_(self, *args): """ return "\\mathcal{L}\\left(%s\\right)" % (', '.join(latex(x) for x in args)) + def _inverse_laplace_latex_(self, *args): r""" Return LaTeX expression for inverse Laplace transform @@ -2027,23 +2036,19 @@ def _inverse_laplace_latex_(self, *args): ######################################i################ - - -####################################################### - # Conversion dict for special maxima objects # c,k1,k2 are from ode2() -symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma',\ - '%c' : '_C', '%k1' : '_K1', '%k2' : '_K2', - 'e':'_e', 'i':'_i', 'I':'_I'} +symtable = {'%pi': 'pi', '%e': 'e', '%i': 'I', + '%gamma': 'euler_gamma', + '%c': '_C', '%k1': '_K1', '%k2': '_K2', + 'e': '_e', 'i': '_i', 'I': '_I'} +maxima_tick = re.compile(r"'[\w]*") -maxima_tick = re.compile("'[a-z|A-Z|0-9|_]*") +maxima_qp = re.compile(r"\?\%[\w]*") # e.g., ?%jacobi_cd -maxima_qp = re.compile(r"\?\%[a-z|A-Z|0-9|_]*") # e.g., ?%jacobi_cd - -maxima_var = re.compile(r"[a-z|A-Z|0-9|_\%]*") # e.g., %jacobi_cd +maxima_var = re.compile(r"[\w\%]*") # e.g., %jacobi_cd sci_not = re.compile(r"(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]\d+)") @@ -2167,7 +2172,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): delayed_functions = maxima_qp.findall(s) if len(delayed_functions): for X in delayed_functions: - if X == '?%at': # we will replace Maxima's "at" with symbolic evaluation, not an SFunction + if X == '?%at': # we will replace Maxima's "at" with symbolic evaluation, not an SFunction pass else: syms[X[2:]] = function_factory(X[2:]) @@ -2189,13 +2194,13 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): s = s.replace("%","") - s = s.replace("#","!=") # a lot of this code should be refactored somewhere... + s = s.replace("#","!=") # a lot of this code should be refactored somewhere... #we apply the square-bracket replacing patterns repeatedly #to ensure that nested brackets get handled (from inside to out) while True: olds = s s = polylog_ex.sub('polylog(\\1,', s) - s = maxima_polygamma.sub(r'psi(\g<1>,', s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below + s = maxima_polygamma.sub(r'psi(\g<1>,', s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below if s == olds: break @@ -2205,15 +2210,15 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): s = s.replace("!==", "!=") #replace %union from to_poly_solve with a list - if s[0:5]=='union': + if s[0:5] == 'union': s = s[5:] - s = s[s.find("(")+1:s.rfind(")")] - s = "[" + s + "]" # turn it into a string that looks like a list + s = s[s.find("(") + 1:s.rfind(")")] + s = "[" + s + "]" # turn it into a string that looks like a list #replace %solve from to_poly_solve with the expressions - if s[0:5]=='solve': + if s[0:5] == 'solve': s = s[5:] - s = s[s.find("(")+1:s.find("]")+1] + s = s[s.find("(") + 1:s.find("]") + 1] #replace all instances of Maxima's scientific notation #with regular notation @@ -2244,10 +2249,11 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): finally: _augmented_syms = {} except SyntaxError: - raise TypeError("unable to make sense of Maxima expression '%s' in Sage"%s) + raise TypeError("unable to make sense of Maxima expression '%s' in Sage" % s) finally: is_simplified = False + # Comma format options for Maxima def mapped_opts(v): """ @@ -2344,7 +2350,8 @@ def _find_var(name): except (KeyError, TypeError): return var(name) -def _find_func(name, create_when_missing = True): + +def _find_func(name, create_when_missing=True): """ Function to pass to Parser for constructing functions from strings. For internal use. @@ -2382,10 +2389,12 @@ def _find_func(name, create_when_missing = True): else: return None -SR_parser = Parser(make_int = lambda x: SR(Integer(x)), - make_float = lambda x: SR(create_RealNumber(x)), - make_var = _find_var, - make_function = _find_func) + +SR_parser = Parser(make_int=lambda x: SR(Integer(x)), + make_float=lambda x: SR(create_RealNumber(x)), + make_var=_find_var, + make_function=_find_func) + def symbolic_expression_from_string(s, syms=None, accept_sequence=False): """ @@ -2432,6 +2441,7 @@ def symbolic_expression_from_string(s, syms=None, accept_sequence=False): finally: _augmented_syms = {} + def _find_Mvar(name): """ Function to pass to Parser for constructing @@ -2459,7 +2469,8 @@ def _find_Mvar(name): except (KeyError, TypeError): return var(name) -SRM_parser = Parser(make_int = lambda x: SR(Integer(x)), - make_float = lambda x: SR(RealDoubleElement(x)), - make_var = _find_Mvar, - make_function = _find_func) + +SRM_parser = Parser(make_int=lambda x: SR(Integer(x)), + make_float=lambda x: SR(RealDoubleElement(x)), + make_var=_find_Mvar, + make_function=_find_func) diff --git a/src/sage/calculus/interpolators.pyx b/src/sage/calculus/interpolators.pyx index a62b9e8e19d..d920acf8b59 100644 --- a/src/sage/calculus/interpolators.pyx +++ b/src/sage/calculus/interpolators.pyx @@ -89,7 +89,7 @@ cdef class PSpline: """ if type(pts[0]) == type((0,0)): self.pts = np.array( - [np.complex(i[0], i[1]) for i in pts], dtype=np.complex128) + [complex(i[0], i[1]) for i in pts], dtype=np.complex128) else: self.pts = np.array(pts, dtype=np.complex128) self.N = len(pts) @@ -221,7 +221,7 @@ cdef class CCSpline: """ if type(pts[0]) == type((0,0)): pts = np.array( - [np.complex(pt[0], pt[1]) for pt in pts], dtype=np.complex128) + [complex(pt[0], pt[1]) for pt in pts], dtype=np.complex128) cdef int N, i, k N = len(pts) yvec = np.zeros(N, dtype=np.complex128) diff --git a/src/sage/calculus/riemann.pyx b/src/sage/calculus/riemann.pyx index 0bbc5507120..c8569895dc5 100644 --- a/src/sage/calculus/riemann.pyx +++ b/src/sage/calculus/riemann.pyx @@ -255,13 +255,13 @@ cdef class Riemann_Map: for k in xrange(self.B): for i in xrange(N): fk = fs[k](self.tk[N-i-1]) - cps[k, i] = np.complex(1/fk) - dps[k, i] = np.complex(1/fk**2*fprimes[k](self.tk[N-i-1])) + cps[k, i] = complex(1/fk) + dps[k, i] = complex(1/fk**2*fprimes[k](self.tk[N-i-1])) else: for k in xrange(self.B): for i in xrange(N): - cps[k, i] = np.complex(fs[k](self.tk[i])) - dps[k, i] = np.complex(fprimes[k](self.tk[i])) + cps[k, i] = complex(fs[k](self.tk[i])) + dps[k, i] = complex(fprimes[k](self.tk[i])) if self.exterior: xmax = (1/cps).real.max() xmin = (1/cps).real.min() @@ -605,8 +605,7 @@ cdef class Riemann_Map: (-1.56...e-05+0.989694...j) sage: m.riemann_map(0.4) (0.73324...+3.2...e-06j) - sage: import numpy as np - sage: m.riemann_map(np.complex(-3, 0.0001)) + sage: m.riemann_map(complex(-3, 0.0001)) (1.405757...e-05+8.06...e-10j) """ @@ -691,8 +690,7 @@ cdef class Riemann_Map: (0.486319...-4.90019052...j) sage: m.inverse_riemann_map(0.25 - 0.3*I) (0.1653244...-0.180936...j) - sage: import numpy as np - sage: m.inverse_riemann_map(np.complex(-0.2, 0.5)) + sage: m.inverse_riemann_map(complex(-0.2, 0.5)) (-0.156280...+0.321819...j) """ if self.exterior: @@ -974,7 +972,7 @@ cdef class Riemann_Map: for i in xrange(pts - 1): temp[i] = self.inverse_riemann_map( (i * 1.0) / (pts * 1.0) * exp(I * angle) * linescale) - temp[pts - 1] = np.complex( + temp[pts - 1] = complex( self.f(s(angle)) if angle <= tmax else self.f(s(angle-TWOPI))) if plotjoined: line_list[k] = list_plot( diff --git a/src/sage/categories/discrete_valuation.py b/src/sage/categories/discrete_valuation.py index 2cd61e3705c..5b68f8b6c12 100644 --- a/src/sage/categories/discrete_valuation.py +++ b/src/sage/categories/discrete_valuation.py @@ -64,6 +64,48 @@ def residue_field(self): Rational Field """ + def _matrix_charpoly(self, M, var): + r""" + Return the characteristic polynomial of `M`. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(GF(5)) + sage: M = matrix(4, 4, [ (t^(i+j)).add_bigoh(10) + ....: for i in range(4) for j in range(4) ]) + sage: M + [ 1 + O(t^10) t + O(t^10) t^2 + O(t^10) t^3 + O(t^10)] + [ t + O(t^10) t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)] + [t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10)] + [t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10) t^6 + O(t^10)] + sage: M.charpoly() # indirect doctest + x^4 + (4 + 4*t^2 + 4*t^4 + 4*t^6 + O(t^10))*x^3 + + Note that this function uses a Hessenberg-like algorithm + that performs divisions. Hence, truncations may show up + even if the input matrix is exact:: + + sage: M = matrix(3, 3, [ 1, t, t^2, 1+t, t^2, t^3, t^2, t^3, t^4 ]) + sage: M + [ 1 t t^2] + [1 + t t^2 t^3] + [ t^2 t^3 t^4] + sage: M.charpoly() + x^3 + (4 + 4*t^2 + 4*t^4 + O(t^25))*x^2 + (4*t + O(t^24))*x + + Another example over the p-adics:: + + sage: R = Zp(5, print_mode="digits", prec=5) + sage: M = matrix(R, 3, 3, range(9)) + sage: M + [ 0 ...00001 ...00002] + [ ...00003 ...00004 ...000010] + [ ...00011 ...00012 ...00013] + sage: M.charpoly() + ...00001*x^3 + ...44423*x^2 + ...44412*x + ...00000 + """ + return M._charpoly_hessenberg(var) + class ElementMethods: @abstract_method def valuation(self): @@ -200,7 +242,7 @@ def uniformizer(self): @abstract_method def residue_field(self): """ - Return the residue field of the ring of integers of + Return the residue field of the ring of integers of this discrete valuation field. EXAMPLES:: @@ -213,6 +255,45 @@ def residue_field(self): Rational Field """ + def _matrix_hessenbergize(self, H): + r""" + Replace `H` with an Hessenberg form of it. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(GF(5)) + sage: K = R.fraction_field() + sage: H = matrix(K, 4, 4, [ (t^(i+j)).add_bigoh(10) + ....: for i in range(4) for j in range(4) ]) + sage: H + [ 1 + O(t^10) t + O(t^10) t^2 + O(t^10) t^3 + O(t^10)] + [ t + O(t^10) t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)] + [t^2 + O(t^10) t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10)] + [t^3 + O(t^10) t^4 + O(t^10) t^5 + O(t^10) t^6 + O(t^10)] + sage: H.hessenbergize() + sage: H + [ 1 + O(t^10) t + t^3 + t^5 + O(t^10) t^2 + O(t^10) t^3 + O(t^10)] + [ t + O(t^10) t^2 + t^4 + t^6 + O(t^10) t^3 + O(t^10) t^4 + O(t^10)] + [ O(t^10) O(t^10) O(t^10) O(t^10)] + [ O(t^10) O(t^10) O(t^10) O(t^10)] + + Another example over the p-adics:: + + sage: K = Qp(5, print_mode="digits", prec=5) + sage: H = matrix(K, 3, 3, range(9)) + sage: H + [ 0 ...00001 ...00002] + [ ...00003 ...00004 ...000010] + [ ...00011 ...00012 ...00013] + sage: H.hessenbergize() + sage: H + [ 0 ...00010 ...00002] + [ ...00003 ...00024 ...000010] + [ ...00000 ...44440 ...44443] + """ + from sage.matrix.matrix_cdv import hessenbergize_cdvf + hessenbergize_cdvf(H) + class ElementMethods: @abstract_method def valuation(self): diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index ba5dc3a488d..0f4a5758086 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -1116,7 +1116,7 @@ def product(self, left, right): sage: x*y B[(0, [1, 2, 3])] + B[(1, [3, 1, 2])] """ - return self._cartesian_product_of_elements([(a*b) for (a,b) in zip(left.cartesian_factors(), right.cartesian_factors())]) + return self._cartesian_product_of_elements( (a*b) for (a,b) in zip(left.cartesian_factors(), right.cartesian_factors()) ) class Subquotients(SubquotientsCategory): r""" diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 9379bda43b2..056526d2d98 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -339,6 +339,13 @@ cdef class Morphism(Map): Traceback (most recent call last): ... NotImplementedError: unable to compare morphisms of type <... 'sage.categories.morphism.IdentityMorphism'> and <... 'sage.categories.morphism.SetMorphism'> with domain Partitions of the integer 5 + + Check that :trac:`29632` is fixed:: + + sage: R. = QuadraticField(-1)[] + sage: f = R.hom(R.gens(), R) + sage: f.is_identity() + True """ if self is other: return rich_to_bool(op, 0) @@ -362,7 +369,8 @@ cdef class Morphism(Map): # gens by picking an element of the initial domain (e) and # multiplying it with the gens of the scalar ring. if e is not None and isinstance(e, ModuleElement): - gens = [(e)._lmul_(x) for x in gens] + B = (e)._parent._base + gens = [(e)._lmul_(B.coerce(x)) for x in gens] for e in gens: x = self(e) y = other(e) diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py index 0317f520a5e..61978ecd419 100644 --- a/src/sage/categories/number_fields.py +++ b/src/sage/categories/number_fields.py @@ -1,19 +1,20 @@ r""" Number fields """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 David Kohel # William Stein # 2008 Teresa Gomez-Diaz (CNRS) # 2008-2009 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.category_singleton import Category_singleton from sage.categories.basic import Fields + class NumberFields(Category_singleton): r""" The category of number fields. @@ -116,11 +117,91 @@ def _call_(self, x): try: return x.number_field() except AttributeError: - raise TypeError("unable to canonically associate a number field to %s"%x) - + raise TypeError("unable to canonically associate a number field to %s" % x) class ParentMethods: - pass + def zeta_function(self, prec=53, + max_imaginary_part=0, + max_asymp_coeffs=40, algorithm='pari'): + r""" + Return the Dedekind zeta function of this number field. + + Actually, this returns an interface for computing with the + Dedekind zeta function `\zeta_F(s)` of the number field `F`. + + INPUT: + + - ``prec`` -- optional integer (default 53) bits precision + + - ``max_imaginary_part`` -- optional real number (default 0) + + - ``max_asymp_coeffs`` -- optional integer (default 40) + + - ``algorithm`` -- optional (default "pari") either "gp" or "pari" + + OUTPUT: The zeta function of this number field. + + If algorithm is "gp", this returns an interface to Tim + Dokchitser's gp script for computing with L-functions. + + If algorithm is "pari", this returns instead an interface to Pari's + own general implementation of L-functions. + + EXAMPLES:: + + sage: K. = NumberField(ZZ['x'].0^2+ZZ['x'].0-1) + sage: Z = K.zeta_function(); Z + PARI zeta function associated to Number Field in a with defining polynomial x^2 + x - 1 + sage: Z(-1) + 0.0333333333333333 + sage: L. = NumberField([x^2 - 5, x^2 + 3, x^2 + 1]) + sage: Z = L.zeta_function() + sage: Z(5) + 1.00199015670185 + + Using the algorithm "pari":: + + sage: K. = NumberField(ZZ['x'].0^2+ZZ['x'].0-1) + sage: Z = K.zeta_function(algorithm="pari") + sage: Z(-1) + 0.0333333333333333 + sage: L. = NumberField([x^2 - 5, x^2 + 3, x^2 + 1]) + sage: Z = L.zeta_function(algorithm="pari") + sage: Z(5) + 1.00199015670185 + + TESTS:: + + sage: QQ.zeta_function() + PARI zeta function associated to Rational Field + """ + if algorithm == 'gp': + from sage.lfunctions.all import Dokchitser + r1, r2 = self.signature() + zero = [0] + one = [1] + Z = Dokchitser(conductor=abs(self.absolute_discriminant()), + gammaV=(r1 + r2) * zero + r2 * one, + weight=1, + eps=1, + poles=[1], + prec=prec) + s = 'nf = nfinit(%s);' % self.absolute_polynomial() + s += 'dzk = dirzetak(nf,cflength());' + Z.init_coeffs('dzk[k]', pari_precode=s, + max_imaginary_part=max_imaginary_part, + max_asymp_coeffs=max_asymp_coeffs) + Z.check_functional_equation() + Z.rename('Dokchitser Zeta function associated to %s' % self) + return Z + + if algorithm == 'pari': + from sage.lfunctions.pari import lfun_number_field, LFunction + Z = LFunction(lfun_number_field(self), prec=prec) + Z.rename('PARI zeta function associated to %s' % self) + return Z + + raise ValueError('algorithm must be "gp" or "pari"') class ElementMethods: pass diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 2d4eb607f6b..e270892257f 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2542,14 +2542,10 @@ def merge(self, other): We check that :trac:`12353` has been resolved:: - sage: RealIntervalField(53)(-1) > RR(1) - False - sage: RealIntervalField(54)(-1) > RR(1) - False - sage: RealIntervalField(54)(1) > RR(-1) - True - sage: RealIntervalField(53)(1) > RR(-1) - True + sage: RIF(1) > RR(1) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for >: 'Real Interval Field with 53 bits of precision' and 'Real Field with 53 bits of precision' We check that various pushouts work:: diff --git a/src/sage/categories/vector_spaces.py b/src/sage/categories/vector_spaces.py index c31f47ead35..00765d672e0 100644 --- a/src/sage/categories/vector_spaces.py +++ b/src/sage/categories/vector_spaces.py @@ -88,7 +88,7 @@ def __init__(self, K): """ Category_module.__init__(self, K) - def __call__(self, x): + def _call_(self, x): """ Try to coerce ``x`` into an object of this category @@ -97,6 +97,14 @@ def __call__(self, x): sage: VectorSpaces(QQ)(ZZ^3) Vector space of dimension 3 over Rational Field + TESTS: + + Check whether :trac:`30174` is fixed:: + + sage: Q3 = FiniteRankFreeModule(QQ, 3) + sage: Modules(QQ)(Q3) is Q3 + True + """ try: V = x.vector_space(self.base_field()) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index e26423fdc07..5342aad0fbc 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -951,7 +951,7 @@ def is_projective(self): A linear code `C` over a field is called *projective* when its dual `Cd` has minimum weight `\geq 3`, i.e. when no two coordinate positions of `C` are linearly independent (cf. definition 3 from [BS2011]_ or 9.8.1 from - [BH12]_). + [BH2012]_). EXAMPLES:: diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index e69c015e758..ee309fd1ba6 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -148,7 +148,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from numbers import Integral +from typing import Iterator from sage.rings.all import ZZ, QQ, Integer, infinity from sage.arith.all import bernoulli, factorial @@ -170,7 +170,7 @@ lazy_import('sage.interfaces.maxima_lib', 'maxima') -def bell_number(n, algorithm='flint', **options) -> Integral: +def bell_number(n, algorithm='flint', **options) -> Integer: r""" Return the `n`-th Bell number. @@ -476,7 +476,7 @@ def catalan_number(n): return (2 * n).binomial(n).divide_knowing_divisible_by(n + 1) -def narayana_number(n, k) -> Integral: +def narayana_number(n: Integer, k: Integer) -> Integer: r""" Return the Narayana number of index ``(n, k)``. @@ -511,7 +511,7 @@ def narayana_number(n, k) -> Integral: return (n.binomial(k + 1) * n.binomial(k)).divide_knowing_divisible_by(n) -def euler_number(n, algorithm='flint') -> Integral: +def euler_number(n, algorithm='flint') -> Integer: """ Return the `n`-th Euler number. @@ -561,7 +561,7 @@ def euler_number(n, algorithm='flint') -> Integral: @cached_function(key=lambda n, k, a: (n, k)) -def eulerian_number(n, k, algorithm='recursive') -> Integral: +def eulerian_number(n, k, algorithm='recursive') -> Integer: """ Return the Eulerian number of index ``(n, k)``. @@ -659,7 +659,7 @@ def eulerian_polynomial(n, algorithm='derivative'): return R([eulerian_number(n, k, "formula") for k in range(n)]) -def fibonacci(n, algorithm="pari") -> Integral: +def fibonacci(n, algorithm="pari") -> Integer: """ Return the `n`-th Fibonacci number. @@ -834,7 +834,7 @@ def lucas_number2(n, P, Q): return libgap.Lucas(P, Q, n)[1].sage() -def stirling_number1(n, k) -> Integral: +def stirling_number1(n, k) -> Integer: r""" Return the `n`-th Stirling number `S_1(n,k)` of the first kind. @@ -863,7 +863,7 @@ def stirling_number1(n, k) -> Integral: return libgap.Stirling1(n, k).sage() -def stirling_number2(n, k, algorithm=None) -> Integral: +def stirling_number2(n, k, algorithm=None) -> Integer: r""" Return the `n`-th Stirling number `S_2(n,k)` of the second kind. @@ -1386,7 +1386,7 @@ def __bool__(self) -> bool: __nonzero__ = __bool__ - def __len__(self) -> Integral: + def __len__(self) -> Integer: """ EXAMPLES:: @@ -1691,7 +1691,7 @@ def __hash__(self): """ return hash(repr(self)) - def __cardinality_from_iterator(self) -> Integral: + def __cardinality_from_iterator(self) -> Integer: """ Default implementation of cardinality which just goes through the iterator of the combinatorial class to count the number of objects. @@ -1796,7 +1796,7 @@ def __list_from_iterator(self): # Set the default object class to be CombinatorialObject Element = CombinatorialObject - def __iterator_from_next(self): + def __iterator_from_next(self) -> Iterator: """ An iterator to use when the .first() and .next(x) methods are provided. @@ -1857,7 +1857,7 @@ def __iterator_from_previous(self): li.append(l) return reversed(li) - def __iterator_from_unrank(self): + def __iterator_from_unrank(self) -> Iterator: """ An iterator to use when .unrank() is provided. @@ -1884,7 +1884,7 @@ def __iterator_from_unrank(self): else: yield u - def __iterator_from_list(self): + def __iterator_from_list(self) -> Iterator: """ An iterator to use when .list() is provided() @@ -2180,7 +2180,7 @@ def __contains__(self, x) -> bool: """ return x in self.combinatorial_class and self.f(x) - def cardinality(self) -> Integral: + def cardinality(self) -> Integer: """ EXAMPLES:: @@ -2194,7 +2194,7 @@ def cardinality(self) -> Integral: c += 1 return c - def __iter__(self): + def __iter__(self) -> Iterator: """ EXAMPLES:: @@ -2256,7 +2256,7 @@ def __contains__(self, x) -> bool: """ return x in self.left_cc or x in self.right_cc - def cardinality(self) -> Integral: + def cardinality(self) -> Integer: """ EXAMPLES:: @@ -2285,7 +2285,7 @@ def list(self): """ return self.left_cc.list() + self.right_cc.list() - def __iter__(self): + def __iter__(self) -> Iterator: """ EXAMPLES:: @@ -2443,7 +2443,7 @@ def __repr__(self) -> str: else: return "Image of %s by %s" % (self.cc, self.f) - def cardinality(self) -> Integral: + def cardinality(self) -> Integer: """ Return the cardinality of this combinatorial class @@ -2456,7 +2456,7 @@ def cardinality(self) -> Integral: """ return self.cc.cardinality() - def __iter__(self): + def __iter__(self) -> Iterator: """ Return an iterator over the elements of this combinatorial class @@ -2520,7 +2520,7 @@ def list(self): """ raise NotImplementedError("infinite list") - def __iter__(self): + def __iter__(self) -> Iterator: """ Return an iterator for the infinite combinatorial class ``self`` if possible or raise a NotImplementedError. @@ -2654,7 +2654,7 @@ def _tuples_native(S, k): return ans -def number_of_tuples(S, k, algorithm='naive') -> Integral: +def number_of_tuples(S, k, algorithm='naive') -> Integer: """ Return the size of ``tuples(S, k)`` when `S` is a set. More generally, return the size of ``tuples(set(S), k)``. (So, @@ -2773,7 +2773,7 @@ def unordered_tuples(S, k, algorithm='itertools'): raise ValueError('invalid algorithm') -def number_of_unordered_tuples(S, k, algorithm='naive') -> Integral: +def number_of_unordered_tuples(S, k, algorithm='naive') -> Integer: r""" Return the size of ``unordered_tuples(S, k)`` when `S` is a set. @@ -2820,7 +2820,7 @@ def number_of_unordered_tuples(S, k, algorithm='naive') -> Integral: raise ValueError('invalid algorithm') -def unshuffle_iterator(a, one=1): +def unshuffle_iterator(a, one=1) -> Iterator: r""" Iterate over the unshuffles of a list (or tuple) ``a``, also yielding the signs of the respective permutations. @@ -2882,7 +2882,7 @@ def unshuffle_iterator(a, one=1): (one if sign else - one)) -def bell_polynomial(n: Integral, k: Integral): +def bell_polynomial(n: Integer, k: Integer): r""" Return the Bell Polynomial @@ -2952,7 +2952,7 @@ def bell_polynomial(n: Integral, k: Integral): return result -def fibonacci_sequence(start, stop=None, algorithm=None): +def fibonacci_sequence(start, stop=None, algorithm=None) -> Iterator: r""" Return an iterator over the Fibonacci sequence, for all fibonacci numbers `f_n` from ``n = start`` up to (but @@ -3002,7 +3002,7 @@ def fibonacci_sequence(start, stop=None, algorithm=None): yield fibonacci(n) -def fibonacci_xrange(start, stop=None, algorithm='pari'): +def fibonacci_xrange(start, stop=None, algorithm='pari') -> Iterator: r""" Return an iterator over all of the Fibonacci numbers in the given range, including ``f_n = start`` up to, but not @@ -3063,7 +3063,7 @@ def fibonacci_xrange(start, stop=None, algorithm='pari'): return -def bernoulli_polynomial(x, n: Integral): +def bernoulli_polynomial(x, n: Integer): r""" Return the ``n``-th Bernoulli polynomial evaluated at ``x``. diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 283f92ef1d5..09cd066c8bb 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -19,7 +19,6 @@ from sage.categories.classical_crystals import ClassicalCrystals from sage.structure.parent import Parent -from sage.combinat.partition import Partition from sage.combinat.crystals.letters import CrystalOfLetters from sage.combinat.crystals.tensor_product import TensorProductOfCrystals, \ TensorProductOfRegularCrystalsElement @@ -30,6 +29,7 @@ from sage.combinat.crystals.generalized_young_walls import CrystalOfGeneralizedYoungWalls from sage.combinat.crystals.monomial_crystals import CrystalOfNakajimaMonomials from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations +from sage.rings.integer_ring import ZZ def HighestWeightCrystal(dominant_weight, model=None): r""" @@ -78,6 +78,11 @@ def HighestWeightCrystal(dominant_weight, model=None): sage: crystals.HighestWeight(wt) The crystal of tableaux of type ['C', 2] and shape(s) [[6, 1]] + sage: La = RootSystem(['B',2]).weight_lattice().fundamental_weights() + sage: wt = La[1] + La[2] + sage: crystals.HighestWeight(wt) + The crystal of tableaux of type ['B', 2] and shape(s) [[3/2, 1/2]] + Some type `E` examples:: sage: C = CartanType(['E',6]) @@ -157,6 +162,35 @@ def HighestWeightCrystal(dominant_weight, model=None): sage: crystals.HighestWeight(wt, model='GeneralizedYoungWalls') Highest weight crystal of generalized Young walls of Cartan type ['A', 3, 1] and highest weight Lambda[0] + Lambda[2] + + TESTS: + + Check that the correct crystal is constructed for the fundamental weights:: + + sage: for ct in CartanType.samples(finite=True, crystallographic=True): + ....: L = ct.root_system().weight_lattice() + ....: La = L.fundamental_weights() + ....: for model in ['Tableaux', 'NakajimaMonomials', 'AlcovePaths', 'RiggedConfigurations']: + ....: if model == 'Tableaux' and ct.type() in ["E", "F"]: + ....: continue + ....: for wt in La: + ....: C = crystals.HighestWeight(wt, model=model) + ....: assert L.weyl_dimension(wt) == C.cardinality(), "wrong cardinality in %s, weight %s" % (ct, wt) + ....: assert C.highest_weight_vector().weight() == wt, "wrong weight in %s, weight %s" % (ct, wt) + + Same thing for weights constructed from the simple roots:: + + sage: for ct in CartanType.samples(finite=True, crystallographic=True): + ....: L = ct.root_system().root_space() + ....: La = L.fundamental_weights_from_simple_roots() + ....: for model in ['Tableaux', 'NakajimaMonomials', 'AlcovePaths', 'RiggedConfigurations']: + ....: if model == 'Tableaux' and ct.type() in ["E", "F"]: + ....: continue + ....: for wt in La: + ....: C1 = crystals.HighestWeight(wt.to_ambient().to_weight_space(ZZ), model=model) + ....: C2 = crystals.HighestWeight(wt, model=model) + ....: assert C1 == C2 + """ cartan_type = dominant_weight.parent().cartan_type() if model is None: @@ -170,10 +204,19 @@ def HighestWeightCrystal(dominant_weight, model=None): else: model = 'LSPaths' + # Make sure dominant_weight in the weight space + if cartan_type.is_finite(): + dominant_weight = dominant_weight.to_ambient().to_weight_space(ZZ) + if model == 'Tableaux': - sh = sum([[i]*c for i,c in dominant_weight], []) - sh = Partition(reversed(sh)) - return CrystalOfTableaux(cartan_type, shape=sh.conjugate()) + # we rely on the specific choice of positive roots here + # except in type G_2, the fundamental weights are realized by + # vectors with weakly decreasing nonnegative integer (or in + # type B_n and D_n, half-integer) entries + sh = dominant_weight.to_ambient().to_vector() + if cartan_type.type() == "G": + sh = (-sh)[2:0:-1] + return CrystalOfTableaux(cartan_type, shape=sh) if model == 'TypeE': if not cartan_type.is_finite() or cartan_type.type() != 'E': @@ -185,10 +228,7 @@ def HighestWeightCrystal(dominant_weight, model=None): raise NotImplementedError if model == 'NakajimaMonomials': - # Make sure it's in the weight lattice - P = dominant_weight.parent().root_system.weight_lattice() - wt = P.sum_of_terms((i, c) for i,c in dominant_weight) - return CrystalOfNakajimaMonomials(cartan_type, wt) + return CrystalOfNakajimaMonomials(cartan_type, dominant_weight) if model == 'LSPaths': # Make sure it's in the (extended) weight space @@ -200,10 +240,7 @@ def HighestWeightCrystal(dominant_weight, model=None): return CrystalOfLSPaths(wt) if model == 'AlcovePaths': - # Make sure it's in the weight space - P = dominant_weight.parent().root_system.weight_space() - wt = P.sum_of_terms((i, c) for i,c in dominant_weight) - return CrystalOfAlcovePaths(wt, highest_weight_crystal=True) + return CrystalOfAlcovePaths(dominant_weight, highest_weight_crystal=True) if model == 'GeneralizedYoungWalls': if not cartan_type.is_affine(): @@ -216,10 +253,7 @@ def HighestWeightCrystal(dominant_weight, model=None): return CrystalOfGeneralizedYoungWalls(cartan_type.rank()-1, wt) if model == 'RiggedConfigurations': - # Make sure it's in the weight lattice - P = dominant_weight.parent().root_system.weight_lattice() - wt = P.sum_of_terms((i, c) for i,c in dominant_weight) - return CrystalOfRiggedConfigurations(cartan_type, wt) + return CrystalOfRiggedConfigurations(cartan_type, dominant_weight) raise ValueError("invalid model") diff --git a/src/sage/combinat/crystals/star_crystal.py b/src/sage/combinat/crystals/star_crystal.py index 4d1f312ebbb..2ed01c49422 100644 --- a/src/sage/combinat/crystals/star_crystal.py +++ b/src/sage/combinat/crystals/star_crystal.py @@ -1,289 +1,289 @@ -r""" -Star-Crystal Structure On `B(\infty)` - -AUTHORS: - -- Ben Salisbury: Initial version - -- Travis Scrimshaw: Initial version -""" - -#***************************************************************************** -# Copyright (C) 2016 Ben Salisbury -# Travis Scrimshaw -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element_wrapper import ElementWrapper -from sage.categories.highest_weight_crystals import HighestWeightCrystals -from sage.combinat.crystals.elementary_crystals import ElementaryCrystal - - -class StarCrystal(UniqueRepresentation, Parent): - r""" - The star-crystal or `*`-crystal version of a highest weight crystal. - - The `*`-crystal structure on `B(\infty)` is the structure induced by - the algebra antiautomorphism `* \colon U_q(\mathfrak{g}) \longrightarrow - U_q(\mathfrak{g})` that stabilizes the negative half `U_q^-(\mathfrak{g})`. - It is defined by - - .. MATH:: - - E_i^* = E_i , \ \ \ - F_i^* = F_i , \ \ \ - q^* = q, \ \ \ - (q^h)^* = q^{-h}, - - where `E_i` and `F_i` are the Chevalley generators of `U_q(\mathfrak{g})` - and `h` is an element of the Cartan subalgebra. - - The induced operation on the crystal `B(\infty)` is called the - *Kashiwara involution*. Its implementation here is based on the - recursive algorithm from Theorem 2.2.1 of [Ka1993]_, which states - that for any `i \in I` there is a unique strict crystal embedding - - .. MATH:: - - \Psi_i\colon B(\infty) \longrightarrow B_i \otimes B(\infty) - - such that - - - `u_{\infty} \mapsto b_i(0) \otimes u_{\infty}`, where `u_{\infty}` - is the highest weight vector in `B(\infty)`; - - - if `\Psi_i(b) = f_i^mb_i(0) \otimes b_0`, then - `\Psi_i(f_i^*b) =f_i^{m+1}b_i(0) \otimes b_0` - and `\varepsilon_i(b^*) = m`; - - - the image of `\Psi_i` is `\{f_i^mb_i(0)\otimes b : - \varepsilon_i(b^*) = 0, \ m\ge 0\}`. - - Here, `B_i` is the `i`-th elementary crystal. See - :class:`~sage.combinat.crystals.elementary_crystals.ElementaryCrystal` - for more information. - - INPUT: - - - ``Binf`` -- a crystal from - :class:`~sage.combinat.crystals.catalog_infinity_crystals` - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['A',2]) - sage: Bstar = crystals.infinity.Star(B) - sage: mg = Bstar.highest_weight_vector() - sage: mg - [[1, 1], [2]] - 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``. - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['A',2]) - sage: Bstar = crystals.infinity.Star(B) - sage: TestSuite(Bstar).run(max_runs=40) - sage: TestSuite(Bstar).run(max_runs=1000) # long time - """ - self._Binf = Binf - self._cartan_type = Binf.cartan_type() - Parent.__init__(self, category=HighestWeightCrystals().Infinite()) - self.module_generators = (self(self._Binf.module_generators[0]),) - t0 = Binf.highest_weight_vector() - B = {i: ElementaryCrystal(Binf.cartan_type(),i) for i in self.index_set()} - self._tens = {i: B[i].tensor(Binf) for i in self.index_set()} - gens = {i: self._tens[i](B[i](0), t0) for i in self.index_set()} - self._embedding = {i: Binf.crystal_morphism({t0: gens[i]}) for i in self.index_set()} - self._pullback = {i: self._tens[i].crystal_morphism({gens[i]: t0}) for i in self.index_set()} - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: Y = crystals.infinity.GeneralizedYoungWalls(3) - sage: Ystar = crystals.infinity.Star(Y) - sage: Ystar - Star-crystal version of Crystal of generalized Young walls of type ['A', 3, 1] - """ - return "Star-crystal version of %s" % self._Binf - - class Element(ElementWrapper): - - def e(self,i): - r""" - Return the action of `e_i^*` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) - sage: RCstar = crystals.infinity.Star(RC) - sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) - sage: ascii_art(nuJ.e(1)) - -1[ ]-1 (/) 0[ ]1 (/) -1[ ]-1 (/) -2[ ]-1 - - sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) - sage: Mstar = crystals.infinity.Star(M) - sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) - sage: m.e(1) - Y(0,0)^-1 Y(0,2)^-1 Y(1,1) Y(1,2)^-1 Y(2,1)^2 - """ - P = self.parent() - image = P._embedding[i](self.value) - if image[0].e(i)._m > 0: - return None - return P(P._pullback[i]( P._tens[i](image[0].e(i),image[1]) )) - - def f(self,i): - r""" - Return the action of `f_i^*` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: T = crystals.infinity.Tableaux("G2") - sage: Tstar = crystals.infinity.Star(T) - sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) - sage: t - [[1, 1, 1, 2, 0], [2, 3]] - - sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) - sage: Mstar = crystals.infinity.Star(M) - sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) - sage: m - Y(0,0)^-1 Y(0,2)^-1 Y(1,0)^-1 Y(1,2)^-1 Y(2,0)^2 Y(2,1)^2 - """ - P = self.parent() - image = P._embedding[i](self.value) - return P(P._pullback[i]( P._tens[i](image[0].f(i),image[1]) )) - - def weight(self): - r""" - Return the weight of ``self``. - - EXAMPLES:: - - sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) - sage: RCstar = crystals.infinity.Star(RC) - sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) - sage: nuJ.weight() - -Lambda[0] - 2*Lambda[1] + 2*Lambda[3] - Lambda[4] - + 2*Lambda[5] - 2*Lambda[6] - delta - """ - return self.value.weight() - - def epsilon(self, i): - r""" - Return `\varepsilon_i^*` of ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: Y = crystals.infinity.GeneralizedYoungWalls(3) - sage: Ystar = crystals.infinity.Star(Y) - sage: y = Ystar.module_generators[0].f_string([0,1,3,2,1,0]) - sage: [y.epsilon(i) for i in y.index_set()] - [1, 0, 1, 0] - - sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) - sage: RCstar = crystals.infinity.Star(RC) - sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) - sage: [nuJ.epsilon(i) for i in nuJ.index_set()] - [0, 1, 1, 0, 0, 0, 1] - """ - ep = -1 - while self is not None: - ep += 1 - self = self.e(i) - return ep - - def phi(self, i): - r""" - Return `\varphi_i^*` of ``self``. - - For `b \in B(\infty)`, - - .. MATH:: - - \varphi_i^*(b) = \varepsilon_i^*(b) + \langle h_i, - \mathrm{wt}(b) \rangle, - - where `h_i` is a simple coroot. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: T = crystals.infinity.Tableaux("A2") - sage: Tstar = crystals.infinity.Star(T) - sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) - sage: [t.phi(i) for i in t.index_set()] - [-3, 1] - - sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) - sage: Mstar = crystals.infinity.Star(M) - sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) - sage: [m.phi(i) for i in m.index_set()] - [-1, -1, 4] - """ - P = self.parent().weight_lattice_realization() - ac = P.simple_coroot(i) - return P(self.weight()).scalar(ac) + self.epsilon(i) - - def jump(self, i): - r""" - Return the `i`-jump of ``self``. - - For `b \in B(\infty)`, - - .. MATH:: - - \operatorname{jump}_i(b) = \varepsilon_i(b) + \varepsilon_i^*(b) - + \langle h_i, \mathrm{wt}(b) \rangle, - - where `h_i` is a simple coroot. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: RC = crystals.infinity.RiggedConfigurations("D4") - sage: RCstar = crystals.infinity.Star(RC) - sage: nu0star = RCstar.module_generators[0] - sage: nustar = nu0star.f_string([2,1,3,4,2]) - sage: [nustar.jump(i) for i in RC.index_set()] - [0, 1, 0, 0] - sage: nustar = nu0star.f_string([2,1,3,4,2,2,1,3,2]) # long time - sage: [nustar.jump(i) for i in RC.index_set()] # long time - [1, 0, 1, 2] - """ - P = self.parent().weight_lattice_realization() - ac = P.simple_coroot(i) - return P(self.value.weight()).scalar(ac) + self.epsilon(i) + self.value.epsilon(i) - +r""" +Star-Crystal Structure On `B(\infty)` + +AUTHORS: + +- Ben Salisbury: Initial version + +- Travis Scrimshaw: Initial version +""" + +#***************************************************************************** +# Copyright (C) 2016 Ben Salisbury +# Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.combinat.crystals.elementary_crystals import ElementaryCrystal + + +class StarCrystal(UniqueRepresentation, Parent): + r""" + The star-crystal or `*`-crystal version of a highest weight crystal. + + The `*`-crystal structure on `B(\infty)` is the structure induced by + the algebra antiautomorphism `* \colon U_q(\mathfrak{g}) \longrightarrow + U_q(\mathfrak{g})` that stabilizes the negative half `U_q^-(\mathfrak{g})`. + It is defined by + + .. MATH:: + + E_i^* = E_i , \ \ \ + F_i^* = F_i , \ \ \ + q^* = q, \ \ \ + (q^h)^* = q^{-h}, + + where `E_i` and `F_i` are the Chevalley generators of `U_q(\mathfrak{g})` + and `h` is an element of the Cartan subalgebra. + + The induced operation on the crystal `B(\infty)` is called the + *Kashiwara involution*. Its implementation here is based on the + recursive algorithm from Theorem 2.2.1 of [Ka1993]_, which states + that for any `i \in I` there is a unique strict crystal embedding + + .. MATH:: + + \Psi_i\colon B(\infty) \longrightarrow B_i \otimes B(\infty) + + such that + + - `u_{\infty} \mapsto b_i(0) \otimes u_{\infty}`, where `u_{\infty}` + is the highest weight vector in `B(\infty)`; + + - if `\Psi_i(b) = f_i^mb_i(0) \otimes b_0`, then + `\Psi_i(f_i^*b) =f_i^{m+1}b_i(0) \otimes b_0` + and `\varepsilon_i(b^*) = m`; + + - the image of `\Psi_i` is `\{f_i^mb_i(0)\otimes b : + \varepsilon_i(b^*) = 0, \ m\ge 0\}`. + + Here, `B_i` is the `i`-th elementary crystal. See + :class:`~sage.combinat.crystals.elementary_crystals.ElementaryCrystal` + for more information. + + INPUT: + + - ``Binf`` -- a crystal from + :class:`~sage.combinat.crystals.catalog_infinity_crystals` + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['A',2]) + sage: Bstar = crystals.infinity.Star(B) + sage: mg = Bstar.highest_weight_vector() + sage: mg + [[1, 1], [2]] + 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``. + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['A',2]) + sage: Bstar = crystals.infinity.Star(B) + sage: TestSuite(Bstar).run(max_runs=40) + sage: TestSuite(Bstar).run(max_runs=1000) # long time + """ + self._Binf = Binf + self._cartan_type = Binf.cartan_type() + Parent.__init__(self, category=HighestWeightCrystals().Infinite()) + self.module_generators = (self(self._Binf.module_generators[0]),) + t0 = Binf.highest_weight_vector() + B = {i: ElementaryCrystal(Binf.cartan_type(),i) for i in self.index_set()} + self._tens = {i: B[i].tensor(Binf) for i in self.index_set()} + gens = {i: self._tens[i](B[i](0), t0) for i in self.index_set()} + self._embedding = {i: Binf.crystal_morphism({t0: gens[i]}) for i in self.index_set()} + self._pullback = {i: self._tens[i].crystal_morphism({gens[i]: t0}) for i in self.index_set()} + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Ystar = crystals.infinity.Star(Y) + sage: Ystar + Star-crystal version of Crystal of generalized Young walls of type ['A', 3, 1] + """ + return "Star-crystal version of %s" % self._Binf + + class Element(ElementWrapper): + + def e(self,i): + r""" + Return the action of `e_i^*` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: ascii_art(nuJ.e(1)) + -1[ ]-1 (/) 0[ ]1 (/) -1[ ]-1 (/) -2[ ]-1 + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: m.e(1) + Y(0,0)^-1 Y(0,2)^-1 Y(1,1) Y(1,2)^-1 Y(2,1)^2 + """ + P = self.parent() + image = P._embedding[i](self.value) + if image[0].e(i)._m > 0: + return None + return P(P._pullback[i]( P._tens[i](image[0].e(i),image[1]) )) + + def f(self,i): + r""" + Return the action of `f_i^*` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: T = crystals.infinity.Tableaux("G2") + sage: Tstar = crystals.infinity.Star(T) + sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) + sage: t + [[1, 1, 1, 2, 0], [2, 3]] + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: m + Y(0,0)^-1 Y(0,2)^-1 Y(1,0)^-1 Y(1,2)^-1 Y(2,0)^2 Y(2,1)^2 + """ + P = self.parent() + image = P._embedding[i](self.value) + return P(P._pullback[i]( P._tens[i](image[0].f(i),image[1]) )) + + def weight(self): + r""" + Return the weight of ``self``. + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: nuJ.weight() + -Lambda[0] - 2*Lambda[1] + 2*Lambda[3] - Lambda[4] + + 2*Lambda[5] - 2*Lambda[6] - delta + """ + return self.value.weight() + + def epsilon(self, i): + r""" + Return `\varepsilon_i^*` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Ystar = crystals.infinity.Star(Y) + sage: y = Ystar.module_generators[0].f_string([0,1,3,2,1,0]) + sage: [y.epsilon(i) for i in y.index_set()] + [1, 0, 1, 0] + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: [nuJ.epsilon(i) for i in nuJ.index_set()] + [0, 1, 1, 0, 0, 0, 1] + """ + ep = -1 + while self is not None: + ep += 1 + self = self.e(i) + return ep + + def phi(self, i): + r""" + Return `\varphi_i^*` of ``self``. + + For `b \in B(\infty)`, + + .. MATH:: + + \varphi_i^*(b) = \varepsilon_i^*(b) + \langle h_i, + \mathrm{wt}(b) \rangle, + + where `h_i` is a simple coroot. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: T = crystals.infinity.Tableaux("A2") + sage: Tstar = crystals.infinity.Star(T) + sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) + sage: [t.phi(i) for i in t.index_set()] + [-3, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: [m.phi(i) for i in m.index_set()] + [-1, -1, 4] + """ + P = self.parent().weight_lattice_realization() + ac = P.simple_coroot(i) + return P(self.weight()).scalar(ac) + self.epsilon(i) + + def jump(self, i): + r""" + Return the `i`-jump of ``self``. + + For `b \in B(\infty)`, + + .. MATH:: + + \operatorname{jump}_i(b) = \varepsilon_i(b) + \varepsilon_i^*(b) + + \langle h_i, \mathrm{wt}(b) \rangle, + + where `h_i` is a simple coroot. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations("D4") + sage: RCstar = crystals.infinity.Star(RC) + sage: nu0star = RCstar.module_generators[0] + sage: nustar = nu0star.f_string([2,1,3,4,2]) + sage: [nustar.jump(i) for i in RC.index_set()] + [0, 1, 0, 0] + sage: nustar = nu0star.f_string([2,1,3,4,2,2,1,3,2]) # long time + sage: [nustar.jump(i) for i in RC.index_set()] # long time + [1, 0, 1, 2] + """ + P = self.parent().weight_lattice_realization() + ac = P.simple_coroot(i) + return P(self.value.weight()).scalar(ac) + self.epsilon(i) + self.value.epsilon(i) + diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index b73f154c136..9df369573f8 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -43,7 +43,7 @@ from sage.categories.regular_crystals import RegularCrystals from sage.categories.sets_cat import Sets from sage.combinat.root_system.cartan_type import CartanType, SuperCartanType_standard -from sage.combinat.partition import Partition +from sage.combinat.partition import _Partitions from .letters import CrystalOfLetters from .spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus from sage.combinat.crystals.tensor_product_element import (TensorProductOfCrystalsElement, @@ -51,6 +51,7 @@ TensorProductOfSuperCrystalsElement, TensorProductOfQueerSuperCrystalsElement) from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model +from sage.rings.semirings.non_negative_integer_semiring import NN ############################################################################## # Until trunc gets implemented in sage.function.other @@ -870,6 +871,14 @@ class CrystalOfTableaux(CrystalOfWords): sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]]) sage: Tab in T.list() False + + Check that entries are weakly decreasing also in the spin case:: + + sage: crystals.Tableaux(['D',4], shape=[-1/2,1/2,1/2,-1/2]) + Traceback (most recent call last): + ... + ValueError: entries of each shape must be weakly decreasing + """ @staticmethod @@ -892,29 +901,39 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): sage: T2 = crystals.Tableaux(['A', [1,1]], [3,1,1,1]) sage: T1 is T2 True + """ cartan_type = CartanType(cartan_type) if cartan_type.letter == 'A' and isinstance(cartan_type, SuperCartanType_standard): if shape is None: shape = shapes + shape = _Partitions(shape) from sage.combinat.crystals.bkk_crystals import CrystalOfBKKTableaux return CrystalOfBKKTableaux(cartan_type, shape=shape) if cartan_type.letter == 'Q': if any(shape[i] == shape[i+1] for i in range(len(shape)-1)): raise ValueError("not a strict partition") - shape = Partition(shape) + shape = _Partitions(shape) return CrystalOfQueerTableaux(cartan_type, shape=shape) n = cartan_type.rank() # standardize shape/shapes input into a tuple of tuples + # of length n, or n+1 in type A assert operator.xor(shape is not None, shapes is not None) if shape is not None: shapes = (shape,) - spin_shapes = tuple( tuple(shape) for shape in shapes ) + if cartan_type.type() == "A": + n1 = n + 1 + else: + n1 = n + if not all(all(i == 0 for i in shape[n1:]) for shape in shapes): + raise ValueError("shapes should all have length at most equal to the rank or the rank + 1 in type A") + spin_shapes = tuple((tuple(shape) + (0,)*(n1-len(shape)))[:n1] for shape in shapes) try: - shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes ) + shapes = tuple(tuple(trunc(i) for i in shape) for shape in spin_shapes) except Exception: raise ValueError("shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: + shapes = tuple(_Partitions(shape) if shape[n1-1] in NN else shape for shape in shapes) return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux @@ -926,15 +945,17 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): raise ValueError("the length of all half-integer partition shapes should be the rank") if any(2*i % 2 != 1 for shape in spin_shapes for i in shape): raise ValueError("shapes should be either all partitions or all half-integer partitions") + if any(any(i < j for i, j in zip(shape, shape[1:-1] + (abs(shape[-1]),))) for shape in spin_shapes): + raise ValueError("entries of each shape must be weakly decreasing") if cartan_type.type() == 'D': - if all( i >= 0 for shape in spin_shapes for i in shape): + if all(i >= 0 for shape in spin_shapes for i in shape): S = CrystalOfSpinsPlus(cartan_type) - elif all(shape[-1]<0 for shape in spin_shapes): + elif all(shape[-1] < 0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: raise ValueError("in type D spins should all be positive or negative") else: - if any( i < 0 for shape in spin_shapes for i in shape): + if any(i < 0 for shape in spin_shapes for i in shape): raise ValueError("shapes should all be partitions") S = CrystalOfSpins(cartan_type) B = CrystalOfTableaux(cartan_type, shapes=shapes) @@ -966,7 +987,8 @@ def __init__(self, cartan_type, shapes): self.letters = CrystalOfLetters(cartan_type) self.shapes = shapes self.module_generators = tuple(self.module_generator(la) for la in shapes) - self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes))) + self.rename("The crystal of tableaux of type %s and shape(s) %s" + % (cartan_type, list(list(shape) for shape in shapes))) def cartan_type(self): """ @@ -1009,7 +1031,7 @@ def module_generator(self, shape): shape = shape[:-1] + (-shape[type[1]-1],) else: invert = False - p = Partition(shape).conjugate() + p = _Partitions(shape).conjugate() # The column canonical tableau, read by columns module_generator = flatten([[val-i for i in range(val)] for val in p]) if invert: diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 1e722f41aad..b0cae9ee0f5 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1652,7 +1652,7 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): r""" Test if the incidence structure is a generalized quadrangle. - An incidence structure is a generalized quadrangle iff (see [BH12]_, + An incidence structure is a generalized quadrangle iff (see [BH2012]_, section 9.6): - two blocks intersect on at most one point. diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index d8c63d0c35e..66ddb8f75ca 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -21,7 +21,7 @@ This module implements a direct construction of a two-graph from a list of triples, construction of descendant graphs, regularity checking, and other -things such as constructing the complement two-graph, cf. [BH12]_. +things such as constructing the complement two-graph, cf. [BH2012]_. AUTHORS: diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index d99c7f3d011..6c36c354ed4 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -59,10 +59,6 @@ .. [Kra2001] \C. Krattenthaler -- *Permutations with restricted patterns and Dyck paths*, Adv. Appl. Math. 27 (2001), 510--530. -.. [Cha2005] \F. Chapoton, *Une Base Symétrique de l'algèbre des - Coinvariants Quasi-Symétriques*, Electronic Journal of - Combinatorics Vol 12(1) (2005) N16. - .. [DS1992] \A. Denise, R. Simion, *Two combinatorial statistics on Dyck paths*, Discrete Math 137 (1992), 155--176. """ diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 01eef463886..eb3305f3af1 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1796,8 +1796,8 @@ def _cartesian_product_of_elements(self, elements): INPUT: - - ``elements`` -- a tuple with one element of each Cartesian - factor of ``self`` + - ``elements`` -- an iterable (e.g. tuple, list) with one element of + each Cartesian factor of ``self`` EXAMPLES:: @@ -1811,8 +1811,32 @@ def _cartesian_product_of_elements(self, elements): sage: S._cartesian_product_of_elements([f, g]).parent() == S True - """ - return self.sum(self.summand_embedding(i)(elements[i]) for i in self._sets_keys()) + TESTS: + + The ``elements`` can be a generator as in :trac:`31453`:: + + sage: from sage.categories.magmatic_algebras import ( + ....: MagmaticAlgebras + ....: ) + sage: class TrivialCFM(CombinatorialFreeModule): + ....: def __init__(self): + ....: c = MagmaticAlgebras(QQ).WithBasis().Unital() + ....: super().__init__(QQ,[1],category=c) + ....: + ....: def one(self): + ....: return self.monomial(0) + ....: + sage: c1 = TrivialCFM() + sage: c1.one() + B[0] + sage: CP = cartesian_product([c1,c1]) + sage: CP.one() + B[(0, 0)] + B[(1, 0)] + + """ + return self.sum( self.summand_embedding(i)(element_i) + for (i, element_i) in zip(self._sets_keys(), + elements) ) def cartesian_factors(self): """ diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 742536a89be..61c94086ff8 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -519,8 +519,8 @@ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if `M` is a regular symmetric Hadamard matrix with constant diagonal `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon - \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in - [BH12]_. For the case `n=324`, see :func:`RSHCD_324` and [CP16]_. + \sqrt(n)`. For more information, see [HX2010]_ or 10.5.1 in + [BH2012]_. For the case `n=324`, see :func:`RSHCD_324` and [CP2016]_. INPUT: @@ -574,16 +574,9 @@ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False REFERENCE: - .. [BH12] \A. Brouwer and W. Haemers, - Spectra of graphs, - Springer, 2012, - http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf + - [BH2012]_ - .. [HX10] \W. Haemers and Q. Xiang, - Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`, - European Journal of Combinatorics, - Volume 31, Issue 6, August 2010, Pages 1553-1559, - :doi:`10.1016/j.ejc.2009.07.009` + - [HX2010]_ """ if existence and (n,e) in _rshcd_cache: return _rshcd_cache[n,e] @@ -692,7 +685,7 @@ def RSHCD_324(e): :meth:`JankoKharaghaniTonchevGraph ` and for the case `\epsilon=-1` from the "twist" `M'` of `M`, using Lemma 11 - in [HX10]_. Namely, it turns out that the matrix + in [HX2010]_. Namely, it turns out that the matrix .. MATH:: @@ -704,7 +697,7 @@ def RSHCD_324(e): sums, as needed by [loc.cit.]. Interestingly, the corresponding `(324,152,70,72)`-strongly regular graph has a vertex-transitive automorphism group of order 2592, twice the order of the - (intransitive) automorphism group of the graph corresponding to `M`. Cf. [CP16]_. + (intransitive) automorphism group of the graph corresponding to `M`. Cf. [CP2016]_. INPUT: @@ -724,10 +717,7 @@ def RSHCD_324(e): REFERENCE: - .. [CP16] \N. Cohen, D. Pasechnik, - *Implementing Brouwer's database of strongly regular graphs*, - Designs, Codes, and Cryptography, 2016 - :doi:`10.1007/s10623-016-0264-x` + - [CP2016]_ """ from sage.graphs.generators.smallgraphs import JankoKharaghaniTonchevGraph as JKTG M = JKTG().adjacency_matrix() @@ -743,7 +733,7 @@ def RSHCD_324(e): def _helper_payley_matrix(n, zero_position=True): r""" - Return the matrix constructed in Lemma 1.19 page 291 of [SWW72]_. + Return the matrix constructed in Lemma 1.19 page 291 of [SWW1972]_. This function return a `n^2` matrix `M` whose rows/columns are indexed by the element of a finite field on `n` elements `x_1,...,x_n`. The value @@ -829,7 +819,7 @@ def rshcd_from_close_prime_powers(n): The construction implemented here appears in Theorem 4.3 from [GS1970]_. - Note that the authors of [SWW72]_ claim in Corollary 5.12 (page 342) to have + Note that the authors of [SWW1972]_ claim in Corollary 5.12 (page 342) to have proved the same result without the `n=0\pmod{4}` restriction with a *very* similar construction. So far, however, I (Nathann Cohen) have not been able to make it work. @@ -865,9 +855,7 @@ def rshcd_from_close_prime_powers(n): REFERENCE: - .. [SWW72] \A. Street, W. Wallis, J. Wallis, - Combinatorics: Room squares, sum-free sets, Hadamard matrices. - Lecture notes in Mathematics 292 (1972). + - [SWW1972]_ """ if n%4: raise ValueError("n(={}) must be congruent to 0 mod 4") @@ -1170,7 +1158,7 @@ def symmetric_conference_matrix(n, check=True): and 1s and -1s elsewhere, satisfying `CC^\top=(n-1)I`. If `C=C^\top$ then `n \cong 2 \mod 4` and `C` is Seidel adjacency matrix of a graph, whose descendent graphs are strongly regular graphs with parameters - `(n-1,(n-2)/2,(n-6)/4,(n-2)/4)`, see Sec.10.4 of [BH12]_. Thus we build `C` + `(n-1,(n-2)/2,(n-6)/4,(n-2)/4)`, see Sec.10.4 of [BH2012]_. Thus we build `C` from the Seidel adjacency matrix of the latter by adding row and column of 1s. INPUT: @@ -1214,11 +1202,11 @@ def szekeres_difference_set_pair(m, check=True): r""" Construct Szekeres `(2m+1,m,1)`-cyclic difference family - Let `4m+3` be a prime power. Theorem 3 in [Sz69]_ contains a construction of a pair + Let `4m+3` be a prime power. Theorem 3 in [Sz1969]_ contains a construction of a pair of *complementary difference sets* `A`, `B` in the subgroup `G` of the quadratic residues in `F_{4m+3}^*`. Namely `|A|=|B|=m`, `a\in A` whenever `a-1\in G`, `b\in B` - whenever `b+1 \in G`. See also Theorem 2.6 in [SWW72]_ (there the formula for `B` is - correct, as opposed to (4.2) in [Sz69]_, where the sign before `1` is wrong. + whenever `b+1 \in G`. See also Theorem 2.6 in [SWW1972]_ (there the formula for `B` is + correct, as opposed to (4.2) in [Sz1969]_, where the sign before `1` is wrong. In modern terminology, for `m>1` the sets `A` and `B` form a :func:`difference family` with parameters `(2m+1,m,1)`. @@ -1240,9 +1228,7 @@ def szekeres_difference_set_pair(m, check=True): REFERENCE: - .. [Sz69] \G. Szekeres, - Tournaments and Hadamard matrices, - Enseignement Math. (2) 15(1969), 269-278 + - [Sz1969]_ """ from sage.rings.finite_rings.finite_field_constructor import GF F = GF(4*m+3) @@ -1288,10 +1274,10 @@ def rshcd_from_prime_power_and_conference_matrix(n): r""" Return a `((n-1)^2,1)`-RSHCD if `n` is prime power, and symmetric `(n-1)`-conference matrix exists - The construction implemented here is Theorem 16 (and Corollary 17) from [WW72]_. + The construction implemented here is Theorem 16 (and Corollary 17) from [WW1972]_. - In [SWW72]_ this construction (Theorem 5.15 and Corollary 5.16) - is reproduced with a typo. Note that [WW72]_ refers to [Sz69]_ for the construction, + In [SWW1972]_ this construction (Theorem 5.15 and Corollary 5.16) + is reproduced with a typo. Note that [WW1972]_ refers to [Sz1969]_ for the construction, provided by :func:`szekeres_difference_set_pair`, of complementary difference sets, and the latter has a typo. @@ -1332,9 +1318,7 @@ def rshcd_from_prime_power_and_conference_matrix(n): REFERENCE: - .. [WW72] \J. Wallis and A.L. Whiteman, - Some classes of Hadamard matrices with constant diagonal, - Bull. Austral. Math. Soc. 7(1972), 233-249 + - [WW1972]_ """ from sage.graphs.strongly_regular_db import strongly_regular_graph as srg if is_prime_power(n) and 2==(n-1)%4: diff --git a/src/sage/combinat/non_decreasing_parking_function.py b/src/sage/combinat/non_decreasing_parking_function.py index 67ee647ab81..b3d51b9fd3b 100644 --- a/src/sage/combinat/non_decreasing_parking_function.py +++ b/src/sage/combinat/non_decreasing_parking_function.py @@ -35,6 +35,7 @@ from .combinat import catalan_number 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.all import NN from sage.rings.integer import Integer @@ -291,6 +292,8 @@ def __len__(self): """ return len(self._list) + grade = __len__ # for the category SetsWithGrading + def _repr_(self): """ Return the string representation of ``self``. @@ -374,7 +377,8 @@ def __init__(self): sage: PF == loads(dumps(PF)) True """ - Parent.__init__(self, category=InfiniteEnumeratedSets()) + cat = InfiniteEnumeratedSets() & SetsWithGrading() + Parent.__init__(self, category=cat) def __repr__(self): """ @@ -418,6 +422,18 @@ def __iter__(self): for pf in NonDecreasingParkingFunctions_n(n): yield pf + def graded_component(self, n): + """ + Return the graded component. + + EXAMPLES:: + + sage: P = NonDecreasingParkingFunctions() + sage: P.graded_component(4) == NonDecreasingParkingFunctions(4) + True + """ + return NonDecreasingParkingFunctions_n(n) + class NonDecreasingParkingFunctions_n(UniqueRepresentation, Parent): r""" diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 842d9f24fa1..65ac5d45877 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -51,8 +51,8 @@ AUTHORS: - - used non-decreasing_parking_functions code by Florent Hivert (2009 - 04) - - Dorota Mazur (2012 - 09) +- used non-decreasing_parking_functions code by Florent Hivert (2009 - 04) +- Dorota Mazur (2012 - 09) """ # **************************************************************************** # Copyright (C) 2012 Dorota Mazur @@ -62,11 +62,12 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from numbers import Integral +from typing import NewType, Iterator, Tuple +from copy import copy from sage.rings.integer import Integer -from sage.rings.all import QQ, NN -from copy import copy +from sage.rings.rational_field import QQ +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.combinat.combinat import CombinatorialObject from sage.combinat.permutation import Permutation, Permutations from sage.combinat.dyck_word import DyckWord @@ -80,6 +81,9 @@ from sage.structure.unique_representation import UniqueRepresentation +PF = NewType('PF', 'ParkingFunction_class') + + def ParkingFunctions(n=None): r""" Return the combinatorial class of Parking Functions. @@ -198,10 +202,8 @@ def __init__(self): """ TESTS:: - sage: from sage.combinat.parking_functions import ParkingFunctions - sage: DW = ParkingFunctions() - sage: DW == loads(dumps(DW)) - True + sage: PF = ParkingFunctions() + sage: TestSuite(PF).run() """ cat = InfiniteEnumeratedSets() & SetsWithGrading() Parent.__init__(self, category=cat) @@ -249,7 +251,7 @@ def graded_component(self, n): """ return ParkingFunctions_n(n) - def __iter__(self): + def __iter__(self) -> Iterator: """ Return an iterator. @@ -338,7 +340,7 @@ def __contains__(self, x) -> bool: return True return is_a(x, self.n) - def cardinality(self) -> Integral: + def cardinality(self) -> Integer: r""" Return the number of parking functions of size ``n``. @@ -351,7 +353,7 @@ def cardinality(self) -> Integral: """ return Integer((self.n + 1) ** (self.n - 1)) - def __iter__(self): + def __iter__(self) -> Iterator: """ Return an iterator for parking functions of size `n`. @@ -411,7 +413,7 @@ def iterator_rec(n): yield ParkingFunction(list(pi)) return - def random_element(self) -> 'ParkingFunction': + def random_element(self) -> PF: r""" Return a random parking function of size `n`. @@ -554,6 +556,18 @@ def __getitem__(self, n): """ return self._list[n] + def grade(self): + """ + Return the length of the parking function. + + EXAMPLES:: + + sage: PF = ParkingFunction([1, 1, 2, 2, 5, 6]) + sage: PF.grade() + 6 + """ + return len(self) + def __call__(self, n): """ Return the image of ``n`` under the parking function. @@ -568,7 +582,7 @@ def __call__(self, n): """ return self._list[n - 1] - def diagonal_reading_word(self): + def diagonal_reading_word(self) -> Permutation: r""" Return a diagonal word of the labelled Dyck path corresponding to parking function (see [Hag08]_ p. 75). @@ -614,7 +628,8 @@ def diagonal_reading_word(self): diagonal_word = diagonal_reading_word - def parking_permutation(self): # indices are cars, entries are parking spaces + def parking_permutation(self) -> Permutation: + # indices are cars, entries are parking spaces r""" Return the sequence of parking spots that are taken by cars 1 through `n` and corresponding to the parking function. @@ -652,7 +667,8 @@ def parking_permutation(self): # indices are cars, entries are parking space return self.cars_permutation().inverse() @combinatorial_map(name='to car permutation') - def cars_permutation(self): # indices are parking spaces, entries are car labels + def cars_permutation(self) -> Permutation: + # indices are parking spaces, entries are car labels r""" Return the sequence of cars that take parking spots 1 through `n` and corresponding to the parking function. @@ -729,7 +745,7 @@ def jump_list(self) -> list: # cars displacements pi = self.parking_permutation() return [pi[i] - self[i] for i in range(len(self))] - def jump(self) -> Integral: # sum of all jumps, sum of all displacements + def jump(self) -> Integer: # sum of all jumps, sum of all displacements r""" Return the sum of the differences between the parked and preferred parking spots. @@ -794,7 +810,7 @@ def lucky_cars(self): # the set of cars that can park in their preferred spo w = self.jump_list() return [i + 1 for i in range(len(w)) if w[i] == 0] - def luck(self) -> Integral: # the number of lucky cars + def luck(self) -> Integer: # the number of lucky cars r""" Return the number of cars that parked in their preferred parking spots (see [Shin]_ p. 33). @@ -922,7 +938,7 @@ def dinversion_pairs(self) -> list: """ return self.primary_dinversion_pairs() + self.secondary_dinversion_pairs() - def dinv(self) -> Integral: + def dinv(self) -> Integer: r""" Return the number of inversions of a labelled Dyck path corresponding to the parking function (see [Hag08]_ p. 74). @@ -954,7 +970,7 @@ def dinv(self) -> Integral: """ return len(self.dinversion_pairs()) - def area(self) -> Integral: + def area(self) -> Integer: r""" Return the area of the labelled Dyck path corresponding to the parking function. @@ -1139,7 +1155,7 @@ def touch_composition(self): diagonal_composition = touch_composition @combinatorial_map(name='to labelling permutation') - def to_labelling_permutation(self): + def to_labelling_permutation(self) -> Permutation: r""" Return the labelling of the support Dyck path of the parking function. @@ -1305,7 +1321,7 @@ def to_labelled_dyck_word(self): out.insert(i, 0) return out - def to_labelling_dyck_word_pair(self): + def to_labelling_dyck_word_pair(self) -> Tuple[Permutation, DyckWord]: r""" Return the pair ``(L, D)`` where ``L`` is a labelling and ``D`` is the Dyck word of the parking function. @@ -1339,7 +1355,7 @@ def to_labelling_dyck_word_pair(self): return (self.to_labelling_permutation(), self.to_dyck_word()) @combinatorial_map(name='to non-decreasing parking function') - def to_NonDecreasingParkingFunction(self) -> 'ParkingFunction': + def to_NonDecreasingParkingFunction(self) -> PF: r""" Return the non-decreasing parking function which underlies the parking function. @@ -1526,7 +1542,7 @@ def pretty_print(self, underpath=True): # ***************************************************************************** -def from_labelling_and_area_sequence(L, D) -> ParkingFunction: +def from_labelling_and_area_sequence(L, D) -> PF: r""" Return the parking function corresponding to the labelling area sequence pair. @@ -1563,7 +1579,7 @@ def from_labelling_and_area_sequence(L, D) -> ParkingFunction: for i in range(1, len(L) + 1)]) -def from_labelled_dyck_word(LDW) -> ParkingFunction: +def from_labelled_dyck_word(LDW) -> PF: r""" Return the parking function corresponding to the labelled Dyck word. diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 5d5622bc9e8..94e5b36caf9 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -316,6 +316,7 @@ from sage.combinat.partitions import ZS1_iterator, ZS1_iterator_nk from sage.combinat.integer_vector import IntegerVectors from sage.combinat.integer_lists import IntegerListsLex +from sage.combinat.integer_vector_weighted import iterator_fast as weighted_iterator_fast from sage.combinat.combinat_cython import conjugate from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.combinatorial_map import combinatorial_map @@ -966,7 +967,6 @@ def _latex_exp_high(self): return '%s' % ','.join('%s%s' % (M-m, '' if e==1 else '^{%s}'%e) for (m,e) in enumerate(exp) if e>0) - def ferrers_diagram(self): r""" Return the Ferrers diagram of ``self``. @@ -7114,10 +7114,10 @@ def __iter__(self): EXAMPLES:: - sage: [x for x in Partitions(4)] - [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] + sage: [x for x in Partitions(5, parts_in=[1,2,3])] + [[3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] """ - for p in self._fast_iterator(self.n, self.parts[:]): + for p in self._other_iterator(self.n, self.parts): yield self.element_class(self, p) def _fast_iterator(self, n, parts): @@ -7163,6 +7163,38 @@ def _fast_iterator(self, n, parts): for q in self._fast_iterator(n - k * p, parts[:]): yield k * [p] + q + def _other_iterator(self, n, parts): + """ + A fast iterator for the partitions of ``n`` which returns lists and + not partition types. This function is not intended to be called + directly. + + INPUT: + + - ``n`` -- nonnegative integer. + + - ``parts`` -- a list of parts to use. + + OUTPUT: + + A generator object for partitions of `n` with parts in + ``parts``. + + EXAMPLES:: + + sage: P = Partitions(4, parts_in=[2,4]) + sage: it = P._other_iterator(4, [2,4]) + sage: next(it) + [4] + sage: type(_) + <... 'list'> + """ + sorted_parts = sorted(parts, reverse=True) + for vec in weighted_iterator_fast(n, sorted_parts): + yield sum(([pi] * multi + for pi, multi in zip(sorted_parts, vec)), []) + + class Partitions_starting(Partitions): """ All partitions with a given start. diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 009773132f8..42f858f8cb1 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -237,6 +237,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.sets_with_grading import SetsWithGrading from sage.categories.finite_weyl_groups import FiniteWeylGroups from sage.categories.finite_permutation_groups import FinitePermutationGroups from sage.structure.list_clone import ClonableArray @@ -261,6 +262,7 @@ right_action_product, left_action_same_n, right_action_same_n, map_to_list, next_perm) + class Permutation(CombinatorialElement): r""" A permutation. @@ -699,6 +701,8 @@ def size(self): """ return len(self) + grade = size # for the category SetsWithGrading() + def cycle_string(self, singletons=False): """ Return a string of the permutation in cycle notation. @@ -6341,7 +6345,8 @@ def __init__(self): sage: SP = Permutations() sage: TestSuite(SP).run() """ - Permutations.__init__(self, category=InfiniteEnumeratedSets()) + cat = InfiniteEnumeratedSets() & SetsWithGrading() + Permutations.__init__(self, category=cat) def _repr_(self): """ @@ -6399,6 +6404,18 @@ def __iter__(self): yield self.element_class(self, p) n += 1 + def graded_component(self, n): + """ + Return the graded component. + + EXAMPLES:: + + sage: P = Permutations() + sage: P.graded_component(4) == Permutations(4) + True + """ + return StandardPermutations_n(n) + class StandardPermutations_n_abstract(Permutations): r""" diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index 5aea716b617..75feecd7107 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -7,7 +7,7 @@ - Jang Soo Kim (2016): Initial implementation - Jessica Striker (2016): Added additional methods """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2016 Jang Soo Kim , # 2016 Jessica Striker # @@ -20,8 +20,9 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** +from typing import NewType, Iterator, Tuple from sage.structure.list_clone import ClonableArray from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass @@ -34,6 +35,9 @@ from sage.combinat.tableau import Tableau +PP = NewType('PP', 'PlanePartition') + + class PlanePartition(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" @@ -115,7 +119,7 @@ def check(self): if self[0][0] > self.parent()._box[2]: raise ValueError("too big in x direction") - def _repr_(self): + def _repr_(self) -> str: """ Return a string representation of ``self``. @@ -126,7 +130,7 @@ def _repr_(self): """ return "Plane partition {}".format(list(self)) - def to_tableau(self): + def to_tableau(self) -> Tableau: r""" Return the tableau class of ``self``. @@ -183,7 +187,7 @@ def x_tableau(self): X[C[1]][C[2]] += 1 return X - def cells(self): + def cells(self) -> list: r""" Return the list of cells inside ``self``. @@ -197,10 +201,10 @@ def cells(self): for r in range(len(self)): for c in range(len(self[r])): for h in range(self[r][c]): - L.append([r,c,h]) + L.append([r, c, h]) return L - def _repr_diagram(self, show_box=False, use_unicode=False): + def _repr_diagram(self, show_box=False, use_unicode=False) -> str: r""" Return a string of the 3D diagram of ``self``. @@ -257,10 +261,10 @@ def superpose(l, c, letter): def add_topside(i, j, k): X = z + j - k Y = 2 * x - 2 * i + j + k - superpose(X, Y-2, hori) - superpose(X, Y-1, hori) - superpose(X + 1, Y-2, down) - superpose(X + 1, Y-1, hori) + superpose(X, Y - 2, hori) + superpose(X, Y - 1, hori) + superpose(X + 1, Y - 2, down) + superpose(X + 1, Y - 1, hori) superpose(X + 1, Y, down) def add_rightside(i, j, k): @@ -392,7 +396,8 @@ def pp(self, show_box=False): """ print(self._repr_diagram(show_box)) - def _latex_(self, show_box=False, colors=["white","lightgray","darkgray"]): + def _latex_(self, show_box=False, + colors=["white", "lightgray", "darkgray"]) -> str: r""" Return latex code for ``self``, which uses TikZ package to draw the plane partition. @@ -427,14 +432,14 @@ def _latex_(self, show_box=False, colors=["white","lightgray","darkgray"]): ret = "\\begin{tikzpicture}\n" - def add_topside(i,j,k): - return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(-30:1)--(0,-1)--(210:1)--(0,0);\n".format(colors[0],i,j,k) + def add_topside(i, j, k): + return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(-30:1)--(0,-1)--(210:1)--(0,0);\n".format(colors[0], i, j, k) - def add_leftside(j,k,i): - return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(0,1)--(30:1)--(-30:1)--(0,0);\n".format(colors[1],i,j,k) + def add_leftside(j, k, i): + return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(0,1)--(30:1)--(-30:1)--(0,0);\n".format(colors[1], i, j, k) - def add_rightside(k,i,j): - return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(210:1)--(150:1)--(0,1)--(0,0);\n".format(colors[2],i,j,k) + def add_rightside(k, i, j): + return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(210:1)--(150:1)--(0,1)--(0,0);\n".format(colors[2], i, j, k) funcs = [add_topside, add_rightside, add_leftside] tableaux = [self.z_tableau(), self.y_tableau(), self.x_tableau()] for i in range(3): @@ -446,7 +451,7 @@ def add_rightside(k,i,j): ret += f(r, c, tab[r][c]) return ret + "\\end{tikzpicture}" - def plot(self, show_box=False, colors=["white","lightgray","darkgray"]): + def plot(self, show_box=False, colors=["white", "lightgray", "darkgray"]): r""" Return a plot of ``self``. @@ -468,24 +473,32 @@ def plot(self, show_box=False, colors=["white","lightgray","darkgray"]): from sage.plot.polygon import polygon from sage.symbolic.constants import pi from sage.plot.plot import plot - Uside = [[0,0], [cos(-pi/6),sin(-pi/6)], [0,-1], [cos(7*pi/6),sin(7*pi/6)]] - Lside = [[0,0], [cos(-pi/6),sin(-pi/6)], [cos(pi/6),sin(pi/6)], [0,1]] - Rside = [[0,0], [0,1], [cos(5*pi/6),sin(5*pi/6)], [cos(7*pi/6),sin(7*pi/6)]] - Xdir = [cos(7*pi/6), sin(7*pi/6)] - Ydir = [cos(-pi/6), sin(-pi/6)] + Uside = [[0, 0], [cos(-pi / 6), sin(-pi / 6)], + [0, -1], [cos(7 * pi / 6), sin(7 * pi / 6)]] + Lside = [[0, 0], [cos(-pi / 6), sin(-pi / 6)], + [cos(pi / 6), sin(pi / 6)], [0, 1]] + Rside = [[0, 0], [0, 1], [cos(5 * pi / 6), sin(5 * pi / 6)], + [cos(7 * pi / 6), sin(7 * pi / 6)]] + Xdir = [cos(7 * pi / 6), sin(7 * pi / 6)] + Ydir = [cos(-pi / 6), sin(-pi / 6)] Zdir = [0, 1] def move(side, i, j, k): - return [[P[0]+i*Xdir[0]+j*Ydir[0]+k*Zdir[0], - P[1]+i*Xdir[1]+j*Ydir[1]+k*Zdir[1]] + return [[P[0] + i * Xdir[0] + j * Ydir[0] + k * Zdir[0], + P[1] + i * Xdir[1] + j * Ydir[1] + k * Zdir[1]] for P in side] def add_topside(i, j, k): - return polygon(move(Uside,i,j,k), edgecolor="black", color=colors[0]) + return polygon(move(Uside, i, j, k), edgecolor="black", + color=colors[0]) + def add_leftside(i, j, k): - return polygon(move(Lside,i,j,k), edgecolor="black", color=colors[1]) + return polygon(move(Lside, i, j, k), edgecolor="black", + color=colors[1]) + def add_rightside(i, j, k): - return polygon(move(Rside,i,j,k), edgecolor="black", color=colors[2]) + return polygon(move(Rside, i, j, k), edgecolor="black", + color=colors[2]) TP = plot([]) for r in range(len(self.z_tableau())): for c in range(len(self.z_tableau()[r])): @@ -521,7 +534,7 @@ def complement(self, tableau_only=False): z_tab = self.z_tableau() for r in range(A): for c in range(B): - T[A-1-r][B-1-c] = C - z_tab[r][c] + T[A - 1 - r][B - 1 - c] = C - z_tab[r][c] if tableau_only: return T else: @@ -549,7 +562,7 @@ def transpose(self, tableau_only=False): else: return type(self)(self.parent(), T, check=False) - def is_SPP(self): + def is_SPP(self) -> bool: r""" Return whether ``self`` is a symmetric plane partition. @@ -576,8 +589,6 @@ def is_SPP(self): sage: PP = PlanePartition([[3,2],[2,0],[0,0]]) sage: PP.is_SPP() True - - """ Z = self.z_tableau() c1 = len(Z) @@ -591,7 +602,7 @@ def is_SPP(self): for r in range(size) for c in range(r, size)) - def is_CSPP(self): + def is_CSPP(self) -> bool: r""" Return whether ``self`` is a cyclically symmetric plane partition. @@ -611,7 +622,7 @@ def is_CSPP(self): return True return False - def is_TSPP(self): + def is_TSPP(self) -> bool: r""" Return whether ``self`` is a totally symmetric plane partition. @@ -629,7 +640,7 @@ def is_TSPP(self): """ return self.is_CSPP() and self.is_SPP() - def is_SCPP(self): + def is_SCPP(self) -> bool: r""" Return whether ``self`` is a self-complementary plane partition. @@ -644,7 +655,7 @@ def is_SCPP(self): """ return self.z_tableau() == self.complement(True) - def is_TCPP(self): + def is_TCPP(self) -> bool: r""" Return whether ``self`` is a transpose-complementary plane partition. @@ -659,7 +670,7 @@ def is_TCPP(self): """ return self.transpose(True) == self.complement(True) - def is_SSCPP(self): + def is_SSCPP(self) -> bool: r""" Return whether ``self`` is a symmetric, self-complementary plane partition. @@ -684,7 +695,7 @@ def is_SSCPP(self): """ return self.is_SPP() and self.is_SCPP() - def is_CSTCPP(self): + def is_CSTCPP(self) -> bool: r""" Return whether ``self`` is a cyclically symmetric and transpose-complementary plane partition. @@ -700,7 +711,7 @@ def is_CSTCPP(self): """ return self.is_CSPP() and self.is_TCPP() - def is_CSSCPP(self): + def is_CSSCPP(self) -> bool: r""" Return whether ``self`` is a cyclically symmetric and self-complementary plane partition. @@ -716,7 +727,7 @@ def is_CSSCPP(self): """ return self.is_CSPP() and self.is_SCPP() - def is_TSSCPP(self): + def is_TSSCPP(self) -> bool: r""" Return whether ``self`` is a totally symmetric self-complementary plane partition. @@ -732,6 +743,7 @@ def is_TSSCPP(self): """ return self.is_TSPP() and self.is_SCPP() + class PlanePartitions(UniqueRepresentation, Parent): r""" All plane partitions inside a rectangular box of given side lengths. @@ -784,7 +796,7 @@ def __init__(self, box_size): self._box = box_size Parent.__init__(self, category=FiniteEnumeratedSets()) - def _repr_(self): + def _repr_(self) -> str: """ Return a string representation of ``self``. @@ -794,9 +806,9 @@ def _repr_(self): Plane partitions inside a 4 x 3 x 2 box """ return "Plane partitions inside a {} x {} x {} box".format( - self._box[0], self._box[1], self._box[2]) + self._box[0], self._box[1], self._box[2]) - def __iter__(self): + def __iter__(self) -> Iterator: """ Iterate over ``self``. @@ -811,14 +823,14 @@ def __iter__(self): B = self._box[1] C = self._box[2] from sage.combinat.tableau import SemistandardTableaux - for T in SemistandardTableaux([B for i in range(A)], max_entry=C+A): + for T in SemistandardTableaux([B for i in range(A)], max_entry=C + A): PP = [[0 for i in range(B)] for j in range(A)] for r in range(A): for c in range(B): - PP[A-1-r][B-1-c] = T[r][c] - r - 1 + PP[A - 1 - r][B - 1 - c] = T[r][c] - r - 1 yield self.element_class(self, PP, check=False) - def cardinality(self): + def cardinality(self) -> Integer: r""" Return the cardinality of ``self``. @@ -839,11 +851,12 @@ def cardinality(self): A = self._box[0] B = self._box[1] C = self._box[2] - return Integer(prod( Integer(i+j+k-1) / Integer(i+j+k-2) - for i in range(1, A+1) for j in range(1, B+1) - for k in range(1, C+1) )) + return Integer(prod(Integer(i + j + k - 1) / Integer(i + j + k - 2) + for i in range(1, A + 1) + for j in range(1, B + 1) + for k in range(1, C + 1))) - def box(self): + def box(self) -> Tuple: """ Return the sizes of the box of the plane partitions of ``self`` are contained in. @@ -856,7 +869,7 @@ def box(self): """ return self._box - def random_element(self): + def random_element(self) -> PP: r""" Return a uniformly random element of ``self``. @@ -875,7 +888,8 @@ def random_element(self): """ def leq(thing1, thing2): return all(thing1[i] <= thing2[i] for i in range(len(thing1))) - elem = [(i,j,k) for i in range(self._box[0]) for j in range(self._box[1]) + elem = [(i, j, k) for i in range(self._box[0]) + for j in range(self._box[1]) for k in range(self._box[2])] myposet = Poset((elem, leq)) R = myposet.random_order_ideal() diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f64ab8dc762..7ff9e74c82d 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -861,6 +861,19 @@ class FinitePoset(UniqueRepresentation, Parent): sage: parent(QP[0]) is QP True + Conversion to some other software is possible:: + + sage: P = posets.TamariLattice(3) + sage: libgap(P) # optional - gap_packages + + + sage: P = Poset({1:[2],2:[]}) + sage: macaulay2('needsPackage "Posets"') # optional - macaulay2 + Posets + sage: macaulay2(P) # optional - macaulay2 + Relation Matrix: | 1 1 | + | 0 1 | + .. NOTE:: A class that inherits from this class needs to define @@ -1191,7 +1204,7 @@ def unwrap(self, element): sage: P.unwrap(x) is x True - This method is useful in code where we don't know if ``P`` is + This method is useful in code where we do not know if ``P`` is a facade poset or not. """ if self._is_facade: @@ -1507,7 +1520,7 @@ def linear_extension(self, linear_extension=None, check=True): - ``linear_extension`` -- (default: ``None``) a list of the elements of ``self`` - - ``check`` -- a boolean (default: True); + - ``check`` -- a boolean (default: ``True``); whether to check that ``linear_extension`` is indeed a linear extension of ``self``. @@ -1776,7 +1789,7 @@ def atkinson(self, a): new_a_spec[-1] += k_val * a_spec[i - 1] * n_lin_exts return new_a_spec - def is_linear_extension(self, l): + def is_linear_extension(self, l) -> bool: """ Return whether ``l`` is a linear extension of ``self``. @@ -2245,7 +2258,7 @@ def diamonds(self): w Thus each edge represents a cover relation in the Hasse diagram. - We represent his as the tuple `(w, x, y, z)`. + We represent this as the tuple `(w, x, y, z)`. OUTPUT: @@ -2281,7 +2294,7 @@ def common_upper_covers(self, elmts): vertices = list(map(self._element_to_vertex, elmts)) return list(map(self._vertex_to_element, self._hasse_diagram.common_upper_covers(vertices))) - def is_d_complete(self): + def is_d_complete(self) -> bool: r""" Return ``True`` if a poset is d-complete and ``False`` otherwise. @@ -2549,7 +2562,7 @@ def relations_number(self): # Maybe this should also be deprecated. intervals_number = relations_number - def is_incomparable_chain_free(self, m, n=None): + def is_incomparable_chain_free(self, m, n=None) -> bool: r""" Return ``True`` if the poset is `(m+n)`-free, and ``False`` otherwise. @@ -2666,7 +2679,7 @@ def is_incomparable_chain_free(self, m, n=None): return False return True - def is_lequal(self, x, y): + def is_lequal(self, x, y) -> bool: """ Return ``True`` if `x` is less than or equal to `y` in the poset, and ``False`` otherwise. @@ -2691,7 +2704,7 @@ def is_lequal(self, x, y): le = is_lequal - def is_less_than(self, x, y): + def is_less_than(self, x, y) -> bool: """ Return ``True`` if `x` is less than but not equal to `y` in the poset, and ``False`` otherwise. @@ -2720,7 +2733,7 @@ def is_less_than(self, x, y): lt = is_less_than - def is_gequal(self, x, y): + def is_gequal(self, x, y) -> bool: """ Return ``True`` if `x` is greater than or equal to `y` in the poset, and ``False`` otherwise. @@ -2743,7 +2756,7 @@ def is_gequal(self, x, y): ge = is_gequal - def is_greater_than(self, x, y): + def is_greater_than(self, x, y) -> bool: """ Return ``True`` if `x` is greater than but not equal to `y` in the poset, and ``False`` otherwise. @@ -3024,7 +3037,7 @@ def has_isomorphic_subposet(self, other): return False return True - def is_bounded(self): + def is_bounded(self) -> bool: """ Return ``True`` if the poset is bounded, and ``False`` otherwise. @@ -3057,7 +3070,7 @@ def is_bounded(self): """ return self._hasse_diagram.is_bounded() - def is_chain(self): + def is_chain(self) -> bool: """ Return ``True`` if the poset is totally ordered ("chain"), and ``False`` otherwise. @@ -3083,7 +3096,7 @@ def is_chain(self): """ return self._hasse_diagram.is_chain() - def is_chain_of_poset(self, elms, ordered=False): + def is_chain_of_poset(self, elms, ordered=False) -> bool: """ Return ``True`` if ``elms`` is a chain of the poset, and ``False`` otherwise. @@ -3180,11 +3193,11 @@ def is_antichain_of_poset(self, elms): elms_H = [self._element_to_vertex(e) for e in elms] return self._hasse_diagram.is_antichain_of_poset(elms_H) - def is_connected(self): + def is_connected(self) -> bool: """ Return ``True`` if the poset is connected, and ``False`` otherwise. - A poset is connected if it's Hasse diagram is connected. + A poset is connected if its Hasse diagram is connected. If a poset is not connected, then it can be divided to parts `S_1` and `S_2` so that every element of `S_1` is incomparable to @@ -3209,7 +3222,7 @@ def is_connected(self): """ return self._hasse_diagram.is_connected() - def is_series_parallel(self): + def is_series_parallel(self) -> bool: """ Return ``True`` if the poset is series-parallel, and ``False`` otherwise. @@ -3737,7 +3750,7 @@ def rank(self, element=None): else: raise ValueError("the poset is not ranked") - def is_ranked(self): + def is_ranked(self) -> bool: r""" Return ``True`` if the poset is ranked, and ``False`` otherwise. @@ -3766,7 +3779,7 @@ def is_ranked(self): """ return bool(self.rank_function()) - def is_graded(self): + def is_graded(self) -> bool: r""" Return ``True`` if the poset is graded, and ``False`` otherwise. @@ -3808,7 +3821,7 @@ def is_graded(self): if len(self) <= 2: return True # Let's work with the Hasse diagram in order to avoid some - # indirection (the output doesn't depend on the vertex labels). + # indirection (the output does not depend on the vertex labels). hasse = self._hasse_diagram rf = hasse.rank_function() if rf is None: @@ -6699,7 +6712,7 @@ def maximal_antichains(self): EXAMPLES:: - sage: P=Poset({'a':['b', 'c'], 'b':['d','e']}) + sage: P = Poset({'a':['b', 'c'], 'b':['d','e']}) sage: [sorted(anti) for anti in P.maximal_antichains()] [['a'], ['b', 'c'], ['c', 'd', 'e']] @@ -6761,7 +6774,7 @@ def order_complex(self, on_ints=False): INPUT: - - ``on_ints`` -- a boolean (default: False) + - ``on_ints`` -- a boolean (default: ``False``) OUTPUT: @@ -7090,7 +7103,7 @@ def flag_f_polynomial(self): the maximum of `P`, where `\rho` is the rank function of `P` (normalized to satisfy `\rho(\min P) = 0`), and where `n` is the rank of `\max P`. (Note that the indeterminate - `x_0` doesn't actually appear in the polynomial.) + `x_0` does not actually appear in the polynomial.) For technical reasons, the polynomial is returned in the slightly larger ring `\ZZ[x_0, x_1, x_2, \cdots, x_{n+1}]` by @@ -7538,7 +7551,7 @@ def evacuation(self): """ return self.linear_extension().evacuation().to_poset() - def is_rank_symmetric(self): + def is_rank_symmetric(self) -> bool: r""" Return ``True`` if the poset is rank symmetric, and ``False`` otherwise. @@ -8670,7 +8683,7 @@ def _ford_fulkerson_chronicle(G, s, t, a): .. WARNING:: This method is tailor-made for its use in the - :meth:`FinitePoset.greene_shape()` method of a finite poset. It's not + :meth:`FinitePoset.greene_shape()` method of a finite poset. It is not very useful in general. First of all, as said above, the iterator does not know when to halt. Second, `G` needs to be acyclic for it to correctly work. This must be amended if this method is ever to be @@ -8726,7 +8739,7 @@ def _ford_fulkerson_chronicle(G, s, t, a): # f: flow function as a dictionary. f = { (u, v): 0 for (u, v, l) in G.edge_iterator() } - # val: value of the flow f. (Can't call it v due to Python's asinine + # val: value of the flow f. (Cannot call it v due to Python's asinine # handling of for loops.) val = 0 diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 667b71760d4..315fc98e8c5 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -3,8 +3,9 @@ """ # **************************************************************************** # Copyright (C) 2019 Daniel Bump -# Nicolas Thiery # Guillermo Aboumrad +# Travis Scrimshaw +# Nicolas Thiery # # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ @@ -735,11 +736,18 @@ def s_matrix(self, unitary=False): else: return S + @cached_method def r_matrix(self, i, j, k): r""" Return the R-matrix entry corresponding to the subobject ``k`` in the tensor product of ``i`` with ``j``. + .. WARNING:: + + This method only gives complete information when `N_{ij}^k = 1` + (an important special case). Tables of MTC including R-matrices + may be found in Section 5.3 of [RoStWa2009]_ and in [Bond2007]_. + The R-matrix is a homomorphism `i \otimes j \rightarrow j \otimes i`. This may be hard to describe since the object `i \otimes j` may be reducible. However if `k` is a simple subobject of @@ -756,14 +764,8 @@ def r_matrix(self, i, j, k): If `i \neq j`, the gauge may be used to control the sign of the square root. But if `i = j` then we must be careful - about the sign. This sign is `+` if `k` is a subobject of - the symmetric square of `i` and `-` if it is a subobject of - the exterior square. See [LR1997]_ Corollary 2.22 - (actually due to Reshetikhin). - - This method only gives complete information when `N_{ij}^k = 1` - (an important special case). Tables of MTC including R-matrices - may be found in Section 5.3 of [RoStWa2009]_ and in [Bond2007]_. + about the sign. These cases are computed by a formula + of [BDGRTW2019]_, Proposition 2.3. EXAMPLES:: @@ -782,14 +784,14 @@ def r_matrix(self, i, j, k): """ if self.Nk_ij(i, j, k) == 0: return 0 - r = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) if i != j: - return r - wt = k.weight() - if wt in i.symmetric_power(2).monomial_coefficients(): - return r - # We instead have wt in i.exterior_power(2).monomial_coefficients(): - return -r + 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): r""" @@ -1026,4 +1028,3 @@ def q_dimension(self): expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) return expr.substitute(q=zet) - diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index 51f41137062..a24147df213 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -80,10 +80,7 @@ class HeckeAlgebraRepresentation(WithEqualityById, SageObject): REFERENCES: - .. [HST2008] \F. Hivert, A. Schilling, N. Thiery, - *Hecke group algebras as quotients of affine Hecke algebras at level 0*, - Journal of Combinatorial Theory, Series A 116 (2009) 844-863 - (:arxiv:`0804.3781`) + - [HST2008]_ """ def __init__(self, domain, on_basis, cartan_type, q1, q2, q=ZZ.one(), side="right"): r""" diff --git a/src/sage/combinat/root_system/weight_lattice_realizations.py b/src/sage/combinat/root_system/weight_lattice_realizations.py index ea3f6bf4b20..033152d42ec 100644 --- a/src/sage/combinat/root_system/weight_lattice_realizations.py +++ b/src/sage/combinat/root_system/weight_lattice_realizations.py @@ -902,6 +902,11 @@ def _symmetric_form_matrix(self): EXAMPLES:: + sage: P = RootSystem(['B',2]).weight_lattice() + sage: P._symmetric_form_matrix + [2 1] + [1 1] + sage: P = RootSystem(['C',2]).weight_lattice() sage: P._symmetric_form_matrix [1 1] @@ -920,14 +925,14 @@ def _symmetric_form_matrix(self): [ 0 2 2 1] [ 0 2 4 1] [1/2 1 1 0] + """ from sage.matrix.constructor import matrix ct = self.cartan_type() cm = ct.cartan_matrix() if cm.det() != 0: - cm_inv = cm.inverse() - diag = cm.is_symmetrizable(True) - return cm_inv.transpose() * matrix.diagonal(diag) + diag = matrix.diagonal(cm.symmetrizer()) + return cm.inverse().transpose() * diag if not ct.is_affine(): raise ValueError("only implemented for affine types when the" @@ -1025,6 +1030,26 @@ def symmetric_form(self, la): sage: al = P.simple_roots() sage: [al[i].symmetric_form(al[i]) for i in P.index_set()] [2, 4, 8] + + Check that :trac:`31410` is fixed, and the symmetric form + computed on the weight space is the same as the symmetric + form computed on the root space:: + + sage: def s1(ct): + ....: L = RootSystem(ct).weight_space() + ....: P = L.positive_roots() + ....: rho = L.rho() + ....: return [beta.symmetric_form(rho) for beta in P] + + sage: def s2(ct): + ....: R = RootSystem(ct).root_space() + ....: P = R.positive_roots() + ....: rho = 1/2*sum(P) + ....: return [beta.symmetric_form(rho) for beta in P] + + sage: all(s1(ct) == s2(ct) for ct in CartanType.samples(finite=True, crystallographic=True)) + True + """ P = self.parent() ct = P.cartan_type() @@ -1074,5 +1099,5 @@ def to_weight_space(self, base_ring = None): L = self.parent() if base_ring is None: base_ring = L.base_ring() - + return L.root_system.weight_space(base_ring).sum_of_terms([i, base_ring(self.scalar(L.simple_coroot(i)))] for i in L.cartan_type().index_set()) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 49a42e2786f..e340faec8f4 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -1,7 +1,7 @@ r""" Macdonald Polynomials -Notation used in the definitions follows mainly [Macdonald1995]_. +Notation used in the definitions follows mainly [Mac1995]_. The integral forms of the bases `H` and `Ht` do not appear in Macdonald's book. They correspond to the two bases @@ -14,9 +14,7 @@ REFERENCES: -.. [Macdonald1995] \I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., - The Clarendon Press, Oxford University Press, New York, 1995, With contributions - by A. Zelevinsky, Oxford Science Publications. +- [Mac1995]_ .. [GH1993] \A. Garsia, M. Haiman, A graded representation module for Macdonald's polynomials, Proc. Nat. Acad. U.S.A. no. 90, 3607--3610. @@ -551,7 +549,7 @@ def c1(part, q, t): and ``P(part)``. This coefficient is `c_\lambda` in equation (8.1') p. 352 of - Macdonald's book [Macdonald1995]_. + Macdonald's book [Mac1995]_. INPUT: @@ -583,7 +581,7 @@ def c2(part, q, t): and Q(part). This coefficient is `c_\lambda` in equation (8.1) p. 352 of - Macdonald's book [Macdonald1995]_. + Macdonald's book [Mac1995]_. INPUT: @@ -1016,7 +1014,7 @@ def scalar_qt_basis(self, part1, part2 = None): r""" Returns the scalar product of `P(part1)` and `P(part2)` This scalar product formula is given in equation (4.11) p.323 - and (6.19) p.339 of Macdonald's book [Macdonald1995]_. + and (6.19) p.339 of Macdonald's book [Mac1995]_. INPUT: diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index f683eabcb5c..ab24316debc 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -4312,14 +4312,34 @@ def __classcall_private__(self, t): if t not in Tableaux(): raise ValueError('%s is not a tableau' % t) - if not all(isinstance(c, (int, Integer)) and c > 0 for row in t for c in row): - raise ValueError("entries must be positive integers" % t) - - if any(row[c] > row[c+1] for row in t for c in range(len(row)-1)): - raise ValueError("The rows of %s are not weakly increasing" % t) + for (rix, row) in enumerate(t): + for (cix, v) in enumerate(row): + if not isinstance(v, (int, Integer)): + raise ValueError("expected entry to be an integer at (row=%s, col=%s)" % (rix, cix)) + if v <= 0: + raise ValueError("expected entry to be a positive integer at (row=%s, col=%s). Found (%s)" % (rix, cix, v)) + + for (rix, row) in enumerate(t): + for cix in range(len(row)-1): + if row[cix] > row[cix+1]: + raise ValueError("row (%s) is not weakly increasing between columns (%s, %s)" % (rix, cix, cix+1)) # If we're still here ``t`` cannot be column strict - raise ValueError('%s is not a column strict tableau' % t) + for rix in range(len(t)-1): + rcur = t[rix] + rnext = t[rix+1] + + # check that SST is strictly increasing in columns + # we know that len(rnext) <= len(rcur) as the SST cannot have + # more columns in the next row than the current row. + assert (len(rnext) <= len(rcur)) + + for cix in range(len(rnext)): + if rnext[cix] <= rcur[cix]: + raise ValueError("column (%s) is not strictly increasing between rows (%s, %s)" % (cix, rix, rix+1)) + + # we should have found an error by now. + raise ValueError('We should have found an error by now in tableau %s' % t) def check(self): """ @@ -4330,17 +4350,17 @@ def check(self): sage: SemistandardTableau([[1,2,3],[1]]) # indirect doctest Traceback (most recent call last): ... - ValueError: [[1, 2, 3], [1]] is not a column strict tableau + ValueError: column (0) is not strictly increasing between rows (0, 1) sage: SemistandardTableau([[1,2,1]]) # indirect doctest Traceback (most recent call last): ... - ValueError: The rows of [[1, 2, 1]] are not weakly increasing + ValueError: row (0) is not weakly increasing between columns (1, 2) sage: SemistandardTableau([[0,1]]) # indirect doctest Traceback (most recent call last): ... - ValueError: entries must be positive integers + ValueError: expected entry to be a positive integer at (row=0, col=0). Found (0) """ super(SemistandardTableau, self).check() diff --git a/src/sage/combinat/words/lyndon_word.py b/src/sage/combinat/words/lyndon_word.py index ecebf090d04..16115350bc8 100644 --- a/src/sage/combinat/words/lyndon_word.py +++ b/src/sage/combinat/words/lyndon_word.py @@ -21,6 +21,7 @@ from sage.combinat.necklace import _sfc from sage.combinat.words.words import FiniteWords +from sage.combinat.words.finite_word import FiniteWord_class from sage.combinat.combinat_cython import lyndon_word_iterator @@ -184,8 +185,8 @@ def __contains__(self, w): True """ if isinstance(w, list): - w = self._words(w) - return w.is_lyndon() + w = self._words(w, check=False) + return isinstance(w, FiniteWord_class) and w.is_lyndon() class LyndonWords_evaluation(UniqueRepresentation, Parent): @@ -250,7 +251,7 @@ def __call__(self, *args, **kwds): raise ValueError("evaluation is not {}".format(self._e)) return w - def __contains__(self, x): + def __contains__(self, w): """ EXAMPLES:: @@ -261,9 +262,14 @@ def __contains__(self, x): sage: all(lw in LyndonWords([2,1,3,1]) for lw in LyndonWords([2,1,3,1])) True """ - if isinstance(x, list): - x = self._words(x) - return x in self._words and x.is_lyndon() and x.evaluation() == self._e + if isinstance(w, list): + w = self._words(w, check=False) + if isinstance(w, FiniteWord_class) and all(x in self._words.alphabet() for x in w): + ev_dict = w.evaluation_dict() + evaluation = [ev_dict.get(x, 0) for x in self._words.alphabet()] + return evaluation == self._e and w.is_lyndon() + else: + return False def cardinality(self): """ @@ -408,13 +414,18 @@ def __call__(self, *args, **kwds): sage: L([1,2,2,3,3]) Traceback (most recent call last): ... - ValueError: length is not n=3 + ValueError: length is not k=3 + + Make sure that the correct length is checked (:trac:`30186`):: + + sage: L = LyndonWords(2, 4) + sage: _ = L(L.random_element()) """ w = self._words(*args, **kwds) if kwds.get('check', True) and not w.is_lyndon(): raise ValueError("not a Lyndon word") - if w.length() != self._n: - raise ValueError("length is not n={}".format(self._n)) + if kwds.get('check', True) and w.length() != self._k: + raise ValueError("length is not k={}".format(self._k)) return w def __contains__(self, w): @@ -426,8 +437,9 @@ def __contains__(self, w): True """ if isinstance(w, list): - w = self._words(w) - return w in self._words and w.length() == self._k and len(set(w)) <= self._n + w = self._words(w, check=False) + return isinstance(w, FiniteWord_class) and w.length() == self._k \ + and all(x in self._words.alphabet() for x in w) and w.is_lyndon() def cardinality(self): """ @@ -487,7 +499,7 @@ def StandardBracketedLyndonWords(n, k): [[2, 3], 3] sage: SBLW33.cardinality() 8 - sage: SBLW33.random_element() in SBLW33 # known bug + sage: SBLW33.random_element() in SBLW33 True """ return StandardBracketedLyndonWords_nk(n, k) @@ -539,6 +551,26 @@ def __call__(self, *args, **kwds): """ return standard_bracketing(self._lyndon(*args, **kwds)) + def __contains__(self, sblw): + """ + EXAMPLES:: + + sage: S = StandardBracketedLyndonWords(2, 3) + sage: [[1, 2], 2] in S + True + sage: [1, [2, 2]] in S + False + sage: [1, [2, 3]] in S + False + sage: [1, 2] in S + False + """ + try: + lw = standard_unbracketing(sblw) + except ValueError: + return False + return len(lw) == self._k and all(a in self._lyndon._words.alphabet() for a in lw.parent().alphabet()) + def __iter__(self): """ EXAMPLES:: @@ -580,3 +612,46 @@ def standard_bracketing(lw): for i in range(1, len(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, + otherwise raise an error. + + EXAMPLES:: + + sage: from sage.combinat.words.lyndon_word import standard_unbracketing + sage: standard_unbracketing([1, [2, 3]]) + word: 123 + sage: standard_unbracketing([[1, 2], 3]) + Traceback (most recent call last): + ... + ValueError: not a standard bracketing of a Lyndon word + + TESTS:: + + sage: standard_unbracketing(1) # Letters don't use brackets. + word: 1 + sage: standard_unbracketing([1]) + Traceback (most recent call last): + ... + ValueError: not a standard bracketing of a Lyndon word + """ + # Nested helper function that not only returns (flattened) w, but also its + # right factor in the standard Lyndon factorization. + def standard_unbracketing_rec(w): + if not isinstance(w, list): + return [w], [] + if len(w) != 2: + raise ValueError("not a standard bracketing of a Lyndon word") + x, t = standard_unbracketing_rec(w[0]) + y, _ = standard_unbracketing_rec(w[1]) + # If x = st is a standard Lyndon factorization, and y is a Lyndon word + # such that y <= t, then xy is standard (but not necessarily Lyndon). + if x < y and (len(t) == 0 or y <= t): + x += y + return x, y + else: + raise ValueError("not a standard bracketing of a Lyndon word") + lw, _ = standard_unbracketing_rec(sblw) + return FiniteWords(list(set(lw)))(lw, datatype='list', check=False) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 039a004f5fc..946f59cceb8 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -50,7 +50,6 @@ import sage.schemes.elliptic_curves.constructor as elliptic from .sql_db import SQLDatabase, verify_column -from sage.env import CREMONA_MINI_DATA_DIR, CREMONA_LARGE_DATA_DIR, SAGE_SHARE from sage.features.databases import DatabaseCremona from sage.misc.all import walltime @@ -298,7 +297,7 @@ def old_cremona_letter_code(n): lmfdb_label_regex = re.compile(r'(\d+)\.([a-z]+)(\d*)$') -def parse_cremona_label(label): +def parse_cremona_label(label, numerical_class_code=False): """ Given a Cremona label that defines an elliptic curve, e.g., 11a1 or 37b3, parse the label and return the @@ -314,12 +313,16 @@ def parse_cremona_label(label): INPUT: - - ``label`` - str + - ``label`` (string) - a valid Cremona elliptic curve label + + - ``numerical_class_code`` (boolean, default False) - if ``True``, + convert the isogeny class label from a letter code in base 26 + to an integer; this is useful for sorting OUTPUT: - ``int`` - the conductor - - ``str`` - the isogeny class label + - ``str`` or ``int`` - the isogeny class label - ``int`` - the number EXAMPLES:: @@ -345,6 +348,14 @@ def parse_cremona_label(label): ... ValueError: 5AB2 is not a valid Cremona label + When ``numerical_class_code`` is ``True``, the output is a triple of integers:: + + sage: from sage.databases.cremona import parse_cremona_label + sage: parse_cremona_label('100800hj2') + (100800, 'hj', 2) + sage: parse_cremona_label('100800hj2', numerical_class_code=True) + (100800, 191, 2) + TESTS:: sage: from sage.databases.cremona import parse_cremona_label @@ -352,6 +363,7 @@ def parse_cremona_label(label): Traceback (most recent call last): ... ValueError: x11 is not a valid Cremona label + """ m = cremona_label_regex.match(str(label)) if m is None: @@ -373,10 +385,14 @@ def parse_cremona_label(label): if iso.lower() != iso: raise ValueError('%s is not a valid Cremona label' % label) + # convert class label to an int if requested + if numerical_class_code: + iso = class_to_int(iso) + return int(conductor), iso, int(num) -def parse_lmfdb_label(label): +def parse_lmfdb_label(label, numerical_class_code=False): """ Given an LMFDB label that defines an elliptic curve, e.g., 11.a1 or 37.b3, parse the label and return the conductor, isogeny class @@ -405,10 +421,14 @@ def parse_lmfdb_label(label): - ``label`` - str + - ``numerical_class_code`` (boolean, default False) - if ``True``, + convert the isogeny class label from a letter code in base 26 + to an integer; this is useful for sorting + OUTPUT: - ``int`` - the conductor - - ``str`` - the isogeny class label + - ``str`` or ``int`` - the isogeny class label - ``int`` - the number EXAMPLES:: @@ -420,6 +440,14 @@ def parse_lmfdb_label(label): (37, 'b', 1) sage: parse_lmfdb_label('10.bb2') (10, 'bb', 2) + + When ``numerical_class_code`` is ``True``, the output is a triple of integers:: + + sage: from sage.databases.cremona import parse_lmfdb_label + sage: parse_lmfdb_label('100800.bg4') + (100800, 'bg', 4) + sage: parse_lmfdb_label('100800.bg4', numerical_class_code=True) + (100800, 32, 4) """ m = lmfdb_label_regex.match(str(label).lower()) if m is None: @@ -429,6 +457,10 @@ def parse_lmfdb_label(label): iso = "a" if len(num) == 0: num = "1" + # convert class label to an int if requested + if numerical_class_code: + iso = class_to_int(iso) + return int(conductor), iso, int(num) diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index 527c664f84a..d249f2cdafe 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -12,17 +12,14 @@ import sphinx.ext.intersphinx as intersphinx from IPython.lib.lexers import IPythonConsoleLexer, IPyLexer -# If your extensions are in another directory, add it here. -sys.path.append(os.path.join(SAGE_SRC, "sage_setup", "docbuild", "ext")) - # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['inventory_builder', - 'multidocs', - 'sage_autodoc', +extensions = ['sage_docbuild.ext.inventory_builder', + 'sage_docbuild.ext.multidocs', + 'sage_docbuild.ext.sage_autodoc', 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.todo', @@ -507,6 +504,14 @@ def set_intersphinx_mappings(app, config): \let\textLaTeX\LaTeX \AtBeginDocument{\renewcommand*{\LaTeX}{\hbox{\textLaTeX}}} + +% Workaround for a LaTeX bug -- see trac #31397 and +% https://tex.stackexchange.com/questions/583391/mactex-2020-error-with-report-hyperref-mathbf-in-chapter. +\makeatletter +\pdfstringdefDisableCommands{% + \let\mathbf\@firstofone +} +\makeatother """ # Documents to append as an appendix to all manuals. diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 7d2b84b9c3a..f96074f616f 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -44,11 +44,14 @@ # Optional tags which are always automatically added -from sage.libs.arb.arb_version import version as arb_vers -arb_tag = 'arb2' + arb_vers().split('.')[1] - -auto_optional_tags = set(['py3', arb_tag]) +auto_optional_tags = set(['py3']) +try: + from sage.libs.arb.arb_version import version as arb_vers + arb_tag = 'arb2' + arb_vers().split('.')[1] + auto_optional_tags.add(arb_tag) +except ImportError: + pass class DocTestDefaults(SageObject): """ @@ -150,7 +153,7 @@ def _repr_(self): for k in sorted(dict_difference(self.__dict__, DocTestDefaults().__dict__).keys()): if s[-1] != "(": s += ", " - s += str(k) + "=" + repr(getattr(self,k)) + s += str(k) + "=" + repr(getattr(self, k)) s += ")" return s @@ -172,7 +175,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test for unequality. + Test for non-equality. EXAMPLES:: @@ -697,6 +700,7 @@ def all_files(): # don't make sense to run outside a build environment if have_git: self.files.append(opj(SAGE_SRC, 'sage_setup')) + self.files.append(opj(SAGE_SRC, 'sage_docbuild')) self.files.append(SAGE_DOC_SRC) if self.options.all or (self.options.new and not have_git): @@ -1240,6 +1244,7 @@ def run_doctests(module, options=None): """ import sys sys.stdout.flush() + def stringify(x): if isinstance(x, (list, tuple)): F = [stringify(a) for a in x] diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 4e1a6b32e41..cc4edd34bf5 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -267,7 +267,7 @@ def subst(m): # 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 recevied outputs are normalized, depending on the +# of the expected or received outputs are normalized, depending on the # application. # For example, on Python 3 we strip all u prefixes from unicode strings in the # expected output, because we never expect to see those on Python 3. @@ -527,7 +527,7 @@ def __reduce__(self): def make_marked_output(s, D): """ - Auxilliary function for pickling. + Auxiliary function for pickling. EXAMPLES:: @@ -693,7 +693,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test for unequality. + Test for non-equality. EXAMPLES:: @@ -935,17 +935,17 @@ def add_tolerance(self, wantval, want): sage: want_tol = MarkedOutput().update(tol=0.0001) sage: want_abs = MarkedOutput().update(abs_tol=0.0001) sage: want_rel = MarkedOutput().update(rel_tol=0.0001) - sage: OC.add_tolerance(pi.n(64), want_tol).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_tol).endpoints() (3.14127849432443, 3.14190681285516) - sage: OC.add_tolerance(pi.n(64), want_abs).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_abs).endpoints() (3.14149265358979, 3.14169265358980) - sage: OC.add_tolerance(pi.n(64), want_rel).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_rel).endpoints() (3.14127849432443, 3.14190681285516) - sage: OC.add_tolerance(1e1000, want_tol) + sage: OC.add_tolerance(RIF(1e1000), want_tol) 1.000?e1000 - sage: OC.add_tolerance(1e1000, want_abs) + sage: OC.add_tolerance(RIF(1e1000), want_abs) 1.000000000000000?e1000 - sage: OC.add_tolerance(1e1000, want_rel) + sage: OC.add_tolerance(RIF(1e1000), want_rel) 1.000?e1000 sage: OC.add_tolerance(0, want_tol) 0.000? @@ -956,13 +956,13 @@ def add_tolerance(self, wantval, want): """ if want.tol: if wantval == 0: - return want.tol * RIFtol(-1,1) + return RIFtol(want.tol) * RIFtol(-1,1) else: - return wantval * (1 + want.tol * RIFtol(-1,1)) + return wantval * (1 + RIFtol(want.tol) * RIFtol(-1,1)) elif want.abs_tol: - return wantval + want.abs_tol * RIFtol(-1,1) + return wantval + RIFtol(want.abs_tol) * RIFtol(-1,1) elif want.rel_tol: - return wantval * (1 + want.rel_tol * RIFtol(-1,1)) + return wantval * (1 + RIFtol(want.rel_tol) * RIFtol(-1,1)) else: return wantval diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index d9ecc9497f1..7acb33de658 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -157,7 +157,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test for unequality. + Test for non-equality. EXAMPLES:: diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index 07173e80648..f056ceb21b0 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -194,7 +194,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test for unequality + Test for non-equality EXAMPLES:: @@ -376,9 +376,10 @@ def __reduce__(self): """ return make_recording_dict, (dict(self), self.set, self.got) + def make_recording_dict(D, st, gt): """ - Auxilliary function for pickling. + Auxiliary function for pickling. EXAMPLES:: @@ -508,7 +509,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test for unequality. + Test for non-equality. EXAMPLES:: diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index 6aef7fe150e..116ee4e12b9 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -91,7 +91,7 @@ class DynamicalSystem_affine(SchemeMorphism_polynomial_affine_space, * ``morphism_or_polys`` is a list of polynomials or rational functions and ``domain`` is unspecified; ``domain`` is then - taken to be the affine space of appropriate dimension over the + taken to be the affine space of appropriate dimension over the common base ring, if one exists, of the elements of ``morphism_or_polys`` * ``morphism_or_polys`` is a single polynomial or rational @@ -259,7 +259,7 @@ def __classcall_private__(cls, morphism_or_polys, domain=None): else: polys = [morphism_or_polys] - PR = get_coercion_model().common_parent(*polys) + PR = get_coercion_model().common_parent(*polys) fraction_field = any(is_FractionField(poly.parent()) for poly in polys) if fraction_field: K = PR.base_ring().fraction_field() @@ -281,7 +281,7 @@ def __classcall_private__(cls, morphism_or_polys, domain=None): PR = PR.ring() domain = AffineSpace(PR) else: - # Check if we can coerce the given polynomials over the given domain + # Check if we can coerce the given polynomials over the given domain PR = domain.ambient_space().coordinate_ring() try: if fraction_field: @@ -1020,7 +1020,7 @@ def cyclegraph(self): V = [] E = [] from sage.schemes.affine.affine_space import is_AffineSpace - if is_AffineSpace(self.domain()) == True: + if is_AffineSpace(self.domain()): for P in self.domain(): V.append(str(P)) Q = self(P) @@ -1036,6 +1036,4 @@ def cyclegraph(self): except TypeError: # not on the scheme pass from sage.graphs.digraph import DiGraph - g = DiGraph(dict(zip(V, E)), loops=True) - return g - + return DiGraph(dict(zip(V, E)), loops=True) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 4584510aeb4..d46c8601b25 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -712,7 +712,7 @@ def automorphism_group_QQ_CRT(rational_function, prime_lower_bound=4, return_fun MaxH = height_bound(h) congruence = 1 - primes = Primes(); + primes = Primes() p = primes.next(ZZ(prime_lower_bound)) primepowers = [] automorphisms = [] @@ -1563,7 +1563,7 @@ def automorphism_group_FF_alg3(rational_function): # Compute the set of distinct F-rational and F-quadratic # factors of the fixed point polynomial fix = R(f(z) - z*g(z)) - linear_fix = gcd(fix, z**q - z); + linear_fix = gcd(fix, z**q - z) quad_temp = fix.quo_rem(linear_fix)[0] residual = gcd(quad_temp, z**q - z) while residual.degree() > 0: diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 5c621cf4c00..e61061c5622 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -1092,7 +1092,7 @@ def nth_iterate(self, P, n, **kwds): sage: f.nth_iterate(0,0) (0 : 1) """ - n = ZZ(n) + n = Integer(n) if n < 0: raise TypeError("must be a forward orbit") return self.orbit(P, [n,n+1], **kwds)[0] @@ -1316,8 +1316,8 @@ def orbit(self, P, N, **kwds): """ if not isinstance(N,(list,tuple)): N = [0,N] - N[0] = ZZ(N[0]) - N[1] = ZZ(N[1]) + N[0] = Integer(N[0]) + N[1] = Integer(N[1]) if N[0] < 0 or N[1] < 0: raise TypeError("orbit bounds must be non-negative") if N[0] > N[1]: @@ -2248,7 +2248,7 @@ def multiplier(self, P, n, check=True): while Q[index] == 0: index -= 1 indexlist.append(index) - for i in range(0, n): + for i in range(n): F = [] R = self(Q) R.normalize_coordinates() @@ -2313,7 +2313,7 @@ def _multipliermod(self, P, n, p, k): while Q[index] % p == 0: index -= 1 indexlist.append(index) - for i in range(0, n): + for i in range(n): F = [] R = self(Q, False) g = gcd(R._coords) @@ -3451,7 +3451,7 @@ def is_postcritically_finite(self, err=0.01, use_algebraic_closure=True): F = self.change_ring(Kbar) else: embeds = K.embeddings(Kbar) - if len(embeds) != 0: + if embeds: F = self.change_ring(embeds[0]) else: raise ValueError("no embeddings of base field to algebraic closure") @@ -3561,7 +3561,7 @@ def critical_point_portrait(self, check=True, use_algebraic_closure=True): F = self.change_ring(Kbar) else: embeds = K.embeddings(Kbar) - if len(embeds) != 0: + if embeds: F = self.change_ring(embeds[0]) else: raise ValueError("no embeddings of base field to algebraic closure") @@ -3657,7 +3657,7 @@ def critical_height(self, **kwds): F = self.change_ring(Kbar) else: embeds = K.embeddings(Kbar) - if len(embeds) != 0: + if embeds: F = self.change_ring(embeds[0]) else: raise ValueError("no embeddings of base field to algebraic closure") @@ -3856,7 +3856,7 @@ def preperiodic_points(self, m, n, **kwds): N = PS.dimension_relative() + 1 F_1 = f.nth_iterate_map(n+m) F_2 = f.nth_iterate_map(m) - L = [F_1[i]*F_2[j] - F_1[j]*F_2[i] for i in range(0,N) + L = [F_1[i]*F_2[j] - F_1[j]*F_2[i] for i in range(N) for j in range(i+1, N)] X = PS.subscheme(L + list(dom.defining_polynomials())) minimal = kwds.pop('minimal',True) @@ -4107,7 +4107,7 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety', raise TypeError("ring must be finite to generate cyclegraph") elif algorithm == 'variety': F = f.nth_iterate_map(n) - L = [F[i]*CR.gen(j) - F[j]*CR.gen(i) for i in range(0,N) + L = [F[i]*CR.gen(j) - F[j]*CR.gen(i) for i in range(N) for j in range(i+1, N)] L = [t for t in L if t != 0] X = PS.subscheme(L + list(dom.defining_polynomials())) @@ -4310,7 +4310,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur f = self.change_ring(Kbar) else: embeds = K.embeddings(Kbar) - if len(embeds) != 0: + if embeds: f = self.change_ring(embeds[0]) else: raise ValueError("no embeddings of base field to algebraic closure") @@ -6122,7 +6122,7 @@ def conjugating_set(self, other, R=None): - Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray [FMV2014]_. - - Implimented by Rebecca Lauren Miller, as part of GSOC 2016. + - Implemented by Rebecca Lauren Miller, as part of GSOC 2016. EXAMPLES:: @@ -6396,7 +6396,7 @@ def is_conjugate(self, other, R=None): - Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray [FMV2014]_. - - Implimented by Rebecca Lauren Miller as part of GSOC 2016. + - Implemented by Rebecca Lauren Miller as part of GSOC 2016. EXAMPLES:: @@ -6996,10 +6996,10 @@ def potential_good_reduction(self, prime): old_parent = K.defining_polynomial().parent() new_parent = field_of_definition_periodic.defining_polynomial().parent() hom = old_parent.hom([new_parent.gens()[0]]) - if hom(K.defining_polynomial()) != \ - field_of_definition_periodic.defining_polynomial(): - raise ValueError('prime ideal of %s ' %K + \ - 'but field of definition of fixed points is %s. ' %L + \ + 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 + 'see documentation for examples') embedding = K.embeddings(field_of_definition_periodic)[0] prime = embedding(prime) diff --git a/src/sage/dynamics/cellular_automata/elementary.py b/src/sage/dynamics/cellular_automata/elementary.py index 754012c7df8..e96dddb746c 100644 --- a/src/sage/dynamics/cellular_automata/elementary.py +++ b/src/sage/dynamics/cellular_automata/elementary.py @@ -194,20 +194,20 @@ class ElementaryCellularAutomata(SageObject): X XX # - X - XX + X + XX # XX XXX # - X - X + X + X # X X XXX # - XX - XX + XX + XX # XXX X X @@ -606,4 +606,3 @@ def plot(self, number=None): self.evolve() M = matrix(self._states[:number]) return matrix_plot(M) - diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia.py b/src/sage/dynamics/complex_dynamics/mandel_julia.py index 278f47656d8..680020cf816 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia.py +++ b/src/sage/dynamics/complex_dynamics/mandel_julia.py @@ -683,7 +683,8 @@ def julia_plot(f=None, **kwds): if f is not None and period is None: # f user-specified and no period given # try to coerce f to live in a polynomial ring - S = PolynomialRing(CC,names='z'); z = S.gen() + S = PolynomialRing(CC,names='z') + z = S.gen() try: f_poly = S(f) except TypeError: diff --git a/src/sage/env.py b/src/sage/env.py index 4e7c9ee8f92..2908f5d04fa 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -36,6 +36,7 @@ import sysconfig from . import version from pathlib import Path +import subprocess # All variables set by var() appear in this SAGE_ENV dict @@ -209,6 +210,9 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st SAGE_NAUTY_BINS_PREFIX = var("SAGE_NAUTY_BINS_PREFIX", "") ARB_LIBRARY = var("ARB_LIBRARY", "arb") CBLAS_PC_MODULES = var("CBLAS_PC_MODULES", "cblas:openblas:blas") +ECL_CONFIG = var("ECL_CONFIG", "ecl-config") +NTL_INCDIR = var("NTL_INCDIR") +NTL_LIBDIR = var("NTL_LIBDIR") # misc SAGE_BANNER = var("SAGE_BANNER", "") @@ -462,4 +466,20 @@ def uname_specific(name, value, alternative): except ValueError: pass + # Determine ecl-specific compiler arguments using the ecl-config script + ecl_cflags = subprocess.run([ECL_CONFIG, "--cflags"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout.split() + aliases["ECL_CFLAGS"] = list(filter(lambda s: not s.startswith('-I'), ecl_cflags)) + aliases["ECL_INCDIR"] = list(map(lambda s: s[2:], filter(lambda s: s.startswith('-I'), ecl_cflags))) + ecl_libs = subprocess.run([ECL_CONFIG, "--libs"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout.split() + aliases["ECL_LIBDIR"] = list(map(lambda s: s[2:], filter(lambda s: s.startswith('-L'), ecl_libs))) + aliases["ECL_LIBRARIES"] = list(map(lambda s: s[2:], filter(lambda s: s.startswith('-l'), ecl_libs))) + aliases["ECL_LIBEXTRA"] = list(filter(lambda s: not s.startswith(('-l','-L')), ecl_libs)) + + # NTL + aliases["NTL_CFLAGS"] = ['-std=c++11'] + aliases["NTL_INCDIR"] = [NTL_INCDIR] if NTL_INCDIR else [] + aliases["NTL_LIBDIR"] = [NTL_LIBDIR] if NTL_LIBDIR else [] + aliases["NTL_LIBRARIES"] = ['ntl'] + aliases["NTL_LIBEXTRA"] = [] + return aliases diff --git a/src/sage/ext_data/gap/sage.g b/src/sage/ext_data/gap/sage.g index 9b28df61c1e..36e131146d6 100644 --- a/src/sage/ext_data/gap/sage.g +++ b/src/sage/ext_data/gap/sage.g @@ -4,12 +4,12 @@ # Prevent loading the xgap package; we use the -p flag to GAP in order to # communicate with it via the pexpect interface; this is normally used by -# for an xgap window to communicate with GAP, so unfortunatelly setting this +# for an xgap window to communicate with GAP, so unfortunately setting this # flag also allows the xgap package to be loaded and for some packages to -# attempt to communicate with a "window handler" that doesn't exist. +# attempt to communicate with a "window handler" that does not exist. # Therefore we must explicitly disable loading of the xgap package. # -# Don't use SetUserPreference since that leads to reloading the workspace, +# Do not use SetUserPreference since that leads to reloading the workspace, # which is confusing to the pexpect interface if IsBound(GAPInfo.ExcludeFromAutoload) then Append(GAPInfo.ExcludeFromAutoload, "xgap"); diff --git a/src/sage/ext_data/pari/dokchitser/ex-shin b/src/sage/ext_data/pari/dokchitser/ex-shin index b8721f0ffa8..e7a867815e5 100644 --- a/src/sage/ext_data/pari/dokchitser/ex-shin +++ b/src/sage/ext_data/pari/dokchitser/ex-shin @@ -38,7 +38,7 @@ c2=[1,1,1,1,1,1,1,2,1,1,3,1,2,3,3,2,1,1,1,1,1,3,1,2,1,1,1,1,3,3,1,2,1,1,1,3,1, a(n) = if(n%4>2,c2[n\2]/sqrt(3),if(n%4>1,0,if(n%4>0,c1[n\2+1],c1[n/2]+c2[n/2]/sqrt(3)))); coefgrow(n) = n^(1/3); \\ approx. growth of the coefficients in general -initLdata("a(k)"); \\ Initalized L-series with coefficients a(k) +initLdata("a(k)"); \\ Initialized L-series with coefficients a(k) \\ actually uses cflength()=352 coeffs print("EXAMPLE: L(s)=Shintani's zeta function"); diff --git a/src/sage/ext_data/pari/simon/ellQ.gp b/src/sage/ext_data/pari/simon/ellQ.gp index aede9fc9419..1cfe6318f82 100644 --- a/src/sage/ext_data/pari/simon/ellQ.gp +++ b/src/sage/ext_data/pari/simon/ellQ.gp @@ -516,7 +516,7 @@ return(listpoints); \\ pol is a squarefree polynomial in Z[x]. \\ Returns a list of vectors [a,b] with a and b rationals \\ such that the intervals ]a,b] are disjoint and contain -\\ all the real roots of pol, and excatly one in each interval. +\\ all the real roots of pol, and exactly one in each interval. my(st,a,res,ind,b,c,stab,stac); if( DEBUGLEVEL_ell >= 5, print(" starting polrealrootsisolate with pol = ",pol)); diff --git a/src/sage/ext_data/threejs/fat_lines.js b/src/sage/ext_data/threejs/fat_lines.js new file mode 100644 index 00000000000..a865cddaf48 --- /dev/null +++ b/src/sage/ext_data/threejs/fat_lines.js @@ -0,0 +1,48 @@ +// fat_lines.js + +var _fatLines = []; + +function _createFatLine( lineStrip, geometry, materialOptions ) { + + var vertexCount = geometry.vertices.length; + var positions = []; + for ( var i=0 ; i < vertexCount ; i++ ) { + var v = geometry.vertices[i]; + positions.push( v.x, v.y, v.z ); + // For a line strip, duplicate all but the first and last vertices. + if ( lineStrip && i > 0 && i < vertexCount - 1 ) { + positions.push( v.x, v.y, v.z ); + } + } + + geometry = new THREE.LineSegmentsGeometry(); + geometry.setPositions( positions ); + + var material = new THREE.LineMaterial( materialOptions ); + material.resolution = new THREE.Vector2( window.innerWidth, window.innerHeight ); + + var line = new THREE.LineSegments2( geometry, material ); + line.computeLineDistances(); + line.scale.set( 1, 1, 1 ); + + _fatLines.push( line ); + + return line; + +} + +function createFatLineStrip( geometry, materialOptions ) { + return _createFatLine( true, geometry, materialOptions ); +} + +function createFatLineSegments( geometry, materialOptions ) { + return _createFatLine( false, geometry, materialOptions ); +} + +function rescaleFatLines() { + var res = new THREE.Vector2( window.innerWidth, window.innerHeight ); + var n = _fatLines.length; + for ( var i=0 ; i < n ; i++ ) { + _fatLines[i].material.resolution = res; + } +} diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index c9f2726a894..1db1e15d7bd 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -285,6 +285,7 @@ renderer.setSize( window.innerWidth, window.innerHeight ); updateCameraAspect( camera, window.innerWidth / window.innerHeight ); + if ( window.rescaleFatLines ) rescaleFatLines(); if ( !animate ) render(); } ); @@ -347,16 +348,23 @@ geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) ); } - var transparent = json.opacity < 1 ? true : false; - var material = new THREE.LineBasicMaterial( { color: json.color, linewidth: json.linewidth, - transparent: transparent, opacity: json.opacity } ); - var c = new THREE.Vector3(); geometry.computeBoundingBox(); geometry.boundingBox.getCenter( c ); geometry.translate( -c.x, -c.y, -c.z ); - var mesh = new THREE.Line( geometry, material ); + var transparent = json.opacity < 1 ? true : false; + var materialOptions = { color: json.color, linewidth: json.linewidth, + transparent: transparent, opacity: json.opacity }; + + var mesh; + if ( json.linewidth > 1 && window.createFatLineStrip ) { + mesh = createFatLineStrip( geometry, materialOptions ); + } else { + var material = new THREE.LineBasicMaterial( materialOptions ); + mesh = new THREE.Line( geometry, material ); + } + mesh.position.set( c.x, c.y, c.z ); mesh.userData = json; scene.add( mesh ); @@ -406,38 +414,48 @@ mesh.userData = json; scene.add( mesh ); - if ( json.showMeshGrid ) { - - var geometry = new THREE.Geometry(); - - for ( var i=0 ; i < json.faces.length ; i++ ) { - var f = json.faces[i]; - for ( var j=0 ; j < f.length ; j++ ) { - var k = j === f.length-1 ? 0 : j+1; - var v1 = json.vertices[f[j]]; - var v2 = json.vertices[f[k]]; - // vertices in opposite directions on neighboring faces - var nudge = f[j] < f[k] ? .0005*zRange : -.0005*zRange; - geometry.vertices.push( new THREE.Vector3( a[0]*v1.x, a[1]*v1.y, a[2]*(v1.z+nudge) ) ); - geometry.vertices.push( new THREE.Vector3( a[0]*v2.x, a[1]*v2.y, a[2]*(v2.z+nudge) ) ); - } - } + if ( json.showMeshGrid ) addSurfaceMeshGrid( json ); - var gridColor = options.theme === 'dark' ? 'white' : 'black'; - var material = new THREE.LineBasicMaterial( { color: gridColor, linewidth: 1 } ); + } - var c = new THREE.Vector3(); - geometry.computeBoundingBox(); - geometry.boundingBox.getCenter( c ); - geometry.translate( -c.x, -c.y, -c.z ); + function addSurfaceMeshGrid( json ) { - var mesh = new THREE.LineSegments( geometry, material ); - mesh.position.set( c.x, c.y, c.z ); - mesh.userData = json; - scene.add( mesh ); + var geometry = new THREE.Geometry(); + for ( var i=0 ; i < json.faces.length ; i++ ) { + var f = json.faces[i]; + for ( var j=0 ; j < f.length ; j++ ) { + var k = j === f.length-1 ? 0 : j+1; + var v1 = json.vertices[f[j]]; + var v2 = json.vertices[f[k]]; + // vertices in opposite directions on neighboring faces + var nudge = f[j] < f[k] ? .0005*zRange : -.0005*zRange; + geometry.vertices.push( new THREE.Vector3( a[0]*v1.x, a[1]*v1.y, a[2]*(v1.z+nudge) ) ); + geometry.vertices.push( new THREE.Vector3( a[0]*v2.x, a[1]*v2.y, a[2]*(v2.z+nudge) ) ); + } } + var c = new THREE.Vector3(); + geometry.computeBoundingBox(); + geometry.boundingBox.getCenter( c ); + geometry.translate( -c.x, -c.y, -c.z ); + + var gridColor = options.theme === 'dark' ? 'white' : 'black'; + var linewidth = json.linewidth || 1; + var materialOptions = { color: gridColor, linewidth: linewidth }; + + var mesh; + if ( linewidth > 1 && window.createFatLineSegments ) { + mesh = createFatLineSegments( geometry, materialOptions ); + } else { + var material = new THREE.LineBasicMaterial( materialOptions ); + mesh = new THREE.LineSegments( geometry, material ); + } + + mesh.position.set( c.x, c.y, c.z ); + mesh.userData = json; + scene.add( mesh ); + } function render() { diff --git a/src/sage/features/graphviz.py b/src/sage/features/graphviz.py index 8e40c33b502..d3c24d7e804 100644 --- a/src/sage/features/graphviz.py +++ b/src/sage/features/graphviz.py @@ -34,6 +34,7 @@ def __init__(self): True """ Executable.__init__(self, "dot", executable="dot", + spkg="graphviz", url="https://www.graphviz.org/") @@ -57,6 +58,7 @@ def __init__(self): True """ Executable.__init__(self, "neato", executable="neato", + spkg="graphviz", url="https://www.graphviz.org/") @@ -80,6 +82,7 @@ def __init__(self): True """ Executable.__init__(self, "twopi", executable="twopi", + spkg="graphviz", url="https://www.graphviz.org/") @@ -103,6 +106,7 @@ def __init__(self): True """ Feature.__init__(self, "Graphviz", + spkg="graphviz", url="https://www.graphviz.org/") def _is_present(self): diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index b46759c0d04..e45d4abcb23 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -476,7 +476,7 @@ def n_hyperplanes(self): def hyperplanes(self): r""" - Return the number of hyperplanes in the arrangement. + Return the hyperplanes in the arrangement as a tuple. OUTPUT: diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 3c24fae867f..20d0868894a 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -535,7 +535,7 @@ def _init_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep, verbose=Fal In comparison, the "normal" initialization from Vrepresentation over RDF expects the output length to be consistent with the computed length - when re-feeding cdd the outputed Hrepresentation. + when re-feeding cdd the outputted Hrepresentation. EXAMPLES:: diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 1f6e3d62416..95c489c8669 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1097,7 +1097,7 @@ def _number_field_triple(normaliz_field): @staticmethod def _make_normaliz_cone(data, verbose=False): r""" - Returns a normaliz cone from ``data``. + Return a normaliz cone from ``data``. INPUT: diff --git a/src/sage/geometry/polyhedron/base_ZZ.py b/src/sage/geometry/polyhedron/base_ZZ.py index 64baacd5769..95bc38f2279 100644 --- a/src/sage/geometry/polyhedron/base_ZZ.py +++ b/src/sage/geometry/polyhedron/base_ZZ.py @@ -2,7 +2,7 @@ Base class for polyhedra over `\ZZ` """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Volker Braun # # This program is free software: you can redistribute it and/or modify @@ -10,7 +10,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.rings.all import ZZ, QQ from sage.misc.all import cached_method @@ -741,7 +741,7 @@ def _subpoly_parallel_facets(self): yield self return edge_vectors = [] - for i in range(0,n): + for i in range(n): v = vertices[(i+1) % n].vector() - vertices[i].vector() d = gcd(list(v)) v_prim = (v/d).change_ring(ZZ) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d4a086962fc..d11e8577d88 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -682,7 +682,7 @@ cdef class CombinatorialPolyhedron(SageObject): def Hrepresentation(self): r""" - Returns a list of names of facets and possibly some equalities. + Return a list of names of facets and possibly some equalities. EXAMPLES:: @@ -1727,7 +1727,7 @@ cdef class CombinatorialPolyhedron(SageObject): # There are no faces of dimension 0,...,n_lines. flag[comb] = smallInteger(0) else: - # Shift the old entires up by the number of lines. + # Shift the old entries up by the number of lines. flag[comb] = flag_old[tuple(i - n_lines for i in comb)] flag[self.dimension()] = smallInteger(1) @@ -2577,7 +2577,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: G._elements ((), (0,), (0, 1), (0, 2), (0, 1, 2)) """ - self._record_all_faces() # Initalize ``_all_faces``, if not done yet. + self._record_all_faces() # Initialize ``_all_faces``, if not done yet. dim = self._face_lattice_dimension(index) # Determine dimension to that index. newindex = index - sum(self._f_vector[:dim + 1]) # Index in that level-set. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index ace9c7a4f33..2b509aaec01 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -28,7 +28,7 @@ ctypedef fused algorithm_variant: standard ############################################################################# -# Face Initalization +# Face Initialization ############################################################################# cdef inline bint face_init(face_t face, mp_bitcnt_t n_atoms, mp_bitcnt_t n_coatoms, MemoryAllocator mem) except -1: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 56b062af495..63ed7858024 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -31,11 +31,11 @@ cdef struct iter_s: face_list_t* visited_all # ``new_faces`` is where the new faces are stored. - # Needs to be long enought to store all possible intersections of a face with all coatoms. + # Needs to be long enough to store all possible intersections of a face with all coatoms. face_list_t* new_faces # After having visited a face completely, we want to add it to ``visited_all``. - # ``first_dim[i]`` will indicate, wether there is one more face in + # ``first_dim[i]`` will indicate, whether there is one more face in # ``newfaces[i]`` then ``n_newfaces[i]`` suggests # that has to be added to ``visited_all``. # If ``first_time[i] == False``, we still need to diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9086e8c339a..fc4929bf29f 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -233,7 +233,7 @@ cdef class FaceIterator_base(SageObject): self.structure.lowest_dimension = 0 if output_dimension is not None: - if not output_dimension in range(0,self.structure.dimension): + if output_dimension not in range(self.structure.dimension): raise ValueError("``output_dimension`` must be the dimension of proper faces") if self.dual: # In dual mode, the dimensions are reversed. @@ -1258,9 +1258,9 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: if new_faces_counter: # ``faces[n_faces]`` contains new faces. - # We will visted them on next call, starting with codimension 1. + # We will visit them on next call, starting with codimension 1. - # Setting the variables correclty for next call of ``next_face_loop``. + # Setting the variables correctly for next call of ``next_face_loop``. structure.current_dimension -= 1 structure.first_time[structure.current_dimension] = True structure.visited_all[structure.current_dimension][0] = visited_all[0][0] diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 693cb7df488..fa28c8c5473 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -27,7 +27,7 @@ cdef struct face_list_s: ctypedef face_list_s face_list_t[1] ############################################################################# -# Face List Initalization +# Face List Initialization ############################################################################# cdef inline int face_list_init(face_list_t faces, size_t n_faces, size_t n_atoms, size_t n_coatoms, MemoryAllocator mem) except -1: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index ea4d11db09c..6c9f5c678b5 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -3,7 +3,7 @@ PolyhedronFaceLattice This module provides a class that stores and sorts all faces of the polyhedron. -:class:`~sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron` implicitely uses this class to generate +:class:`~sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron` implicitly uses this class to generate the face lattice of a polyhedron. Terminology in this module: @@ -106,7 +106,7 @@ cdef class PolyhedronFaceLattice: The faces are recorded with :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator` in Bit-representation. Once created, all level-sets but the coatoms are sorted with merge sort. - Non-trivial incidences of elements whos rank differs by 1 are determined + Non-trivial incidences of elements whose rank differs by 1 are determined by intersecting with all coatoms. Then each intersection is looked up in the sorted level sets. """ @@ -220,7 +220,7 @@ cdef class PolyhedronFaceLattice: if unlikely(self.f_vector[i] != self.faces[i].n_faces): raise ValueError("``PolyhedronFaceLattice`` does not contain all faces") - for i in range(0, dim-1): + for i in range(dim - 1): # Sort each level set, except for the facets, the full- and empty polyhedron. sort_faces_list(self.faces[i+1]) @@ -477,7 +477,7 @@ cdef class PolyhedronFaceLattice: coatoms.faces[self.incidence_counter_two]) # Get the location of the intersection and - # check, wether it is correct. + # check whether it is correct. location = self.find_face(self.incidence_dim_two, self.incidence_face) two[0] = location diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index 93f702271fc..29d84875ac2 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -440,8 +440,8 @@ def Polyhedron(vertices=None, rays=None, lines=None, sage: R0. = QQ[] sage: R1. = NumberField(r0^2-5, embedding=AA(5)**(1/2)) - sage: grat = (1+r1)/2 - sage: v = [[0, 1, grat], [0, 1, -grat], [0, -1, grat], [0, -1, -grat]] + sage: gold = (1+r1)/2 + sage: v = [[0, 1, gold], [0, 1, -gold], [0, -1, gold], [0, -1, -gold]] sage: pp = Permutation((1, 2, 3)) sage: icosah = Polyhedron([(pp^2).action(w) for w in v] ....: + [pp.action(w) for w in v] + v, base_ring=R1) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 218bb4095ca..ee76b16f8cd 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -334,7 +334,7 @@ def gale_transform_to_primal(vectors, base_ring=None, backend=None): - ``backend`` -- string (default: `None`); the backend to be use to construct a polyhedral, - used interally in case the center is not the origin, + used internally in case the center is not the origin, see :func:`~sage.geometry.polyhedron.constructor.Polyhedron` OUTPUT: An ordered point configuration as list of vectors. @@ -3045,7 +3045,7 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete phi = (1 + sqrt5) / 2 phi_inv = base_ring.one() / phi - # The 24 permutations of [0,0,±2,±2] (the ± are independant) + # The 24 permutations of [0,0,±2,±2] (the ± are independent) verts = Permutations([0,0,2,2]).list() + Permutations([0,0,-2,-2]).list() + Permutations([0,0,2,-2]).list() # The 64 permutations of the following vectors: diff --git a/src/sage/geometry/polyhedron/misc.py b/src/sage/geometry/polyhedron/misc.py index ff41b7a2ca2..1e0345c054f 100644 --- a/src/sage/geometry/polyhedron/misc.py +++ b/src/sage/geometry/polyhedron/misc.py @@ -40,8 +40,9 @@ def _to_space_separated_string(l, base_ring=None): def _set_to_None_if_empty(x): """ - Helper function to clean up arguments: Returns None if x==None or - x is an empty container. + Helper function to clean up arguments. + + This returns None if x is None or x is an empty container. EXAMPLES:: diff --git a/src/sage/geometry/polyhedron/palp_database.py b/src/sage/geometry/polyhedron/palp_database.py index cad4b33ac2e..b3e2330366b 100644 --- a/src/sage/geometry/polyhedron/palp_database.py +++ b/src/sage/geometry/polyhedron/palp_database.py @@ -154,9 +154,9 @@ def _read_vertices(self, stdout, rows, cols): sage: polygons._read_vertices(palp.stdout, 2, 3) [[1, 0], [0, 1], [-1, -1]] """ - m = [[] for col in range(0, cols)] - for row in range(0, rows): - for col,x in enumerate(stdout.readline().split()): + m = [[] for col in range(cols)] + for row in range(rows): + for col, x in enumerate(stdout.readline().split()): m[col].append(ZZ(x)) return m @@ -179,7 +179,7 @@ def _read_vertices_transposed(self, stdout, rows, cols): [[1, 0, -1], [0, 1, -1]] """ m = [] - for row in range(0, rows): + for row in range(rows): m.append([ZZ(x) for x in stdout.readline().split()]) return m @@ -229,7 +229,7 @@ def _iterate_list(self, start, stop, step): raise ValueError('PALP output dimension mismatch.') yield vertices else: - for row in range(0, dim): + for row in range(dim): palp_out.readline() i += 1 if stop is not None and i >= stop: @@ -392,8 +392,6 @@ def __getitem__(self, item): raise IndexError('Index out of range.') - -######################################################################### class Reflexive4dHodge(PALPreader): """ Read the PALP database for Hodge numbers of 4d polytopes. @@ -463,4 +461,4 @@ def _palp_Popen(self): return Popen(['class-4d.x', '-He', 'H{}:{}L100000000'.format(self._h21, self._h11), '-di', self._data_basename], stdout=PIPE, - encoding='utf-8', errors='surrogateescape') + encoding='utf-8', errors='surrogateescape') diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 72160195d1c..57f3a349bfc 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -324,7 +324,7 @@ def backend(self): @cached_method def an_element(self): r""" - Returns a Polyhedron. + Return a Polyhedron. EXAMPLES:: @@ -336,7 +336,7 @@ def an_element(self): one = self.base_ring().one() p = [zero] * self.ambient_dim() points = [p] - for i in range(0, self.ambient_dim()): + for i in range(self.ambient_dim()): p = [zero] * self.ambient_dim() p[i] = one points.append(p) @@ -345,7 +345,7 @@ def an_element(self): @cached_method def some_elements(self): r""" - Returns a list of some elements of the semigroup. + Return a list of some elements of the semigroup. EXAMPLES:: @@ -365,8 +365,8 @@ def some_elements(self): self.element_class(self, None, [[], []])] points = [] R = self.base_ring() - for i in range(0, self.ambient_dim() + 5): - points.append([R(i*j^2) for j in range(0, self.ambient_dim())]) + for i in range(self.ambient_dim() + 5): + points.append([R(i*j^2) for j in range(self.ambient_dim())]) return [ self.element_class(self, [points[0:self.ambient_dim()+1], [], []], None), self.element_class(self, [points[0:1], points[1:self.ambient_dim()+1], []], None), diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index de7ff54a3ce..a38a0a7db15 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -764,8 +764,8 @@ def adjacent_vertices(i): polygons = [] if polyhedron.n_lines() == 1: - aline = next(polyhedron.line_generator()) - for shift in [aline(), -aline()]: + a_line = next(polyhedron.line_generator()) + for shift in [a_line(), -a_line()]: for i in range(len(coords)): polygons.append([coords[i - 1], coords[i], coords[i] + shift, coords[i - 1] + shift]) @@ -838,7 +838,7 @@ def adjacent_vertices(i): self.face_inequalities = face_inequalities if polyhedron.n_lines() == 0: - assert len(faces) > 0, "no vertices?" + assert faces, "no vertices?" self.polygons.extend( [self.coord_indices_of(f) for f in faces] ) return @@ -846,9 +846,9 @@ def adjacent_vertices(i): polygons = [] if polyhedron.n_lines() == 1: - assert len(faces) > 0, "no vertices?" - aline = next(polyhedron.line_generator()) - for shift in [aline(), -aline()]: + assert faces, "no vertices?" + a_line = next(polyhedron.line_generator()) + for shift in [a_line(), -a_line()]: for coords in faces: assert len(coords) == 2, "There must be two points." polygons.append([coords[0], coords[1], diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py index d10986da99b..65d261d4283 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py @@ -303,7 +303,7 @@ def bounding_box(self): box_max = [] if self.is_empty(): raise ValueError('empty polytope is not allowed') - for i in range(0, self.space_dimension()): + for i in range(self.space_dimension()): x = Variable(i) coords = [Integer(v.coefficient(x)) for v in self.generators()] max_coord = max(coords) @@ -494,7 +494,7 @@ def vertices(self): v = vector(ZZ, d) points = [] for g in self.minimized_generators(): - for i in range(0,d): + for i in range(d): v[i] = g.coefficient(Variable(i)) v_copy = copy.copy(v) v_copy.set_immutable() diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 95805d61d88..3c73a959ac7 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -60,7 +60,7 @@ class PolyhedronRepresentation(SageObject): def __len__(self): """ - Returns the length of the representation data. + Return the length of the representation data. TESTS:: @@ -198,7 +198,7 @@ def _comparison_scalar(self): def vector(self, base_ring=None): """ - Returns the vector representation of the H/V-representation object. + Return the vector representation of the H/V-representation object. INPUT: @@ -250,7 +250,7 @@ def vector(self, base_ring=None): def polyhedron(self): """ - Returns the underlying polyhedron. + Return the underlying polyhedron. TESTS:: @@ -263,7 +263,7 @@ def polyhedron(self): def __call__(self): """ - Returns the vector representation of the representation + Return the vector representation of the representation object. Shorthand for the vector() method. TESTS:: @@ -430,7 +430,7 @@ def _set_data(self, polyhedron, data): def is_H(self): """ - Returns True if the object is part of a H-representation + Return True if the object is part of a H-representation (inequality or equation). EXAMPLES:: @@ -444,7 +444,7 @@ def is_H(self): def is_inequality(self): """ - Returns True if the object is an inequality of the H-representation. + Return True if the object is an inequality of the H-representation. EXAMPLES:: @@ -457,7 +457,7 @@ def is_inequality(self): def is_equation(self): """ - Returns True if the object is an equation of the H-representation. + Return True if the object is an equation of the H-representation. EXAMPLES:: @@ -470,7 +470,7 @@ def is_equation(self): def A(self): r""" - Returns the coefficient vector `A` in `A\vec{x}+b`. + Return the coefficient vector `A` in `A\vec{x}+b`. EXAMPLES:: @@ -492,7 +492,7 @@ def A(self): def b(self): r""" - Returns the constant `b` in `A\vec{x}+b`. + Return the constant `b` in `A\vec{x}+b`. EXAMPLES:: @@ -568,7 +568,7 @@ def adjacent(self): def is_incident(self, Vobj): """ - Returns whether the incidence matrix element (Vobj,self) == 1 + Return whether the incidence matrix element (Vobj,self) == 1 EXAMPLES:: @@ -631,7 +631,7 @@ def eval(self, Vobj): def incident(self): """ - Returns a generator for the incident H-representation objects, + Return a generator for the incident H-representation objects, that is, the vertices/rays/lines satisfying the (in)equality. EXAMPLES:: @@ -713,7 +713,7 @@ class Inequality(Hrepresentation): def type(self): r""" - Returns the type (equation/inequality/vertex/ray/line) as an + Return the type (equation/inequality/vertex/ray/line) as an integer. OUTPUT: @@ -743,7 +743,7 @@ def type(self): def is_inequality(self): """ - Returns True since this is, by construction, an inequality. + Return True since this is, by construction, an inequality. EXAMPLES:: @@ -991,7 +991,7 @@ class Equation(Hrepresentation): def type(self): r""" - Returns the type (equation/inequality/vertex/ray/line) as an + Return the type (equation/inequality/vertex/ray/line) as an integer. OUTPUT: @@ -1086,7 +1086,7 @@ def interior_contains(self, Vobj): NOTE: - Returns False for any equation. + Return False for any equation. EXAMPLES:: @@ -1160,7 +1160,7 @@ def _set_data(self, polyhedron, data): def is_V(self): """ - Returns True if the object is part of a V-representation + Return True if the object is part of a V-representation (a vertex, ray, or line). EXAMPLES:: @@ -1174,7 +1174,7 @@ def is_V(self): def is_vertex(self): """ - Returns True if the object is a vertex of the V-representation. + Return True if the object is a vertex of the V-representation. This method is over-ridden by the corresponding method in the derived class Vertex. @@ -1193,7 +1193,7 @@ def is_vertex(self): def is_ray(self): """ - Returns True if the object is a ray of the V-representation. + Return True if the object is a ray of the V-representation. This method is over-ridden by the corresponding method in the derived class Ray. @@ -1213,7 +1213,7 @@ def is_ray(self): def is_line(self): """ - Returns True if the object is a line of the V-representation. + Return True if the object is a line of the V-representation. This method is over-ridden by the corresponding method in the derived class Line. @@ -1231,7 +1231,7 @@ def is_line(self): def neighbors(self): """ - Returns a generator for the adjacent vertices/rays/lines. + Return a generator for the adjacent vertices/rays/lines. EXAMPLES:: @@ -1262,7 +1262,7 @@ def adjacent(self): def is_incident(self, Hobj): """ - Returns whether the incidence matrix element (self,Hobj) == 1 + Return whether the incidence matrix element (self,Hobj) == 1 EXAMPLES:: @@ -1294,7 +1294,7 @@ def __mul__(self, Hobj): def incident(self): """ - Returns a generator for the equations/inequalities that are satisfied on the given + Return a generator for the equations/inequalities that are satisfied on the given vertex/ray/line. EXAMPLES:: @@ -1327,7 +1327,7 @@ class Vertex(Vrepresentation): def type(self): r""" - Returns the type (equation/inequality/vertex/ray/line) as an + Return the type (equation/inequality/vertex/ray/line) as an integer. OUTPUT: @@ -1369,7 +1369,7 @@ def is_vertex(self): def _repr_(self): """ - Returns a string representation of the vertex. + Return a string representation of the vertex. OUTPUT: @@ -1408,7 +1408,7 @@ def homogeneous_vector(self, base_ring=None): def evaluated_on(self, Hobj): r""" - Returns `A\vec{x}+b` + Return `A\vec{x}+b` EXAMPLES:: @@ -1448,7 +1448,7 @@ class Ray(Vrepresentation): def type(self): r""" - Returns the type (equation/inequality/vertex/ray/line) as an + Return the type (equation/inequality/vertex/ray/line) as an integer. OUTPUT: @@ -1525,7 +1525,7 @@ def homogeneous_vector(self, base_ring=None): def evaluated_on(self, Hobj): r""" - Returns `A\vec{r}` + Return `A\vec{r}` EXAMPLES:: @@ -1546,7 +1546,7 @@ class Line(Vrepresentation): def type(self): r""" - Returns the type (equation/inequality/vertex/ray/line) as an + Return the type (equation/inequality/vertex/ray/line) as an integer. OUTPUT: @@ -1623,7 +1623,7 @@ def homogeneous_vector(self, base_ring=None): def evaluated_on(self, Hobj): r""" - Returns `A\vec{\ell}` + Return `A\vec{\ell}` EXAMPLES:: diff --git a/src/sage/graphs/base/boost_graph.pxd b/src/sage/graphs/base/boost_graph.pxd index f29031e423e..7c7333a525b 100644 --- a/src/sage/graphs/base/boost_graph.pxd +++ b/src/sage/graphs/base/boost_graph.pxd @@ -1,4 +1,5 @@ # distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 #***************************************************************************** # Copyright (C) 2015 Michele Borassi michele.borassi@imtlucca.it diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index b68bc68bd5f..930fe12ed55 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -5,24 +5,25 @@ The methods defined here appear in :mod:`sage.graphs.graph_generators`. """ -########################################################################### -# +# **************************************************************************** # Copyright (C) 2006 Robert L. Miller # and Emily A. Kirkman -# Copyright (C) 2009 Michael C. Yurko +# 2009 Michael C. Yurko # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -########################################################################### +# 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 from Sage library from sage.graphs.graph import Graph -from sage.graphs import graph from math import sin, cos, pi def BullGraph(): r""" - Returns a bull graph with 5 nodes. + Return a bull graph with 5 nodes. A bull graph is named for its shape. It's a triangle with horns. See the :wikipedia:`Bull_graph` for more information. @@ -35,18 +36,17 @@ def BullGraph(): (1 and 2) complete the triangle. Node 3 is the horn connected to 1 and node 4 is the horn connected to node 2. - EXAMPLES: Construct and show a bull graph:: sage: g = graphs.BullGraph(); g Bull graph: Graph on 5 vertices - sage: g.show() # long time + sage: g.show() # long time The bull graph has 5 vertices and 5 edges. Its radius is 2, its diameter 3, and its girth 3. The bull graph is planar with chromatic - number 3 and chromatic index also 3. :: + number 3 and chromatic index also 3:: sage: g.order(); g.size() 5 @@ -63,7 +63,7 @@ def BullGraph(): is `x(x^2 - x - 3)(x^2 + x - 1)`, which follows from the definition of characteristic polynomials for graphs, i.e. `\det(xI - A)`, where `x` is a variable, `A` the adjacency matrix of the graph, and `I` - the identity matrix of the same dimensions as `A`. :: + the identity matrix of the same dimensions as `A`:: sage: chrompoly = g.chromatic_polynomial() sage: bool(expand(x * (x - 2) * (x - 1)^3) == chrompoly) @@ -83,12 +83,12 @@ def BullGraph(): True """ edge_list = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 4)] - pos_dict = {0:(0,0), 1:(-1,1), 2:(1,1), 3:(-2,2), 4:(2,2)} - return graph.Graph(edge_list, pos=pos_dict, name="Bull graph") + pos_dict = {0: (0, 0), 1: (-1, 1), 2: (1, 1), 3: (-2, 2), 4: (2, 2)} + return Graph(edge_list, pos=pos_dict, name="Bull graph") def ButterflyGraph(): r""" - Returns the butterfly graph. + Return the butterfly graph. Let `C_3` be the cycle graph on 3 vertices. The butterfly or bowtie graph is obtained by joining two copies of `C_3` at a common vertex, @@ -101,8 +101,7 @@ def ButterflyGraph(): EXAMPLES: - The butterfly graph is a planar graph on 5 vertices and having - 6 edges. :: + The butterfly graph is a planar graph on 5 vertices and having 6 edges:: sage: G = graphs.ButterflyGraph(); G Butterfly graph: Graph on 5 vertices @@ -114,7 +113,7 @@ def ButterflyGraph(): sage: G.size() 6 - It has diameter 2, girth 3, and radius 1. :: + It has diameter 2, girth 3, and radius 1:: sage: G.diameter() 2 @@ -123,7 +122,7 @@ def ButterflyGraph(): sage: G.radius() 1 - The butterfly graph is Eulerian, with chromatic number 3. :: + The butterfly graph is Eulerian, with chromatic number 3:: sage: G.is_eulerian() True @@ -141,7 +140,7 @@ def ButterflyGraph(): 2: [1, -1], 3: [-1, -1], 4: [0, 0]} - return graph.Graph(edge_dict, pos=pos_dict, name="Butterfly graph") + return Graph(edge_dict, pos=pos_dict, name="Butterfly graph") def CircularLadderGraph(n): @@ -193,40 +192,38 @@ def CircularLadderGraph(n): G._circle_embedding(list(range(n, 2*n)), radius=2, angle=pi/2) G.add_cycle(list(range(n))) G.add_cycle(list(range(n, 2 * n))) - G.add_edges( (i,i+n) for i in range(n) ) + G.add_edges((i, i + n) for i in range(n)) return G def ClawGraph(): """ - Returns a claw graph. + Return a claw graph. A claw graph is named for its shape. It is actually a complete - bipartite graph with (n1, n2) = (1, 3). + bipartite graph with ``(n1, n2) = (1, 3)``. - PLOTTING: See CompleteBipartiteGraph. + PLOTTING: See :meth:`CompleteBipartiteGraph`. - EXAMPLES: Show a Claw graph - - :: + EXAMPLES: - sage: (graphs.ClawGraph()).show() # long time + Show a Claw graph:: - Inspect a Claw graph + sage: (graphs.ClawGraph()).show() # long time - :: + Inspect a Claw graph:: sage: G = graphs.ClawGraph() sage: G Claw graph: Graph on 4 vertices """ edge_list = [(0, 1), (0, 2), (0, 3)] - pos_dict = {0:(0,1),1:(-1,0),2:(0,0),3:(1,0)} - return graph.Graph(edge_list, pos=pos_dict, name="Claw graph") + pos_dict = {0: (0, 1), 1: (-1, 0), 2: (0, 0), 3: (1, 0)} + return Graph(edge_list, pos=pos_dict, name="Claw graph") def CycleGraph(n): r""" - Return a cycle graph with n nodes. + Return a cycle graph with `n` nodes. A cycle graph is a basic structure which is also typically called an `n`-gon. @@ -243,14 +240,16 @@ def CycleGraph(n): Filling the position dictionary in advance adds `O(n)` to the constructor. - EXAMPLES: Compare plotting using the predefined layout and networkx:: + EXAMPLES: + + Compare plotting using the predefined layout and networkx:: sage: import networkx sage: n = networkx.cycle_graph(23) sage: spring23 = Graph(n) sage: posdict23 = graphs.CycleGraph(23) - sage: spring23.show() # long time - sage: posdict23.show() # long time + sage: spring23.show() # long time + sage: posdict23.show() # long time We next view many cycle graphs as a Sage graphics array. First we use the ``CycleGraph`` constructor, which fills in the position dictionary:: @@ -266,7 +265,7 @@ def CycleGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time Compare to plotting with the spring-layout algorithm:: @@ -282,7 +281,7 @@ def CycleGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time TESTS: @@ -298,15 +297,15 @@ def CycleGraph(n): G = Graph(n, name="Cycle graph") if n == 1: - G.set_pos({0:(0, 0)}) + G.set_pos({0: (0, 0)}) else: G._circle_embedding(list(range(n)), angle=pi/2) G.add_cycle(list(range(n))) return G def CompleteGraph(n): - """ - Return a complete graph on n nodes. + r""" + Return a complete graph on `n` nodes. A Complete Graph is a graph in which all nodes are connected to all other nodes. @@ -325,9 +324,10 @@ def CompleteGraph(n): the graph to appear as a 3-dimensional pointy ball. (See examples below). - EXAMPLES: We view many Complete graphs with a Sage Graphics Array, - first with this constructor (i.e., the position dictionary - filled):: + EXAMPLES: + + We view many Complete graphs with a Sage Graphics Array, first with this + constructor (i.e., the position dictionary filled):: sage: g = [] sage: j = [] @@ -340,7 +340,7 @@ def CompleteGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time We compare to plotting with the spring-layout algorithm:: @@ -357,20 +357,18 @@ def CompleteGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time - - Compare the constructors (results will vary) + sage: G.show() # long time - :: + Compare the constructors (results will vary):: sage: import networkx sage: t = cputime() sage: n = networkx.complete_graph(389); spring389 = Graph(n) - sage: cputime(t) # random + sage: cputime(t) # random 0.59203700000000126 sage: t = cputime() sage: posdict389 = graphs.CompleteGraph(389) - sage: cputime(t) # random + sage: cputime(t) # random 0.6680419999999998 We compare plotting:: @@ -379,49 +377,48 @@ def CompleteGraph(n): sage: n = networkx.complete_graph(23) sage: spring23 = Graph(n) sage: posdict23 = graphs.CompleteGraph(23) - sage: spring23.show() # long time - sage: posdict23.show() # long time + sage: spring23.show() # long time + sage: posdict23.show() # long time """ - G = graph.Graph(n, name="Complete graph") + G = Graph(n, name="Complete graph") if n == 1: - G.set_pos({0:(0, 0)}) + G.set_pos({0: (0, 0)}) else: G._circle_embedding(list(range(n)), angle=pi/2) G.add_edges(((i,j) for i in range(n) for j in range(i+1,n))) return G -def CompleteBipartiteGraph(n1, n2, set_position=True): +def CompleteBipartiteGraph(p, q, set_position=True): r""" - Return a Complete Bipartite Graph on `n1 + n2` vertices. + Return a Complete Bipartite Graph on `p + q` vertices. A Complete Bipartite Graph is a graph with its vertices partitioned into two - groups, `V_1 = \{0,...,n1-1\}` and `V_2 = \{n1,...,n1+n2-1\}`. Each `u \in + groups, `V_1 = \{0,...,p-1\}` and `V_2 = \{p,...,p+q-1\}`. Each `u \in V_1` is connected to every `v \in V_2`. INPUT: - - ``n1, n2`` -- number of vertices in each side + - ``p,q`` -- number of vertices in each side - ``set_position`` -- boolean (default ``True``); if set to ``True``, we - assign positions to the vertices so that the set of cardinality `n1` is - on the line `y=1` and the set of cardinality `n2` is on the line `y=0`. + assign positions to the vertices so that the set of cardinality `p` is + on the line `y=1` and the set of cardinality `q` is on the line `y=0`. PLOTTING: Upon construction, the position dictionary is filled to override the spring-layout algorithm. By convention, each complete bipartite graph - will be displayed with the first `n1` nodes on the top row (at `y=1`) from - left to right. The remaining `n2` nodes appear at `y=0`, also from left to + will be displayed with the first `p` nodes on the top row (at `y=1`) from + left to right. The remaining `q` nodes appear at `y=0`, also from left to right. The shorter row (partition with fewer nodes) is stretched to the same length as the longer row, unless the shorter row has 1 node; in which case - it is centered. The `x` values in the plot are in domain `[0, \max(n1, - n2)]`. + it is centered. The `x` values in the plot are in domain `[0, \max(p, q)]`. In the Complete Bipartite graph, there is a visual difference in using the spring-layout algorithm vs. the position dictionary used in this constructor. The position dictionary flattens the graph and separates the partitioned nodes, making it clear which nodes an edge is connected to. The Complete Bipartite graph plotted with the spring-layout algorithm tends to - center the nodes in n1 (see spring_med in examples below), thus overlapping - its nodes and edges, making it typically hard to decipher. + center the nodes in `p` (see ``spring_med`` in examples below), thus + overlapping its nodes and edges, making it typically hard to decipher. Filling the position dictionary in advance adds `O(n)` to the constructor. Feel free to race the constructors below in the examples section. The much @@ -447,8 +444,8 @@ def CompleteBipartiteGraph(n1, n2, set_position=True): Notice here how the spring-layout tends to center the nodes of `n1`:: - sage: spring_med.show() # long time - sage: posdict_med.show() # long time + sage: spring_med.show() # long time + sage: posdict_med.show() # long time View many complete bipartite graphs with a Sage Graphics Array, with this constructor (i.e., the position dictionary filled):: @@ -464,7 +461,7 @@ def CompleteBipartiteGraph(n1, n2, set_position=True): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time We compare to plotting with the spring-layout algorithm:: @@ -480,7 +477,7 @@ def CompleteBipartiteGraph(n1, n2, set_position=True): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time :trac:`12155`:: @@ -494,42 +491,52 @@ def CompleteBipartiteGraph(n1, n2, set_position=True): sage: graphs.CompleteBipartiteGraph(-1,1) Traceback (most recent call last): ... - ValueError: the arguments n1(=-1) and n2(=1) must be positive integers + ValueError: the arguments p(=-1) and q(=1) must be positive integers sage: graphs.CompleteBipartiteGraph(1,-1) Traceback (most recent call last): ... - ValueError: the arguments n1(=1) and n2(=-1) must be positive integers + ValueError: the arguments p(=1) and q(=-1) must be positive integers """ - if n1 < 0 or n2 < 0: - raise ValueError('the arguments n1(={}) and n2(={}) must be positive integers'.format(n1,n2)) + if p < 0 or q < 0: + raise ValueError('the arguments p(={}) and q(={}) must be positive integers'.format(p, q)) - G = Graph(n1+n2, name="Complete bipartite graph of order {}+{}".format(n1, n2)) - G.add_edges((i,j) for i in range(n1) for j in range(n1,n1+n2)) + G = Graph(p + q, name="Complete bipartite graph of order {}+{}".format(p, q)) + G.add_edges((i, j) for i in range(p) for j in range(p, p + q)) # We now assign positions to vertices: - # - vertices 0,..,n1-1 are placed on the line (0, 1) to (max(n1, n2), 1) - # - vertices n1,..,n1+n2-1 are placed on the line (0, 0) to (max(n1, n2), 0) - # If n1 (or n2) is 1, the vertex is centered in the line. + # - vertices 0,..,p-1 are placed on the line (0, 1) to (max(p, q), 1) + # - vertices p,..,p+q-1 are placed on the line (0, 0) to (max(p, q), 0) + # If p (or q) is 1, the vertex is centered in the line. if set_position: - nmax = max(n1, n2) - G._line_embedding(list(range(n1)), first=(0, 1), last=(nmax, 1)) - G._line_embedding(list(range(n1, n1+n2)), first=(0, 0), last=(nmax, 0)) + nmax = max(p, q) + G._line_embedding(list(range(p)), first=(0, 1), last=(nmax, 1)) + G._line_embedding(list(range(p, p + q)), first=(0, 0), last=(nmax, 0)) return G def CompleteMultipartiteGraph(l): r""" - Returns a complete multipartite graph. + Return a complete multipartite graph. INPUT: - - ``l`` -- a list of integers : the respective sizes - of the components. + - ``l`` -- a list of integers; the respective sizes of the components + + PLOTTING: Produce a layout of the vertices so that vertices in the same + vertex set are adjacent and clearly separated from vertices in other vertex + sets. + + This is done by calculating the vertices of an `r`-gon then calculating the + slope between adjacent vertices. We then 'walk' around the `r`-gon placing + graph vertices in regular intervals between adjacent vertices of the + `r`-gon. + + Makes a nicely organized graph like in this picture: + https://commons.wikimedia.org/wiki/File:Turan_13-4.svg EXAMPLES: - A complete tripartite graph with sets of sizes - `5, 6, 8`:: + A complete tripartite graph with sets of sizes `5, 6, 8`:: sage: g = graphs.CompleteMultipartiteGraph([5, 6, 8]); g Multipartite Graph with set sizes [5, 6, 8]: Graph on 19 vertices @@ -539,73 +546,66 @@ def CompleteMultipartiteGraph(l): sage: g.chromatic_number() 3 """ - r = len(l) #getting the number of partitions - positions = {} - - if r > 2: #position code gives bad results on bipartite or isolated graphs - - ''' - Produce a layout of the vertices so that vertices in the same - vertex set are adjacent and clearly separated from vertices in other - vertex sets. - - This is done by calculating the vertices of an r-gon then - calculating the slope between adjacent vertices. We then 'walk' - around the r-gon placing graph vertices in regular intervals between - adjacent vertices of the r-gon. - - Makes a nicely organized graph like in this picture: - https://commons.wikimedia.org/wiki/File:Turan_13-4.svg - ''' - - points = [[cos(2*pi*i/r),sin(2*pi*i/r)] for i in range(r)] - slopes = [[(points[(i+1)%r][0]-points[i%r][0]), - (points[(i+1)%r][1]-points[i%r][1])] for i in range(r)] + r = len(l) # getting the number of partitions + name = "Multipartite Graph with set sizes {}".format(l) + + if not r: + g = Graph() + elif r == 1: + g = Graph(l[0]) + g._line_embedding(range(l[0]), first=(0, 0), last=(l[0], 0)) + elif r == 2: + g = CompleteBipartiteGraph(l[0], l[1]) + g.name(name) + else: + # This position code gives bad results on bipartite or isolated graphs + points = [(cos(2 * pi * i / r), sin(2 * pi * i / r)) for i in range(r)] + slopes = [(points[(i + 1) % r][0] - points[i % r][0], + points[(i + 1) % r][1] - points[i % r][1]) for i in range(r)] counter = 0 - - for i in range(len(l)): - vertex_set_size = l[i]+1 - for j in range(1,vertex_set_size): - x = points[i][0]+slopes[i][0]*j/(vertex_set_size) - y = points[i][1]+slopes[i][1]*j/(vertex_set_size) - positions[counter] = (x,y) + positions = {} + for i in range(r): + vertex_set_size = l[i] + 1 + for j in range(1, vertex_set_size): + x = points[i][0] + slopes[i][0] * j / vertex_set_size + y = points[i][1] + slopes[i][1] * j / vertex_set_size + positions[counter] = (x, y) counter += 1 - g = Graph() - for i in l: - g = g + CompleteGraph(i) + g = Graph(sum(l)) + s = 0 + for i in l: + g.add_clique(range(s, s + i)) + s += i - g = g.complement() - g.set_pos(positions) - g.name("Multipartite Graph with set sizes "+str(l)) + g = g.complement() + g.set_pos(positions) + g.name(name) return g - def DiamondGraph(): """ - Returns a diamond graph with 4 nodes. + Return a diamond graph with 4 nodes. - A diamond graph is a square with one pair of diagonal nodes - connected. + A diamond graph is a square with one pair of diagonal nodes connected. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the diamond - graph is drawn as a diamond, with the first node on top, second on - the left, third on the right, and fourth on the bottom; with the - second and third node connected. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the diamond graph is drawn as a + diamond, with the first node on top, second on the left, third on the right, + and fourth on the bottom; with the second and third node connected. - EXAMPLES: Construct and show a diamond graph + EXAMPLES: - :: + Construct and show a diamond graph:: sage: g = graphs.DiamondGraph() - sage: g.show() # long time + sage: g.show() # long time """ pos_dict = {0:(0,1),1:(-1,0),2:(1,0),3:(0,-1)} edges = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)] - return graph.Graph(edges, pos=pos_dict, name="Diamond Graph") + return Graph(edges, pos=pos_dict, name="Diamond Graph") def GemGraph(): """ @@ -613,20 +613,20 @@ def GemGraph(): A gem graph is a fan graph (4,1). - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the gem - graph is drawn as a gem, with the sharp part on the bottom. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the gem graph is drawn as a gem, + with the sharp part on the bottom. EXAMPLES: Construct and show a gem graph:: sage: g = graphs.GemGraph() - sage: g.show() # long time + sage: g.show() # long time """ - pos_dict = {0:(0.5,0),1:(0,0.75),2:(0.25,1),3:(0.75,1),4:(1,0.75)} + pos_dict = {0: (0.5, 0), 1: (0, 0.75), 2: (0.25, 1), 3: (0.75, 1), 4: (1, 0.75)} edges = [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (3, 4)] - return graph.Graph(edges, pos=pos_dict, name="Gem Graph") + return Graph(edges, pos=pos_dict, name="Gem Graph") def ForkGraph(): """ @@ -634,43 +634,43 @@ def ForkGraph(): A fork graph, sometimes also called chair graph, is 5 vertex tree. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the fork - graph is drawn as a fork, with the sharp part on the bottom. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the fork graph is drawn as a + fork, with the sharp part on the bottom. EXAMPLES: Construct and show a fork graph:: sage: g = graphs.ForkGraph() - sage: g.show() # long time + sage: g.show() # long time """ - pos_dict = {0:(0,0),1:(1,0),2:(0,1),3:(1,1),4:(0,2)} + pos_dict = {0: (0, 0), 1: (1, 0), 2: (0, 1), 3: (1, 1), 4: (0, 2)} edges = [(0, 2), (2, 3), (3, 1), (2, 4)] - return graph.Graph(edges, pos=pos_dict, name="Fork Graph") + return Graph(edges, pos=pos_dict, name="Fork Graph") def DartGraph(): """ Return a dart graph with 5 nodes. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the dart - graph is drawn as a dart, with the sharp part on the bottom. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the dart graph is drawn as a + dart, with the sharp part on the bottom. EXAMPLES: Construct and show a dart graph:: sage: g = graphs.DartGraph() - sage: g.show() # long time + sage: g.show() # long time """ - pos_dict = {0:(0,1),1:(-1,0),2:(1,0),3:(0,-1),4:(0,0)} + pos_dict = {0: (0, 1), 1: (-1, 0), 2: (1, 0), 3: (0, -1), 4: (0, 0)} edges = [(0, 1), (0, 2), (1, 4), (2, 4), (0, 4), (3, 4)] - return graph.Graph(edges, pos=pos_dict, name="Dart Graph") + return Graph(edges, pos=pos_dict, name="Dart Graph") def EmptyGraph(): """ - Returns an empty graph (0 nodes and 0 edges). + Return an empty graph (0 nodes and 0 edges). This is useful for constructing graphs by adding edges and vertices individually or in a loop. @@ -679,39 +679,41 @@ def EmptyGraph(): spring-layout algorithm, unless a position dictionary is specified. - EXAMPLES: Add one vertex to an empty graph and then show:: + EXAMPLES: + + Add one vertex to an empty graph and then show:: sage: empty1 = graphs.EmptyGraph() sage: empty1.add_vertex() 0 - sage: empty1.show() # long time + sage: empty1.show() # long time Use for loops to build a graph from an empty graph:: sage: empty2 = graphs.EmptyGraph() sage: for i in range(5): - ....: empty2.add_vertex() # add 5 nodes, labeled 0-4 + ....: empty2.add_vertex() # add 5 nodes, labeled 0-4 0 1 2 3 4 sage: for i in range(3): - ....: empty2.add_edge(i,i+1) # add edges {[0:1],[1:2],[2:3]} + ....: empty2.add_edge(i,i+1) # add edges {[0:1],[1:2],[2:3]} sage: for i in range(1, 4): - ....: empty2.add_edge(4,i) # add edges {[1:4],[2:4],[3:4]} - sage: empty2.show() # long time + ....: empty2.add_edge(4,i) # add edges {[1:4],[2:4],[3:4]} + sage: empty2.show() # long time """ - return graph.Graph(sparse=True) + return Graph(sparse=True) -def ToroidalGrid2dGraph(n1, n2): +def ToroidalGrid2dGraph(p, q): r""" - Returns a toroidal 2-dimensional grid graph with `n_1n_2` nodes (`n_1` - rows and `n_2` columns). + Return a toroidal 2-dimensional grid graph with `p \times q` nodes (`p` rows + and `q` columns). - The toroidal 2-dimensional grid with parameters `n_1,n_2` is the - 2-dimensional grid graph with identical parameters to which are added - the edges `((i,0),(i,n_2-1))` and `((0,i),(n_1-1,i))`. + The toroidal 2-dimensional grid with parameters `p,q` is the 2-dimensional + grid graph with identical parameters to which are added the edges + `((i, 0), (i, q - 1))` and `((0, i), (p - 1, i))`. EXAMPLES: @@ -727,40 +729,40 @@ def ToroidalGrid2dGraph(n1, n2): sage: tgrid.is_regular() True """ + g = Grid2dGraph(p, q, set_positions=False) - g = Grid2dGraph(n1,n2, set_positions=False) - - g.add_edges([((i,0),(i,n2-1)) for i in range(n1)] + [((0,i),(n1-1,i)) for i in range(n2)]) + g.add_edges([((i, 0), (i, q - 1)) for i in range(p)]) + g.add_edges([((0, i), (p - 1, i)) for i in range(q)]) - g.name("Toroidal 2D Grid Graph with parameters "+str(n1)+","+str(n2)) + g.name("Toroidal 2D Grid Graph with parameters {},{}".format(p, q)) d = g.get_pos() - n1 += 0. - n2 += 0. - uf = (n1/2)*(n1/2) - vf = (n2/2)*(n2/2) - for u,v in d: - x,y = d[(u,v)] - x += 0.25*(1.0+u*(u-n1+1)/uf) - y += 0.25*(1+v*(v-n2+1)/vf) - d[(u,v)] = (x,y) + p += 0. + q += 0. + uf = (p / 2) * (p / 2) + vf = (q / 2) * (q / 2) + for u, v in d: + x, y = d[u, v] + x += 0.25 * (1.0 + u * (u - p + 1) / uf) + y += 0.25 * (1 + v * (v - q + 1) / vf) + d[u, v] = (x, y) return g -def Toroidal6RegularGrid2dGraph(n1, n2): +def Toroidal6RegularGrid2dGraph(p, q): r""" - Returns a toroidal 6-regular grid. + Return a toroidal 6-regular grid. - The toroidal 6-regular grid is a 6-regular graph on `n_1\times n_2` - vertices and its elements have coordinates `(i,j)` for `i \in \{0...i-1\}` - and `j \in \{0...j-1\}`. + The toroidal 6-regular grid is a 6-regular graph on `p \times q` vertices + and its elements have coordinates `(i, j)` for `i \in \{0...p-1\}` and + `j \in \{0...q-1\}`. - Its edges are those of the :meth:`ToroidalGrid2dGraph`, to which are - added the edges between `(i,j)` and `((i+1)\%n_1, (j+1)\%n_2)`. + Its edges are those of the :meth:`ToroidalGrid2dGraph`, to which are added + the edges between `(i, j)` and `((i + 1) \% p, (j + 1) \% q)`. INPUT: - - ``n1, n2`` (integers) -- see above. + - ``p, q`` -- integers (see above) EXAMPLES: @@ -785,52 +787,51 @@ def Toroidal6RegularGrid2dGraph(n1, n2): sage: graphs.Toroidal6RegularGrid2dGraph(5,2) Traceback (most recent call last): ... - ValueError: Parameters n1 and n2 must be integers larger than 3 ! + ValueError: parameters p and q must be integers larger than 3 sage: graphs.Toroidal6RegularGrid2dGraph(2,0) Traceback (most recent call last): ... - ValueError: Parameters n1 and n2 must be integers larger than 3 ! + ValueError: parameters p and q must be integers larger than 3 """ - if n1 <= 3 or n2 <= 3: - raise ValueError("Parameters n1 and n2 must be integers larger than 3 !") + if p <= 3 or q <= 3: + raise ValueError("parameters p and q must be integers larger than 3") - g = ToroidalGrid2dGraph(n1,n2) - for u,v in g: - g.add_edge((u,v),((u+1)%n1,(v+1)%n2)) + g = ToroidalGrid2dGraph(p, q) + for u, v in g: + g.add_edge((u, v), ((u + 1) % p, (v + 1) % q)) - g.name("Toroidal Hexagonal Grid graph on "+str(n1)+"x"+str(n2)+" elements") + g.name("Toroidal Hexagonal Grid graph on {}x{} elements".format(p, q)) return g -def Grid2dGraph(n1, n2, set_positions=True): +def Grid2dGraph(p, q, set_positions=True): r""" - Returns a `2`-dimensional grid graph with `n_1n_2` nodes (`n_1` rows and - `n_2` columns). + Return a `2`-dimensional grid graph with `p \times q` nodes (`p` rows and + `q` columns). A 2d grid graph resembles a `2` dimensional grid. All inner nodes are - connected to their `4` neighbors. Outer (non-corner) nodes are - connected to their `3` neighbors. Corner nodes are connected to their - 2 neighbors. + connected to their `4` neighbors. Outer (non-corner) nodes are connected to + their `3` neighbors. Corner nodes are connected to their 2 neighbors. INPUT: - - ``n1`` and ``n2`` -- two positive integers + - ``p`` and ``q`` -- two positive integers - - ``set_positions`` -- (default: ``True``) boolean use to prevent setting - the position of the nodes. + - ``set_positions`` -- boolean (default: ``True``); whether to set the + position of the nodes - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, nodes are - labelled in (row, column) pairs with `(0, 0)` in the top left corner. - Edges will always be horizontal and vertical - another advantage of - filling the position dictionary. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, nodes are labelled in (row, + column) pairs with `(0, 0)` in the top left corner. Edges will always be + horizontal and vertical - another advantage of filling the position + dictionary. - EXAMPLES: Construct and show a grid 2d graph Rows = `5`, Columns = `7` + EXAMPLES: - :: + Construct and show a grid 2d graph Rows = `5`, Columns = `7`:: sage: g = graphs.Grid2dGraph(5,7) - sage: g.show() # long time + sage: g.show() # long time TESTS: @@ -839,11 +840,11 @@ def Grid2dGraph(n1, n2, set_positions=True): sage: graphs.Grid2dGraph(5,0) Traceback (most recent call last): ... - ValueError: Parameters n1 and n2 must be positive integers ! + ValueError: parameters p and q must be positive integers sage: graphs.Grid2dGraph(-1,0) Traceback (most recent call last): ... - ValueError: Parameters n1 and n2 must be positive integers ! + ValueError: parameters p and q must be positive integers The graph name contains the dimension:: @@ -851,38 +852,34 @@ def Grid2dGraph(n1, n2, set_positions=True): sage: g.name() '2D Grid Graph for [5, 7]' """ - - if n1 <= 0 or n2 <= 0: - raise ValueError("Parameters n1 and n2 must be positive integers !") + if p <= 0 or q <= 0: + raise ValueError("parameters p and q must be positive integers") pos_dict = {} if set_positions: - for i in range(n1): + for i in range(p): y = -i - for j in range(n2): + for j in range(q): x = j pos_dict[i, j] = (x, y) - G = graph.Graph(pos=pos_dict, name="2D Grid Graph for [{}, {}]".format(n1, n2)) - G.add_vertices( (i,j) for i in range(n1) for j in range(n2) ) - G.add_edges( ((i,j),(i+1,j)) for i in range(n1-1) for j in range(n2) ) - G.add_edges( ((i,j),(i,j+1)) for i in range(n1) for j in range(n2-1) ) + G = Graph(pos=pos_dict, name="2D Grid Graph for [{}, {}]".format(p, q)) + G.add_vertices((i, j) for i in range(p) for j in range(q)) + G.add_edges(((i, j), (i + 1, j)) for i in range(p - 1) for j in range(q)) + G.add_edges(((i, j), (i, j + 1)) for i in range(p) for j in range(q - 1)) return G def GridGraph(dim_list): - """ - Returns an n-dimensional grid graph. + r""" + Return an `n`-dimensional grid graph. INPUT: + - ``dim_list`` -- a list of integers representing the number of nodes to + extend in each dimension - - ``dim_list`` - a list of integers representing the - number of nodes to extend in each dimension. - - - PLOTTING: When plotting, this graph will use the default - spring-layout algorithm, unless a position dictionary is - specified. + PLOTTING: When plotting, this graph will use the default spring-layout + algorithm, unless a position dictionary is specified. EXAMPLES:: @@ -951,11 +948,11 @@ def GridGraph(dim_list): sage: g = graphs.GridGraph([2,-1,3]) Traceback (most recent call last): ... - ValueError: All dimensions must be positive integers ! + ValueError: all dimensions must be positive integers """ dim = [int(a) for a in dim_list] if any(a <= 0 for a in dim): - raise ValueError("All dimensions must be positive integers !") + raise ValueError("all dimensions must be positive integers") g = Graph() n_dim = len(dim) @@ -968,98 +965,91 @@ def GridGraph(dim_list): elif n_dim > 2: # Vertices are tuples of dimension n_dim, and the graph contains at # least vertex (0, 0, ..., 0) - g.add_vertex(tuple([0]*n_dim)) + g.add_vertex(tuple([0] * n_dim)) import itertools for u in itertools.product(*[range(d) for d in dim]): for i in range(n_dim): if u[i] + 1 < dim[i]: v = list(u) - v[i] = u[i]+1 + v[i] = u[i] + 1 g.add_edge(u, tuple(v)) g.name("Grid Graph for {}".format(dim)) return g - def HouseGraph(): """ - Returns a house graph with 5 nodes. + Return a house graph with 5 nodes. A house graph is named for its shape. It is a triangle (roof) over a square (walls). - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the house - graph is drawn with the first node in the lower-left corner of the - house, the second in the lower-right corner of the house. The third - node is in the upper-left corner connecting the roof to the wall, - and the fourth is in the upper-right corner connecting the roof to - the wall. The fifth node is the top of the roof, connected only to - the third and fourth. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the house graph is drawn with + the first node in the lower-left corner of the house, the second in the + lower-right corner of the house. The third node is in the upper-left corner + connecting the roof to the wall, and the fourth is in the upper-right corner + connecting the roof to the wall. The fifth node is the top of the roof, + connected only to the third and fourth. - EXAMPLES: Construct and show a house graph + EXAMPLES: - :: + Construct and show a house graph:: sage: g = graphs.HouseGraph() - sage: g.show() # long time + sage: g.show() # long time """ - pos_dict = {0:(-1,0),1:(1,0),2:(-1,1),3:(1,1),4:(0,2)} + pos_dict = {0: (-1, 0), 1: (1, 0), 2: (-1, 1), 3: (1, 1), 4: (0, 2)} edges = [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)] - return graph.Graph(edges, pos=pos_dict, name="House Graph") + return Graph(edges, pos=pos_dict, name="House Graph") def HouseXGraph(): """ - Returns a house X graph with 5 nodes. + Return a house X graph with 5 nodes. - A house X graph is a house graph with two additional edges. The - upper-right corner is connected to the lower-left. And the - upper-left corner is connected to the lower-right. + A house X graph is a house graph with two additional edges. The upper-right + corner is connected to the lower-left. And the upper-left corner is + connected to the lower-right. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the house X - graph is drawn with the first node in the lower-left corner of the - house, the second in the lower-right corner of the house. The third - node is in the upper-left corner connecting the roof to the wall, - and the fourth is in the upper-right corner connecting the roof to - the wall. The fifth node is the top of the roof, connected only to - the third and fourth. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the house X graph is drawn with + the first node in the lower-left corner of the house, the second in the + lower-right corner of the house. The third node is in the upper-left corner + connecting the roof to the wall, and the fourth is in the upper-right corner + connecting the roof to the wall. The fifth node is the top of the roof, + connected only to the third and fourth. - EXAMPLES: Construct and show a house X graph + EXAMPLES: - :: + Construct and show a house X graph:: sage: g = graphs.HouseXGraph() - sage: g.show() # long time + sage: g.show() # long time """ - pos_dict = {0:(-1,0),1:(1,0),2:(-1,1),3:(1,1),4:(0,2)} + pos_dict = {0: (-1, 0), 1: (1, 0), 2: (-1, 1), 3: (1, 1), 4: (0, 2)} edges = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)] - return graph.Graph(edges, pos=pos_dict, name="House Graph") - + return Graph(edges, pos=pos_dict, name="House Graph") def LadderGraph(n): r""" - Returns a ladder graph with 2\*n nodes. + Return a ladder graph with `2 * n` nodes. - A ladder graph is a basic structure that is typically displayed as - a ladder, i.e.: two parallel path graphs connected at each - corresponding node pair. + A ladder graph is a basic structure that is typically displayed as a ladder, + i.e.: two parallel path graphs connected at each corresponding node pair. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, each ladder - graph will be displayed horizontally, with the first n nodes - displayed left to right on the top horizontal line. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, each ladder graph will be + displayed horizontally, with the first n nodes displayed left to right on + the top horizontal line. - EXAMPLES: Construct and show a ladder graph with 14 nodes + EXAMPLES: - :: + Construct and show a ladder graph with 14 nodes:: sage: g = graphs.LadderGraph(7) - sage: g.show() # long time + sage: g.show() # long time - Create several ladder graphs in a Sage graphics array - - :: + Create several ladder graphs in a Sage graphics array:: sage: g = [] sage: j = [] @@ -1072,37 +1062,35 @@ def LadderGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time """ pos_dict = {} for i in range(n): - pos_dict[i] = (i,1) - for i in range(n,2*n): + pos_dict[i] = (i, 1) + for i in range(n, 2 * n): x = i - n - pos_dict[i] = (x,0) - G = Graph(pos=pos_dict, name="Ladder graph") - G.add_vertices( range(2*n) ) + pos_dict[i] = (x, 0) + G = Graph(2 * n, pos=pos_dict, name="Ladder graph") G.add_path(list(range(n))) G.add_path(list(range(n, 2 * n))) - G.add_edges( (i,i+n) for i in range(n) ) + G.add_edges((i, i + n) for i in range(n)) return G - def PathGraph(n, pos=None): r""" Return a path graph with `n` nodes. A path graph is a graph where all inner nodes are connected to their two - neighbors and the two end-nodes are connected to their one inner - neighbors (i.e.: a cycle graph without the first and last node connected). + neighbors and the two end-nodes are connected to their one inner neighbors + (i.e.: a cycle graph without the first and last node connected). INPUT: - ``n`` -- number of nodes of the path graph - - ``pos`` (default: ``None``) -- a string which is either 'circle' or 'line' - (otherwise the default is used) indicating which embedding algorithm to - use. See the plotting section below for more detail. + - ``pos`` -- string (default: ``None``); indicates the embedding to use + between 'circle', 'line' or the default algorithm. See the plotting + section below for more detail. PLOTTING: Upon construction, the position dictionary is filled to override the spring-layout algorithm. By convention, the graph may be drawn in one of @@ -1120,28 +1108,28 @@ def PathGraph(n, pos=None): :: sage: p = graphs.PathGraph(10) - sage: p.show() # long time + sage: p.show() # long time 'circle': `10 < n < 41` :: sage: q = graphs.PathGraph(25) - sage: q.show() # long time + sage: q.show() # long time 'line': `n \geq 41` :: sage: r = graphs.PathGraph(55) - sage: r.show() # long time + sage: r.show() # long time Override the default drawing:: sage: s = graphs.PathGraph(5,'circle') - sage: s.show() # long time + sage: s.show() # long time """ - G = graph.Graph(n, name="Path graph") + G = Graph(n, name="Path graph") pos_dict = {} @@ -1158,45 +1146,45 @@ def PathGraph(n, pos=None): # Draw 'circle' if circle: if n == 1: - G.set_pos({0:(0, 0)}) + G.set_pos({0: (0, 0)}) else: G._circle_embedding(list(range(n)), angle=pi/2) # Draw 'line' else: - counter = 0 # node index - rem = n%10 # remainder to appear on last row - rows = n//10 # number of rows (not counting last row) - lr = True # left to right + counter = 0 # node index + rem = n % 10 # remainder to appear on last row + rows = n // 10 # number of rows (not counting last row) + lr = True # left to right - for i in range(rows): # note that rows doesn't include last row + for i in range(rows): # note that rows doesn't include last row y = -i for j in range(10): if lr: x = j else: x = 9 - j - pos_dict[counter] = (x,y) + pos_dict[counter] = (x, y) counter += 1 if lr: lr = False else: lr = True y = -rows - for j in range(rem): # last row + for j in range(rem): # last row if lr: x = j else: x = 9 - j - pos_dict[counter] = (x,y) + pos_dict[counter] = (x, y) counter += 1 G.set_pos(pos_dict) - G.add_edges( (i,i+1) for i in range(n-1) ) + G.add_edges((i, i + 1) for i in range(n - 1)) return G def StarGraph(n): r""" - Return a star graph with `n+1` nodes. + Return a star graph with `n + 1` nodes. A Star graph is a basic structure where one node is connected to all other nodes. @@ -1221,8 +1209,8 @@ def StarGraph(n): sage: n = networkx.star_graph(23) sage: spring23 = Graph(n) sage: posdict23 = graphs.StarGraph(23) - sage: spring23.show() # long time - sage: posdict23.show() # long time + sage: spring23.show() # long time + sage: posdict23.show() # long time View many star graphs as a Sage Graphics Array @@ -1241,7 +1229,7 @@ def StarGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time Compared to plotting with the spring-layout algorithm @@ -1259,9 +1247,9 @@ def StarGraph(n): ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) ....: j.append(n) sage: G = graphics_array(j) - sage: G.show() # long time + sage: G.show() # long time """ - G = Graph({0: list(range(1, n + 1))}, name="Star graph") - G.set_pos({0:(0, 0)}) + G = Graph({0: list(range(1, n + 1))}, name="Star graph", format="dict_of_lists") + G.set_pos({0: (0, 0)}) G._circle_embedding(list(range(1, n + 1)), angle=pi/2) return G diff --git a/src/sage/graphs/generators/chessboard.py b/src/sage/graphs/generators/chessboard.py index f20dba0a788..9a10f7e8ada 100644 --- a/src/sage/graphs/generators/chessboard.py +++ b/src/sage/graphs/generators/chessboard.py @@ -15,20 +15,23 @@ - David Coudert (2012) """ -################################################################################ +# **************************************************************************** # Copyright (C) 2012 David Coudert # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ -################################################################################ +# **************************************************************************** -def ChessboardGraphGenerator(dim_list, - rook = True, rook_radius = None, - bishop = True, bishop_radius = None, - knight = True, knight_x = 1, knight_y = 2, - relabel = False): +from itertools import product +from itertools import combinations +from sage.graphs.graph import Graph + +def ChessboardGraphGenerator(dim_list, rook=True, rook_radius=None, + bishop=True, bishop_radius=None, + knight=True, knight_x=1, knight_y=2, + relabel=False): r""" - Returns a Graph built on a `d`-dimensional chessboard with prescribed + Return a Graph built on a `d`-dimensional chessboard with prescribed dimensions and interconnections. This function allows to generate many kinds of graphs corresponding to legal @@ -38,33 +41,32 @@ def ChessboardGraphGenerator(dim_list, INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``rook`` -- (default: ``True``) boolean value indicating if the chess - piece is able to move as a rook, that is at any distance along a - dimension. + - ``rook`` -- boolean (default: ``True``); indicates whether the chess piece + is able to move as a rook, that is at any distance along a dimension - - ``rook_radius`` -- (default: None) integer value restricting the rook-like - movements to distance at most `rook_radius`. + - ``rook_radius`` -- integer (default: ``None``); restriction on the + rook-like movements to distance at most ``rook_radius`` - - ``bishop`` -- (default: ``True``) boolean value indicating if the chess - piece is able to move like a bishop, that is along diagonals. + - ``bishop`` -- boolean (default: ``True``); indicates whether the chess + piece is able to move like a bishop, that is along diagonals - - ``bishop_radius`` -- (default: None) integer value restricting the - bishop-like movements to distance at most `bishop_radius`. + - ``bishop_radius`` -- integer (default: ``None``); restriction on the + bishop-like movements to distance at most ``bishop_radius`` - - ``knight`` -- (default: ``True``) boolean value indicating if the chess - piece is able to move like a knight. + - ``knight`` -- boolean (default: ``True``); indicating whether the chess + piece is able to move like a knight - - ``knight_x`` -- (default: 1) integer indicating the number on steps the - chess piece moves in one dimension when moving like a knight. + - ``knight_x`` -- integer (default: ``1``); indicates the number on steps + the chess piece moves in one dimension when moving like a knight - - ``knight_y`` -- (default: 2) integer indicating the number on steps the - chess piece moves in the second dimension when moving like a knight. + - ``knight_y`` -- integer (default: ``2``); indicates the number on steps + the chess piece moves in the second dimension when moving like a knight - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers OUTPUT: @@ -85,8 +87,8 @@ def ChessboardGraphGenerator(dim_list, A Rook's Graph in 2 dimensions is isomorphic to the Cartesian product of 2 complete graphs:: - sage: G, _ = graphs.ChessboardGraphGenerator( [3,4], rook=True, rook_radius=None, bishop=False, knight=False ) - sage: H = ( graphs.CompleteGraph(3) ).cartesian_product( graphs.CompleteGraph(4) ) + sage: G, _ = graphs.ChessboardGraphGenerator([3,4], rook=True, rook_radius=None, bishop=False, knight=False) + sage: H = (graphs.CompleteGraph(3)).cartesian_product(graphs.CompleteGraph(4)) sage: G.is_isomorphic(H) True @@ -94,45 +96,45 @@ def ChessboardGraphGenerator(dim_list, Giving dimensions less than 2:: - sage: graphs.ChessboardGraphGenerator( [0, 2] ) + sage: graphs.ChessboardGraphGenerator([0, 2]) Traceback (most recent call last): ... - ValueError: The dimensions must be positive integers larger than 1. + ValueError: the dimensions must be positive integers larger than 1 Giving non integer dimensions:: - sage: graphs.ChessboardGraphGenerator( [4.5, 2] ) + sage: graphs.ChessboardGraphGenerator([4.5, 2]) Traceback (most recent call last): ... - ValueError: The dimensions must be positive integers larger than 1. + ValueError: the dimensions must be positive integers larger than 1 Giving too few dimensions:: - sage: graphs.ChessboardGraphGenerator( [2] ) + sage: graphs.ChessboardGraphGenerator([2]) Traceback (most recent call last): ... - ValueError: The chessboard must have at least 2 dimensions. + ValueError: the chessboard must have at least 2 dimensions Giving a non-iterable object as first parameter:: - sage: graphs.ChessboardGraphGenerator( 2, 3 ) + sage: graphs.ChessboardGraphGenerator(2, 3) Traceback (most recent call last): ... - TypeError: The first parameter must be an iterable object. + TypeError: the first parameter must be an iterable object Giving too small rook radius:: - sage: graphs.ChessboardGraphGenerator( [2, 3], rook=True, rook_radius=0 ) + sage: graphs.ChessboardGraphGenerator([2, 3], rook=True, rook_radius=0) Traceback (most recent call last): ... - ValueError: The rook_radius must be either None or have an integer value >= 1. + ValueError: the rook_radius must be either None or have an integer value >= 1 Giving wrong values for knights movements:: - sage: graphs.ChessboardGraphGenerator( [2, 3], rook=False, bishop=False, knight=True, knight_x=1, knight_y=-1 ) + sage: graphs.ChessboardGraphGenerator([2, 3], rook=False, bishop=False, knight=True, knight_x=1, knight_y=-1) Traceback (most recent call last): ... - ValueError: The knight_x and knight_y values must be integers of value >= 1. + ValueError: the knight_x and knight_y values must be integers of value >= 1 """ from sage.rings.integer_ring import ZZ @@ -141,11 +143,11 @@ def ChessboardGraphGenerator(dim_list, dim = list(dim_list) nb_dim = len(dim) except TypeError: - raise TypeError('The first parameter must be an iterable object.') + raise TypeError('the first parameter must be an iterable object') if nb_dim < 2: - raise ValueError('The chessboard must have at least 2 dimensions.') + raise ValueError('the chessboard must have at least 2 dimensions') if any(a not in ZZ or a < 1 for a in dim): - raise ValueError('The dimensions must be positive integers larger than 1.') + raise ValueError('the dimensions must be positive integers larger than 1') dimstr = str(tuple(dim)) # We check the radius toward neighbors @@ -153,23 +155,18 @@ def ChessboardGraphGenerator(dim_list, if rook_radius is None: rook_radius = max(dim) elif not rook_radius in ZZ or rook_radius < 1: - raise ValueError('The rook_radius must be either None or have an integer value >= 1.') + raise ValueError('the rook_radius must be either None or have an integer value >= 1') if bishop: if bishop_radius is None: bishop_radius = max(dim) elif not bishop_radius in ZZ or bishop_radius < 1: - raise ValueError('The bishop_radius must be either None or have an integer value >= 1.') + raise ValueError('the bishop_radius must be either None or have an integer value >= 1') if knight and ( not knight_x in ZZ or not knight_y in ZZ or knight_x < 1 or knight_y < 1 ): - raise ValueError('The knight_x and knight_y values must be integers of value >= 1.') + raise ValueError('the knight_x and knight_y values must be integers of value >= 1') # We build the set of vertices of the d-dimensional chessboard - from itertools import product V = [list(x) for x in list(product(*[range(_) for _ in dim]))] - from sage.combinat.combination import Combinations - combin = Combinations(range(nb_dim), 2) - - from sage.graphs.graph import Graph G = Graph() for u in V: uu = tuple(u) @@ -179,13 +176,13 @@ def ChessboardGraphGenerator(dim_list, # We add edges to vertices we can reach when moving in one dimension for d in range(nb_dim): v = u[:] - for k in range(v[d]+1, min(dim[d],v[d]+1+rook_radius)): + for k in range(v[d] + 1, min(dim[d], v[d] + 1 + rook_radius)): v[d] = k - G.add_edge( uu, tuple(v) ) + G.add_edge(uu, tuple(v)) if bishop or knight: # We add edges to vertices we can reach when moving in two dimensions - for dx,dy in combin: + for dx, dy in combinations(range(nb_dim), 2): n = dim[dx] m = dim[dy] v = u[:] @@ -194,47 +191,46 @@ def ChessboardGraphGenerator(dim_list, if bishop: # Diagonal - for k in range(1, min(n-i,m-j,bishop_radius+1)): - v[dx] = i+k - v[dy] = j+k - G.add_edge( uu, tuple(v) ) + for k in range(1, min(n - i, m - j, bishop_radius + 1)): + v[dx] = i + k + v[dy] = j + k + G.add_edge(uu, tuple(v)) # Anti-diagonal - for k in range(min(i, m-j-1, bishop_radius)): - v[dx] = i-k-1 - v[dy] = j+k+1 - G.add_edge( uu, tuple(v) ) + for k in range(min(i, m - j - 1, bishop_radius)): + v[dx] = i - k - 1 + v[dy] = j + k + 1 + G.add_edge(uu, tuple(v)) if knight: # Moving knight_x in one dimension and knight_y in another # dimension - if i+knight_y < n: - if j+knight_x < m: - v[dx] = i+knight_y - v[dy] = j+knight_x - G.add_edge( uu, tuple(v) ) - if j-knight_x >= 0: - v[dx] = i+knight_y - v[dy] = j-knight_x - G.add_edge( uu, tuple(v) ) - if j+knight_y < m: - if i+knight_x < n: - v[dx] = i+knight_x - v[dy] = j+knight_y - G.add_edge( uu, tuple(v) ) - if i-knight_x >= 0: - v[dx] = i-knight_x - v[dy] = j+knight_y - G.add_edge( uu, tuple(v) ) + if i + knight_y < n: + if j + knight_x < m: + v[dx] = i + knight_y + v[dy] = j + knight_x + G.add_edge(uu, tuple(v)) + if j - knight_x >= 0: + v[dx] = i + knight_y + v[dy] = j - knight_x + G.add_edge(uu, tuple(v)) + if j + knight_y < m: + if i + knight_x < n: + v[dx] = i + knight_x + v[dy] = j + knight_y + G.add_edge(uu, tuple(v)) + if i - knight_x >= 0: + v[dx] = i - knight_x + v[dy] = j + knight_y + G.add_edge(uu, tuple(v)) if relabel: - G.relabel( inplace=True ) + G.relabel(inplace=True) return G, dimstr - def QueenGraph(dim_list, radius=None, relabel=False): r""" - Returns the `d`-dimensional Queen Graph with prescribed dimensions. + Return the `d`-dimensional Queen Graph with prescribed dimensions. The 2-dimensional Queen Graph of parameters `n` and `m` is a graph with `nm` vertices in which each vertex represents a square in an `n \times m` @@ -250,36 +246,36 @@ def QueenGraph(dim_list, radius=None, relabel=False): INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``radius`` -- (default: ``None``) by setting the radius to a positive - integer, one may reduce the visibility of the queen to at most ``radius`` - steps. When radius is 1, the resulting graph is a King Graph. + - ``radius`` -- integer (default: ``None``); by setting the radius to a + positive integer, one may reduce the visibility of the queen to at most + ``radius`` steps. When radius is 1, the resulting graph is a King Graph. - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers EXAMPLES: The `(2,2)`-Queen Graph is isomorphic to the complete graph on 4 vertices:: - sage: G = graphs.QueenGraph( [2, 2] ) - sage: G.is_isomorphic( graphs.CompleteGraph(4) ) + sage: G = graphs.QueenGraph([2, 2]) + sage: G.is_isomorphic(graphs.CompleteGraph(4)) True The Queen Graph with radius 1 is isomorphic to the King Graph:: - sage: G = graphs.QueenGraph( [4, 5], radius=1 ) - sage: H = graphs.KingGraph( [5, 4] ) - sage: G.is_isomorphic( H ) + sage: G = graphs.QueenGraph([4, 5], radius=1) + sage: H = graphs.KingGraph([5, 4]) + sage: G.is_isomorphic(H) True Also True in higher dimensions:: - sage: G = graphs.QueenGraph( [3, 4, 5], radius=1 ) - sage: H = graphs.KingGraph( [5, 3, 4] ) - sage: G.is_isomorphic( H ) + sage: G = graphs.QueenGraph([3, 4, 5], radius=1) + sage: H = graphs.KingGraph([5, 3, 4]) + sage: G.is_isomorphic(H) True The Queen Graph can be obtained from the Rook Graph and the Bishop Graph:: @@ -291,7 +287,7 @@ def QueenGraph(dim_list, radius=None, relabel=False): ....: B = graphs.BishopGraph([d,d],radius=r) ....: H.add_edges(B.edges()) ....: if not G.is_isomorphic(H): - ....: print("that's not good!") + ....: print("that's not good!") """ G, dimstr = ChessboardGraphGenerator(dim_list, @@ -300,15 +296,15 @@ def QueenGraph(dim_list, radius=None, relabel=False): knight=False, relabel=relabel) if radius is None: - G.name(dimstr+"-Queen Graph") + G.name(dimstr + "-Queen Graph") else: - G.name(dimstr+"-Queen Graph with radius "+str(radius)) + G.name(dimstr + "-Queen Graph with radius {}".format(radius)) return G def KingGraph(dim_list, radius=None, relabel=False): r""" - Returns the `d`-dimensional King Graph with prescribed dimensions. + Return the `d`-dimensional King Graph with prescribed dimensions. The 2-dimensional King Graph of parameters `n` and `m` is a graph with `nm` vertices in which each vertex represents a square in an `n \times m` @@ -323,16 +319,16 @@ def KingGraph(dim_list, radius=None, relabel=False): INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``radius`` -- (default: ``None``) by setting the radius to a positive - integer, one may increase the power of the king to at least ``radius`` - steps. When the radius equals the higher size of the dimensions, the - resulting graph is a Queen Graph. + - ``radius`` -- integer (default: ``None``); by setting the radius to a + positive integer, one may increase the power of the king to at least + ``radius`` steps. When the radius equals the higher size of the + dimensions, the resulting graph is a Queen Graph. - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers EXAMPLES: @@ -356,21 +352,23 @@ def KingGraph(dim_list, radius=None, relabel=False): sage: G.is_isomorphic( H ) True """ + rook_radius = 1 if radius is None else radius + bishop_radius = 1 if radius is None else radius G, dimstr = ChessboardGraphGenerator(dim_list, - rook=True, rook_radius=(1 if radius is None else radius), - bishop=True, bishop_radius=(1 if radius is None else radius), + rook=True, rook_radius=rook_radius, + bishop=True, bishop_radius=bishop_radius, knight=False, relabel=relabel) if radius is None: - G.name(dimstr+"-King Graph") + G.name(dimstr + "-King Graph") else: - G.name(dimstr+"-King Graph with radius "+str(radius)) + G.name(dimstr + "-King Graph with radius {}".format(radius)) return G def KnightGraph(dim_list, one=1, two=2, relabel=False): r""" - Returns the d-dimensional Knight Graph with prescribed dimensions. + Return the d-dimensional Knight Graph with prescribed dimensions. The 2-dimensional Knight Graph of parameters `n` and `m` is a graph with `nm` vertices in which each vertex represents a square in an `n \times m` @@ -384,17 +382,17 @@ def KnightGraph(dim_list, one=1, two=2, relabel=False): INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``one`` -- (default: ``1``) integer indicating the number on steps in one - dimension. + - ``one`` -- integer (default: ``1``); indicates the number of steps in the + first dimension - - ``two`` -- (default: ``2``) integer indicating the number on steps in the - second dimension. + - ``two`` -- integer (default: ``2``); indicates the number of steps in the + second dimension - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers EXAMPLES: @@ -421,16 +419,16 @@ def KnightGraph(dim_list, one=1, two=2, relabel=False): rook=False, bishop=False, knight=True, knight_x=one, knight_y=two, relabel=relabel) - if one+two == 3: - G.name(dimstr+"-Knight Graph") + if one + two == 3: + G.name(dimstr + "-Knight Graph") else: - G.name(dimstr+"-Knight Graph with edges at distance ("+str(one)+", "+str(two)+")") + G.name(dimstr + "-Knight Graph with edges at distance ({}, {})".format(one, two)) return G def RookGraph(dim_list, radius=None, relabel=False): r""" - Returns the `d`-dimensional Rook's Graph with prescribed dimensions. + Return the `d`-dimensional Rook's Graph with prescribed dimensions. The 2-dimensional Rook's Graph of parameters `n` and `m` is a graph with `nm` vertices in which each vertex represents a square in an `n \times m` @@ -445,15 +443,16 @@ def RookGraph(dim_list, radius=None, relabel=False): INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``radius`` -- (default: ``None``) by setting the radius to a positive - integer, one may decrease the power of the rook to at most ``radius`` - steps. When the radius is 1, the resulting graph is a d-dimensional grid. + - ``radius`` -- integer (default: ``None``); by setting the radius to a + positive integer, one may decrease the power of the rook to at most + ``radius`` steps. When the radius is 1, the resulting graph is a + `d`-dimensional grid. - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers EXAMPLES: @@ -477,15 +476,15 @@ def RookGraph(dim_list, radius=None, relabel=False): bishop=False, knight=False, relabel=relabel) if radius is None: - G.name(dimstr+"-Rook Graph") + G.name(dimstr + "-Rook Graph") else: - G.name(dimstr+"-Rook Graph with radius "+str(radius)) + G.name(dimstr + "-Rook Graph with radius {}".format(radius)) return G def BishopGraph(dim_list, radius=None, relabel=False): r""" - Returns the `d`-dimensional Bishop Graph with prescribed dimensions. + Return the `d`-dimensional Bishop Graph with prescribed dimensions. The 2-dimensional Bishop Graph of parameters `n` and `m` is a graph with `nm` vertices in which each vertex represents a square in an `n \times m` @@ -499,15 +498,15 @@ def BishopGraph(dim_list, radius=None, relabel=False): INPUT: - - ``dim_list`` -- an iterable object (list, set, dict) providing the - dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. + - ``dim_list`` -- iterable (list, set, dict); provides the dimensions + `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard - - ``radius`` -- (default: ``None``) by setting the radius to a positive - integer, one may decrease the power of the bishop to at most ``radius`` - steps. + - ``radius`` -- integer (default: ``None``); by setting the radius to a + positive integer, one may decrease the power of the bishop to at most + ``radius`` steps. - - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices - must be relabeled as integers. + - ``relabel`` -- boolean (default: ``False``); indicates whether the + vertices must be relabeled as integers EXAMPLES: @@ -533,7 +532,7 @@ def BishopGraph(dim_list, radius=None, relabel=False): bishop=True, bishop_radius=radius, relabel=relabel) if radius is None: - G.name(dimstr+"-Bishop Graph") + G.name(dimstr + "-Bishop Graph") else: - G.name(dimstr+"-Bishop Graph with radius "+str(radius)) + G.name(dimstr + "-Bishop Graph with radius {}".format(radius)) return G diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index ecc4538712c..238380c31e7 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -396,7 +396,7 @@ def NonisotropicOrthogonalPolarGraph(m, q, sign="+", perp=None): is degenerate (cf. Sect. 7.C of [BL1984]_). Note that for `q=2` one will get a complete graph. - For more information, see Sect. 9.9 of [BH12]_ and [BL1984]_. Note that + For more information, see Sect. 9.9 of [BH2012]_ and [BL1984]_. Note that the `page of Andries Brouwer's website `_ uses different notation. @@ -656,7 +656,7 @@ def NonisotropicUnitaryPolarGraph(m, q): Hermitean form, points of the `(m-1)`-dimensional projective space over `F_q`, with points adjacent whenever they lie on a tangent (to the set of isotropic points) line. - For more information, see Sect. 9.9 of [BH12]_ and series C14 in [Hub1975]_. + For more information, see Sect. 9.9 of [BH2012]_ and series C14 in [Hub1975]_. INPUT: diff --git a/src/sage/graphs/generators/degree_sequence.py b/src/sage/graphs/generators/degree_sequence.py index 408c880f19a..2a7011a4375 100644 --- a/src/sage/graphs/generators/degree_sequence.py +++ b/src/sage/graphs/generators/degree_sequence.py @@ -24,20 +24,20 @@ def DegreeSequence(deg_sequence): """ - Returns a graph with the given degree sequence. Raises a NetworkX - error if the proposed degree sequence cannot be that of a graph. + Return a graph with the given degree sequence. - Graph returned is the one returned by the Havel-Hakimi algorithm, - which constructs a simple graph by connecting vertices of highest - degree to other vertices of highest degree, resorting the remaining - vertices by degree and repeating the process. See Theorem 1.4 in - [CL1996]_. + This method raises a NetworkX error if the proposed degree sequence cannot + be that of a graph. - INPUT: + Graph returned is the one returned by the Havel-Hakimi algorithm, which + constructs a simple graph by connecting vertices of highest degree to other + vertices of highest degree, resorting the remaining vertices by degree and + repeating the process. See Theorem 1.4 in [CL1996]_. - - ``deg_sequence`` - a list of integers with each - entry corresponding to the degree of a different vertex. + INPUT: + - ``deg_sequence`` -- list of integers with each entry corresponding to the + degree of a different vertex EXAMPLES:: @@ -64,48 +64,46 @@ def DegreeSequence(deg_sequence): import networkx return Graph(networkx.havel_hakimi_graph([int(i) for i in deg_sequence])) -def DegreeSequenceBipartite(s1 ,s2 ): +def DegreeSequenceBipartite(s1, s2): r""" - Returns a bipartite graph whose two sets have the given - degree sequences. + Return a bipartite graph whose two sets have the given degree sequences. - Given two different sequences of degrees `s_1` and `s_2`, - this functions returns ( if possible ) a bipartite graph - on sets `A` and `B` such that the vertices in `A` have - `s_1` as their degree sequence, while `s_2` is the degree - sequence of the vertices in `B`. + Given two different sequences of degrees `s_1` and `s_2`, this functions + returns ( if possible ) a bipartite graph on sets `A` and `B` such that the + vertices in `A` have `s_1` as their degree sequence, while `s_2` is the + degree sequence of the vertices in `B`. INPUT: - - ``s_1`` -- list of integers corresponding to the degree - sequence of the first set. - - ``s_2`` -- list of integers corresponding to the degree - sequence of the second set. + - ``s_1`` -- list of integers corresponding to the degree sequence of the + first set of vertices + + - ``s_2`` -- list of integers corresponding to the degree sequence of the + second set of vertices ALGORITHM: - This function works through the computation of the matrix - given by the Gale-Ryser theorem, which is in this case - the adjacency matrix of the bipartite graph. + This function works through the computation of the matrix given by the + Gale-Ryser theorem, which is in this case the adjacency matrix of the + bipartite graph. EXAMPLES: - If we are given as sequences ``[2,2,2,2,2]`` and ``[5,5]`` - we are given as expected the complete bipartite - graph `K_{2,5}` :: + If we are given as sequences ``[2,2,2,2,2]`` and ``[5,5]`` we are given as + expected the complete bipartite graph `K_{2,5}`:: sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,2],[5,5]) sage: g.is_isomorphic(graphs.CompleteBipartiteGraph(5,2)) True - Some sequences being incompatible if, for example, their sums - are different, the functions raises a ``ValueError`` when no - graph corresponding to the degree sequences exists. :: + Some sequences being incompatible if, for example, their sums are different, + the functions raises a ``ValueError`` when no graph corresponding to the + degree sequences exists:: sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,1],[5,5]) Traceback (most recent call last): ... - ValueError: There exists no bipartite graph corresponding to the given degree sequences + ValueError: there exists no bipartite graph corresponding to the given degree sequences TESTS: @@ -118,33 +116,34 @@ def DegreeSequenceBipartite(s1 ,s2 ): from sage.combinat.integer_vector import gale_ryser_theorem from sage.graphs.bipartite_graph import BipartiteGraph - s1 = sorted(s1, reverse = True) - s2 = sorted(s2, reverse = True) + s1 = sorted(s1, reverse=True) + s2 = sorted(s2, reverse=True) - m = gale_ryser_theorem(s1,s2) + m = gale_ryser_theorem(s1, s2) if m is False: - raise ValueError("There exists no bipartite graph corresponding to the given degree sequences") + raise ValueError("there exists no bipartite graph corresponding to " + "the given degree sequences") else: return Graph(BipartiteGraph(m)) def DegreeSequenceConfigurationModel(deg_sequence, seed=None): """ - Returns a random pseudograph with the given degree sequence. Raises - a NetworkX error if the proposed degree sequence cannot be that of - a graph with multiple edges and loops. + Return a random pseudograph with the given degree sequence. - One requirement is that the sum of the degrees must be even, since - every edge must be incident with two vertices. + This method raises a NetworkX error if the proposed degree sequence cannot + be that of a graph with multiple edges and loops. - INPUT: + One requirement is that the sum of the degrees must be even, since every + edge must be incident with two vertices. - - ``deg_sequence`` - a list of integers with each entry corresponding to the - expected degree of a different vertex. + INPUT: - - ``seed`` - a ``random.Random`` seed or a Python ``int`` for the random - number generator (default: ``None``). + - ``deg_sequence`` -- list of integers with each entry corresponding to the + expected degree of a different vertex + - ``seed`` -- (optional) a ``random.Random`` seed or a Python ``int`` for + the random number generator EXAMPLES:: @@ -153,15 +152,14 @@ def DegreeSequenceConfigurationModel(deg_sequence, seed=None): [0 1] [1 0] - Note: as of this writing, plotting of loops and multiple edges is - not supported, and the output is allowed to contain both types of - edges. - - :: + The output is allowed to contain both loops and multiple edges:: - sage: G = graphs.DegreeSequenceConfigurationModel([3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]) - sage: len(G.edges()) - 30 + sage: deg_sequence = [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3] + sage: G = graphs.DegreeSequenceConfigurationModel(deg_sequence) + sage: G.order(), G.size() + (20, 30) + sage: G.has_loops() or G.has_multiple_edges() # random + True sage: G.show() # long time REFERENCE: @@ -171,22 +169,24 @@ def DegreeSequenceConfigurationModel(deg_sequence, seed=None): if seed is None: seed = int(current_randstate().long_seed() % sys.maxsize) import networkx - return Graph(networkx.configuration_model([int(i) for i in deg_sequence], seed=seed), loops=True, multiedges=True, sparse=True) - + deg_sequence = [int(i) for i in deg_sequence] + return Graph(networkx.configuration_model(deg_sequence, seed=seed), + loops=True, multiedges=True, sparse=True) def DegreeSequenceTree(deg_sequence): """ - Returns a tree with the given degree sequence. Raises a NetworkX - error if the proposed degree sequence cannot be that of a tree. + Return a tree with the given degree sequence. + + This method raises a NetworkX error if the proposed degree sequence cannot + be that of a tree. Since every tree has one more vertex than edge, the degree sequence - must satisfy len(deg_sequence) - sum(deg_sequence)/2 == 1. + must satisfy ``len(deg_sequence) - sum(deg_sequence)/2 == 1``. INPUT: - - ``deg_sequence`` - a list of integers with each - entry corresponding to the expected degree of a different vertex. - + - ``deg_sequence`` -- list of integers with each entry corresponding to the + expected degree of a different vertex EXAMPLES:: @@ -200,21 +200,21 @@ def DegreeSequenceTree(deg_sequence): def DegreeSequenceExpected(deg_sequence, seed=None): """ - Returns a random graph with expected given degree sequence. Raises - a NetworkX error if the proposed degree sequence cannot be that of - a graph. + Return a random graph with expected given degree sequence. - One requirement is that the sum of the degrees must be even, since - every edge must be incident with two vertices. + This method raises a NetworkX error if the proposed degree sequence cannot + be that of a graph. - INPUT: + One requirement is that the sum of the degrees must be even, since every + edge must be incident with two vertices. - - ``deg_sequence`` - a list of integers with each entry corresponding to the - expected degree of a different vertex. + INPUT: - - ``seed`` - a ``random.Random`` seed or a Python ``int`` for the random - number generator (default: ``None``). + - ``deg_sequence`` -- list of integers with each entry corresponding to the + expected degree of a different vertex + - ``seed`` -- (optional) a ``random.Random`` seed or a Python ``int`` for + the random number generator EXAMPLES:: @@ -230,4 +230,5 @@ def DegreeSequenceExpected(deg_sequence, seed=None): if seed is None: seed = int(current_randstate().long_seed() % sys.maxsize) import networkx - return Graph(networkx.expected_degree_graph([int(i) for i in deg_sequence], seed=seed), loops=True) + deg_sequence = [int(i) for i in deg_sequence] + return Graph(networkx.expected_degree_graph(deg_sequence, seed=seed), loops=True) diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 4eee9b06f31..0283ae14876 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -1638,6 +1638,357 @@ def GeneralizedPetersenGraph(n, k): G._circle_embedding(list(range(n, 2*n)), radius=.5, angle=pi/2) return G +def IGraph(n, j, k): + r""" + Return an I-graph with `2n` nodes. + + The I-Graph family as been proposed in [BCMS1988]_ as a generalization of + the generalized Petersen graphs. The variables `n`, `j`, `k` are integers + such that `n > 2` and `0 < j, k \leq \lfloor (n - 1) / 2 \rfloor`. + When `j = 1` the resulting graph is isomorphic to the generalized Petersen + graph with the same `n` and `k`. + + INPUT: + + - ``n`` -- the number of nodes is `2 * n` + + - ``j`` -- integer such that `0 < j \leq \lfloor (n-1) / 2 \rfloor` + determining how outer vertices are connected + + - ``k`` -- integer such that `0 < k \leq \lfloor (n-1) / 2 \rfloor` + determining how inner vertices are connected + + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the I-graphs are displayed as an + inner and outer cycle pair, with the first n nodes drawn on the outer + circle. The first (0) node is drawn at the top of the outer-circle, moving + counterclockwise after that. The inner circle is drawn with the (n)th node + at the top, then counterclockwise as well. + + EXAMPLES: + + When `j = 1` the resulting graph will be isomorphic to a generalized + Petersen graph:: + + sage: g = graphs.IGraph(7,1,2) + sage: g2 = graphs.GeneralizedPetersenGraph(7,2) + sage: g.is_isomorphic(g2) + True + + The IGraph with parameters `(n, j, k)` is isomorphic to the IGraph with + parameters `(n, k, j)`:: + + sage: g = graphs.IGraph(7, 2, 3) + sage: h = graphs.IGraph(7, 3, 2) + sage: g.is_isomorphic(h) + True + + TESTS:: + + sage: graphs.IGraph(1, 1, 1) + Traceback (most recent call last): + ... + ValueError: n must be larger than 2 + sage: graphs.IGraph(3, 0, 1) + Traceback (most recent call last): + ... + ValueError: j must be in 1 <= j <= floor((n - 1) / 2) + sage: graphs.IGraph(3, 33, 1) + Traceback (most recent call last): + ... + ValueError: j must be in 1 <= j <= floor((n - 1) / 2) + sage: graphs.IGraph(3, 1, 0) + Traceback (most recent call last): + ... + ValueError: k must be in 1 <= k <= floor((n - 1) / 2) + sage: graphs.IGraph(3, 1, 3) + Traceback (most recent call last): + ... + ValueError: k must be in 1 <= k <= floor((n - 1) / 2) + """ + if n < 3: + raise ValueError("n must be larger than 2") + if j < 1 or j > (n - 1) // 2: + raise ValueError("j must be in 1 <= j <= floor((n - 1) / 2)") + if k < 1 or k > (n - 1) // 2: + raise ValueError("k must be in 1 <= k <= floor((n - 1) / 2)") + + G = Graph(2 * n, name="I-graph (n={}, j={}, k={})".format(n, j, k)) + for i in range(n): + G.add_edge(i, (i + j) % n) + G.add_edge(i, i + n) + G.add_edge(i + n, n + (i + k) % n) + G._circle_embedding(list(range(n)), radius=1, angle=pi/2) + G._circle_embedding(list(range(n, 2 * n)), radius=.5, angle=pi/2) + return G + +def DoubleGeneralizedPetersenGraph(n, k): + r""" + Return a double generalized Petersen graph with `4n` nodes. + + The double generalized Petersen graphs is a family of graphs proposed in + [ZF2012]_ as a variant of generalized Petersen graphs. The variables `n`, + `k` are integers such that `n > 2` and `0 < k \leq \lfloor (n-1) / 2 + \rfloor`. + + INPUT: + + - ``n`` -- the number of nodes is `4 * n` + + - ``k`` -- integer such that `0 < k \leq \lfloor (n-1) / 2 \rfloor` + determining how vertices on second and third inner rims are connected + + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the double generalized Petersen + graphs are displayed as 4 cocentric cycles, with the first n nodes drawn on + the outer circle. The first (0) node is drawn at the top of the + outer-circle, moving counterclockwise after that. The second circle is drawn + with the (n)th node at the top, then counterclockwise as well. The tird + cycle is drawn with the (2n)th node at the top, then counterclockwise. And + the fourth cycle is drawn with the (3n)th node at the top, then again + counterclockwise. + + EXAMPLES: + + When `n` is even the resulting graph will be isomorphic to a double + generalized Petersen graph with `k' = n / 2 - k`:: + + sage: g = graphs.DoubleGeneralizedPetersenGraph(10, 2) + sage: g2 = graphs.DoubleGeneralizedPetersenGraph(10, 3) + sage: g.is_isomorphic(g2) + True + + TESTS:: + + sage: graphs.DoubleGeneralizedPetersenGraph(1, 1) + Traceback (most recent call last): + ... + ValueError: n must be larger than 2 + sage: graphs.DoubleGeneralizedPetersenGraph(3, 0) + Traceback (most recent call last): + ... + ValueError: k must be in 1 <= k <= floor((n - 1) / 2) + sage: graphs.DoubleGeneralizedPetersenGraph(3, 3) + Traceback (most recent call last): + ... + ValueError: k must be in 1 <= k <= floor((n - 1) / 2) + """ + if n < 3: + raise ValueError("n must be larger than 2") + if k < 1 or k > (n - 1) // 2: + raise ValueError("k must be in 1 <= k <= floor((n - 1) / 2)") + + G = Graph(4 * n, name="Double generalized Petersen graph (n={}, k={})".format(n, k)) + for i in range(n): + G.add_edge(i, (i + 1) % n) + G.add_edge(i + 3 * n, (i + 1) % n + 3 * n) + G.add_edge(i, i + n) + G.add_edge(i + 2 * n, i + 3 * n) + G.add_edge(i + n, (i + k) % n + 2 * n) + G.add_edge(i+ 2 * n, (i + k) % n + n) + G._circle_embedding(list(range(n)), radius=3, angle=pi/2) + G._circle_embedding(list(range(n, 2 * n)), radius=2, angle=pi/2) + G._circle_embedding(list(range(2 * n, 3 * n)), radius=1.5, angle=pi/2) + G._circle_embedding(list(range(3 * n, 4 * n)), radius=0.5, angle=pi/2) + return G + +def RoseWindowGraph(n, a, r): + r""" + Return a rose window graph with `2n` nodes. + + The rose window graphs is a family of tetravalant graphs introduced in + [Wilson2008]_. The parameters `n`, `a` and `r` are integers such that + `n > 2`, `1 \leq a, r < n`, and `r \neq n / 2`. + + INPUT: + + - ``n`` -- the number of nodes is `2 * n` + + - ``a`` -- integer such that `1 \leq a < n` determing a-spoke edges + + - ``r`` -- integer such that `1 \leq r < n` and `r \neq n / 2` determing how + inner vertices are connected + + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the rose window graphs are + displayed as an inner and outer cycle pair, with the first n nodes drawn on + the outer circle. The first (0) node is drawn at the top of the + outer-circle, moving counterclockwise after that. The inner circle is drawn + with the (n)th node at the top, then counterclockwise as well. Vertices in + the outer circle are connected in the circular manner, vertices in the inner + circle are connected when their label have difference `r` (mod n). Vertices + on the outer rim are connected with the vertices on the inner rim when they + are at the same position and when they are `a` apart. + + EXAMPLES: + + The vertices of a rose window graph have all degree 4:: + + sage: G = graphs.RoseWindowGraph(5, 1, 2) + sage: all(G.degree(u) == 4 for u in G) + True + + The smallest rose window graph as parameters `(3, 2, 1)`:: + + sage: G = graphs.RoseWindowGraph(3, 2, 1) + sage: all(G.degree(u) == 4 for u in G) + True + + TESTS: + + sage: graphs.RoseWindowGraph(1, 1, 1) + Traceback (most recent call last): + ... + ValueError: n must be larger than 2 + sage: graphs.RoseWindowGraph(6, 0, 2) + Traceback (most recent call last): + ... + ValueError: a must be an integer such that 1 <= a < n + sage: graphs.RoseWindowGraph(6, 6, 2) + Traceback (most recent call last): + ... + ValueError: a must be an integer such that 1 <= a < n + sage: graphs.RoseWindowGraph(6, 3, 0) + Traceback (most recent call last): + ... + ValueError: r must be an integer such that 1 <= r < n + sage: graphs.RoseWindowGraph(6, 3, 6) + Traceback (most recent call last): + ... + ValueError: r must be an integer such that 1 <= r < n + sage: graphs.RoseWindowGraph(6, 3, 3) + Traceback (most recent call last): + ... + ValueError: r must be different than n / 2 + """ + if n < 3: + raise ValueError("n must be larger than 2") + if a < 1 or a >= n: + raise ValueError("a must be an integer such that 1 <= a < n") + if r < 1 or r >= n: + raise ValueError("r must be an integer such that 1 <= r < n") + if r == n / 2: + raise ValueError("r must be different than n / 2") + + G = Graph(2 * n, name="rose window graph (n={}, a={}, r={})".format(n, a, r)) + for i in range(n): + G.add_edge(i, (i + 1) % n) + G.add_edge(i, i + n) + G.add_edge((i + a) % n, i + n) + G.add_edge(i + n, (i + r) % n + n) + G._circle_embedding(list(range(n)), radius=1, angle=pi/2) + G._circle_embedding(list(range(n, 2 * n)), radius=0.5, angle=pi/2) + return G + +def TabacjnGraph(n, a, b, r): + r""" + Return a Tabačjn graph with `2n` nodes. + + The Tabačjn graphs is a family of pentavalent bicirculants graphs proposed + in [AHKOS2014]_ as a generalization of generalized Petersen graphs. The + parameters `n`, `a`, `b`, `r` are integers such that `n \geq 3`, `1 \leq a, + b, r \leq n - 1`, with `a \neq b` and `r \neq n / 2`. + + INPUT: + + - ``n`` -- the number of nodes is `2 * n` + + - ``a`` -- integer such that `0 < a < n` and `a \neq b`, that determines + a-spoke edges + + - ``b`` -- integer such that `0 < b < n` and `b \neq a`, that determines + b-spoke edges + + - ``r`` -- integer such that `0 < r < n` and `r \neq n/2` determining how + inner vertices are connected + + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the rose window graphs are + displayed as an inner and outer cycle pair, with the first n nodes drawn on + the outer circle. The first (0) node is drawn at the top of the + outer-circle, moving counterclockwise after that. The inner circle is drawn + with the (n)th node at the top, then counterclockwise as well. Vertices in + the outer circle are connected in the circular manner, vertices in the inner + circle are connected when their label have difference `r` (mod n). Vertices + on the outer rim are connected with the vertices on the inner rim when they + are at the same position and when they are `a` and `b` apart. + + EXAMPLES:: + + sage: G = graphs.TabacjnGraph(3, 1, 2, 1) + sage: G.degree() + [5, 5, 5, 5, 5, 5] + sage: G.is_isomorphic(graphs.CompleteGraph(6)) + True + sage: G = graphs.TabacjnGraph(6, 1, 5, 2) + sage: I = graphs.IcosahedralGraph() + sage: G.is_isomorphic(I) + True + + TESTS: + + sage: graphs.TabacjnGraph(1, 1, 1, 1) + Traceback (most recent call last): + ... + ValueError: n must be larger than 2 + sage: graphs.TabacjnGraph(3, 0, 1, 1) + Traceback (most recent call last): + ... + ValueError: a must be an integer such that 1 <= a < n + sage: graphs.TabacjnGraph(3, 3, 1, 1) + Traceback (most recent call last): + ... + ValueError: a must be an integer such that 1 <= a < n + sage: graphs.TabacjnGraph(3, 1, 0, 1) + Traceback (most recent call last): + ... + ValueError: b must be an integer such that 1 <= b < n + sage: graphs.TabacjnGraph(3, 1, 3, 1) + Traceback (most recent call last): + ... + ValueError: b must be an integer such that 1 <= b < n + sage: graphs.TabacjnGraph(3, 1, 1, 1) + Traceback (most recent call last): + ... + ValueError: a must be different than b + sage: graphs.TabacjnGraph(3, 1, 2, 0) + Traceback (most recent call last): + ... + ValueError: r must be an integer such that 1 <= r < n + sage: graphs.TabacjnGraph(3, 1, 2, 3) + Traceback (most recent call last): + ... + ValueError: r must be an integer such that 1 <= r < n + sage: graphs.TabacjnGraph(4, 1, 2, 2) + Traceback (most recent call last): + ... + ValueError: r must be different than n / 2 + """ + if n < 3: + raise ValueError("n must be larger than 2") + if a < 1 or a >= n: + raise ValueError("a must be an integer such that 1 <= a < n") + if b < 1 or b >= n: + raise ValueError("b must be an integer such that 1 <= b < n") + if a == b: + raise ValueError("a must be different than b") + if r < 1 or r >= n: + raise ValueError("r must be an integer such that 1 <= r < n") + if r == n/2: + raise ValueError("r must be different than n / 2") + + G = Graph(2 * n, name="Tabačjn graph (n={}, a={}, b={}, r={})".format(n, a, b, r)) + for i in range(n): + G.add_edge(i, (i + 1) % n) + G.add_edge(i, i + n) + G.add_edge(i + n, n + (i + r) % n) + G.add_edge(i, (i + a) % n + n) + G.add_edge(i, (i + b) % n + n) + G._circle_embedding(list(range(n)), radius=1, angle=pi/2) + G._circle_embedding(list(range(n, 2 * n)), radius=0.5, angle=pi/2) + return G + + def HararyGraph( k, n ): r""" Returns the Harary graph on `n` vertices and connectivity `k`, where @@ -2236,7 +2587,7 @@ def SwitchedSquaredSkewHadamardMatrixGraph(n): ` In this case, the other possible parameter set of a strongly regular graph - in the Seidel switching class of the latter graph (see [BH12]_) coincides + in the Seidel switching class of the latter graph (see [BH2012]_) coincides with the set of parameters of the complement of the graph returned by this function. diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 888798ae9c1..ba3113596a7 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -4,7 +4,7 @@ The methods defined here appear in :mod:`sage.graphs.graph_generators`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 Robert L. Miller # and Emily A. Kirkman # Copyright (C) 2009 Michael C. Yurko @@ -13,7 +13,7 @@ # 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 from Sage library from sage.graphs.graph import Graph @@ -22,10 +22,9 @@ from math import sin, cos, pi -####################################################################### +# **************************************************************************** # Named Graphs -####################################################################### - +# **************************************************************************** def HarborthGraph(): r""" @@ -43,7 +42,7 @@ def HarborthGraph(): True """ - g = Graph(':s_OGKI?@_?g[QABAo__YEFCp@?iIEbqHWuWLbbh?}[OfcXpGhNHdYPY_SgdYX]'+ + g = Graph(':s_OGKI?@_?g[QABAo__YEFCp@?iIEbqHWuWLbbh?}[OfcXpGhNHdYPY_SgdYX]' 'pZkfJPuo[lfZHys^mFcDs}`pG{UNNgoHC}DIgrI[qjMhTyDQrQlVydrBYmWkn', loops=False, multiedges=False) @@ -68,15 +67,14 @@ def HarborthGraph(): g.name("Harborth Graph") return g - def HarriesGraph(embedding=1): r""" Return the Harries Graph. - The Harries graph is a Hamiltonian 3-regular graph on 70 - vertices. See the :wikipedia:`Harries_graph`. + The Harries graph is a Hamiltonian 3-regular graph on 70 vertices. + See the :wikipedia:`Harries_graph`. - The default embedding here is to emphasize the graph's 4 orbits. This graph + The default embedding here is to emphasize the graph's 4 orbits. This graph actually has a funny construction. The following procedure gives an idea of it, though not all the adjacencies are being properly defined. @@ -97,8 +95,8 @@ def HarriesGraph(embedding=1): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -120,7 +118,6 @@ def HarriesGraph(embedding=1): Traceback (most recent call last): ... ValueError: the value of embedding must be 1 or 2 - """ from sage.graphs.generators.families import LCFGraph g = LCFGraph(70, [-29, -19, -13, 13, 21, -27, 27, 33, -13, 13, @@ -132,7 +129,7 @@ def HarriesGraph(embedding=1): ppos = PetersenGraph().get_pos() # The graph's four orbits - o = [None]*4 + o = [None] * 4 o[0] = [0, 2, 6, 8, 14, 16, 20, 22, 28, 30, 34, 36, 42, 44, 48, 50, 56, 58, 62, 64] o[1] = [1, 3, 5, 7, 9, 13, 15, 17, 19, 21, 23, 27, 29, 31, 33, 35, @@ -193,31 +190,31 @@ def HarriesWongGraph(embedding=1): The default embedding is an attempt to emphasize the graph's 8 (!!!) different orbits. In order to understand this better, one can picture the - graph as being built in the following way: + graph as being built in the following way. - #. One first creates a 3-dimensional cube (8 vertices, 12 edges), whose - vertices define the first orbit of the final graph. + #. One first creates a 3-dimensional cube (8 vertices, 12 edges), + whose vertices define the first orbit of the final graph. - #. The edges of this graph are subdivided once, to create 12 new - vertices which define a second orbit. + #. The edges of this graph are subdivided once, to create 12 new vertices + which define a second orbit. - #. The edges of the graph are subdivided once more, to create 24 new - vertices giving a third orbit. + #. The edges of the graph are subdivided once more, to create 24 new + vertices giving a third orbit. - #. 4 vertices are created and made adjacent to the vertices of the - second orbit so that they have degree 3. These 4 vertices also define - a new orbit. + #. 4 vertices are created and made adjacent to the vertices of the second + orbit so that they have degree 3. These 4 vertices also define a new + orbit. - #. In order to make the vertices from the third orbit 3-regular (they - all miss one edge), one creates a binary tree on 1 + 3 + 6 + 12 - vertices. The leaves of this new tree are made adjacent to the 12 - vertices of the third orbit, and the graph is now 3-regular. This - binary tree contributes 4 new orbits to the Harries-Wong graph. + #. In order to make the vertices from the third orbit 3-regular (they all + miss one edge), one creates a binary tree on 1 + 3 + 6 + 12 vertices. The + leaves of this new tree are made adjacent to the 12 vertices of the third + orbit, and the graph is now 3-regular. This binary tree contributes 4 new + orbits to the Harries-Wong graph. INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -230,12 +227,12 @@ def HarriesWongGraph(embedding=1): 10 sage: g.diameter() 6 - sage: orbits = g.automorphism_group(orbits=True)[-1] # long time - sage: g.show(figsize=[15, 15], partition=orbits) # long time + sage: orbits = g.automorphism_group(orbits=True)[-1] # long time + sage: g.show(figsize=[15, 15], partition=orbits) # long time Alternative embedding:: - sage: graphs.HarriesWongGraph(embedding=2).show() + sage: graphs.HarriesWongGraph(embedding=2).show() # long time TESTS:: @@ -244,7 +241,6 @@ def HarriesWongGraph(embedding=1): ... ValueError: the value of embedding must be 1 or 2 """ - L = [9, 25, 31, -17, 17, 33, 9, -29, -15, -9, 9, 25, -25, 29, 17, -9, 9, -27, 35, -9, 9, -17, 21, 27, -29, -9, -25, 13, 19, -9, -33, -17, 19, -31, 27, 11, -25, 29, -33, 13, -13, 21, -29, -21, 25, @@ -301,7 +297,6 @@ def HarriesWongGraph(embedding=1): else: raise ValueError("the value of embedding must be 1 or 2") - def WellsGraph(): r""" Return the Wells graph. @@ -309,13 +304,13 @@ def WellsGraph(): For more information on the Wells graph (also called Armanios-Wells graph), see `this page `_. - The implementation follows the construction given on page 266 of - [BCN1989]_. This requires to create intermediate graphs and run a small - isomorphism test, while everything could be replaced by a pre-computed list - of edges : I believe that it is better to keep "the recipe" in the code, - however, as it is quite unlikely that this could become the most - time-consuming operation in any sensible algorithm, and .... "preserves - knowledge", which is what open-source software is meant to do. + The implementation follows the construction given on page 266 of [BCN1989]_. + This requires to create intermediate graphs and run a small isomorphism + test, while everything could be replaced by a pre-computed list of edges. + I believe that it is better to keep "the recipe" in the code, however, as it + is quite unlikely that this could become the most time-consuming operation + in any sensible algorithm, and .... "preserves knowledge", which is what + open-source software is meant to do. EXAMPLES:: @@ -353,9 +348,9 @@ def WellsGraph(): _, labels = distance3.is_isomorphic(b, certificate=True) # The relabeling that the books claims to exist. - for v,new_name in labels.items(): - x,y = new_name - labels[v] = (x%5,y%5) + for v, new_name in labels.items(): + x, y = new_name + labels[v] = (x % 5, y % 5) dodecahedron.relabel(labels) @@ -368,7 +363,7 @@ def WellsGraph(): if (u[0] != v[0]) and (u[1] != v[1]): continue - if dodecahedron.distance(u,v) != 3: + if dodecahedron.distance(u, v) != 3: raise ValueError("there is something wrong going on !") # The graph we will return, starting from the dodecahedron @@ -376,13 +371,13 @@ def WellsGraph(): # Good ! Now adding 12 new vertices for i in range(5): - g.add_edge((i,'+'),('inf','+')) - g.add_edge((i,'-'),('inf','-')) + g.add_edge((i, '+'), ('inf', '+')) + g.add_edge((i, '-'), ('inf', '-')) for k in range(5): if k == i: continue - g.add_edge((i,'+'),(i,k)) - g.add_edge((i,'-'),(k,i)) + g.add_edge((i, '+'), (i, k)) + g.add_edge((i, '-'), (k, i)) g.name("Wells graph") @@ -409,7 +404,6 @@ def WellsGraph(): return g - def Cell600(embedding=1): r""" Return the 600-Cell graph. @@ -419,7 +413,8 @@ def Cell600(embedding=1): INPUT: - - ``embedding`` (1 (default) or 2) -- two different embeddings for a plot. + - ``embedding`` -- integer (default: ``1``); two different embeddings for a + plot EXAMPLES:: @@ -488,7 +483,6 @@ def Cell600(embedding=1): return g - def Cell120(): r""" Return the 120-Cell graph. @@ -597,7 +591,6 @@ def Cell120(): return g - def SuzukiGraph(): r""" Return the Suzuki Graph. @@ -612,19 +605,18 @@ def SuzukiGraph(): EXAMPLES:: - sage: g = graphs.SuzukiGraph(); g # optional internet # not tested + sage: g = graphs.SuzukiGraph(); g # optional internet # not tested Suzuki graph: Graph on 1782 vertices - sage: g.is_strongly_regular(parameters=True) # optional internet # not tested + sage: g.is_strongly_regular(parameters=True) # optional internet # not tested (1782, 416, 100, 96) """ from sage.groups.perm_gps.permgroup_named import SuzukiSporadicGroup g = Graph() - g.add_edges(SuzukiSporadicGroup().orbit((1,2),"OnSets")) + g.add_edges(SuzukiSporadicGroup().orbit((1, 2), "OnSets")) g.relabel() g.name("Suzuki graph") return g - def HallJankoGraph(from_string=True): r""" Return the Hall-Janko graph. @@ -640,9 +632,9 @@ def HallJankoGraph(from_string=True): INPUT: - - ``from_string`` (boolean) -- whether to build the graph from its sparse6 - string or through GAP. The two methods return the same graph though doing - it through GAP takes more time. It is set to ``True`` by default. + - ``from_string`` -- boolean (default: ``True``); whether to build the graph + from its sparse6 string or through GAP. The two methods return the same + graph though doing it through GAP takes more time. EXAMPLES:: @@ -677,66 +669,63 @@ def HallJankoGraph(from_string=True): TESTS:: - sage: gg = graphs.HallJankoGraph(from_string=False) # long time # optional - internet - sage: g.is_isomorphic(gg) # long time # optional - internet + sage: gg = graphs.HallJankoGraph(from_string=False) # long time # optional - internet + sage: g.is_isomorphic(gg) # long time # optional - internet True """ - - string = (":~?@c__E@?g?A?w?A@GCA_?CA`OWF`W?EAW?@?_OD@_[GAgcIaGGB@OcIA" - "wCE@o_K_?GB@?WGAouC@OsN_?GB@O[GB`A@@_e?@OgLB_{Q_?GC@O[GAOs" - "OCWGBA?kKBPA@?_[KB_{OCPKT`o_RD`]A?o[HBOwODW?DA?cIB?wRDP[X`" - "ogKB_{QD@]B@o_KBPWXE`mC@o_JB?{PDPq@?oWGA_{OCPKTDp_YEwCA@_c" - "IBOwOC`OX_OGB@?WPDPcYFg?C@_gKBp?SE@cYF`{_`?SGAOoOC`_\\FwCE" - "A?gKBO{QD@k[FqI??_OFA_oQE@k\\Fq?`GgCB@pGRD@_XFP{a_?SE@ocIA" - "ooNCPOUEqU@?oODA?cJB_{UEqYC@_kLC@CREPk]GAGbHgCA@?SMBpCSD`[" - "YFq?`Ga]BA?gPC`KSD`_\\Fa?cHWGB@?[IAooPD`[WF@s^HASeIg?@@OcP" - "C`KYF@w^GQ[h`O[HAooMC@CQCpSVEPk\\GaSeIG?FA?kLB_{OC`OVE@cYG" - "QUA@?WLBp?PC`KVEqKgJg?DA?sMBpCSDP[WEQKfIay@?_KD@_[GC`SUE@k" - "[FaKdHa[k_?OLC@CRD@WVEpo^HAWfIAciIqoo_?CB@?kMCpOUE`o\\GAKg" - "IQgq_?GD@_[GB?{OCpWVE@cYFACaHAWhJR?q_?CC@_kKBpC\\GACdHa[kJ" - "a{o_?CA?oOFBpGRD@o\\GaKdIQonKrOt_?WHA`?PC`KTD`k]FqSeIaolJr" - "CqLWCA@OkKCPGRDpcYGAKdIAgjJAsmJr?t__OE@ogJB_{XEps`HA[gIQwn" - "KWKGAOoMBpGUE`k[Fa?aHqckJbSuLw?@?_SHA_kLC@OTFPw^GaOkLg?B@?" - "[HA_{PDP_XFaCbHa[gIqooKRWx_?CFBpOTE@cZFPw^GACcHQgoKrSvMwWG" - "BOwQCp_YFP{`HASfJAwnKRSx_OSSDP[WEq?aGqSfIQsoKR_zNWCE@o_HA_" - "sREPg^GAGcHQWfIAciKbOxNg?A@__IAooMC`KTD`g\\GAKcIasoKrOtLb[" - "wMbyCA?cKBp?TD`[WE`s^GQGbHqcjJrK{NRw~_oODA?sNC@CQCpOZF@s]G" - "QOfIaolJrGsLbk}_?OFA_sRD@SVE`k[HQcjJa{qLb[xMb|?_OOFA?cIAos" - "RDP_ZFa?aGqOfIAsuMbk{Ns@@OsQAA_sPDPWXE`o\\FqKdIQkkJrCuLr_x" - "Mro}NsDAPG?@@OWFApKUE@o`IQolKRKsLrc|NsQC@OWGAOgJCpOWE`o_GQ" - "KiIqwnKr_~OcLCPS]A?oWHA_oMBpKSDP[\\FagjKBWxMbk{OSQ@@O_IAoo" - "LBpCSD`g\\FaGbHQWgIQgmKRKwMRl?PgGC@OWHB@KSE@c[FqCaGqSeIAkk" - "KBCqLBSuMBpGQWCA@?cKBOwRDPWVE@k^GqOfJr?pKbKtLrs}OSHDQwKIBO" - "wPD@WWEQ?`HQWfIQglKBOtLbo}Ns@@OsTE_?kLCpWWHA[gIqomKBGwMRgz" - "NBw~OSPDPc\\H_?CFAOoLCPSVE`o\\GAOeJAwpKbKtMrx?Qcq??OKFA?gJ" - "B`?QDpcYEpo]FqKfIAgjJB?qKr_{NS@A__SE@o_HBO{PC`OTD`{_HaciIq" - "{vMbt?OcPFQCeB@?SKBOwRD@SXE`k[FPw`HQ_lKRKxNRxBPC\\HQclK_?K" - "EB?sOC`OTDa?`GqWgJRCrNBw~OSHFQStMRtDQ_?KC@OoQE`k_GaOdHa[gI" - "q{tMBg|Nb|?OcPMSDDQSwCB@_cJB_{OCpOVFP{dHa[jJQwqKrk}NsHBQCd" - "MRtMA?oSEA_wPDp_YEpo]GAOeIq{pLBk}NsLEQCtNTDU??OKEA_oLC@[[G" - "aKnKBOtLbk~OCPFQStNSDLSTgGKC@GSD`[WEpw_GQGcIAciJAwpKb_xMbk" - "~QShJRc|R`_wNCPcZF@s^GAGbHA_hJR?qKrOvMRg|NsDEPsxTTgCB@?gJB" - "?sMC@CUDp_]FqCaHQcjJQwtLrhCPS\\IRCtQTw?B@?SHA_wPC`_aGqOiJa" - "{oKRKvMRpFQChKRtXVUTi??ocNC@KUE@cYFaGdHa_mJrKsLb[yMro|OcXI" - "RdPTTddZaOgJB@?UEPk[FQCfIaolJrSvMBczNR|AOsXFQCtOTtaB@?WGAP" - "?TEPo\\GAGdHqgmKBCqLR[xMb|?PC`HQs|TTt`XUtu@?o[HB?sNCPGXF@{" - "_GQKcIqolJb_yNCLDPs`MRtDRTTdYUwSEA?kLB`CWF@s]FqGgIqooLRgzN" - "RxFQSlMSDDQTDXVUTi@?_KDAOoLBpKUEQOfIa{oLB_xMrt?Os\\HQcpMST" - "HSTtl[VT}A@ocJBOwSD`_XEpo_Ha_mJrKtLbgzNSTGQspLRtDUUDp\\WG[" - "HB`CQCp[WFQGgIQgkJQ{rLbc{Nc@APsdLRt@PSt\\WUtt_Wn") - if from_string: - g = Graph(string, loops = False, multiedges = False) + string = (":~?@c__E@?g?A?w?A@GCA_?CA`OWF`W?EAW?@?_OD@_[GAgcIaGGB@OcIA" + "wCE@o_K_?GB@?WGAouC@OsN_?GB@O[GB`A@@_e?@OgLB_{Q_?GC@O[GAOs" + "OCWGBA?kKBPA@?_[KB_{OCPKT`o_RD`]A?o[HBOwODW?DA?cIB?wRDP[X`" + "ogKB_{QD@]B@o_KBPWXE`mC@o_JB?{PDPq@?oWGA_{OCPKTDp_YEwCA@_c" + "IBOwOC`OX_OGB@?WPDPcYFg?C@_gKBp?SE@cYF`{_`?SGAOoOC`_\\FwCE" + "A?gKBO{QD@k[FqI??_OFA_oQE@k\\Fq?`GgCB@pGRD@_XFP{a_?SE@ocIA" + "ooNCPOUEqU@?oODA?cJB_{UEqYC@_kLC@CREPk]GAGbHgCA@?SMBpCSD`[" + "YFq?`Ga]BA?gPC`KSD`_\\Fa?cHWGB@?[IAooPD`[WF@s^HASeIg?@@OcP" + "C`KYF@w^GQ[h`O[HAooMC@CQCpSVEPk\\GaSeIG?FA?kLB_{OC`OVE@cYG" + "QUA@?WLBp?PC`KVEqKgJg?DA?sMBpCSDP[WEQKfIay@?_KD@_[GC`SUE@k" + "[FaKdHa[k_?OLC@CRD@WVEpo^HAWfIAciIqoo_?CB@?kMCpOUE`o\\GAKg" + "IQgq_?GD@_[GB?{OCpWVE@cYFACaHAWhJR?q_?CC@_kKBpC\\GACdHa[kJ" + "a{o_?CA?oOFBpGRD@o\\GaKdIQonKrOt_?WHA`?PC`KTD`k]FqSeIaolJr" + "CqLWCA@OkKCPGRDpcYGAKdIAgjJAsmJr?t__OE@ogJB_{XEps`HA[gIQwn" + "KWKGAOoMBpGUE`k[Fa?aHqckJbSuLw?@?_SHA_kLC@OTFPw^GaOkLg?B@?" + "[HA_{PDP_XFaCbHa[gIqooKRWx_?CFBpOTE@cZFPw^GACcHQgoKrSvMwWG" + "BOwQCp_YFP{`HASfJAwnKRSx_OSSDP[WEq?aGqSfIQsoKR_zNWCE@o_HA_" + "sREPg^GAGcHQWfIAciKbOxNg?A@__IAooMC`KTD`g\\GAKcIasoKrOtLb[" + "wMbyCA?cKBp?TD`[WE`s^GQGbHqcjJrK{NRw~_oODA?sNC@CQCpOZF@s]G" + "QOfIaolJrGsLbk}_?OFA_sRD@SVE`k[HQcjJa{qLb[xMb|?_OOFA?cIAos" + "RDP_ZFa?aGqOfIAsuMbk{Ns@@OsQAA_sPDPWXE`o\\FqKdIQkkJrCuLr_x" + "Mro}NsDAPG?@@OWFApKUE@o`IQolKRKsLrc|NsQC@OWGAOgJCpOWE`o_GQ" + "KiIqwnKr_~OcLCPS]A?oWHA_oMBpKSDP[\\FagjKBWxMbk{OSQ@@O_IAoo" + "LBpCSD`g\\FaGbHQWgIQgmKRKwMRl?PgGC@OWHB@KSE@c[FqCaGqSeIAkk" + "KBCqLBSuMBpGQWCA@?cKBOwRDPWVE@k^GqOfJr?pKbKtLrs}OSHDQwKIBO" + "wPD@WWEQ?`HQWfIQglKBOtLbo}Ns@@OsTE_?kLCpWWHA[gIqomKBGwMRgz" + "NBw~OSPDPc\\H_?CFAOoLCPSVE`o\\GAOeJAwpKbKtMrx?Qcq??OKFA?gJ" + "B`?QDpcYEpo]FqKfIAgjJB?qKr_{NS@A__SE@o_HBO{PC`OTD`{_HaciIq" + "{vMbt?OcPFQCeB@?SKBOwRD@SXE`k[FPw`HQ_lKRKxNRxBPC\\HQclK_?K" + "EB?sOC`OTDa?`GqWgJRCrNBw~OSHFQStMRtDQ_?KC@OoQE`k_GaOdHa[gI" + "q{tMBg|Nb|?OcPMSDDQSwCB@_cJB_{OCpOVFP{dHa[jJQwqKrk}NsHBQCd" + "MRtMA?oSEA_wPDp_YEpo]GAOeIq{pLBk}NsLEQCtNTDU??OKEA_oLC@[[G" + "aKnKBOtLbk~OCPFQStNSDLSTgGKC@GSD`[WEpw_GQGcIAciJAwpKb_xMbk" + "~QShJRc|R`_wNCPcZF@s^GAGbHA_hJR?qKrOvMRg|NsDEPsxTTgCB@?gJB" + "?sMC@CUDp_]FqCaHQcjJQwtLrhCPS\\IRCtQTw?B@?SHA_wPC`_aGqOiJa" + "{oKRKvMRpFQChKRtXVUTi??ocNC@KUE@cYFaGdHa_mJrKsLb[yMro|OcXI" + "RdPTTddZaOgJB@?UEPk[FQCfIaolJrSvMBczNR|AOsXFQCtOTtaB@?WGAP" + "?TEPo\\GAGdHqgmKBCqLR[xMb|?PC`HQs|TTt`XUtu@?o[HB?sNCPGXF@{" + "_GQKcIqolJb_yNCLDPs`MRtDRTTdYUwSEA?kLB`CWF@s]FqGgIqooLRgzN" + "RxFQSlMSDDQTDXVUTi@?_KDAOoLBpKUEQOfIa{oLB_xMrt?Os\\HQcpMST" + "HSTtl[VT}A@ocJBOwSD`_XEpo_Ha_mJrKtLbgzNSTGQspLRtDUUDp\\WG[" + "HB`CQCp[WFQGgIQgkJQ{rLbc{Nc@APsdLRt@PSt\\WUtt_Wn") + g = Graph(string, loops=False, multiedges=False) else: - # The following construction is due to version 3 of the ATLAS of # Finite Group Representations, specifically the page at # http://brauer.maths.qmul.ac.uk/Atlas/v5/permrep/J2G1-p100B0 . from sage.libs.gap.libgap import libgap - libgap.load_package("AtlasRep") # representation of HJ on 100 points + libgap.load_package("AtlasRep") # representation of HJ on 100 points G = libgap.AtlasGroup("HJ", libgap.NrMovedPoints, 100) - edges = G.Orbit([1,5], libgap.OnSets) + edges = G.Orbit([1, 5], libgap.OnSets) g = Graph() g.add_edges(edges) g.relabel() @@ -745,7 +734,6 @@ def HallJankoGraph(from_string=True): g.name("Hall-Janko graph") return g - def Balaban10Cage(embedding=1): r""" Return the Balaban 10-cage. @@ -772,8 +760,8 @@ def Balaban10Cage(embedding=1): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to be either 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to be either 1 or 2 EXAMPLES:: @@ -795,7 +783,6 @@ def Balaban10Cage(embedding=1): ... ValueError: the value of embedding must be 1 or 2 """ - L = [-9, -25, -19, 29, 13, 35, -13, -29, 19, 25, 9, -29, 29, 17, 33, 21, 9,-13, -31, -9, 25, 17, 9, -31, 27, -9, 17, -19, -29, 27, -17, -9, -29, 33, -25,25, -21, 17, -17, 29, 35, -29, 17, -17, @@ -832,7 +819,7 @@ def Balaban10Cage(embedding=1): return g -def Balaban11Cage(embedding = 1): +def Balaban11Cage(embedding=1): r""" Return the Balaban 11-cage. @@ -840,8 +827,8 @@ def Balaban11Cage(embedding = 1): INPUT: - - ``embedding`` -- three embeddings are available, and can be selected by - setting ``embedding`` to be 1, 2, or 3. + - ``embedding`` -- integer (default: ``1``); three embeddings are available, + and can be selected by setting ``embedding`` to be 1, 2, or 3 - The first embedding is the one appearing on page 9 of the Fifth Annual Graph Drawing Contest report [EMMN1998]_. It separates vertices based on @@ -885,7 +872,7 @@ def Balaban11Cage(embedding = 1): Proof that the embeddings are the same graph:: - sage: g1.is_isomorphic(g2) # g2 and g3 are obviously isomorphic + sage: g1.is_isomorphic(g2) # g2 and g3 are obviously isomorphic True TESTS:: @@ -1030,7 +1017,7 @@ def BidiakisCube(): EXAMPLES: The Bidiakis cube is a 3-regular graph having 12 vertices and 18 edges. This - means that each vertex has a degree of 3. :: + means that each vertex has a degree of 3:: sage: g = graphs.BidiakisCube(); g Bidiakis cube: Graph on 12 vertices @@ -1088,8 +1075,8 @@ def BiggsSmithGraph(embedding=1): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to be 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to be 1 or 2 EXAMPLES: @@ -1110,7 +1097,7 @@ def BiggsSmithGraph(embedding=1): The other embedding:: - sage: graphs.BiggsSmithGraph(embedding=2).show() # long time + sage: graphs.BiggsSmithGraph(embedding=2).show() # long time TESTS:: @@ -1191,14 +1178,13 @@ def BlanusaFirstSnarkGraph(): sage: g.automorphism_group().cardinality() 8 """ - g = Graph({17:[4,7,1],0:[5], - 3:[8],13:[9],12:[16], - 10:[15],11:[6],14:[2]}, + g = Graph({17: [4, 7, 1], 0: [5], 3: [8], 13: [9], 12: [16], + 10: [15], 11: [6], 14: [2]}, name="Blanusa First Snark Graph") g.add_cycle(list(range(17))) g._circle_embedding(list(range(17)), shift=0.25) - g.get_pos()[17] = (0,0) + g.get_pos()[17] = (0, 0) return g def BlanusaSecondSnarkGraph(): @@ -1266,7 +1252,7 @@ def BrinkmannGraph(): EXAMPLES: The Brinkmann graph is a 4-regular graph having 21 vertices and 42 - edges. This means that each vertex has degree 4. :: + edges. This means that each vertex has degree 4:: sage: G = graphs.BrinkmannGraph(); G Brinkmann graph: Graph on 21 vertices @@ -1278,7 +1264,7 @@ def BrinkmannGraph(): sage: G.is_regular(4) True - It is an Eulerian graph with radius 3, diameter 3, and girth 5. :: + It is an Eulerian graph with radius 3, diameter 3, and girth 5:: sage: G.is_eulerian() True @@ -1363,7 +1349,7 @@ def BrouwerHaemersGraph(): It is indeed strongly regular with parameters `(81,20,1,6)`:: - sage: g.is_strongly_regular(parameters = True) # long time + sage: g.is_strongly_regular(parameters=True) # long time (81, 20, 1, 6) Its has as eigenvalues `20,2` and `-7`:: @@ -1382,7 +1368,7 @@ def BrouwerHaemersGraph(): V = VectorSpace(F,d) M = Matrix(F,identity_matrix(d)) M[1,1]=-1 - G = Graph([[tuple(_) for _ in V], lambda x,y:(V(x)-V(y))*(M*(V(x)-V(y))) == 0], loops = False) + G = Graph([[tuple(_) for _ in V], lambda x,y:(V(x)-V(y))*(M*(V(x)-V(y))) == 0], loops=False) G.relabel() ordering = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 48, 49, 50, 51, 52, 53, @@ -1396,7 +1382,7 @@ def BrouwerHaemersGraph(): def BuckyBall(): r""" - Create the Bucky Ball graph. + Return the Bucky Ball graph. This graph is a 3-regular 60-vertex planar graph. Its vertices and edges correspond precisely to the carbon atoms and bonds in buckminsterfullerene. @@ -1405,14 +1391,14 @@ def BuckyBall(): EXAMPLES: - The Bucky Ball is planar. :: + The Bucky Ball is planar:: sage: g = graphs.BuckyBall() sage: g.is_planar() True The Bucky Ball can also be created by extracting the 1-skeleton of the Bucky - Ball polyhedron, but this is much slower. :: + Ball polyhedron, but this is much slower:: sage: g = polytopes.buckyball().vertex_graph() sage: g.remove_loops() @@ -1420,10 +1406,10 @@ def BuckyBall(): sage: g.is_isomorphic(h) True - The graph is returned along with an attractive embedding. :: + The graph is returned along with an attractive embedding:: - sage: g = graphs.BuckyBall() - sage: g.plot(vertex_labels=False, vertex_size=10).show() # long time + sage: g = graphs.BuckyBall() # long time + sage: g.plot(vertex_labels=False, vertex_size=10).show() # long time """ edges = [(0, 2), (0, 48), (0, 59), (1, 3), (1, 9), (1, 58), (2, 3), (2, 36), (3, 17), (4, 6), (4, 8), (4, 12), @@ -1525,22 +1511,21 @@ def GossetGraph(): sage: g = graphs.GossetGraph(); g Gosset Graph: Graph on 56 vertices - sage: g.order(), g.size() (56, 756) TESTS:: - sage: g.is_isomorphic(polytopes.Gosset_3_21().graph()) # not tested (~16s) + sage: g.is_isomorphic(polytopes.Gosset_3_21().graph()) # not tested (~16s) True """ - string = ('w~~~~rt{~Z\\ZxnvYZYmlfrb}|hDuhLlcmmMNf_^zzQGNYcP\\kcRZbaJjoNBx{'+ - '?N~o^}?A`}F_Kbbm_[QZ\\_]Cj\\oN_dm{BzB{?]WIMM@tPQRYBYRPIuAyJgQv?'+ - '|Bxb_M[kWIR@jTQcciDjShXCkFMgpwqBKxeKoS`TYqdTCcKtkdKwWQXrbEZ@OdU'+ - 'mITZ@_e[{KXn?YPABzvY?IcO`zvYg@caC\\zlf?BaGR]zb{?@wOjv`~w??N_n_~'+ + string = ('w~~~~rt{~Z\\ZxnvYZYmlfrb}|hDuhLlcmmMNf_^zzQGNYcP\\kcRZbaJjoNBx{' + '?N~o^}?A`}F_Kbbm_[QZ\\_]Cj\\oN_dm{BzB{?]WIMM@tPQRYBYRPIuAyJgQv?' + '|Bxb_M[kWIR@jTQcciDjShXCkFMgpwqBKxeKoS`TYqdTCcKtkdKwWQXrbEZ@OdU' + 'mITZ@_e[{KXn?YPABzvY?IcO`zvYg@caC\\zlf?BaGR]zb{?@wOjv`~w??N_n_~' '~w???^_^~~{') - G = Graph(string,name="Gosset Graph") + G = Graph(string, name="Gosset Graph") ordering = [0, 2, 4, 6, 43, 23, 50, 18, 28, 9, 8, 7, 44, 3, 26, 35, 16, 14, 33, 15, 54, 30, 17, 21, 10, 13, 36, 31, 55, 53, 51, 49, 12, 32, @@ -1606,7 +1591,7 @@ def DoubleStarSnark(): , 29: [25, 22, 15] } - g = Graph(d, pos={}, name="Double star snark") + g = Graph(d, format='dict_of_lists', name="Double star snark") g._circle_embedding(list(range(15)), radius=2) g._circle_embedding(list(range(15, 30)), radius=1.4) @@ -1636,7 +1621,7 @@ def MeredithGraph(): 4 sage: g.chromatic_number() 3 - sage: g.is_hamiltonian() # long time + sage: g.is_hamiltonian() # long time False """ g = Graph(name="Meredith Graph") @@ -1644,22 +1629,26 @@ def MeredithGraph(): # Edges between copies of K_{4,3} for i in range(5): - g.add_edge(('outer',i,3),('outer',(i+1)%5,0)) - g.add_edge(('inner',i,3),('inner',(i+2)%5,0)) - g.add_edge(('outer',i,1),('inner',i ,1)) - g.add_edge(('outer',i,2),('inner',i ,2)) + g.add_edge(('outer', i, 3), ('outer', (i + 1) % 5, 0)) + g.add_edge(('inner', i, 3), ('inner', (i + 2) % 5, 0)) + g.add_edge(('outer', i, 1), ('inner', i, 1)) + g.add_edge(('outer', i, 2), ('inner', i, 2)) # Edges inside of the K_{4,3}s. for i in range(5): for j in range(4): for k in range(3): - g.add_edge(('inner',i,j),('inner',i,k+4)) - g.add_edge(('outer',i,j),('outer',i,k+4)) - - g._circle_embedding(sum([[('outer',i,j) for j in range(4)]+10*[0] for i in range(5)],[]), radius = 1, shift = 2) - g._circle_embedding(sum([[('outer',i,j) for j in range(4,7)]+10*[0] for i in range(5)],[]), radius = 1.2, shift = 2.2) - g._circle_embedding(sum([[('inner',i,j) for j in range(4)]+7*[0] for i in range(5)],[]), radius = .6, shift = 1.24) - g._circle_embedding(sum([[('inner',i,j) for j in range(4,7)]+5*[0] for i in range(5)],[]), radius = .4, shift = 1.05) + g.add_edge(('inner', i, j), ('inner', i, k + 4)) + g.add_edge(('outer', i, j), ('outer', i, k + 4)) + + g._circle_embedding(sum([[('outer', i, j) for j in range(4)] + 10 * [0] for i in range(5)], []), + radius=1, shift=2) + g._circle_embedding(sum([[('outer', i, j) for j in range(4, 7)] + 10 * [0] for i in range(5)], []), + radius=1.2, shift=2.2) + g._circle_embedding(sum([[('inner', i, j) for j in range(4)] + 7 * [0] for i in range(5)], []), + radius=.6, shift=1.24) + g._circle_embedding(sum([[('inner', i, j) for j in range(4, 7)] + 5 * [0] for i in range(5)], []), + radius=.4, shift=1.05) g.delete_vertex(0) g.relabel() @@ -1703,12 +1692,12 @@ def KittellGraph(): name = "Kittell Graph") g._circle_embedding(list(range(3)), shift=.75) - g._circle_embedding(list(range(3, 13)), radius = .4) - g._circle_embedding(list(range(15, 22)), radius = .2, shift=-.15) + g._circle_embedding(list(range(3, 13)), radius=.4) + g._circle_embedding(list(range(15, 22)), radius=.2, shift=-.15) pos = g.get_pos() - pos[13] = (-.65,-.35) - pos[14] = (.65,-.35) - pos[22] = (0,0) + pos[13] = (-.65, -.35) + pos[14] = (.65, -.35) + pos[22] = (0, 0) return g @@ -1731,15 +1720,14 @@ def CameronGraph(): 3465 sage: g.is_strongly_regular(parameters = True) # long time (231, 30, 9, 3) - """ from sage.groups.perm_gps.permgroup_named import MathieuGroup from itertools import combinations g = Graph(name="Cameron Graph") - sets = MathieuGroup(22).orbit((1,2,3,7,10,20), action = "OnSets") + sets = MathieuGroup(22).orbit((1, 2, 3, 7, 10, 20), action="OnSets") for s in sets: - for a,b,c,d in combinations(set(s),4): - g.add_edges([((a,b),(c,d)),((a,c),(b,d)), ((a,d),(b,c))]) + for a, b, c, d in combinations(set(s), 4): + g.add_edges([((a, b), (c, d)), ((a, c), (b, d)), ((a, d), (b, c))]) g.relabel() ordering = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, @@ -1768,14 +1756,14 @@ def ChvatalGraph(): Return the Chvatal graph. Chvatal graph is one of the few known graphs to satisfy Grunbaum's - conjecture that for every m, n, there is an m-regular, m-chromatic graph of - girth at least n. For more information, see the + conjecture that for every `m`, `n`, there is an `m`-regular, `m`-chromatic + graph of girth at least `n`. For more information, see the :wikipedia:`Chv%C3%A1tal_graph`. EXAMPLES: The Chvatal graph has 12 vertices and 24 edges. It is a 4-regular, - 4-chromatic graph with radius 2, diameter 2, and girth 4. :: + 4-chromatic graph with radius 2, diameter 2, and girth 4:: sage: G = graphs.ChvatalGraph(); G Chvatal graph: Graph on 12 vertices @@ -1867,7 +1855,7 @@ def CoxeterGraph(): 3 sage: g.diameter() 4 - sage: g.show(figsize=[10, 10]) # long time + sage: g.show(figsize=[10, 10]) # long time """ g = Graph({ 27: [6, 22, 14], @@ -1929,7 +1917,7 @@ def DesarguesGraph(): sage: D.show() # long time """ from sage.graphs.generators.families import GeneralizedPetersenGraph - G = GeneralizedPetersenGraph(10,3) + G = GeneralizedPetersenGraph(10, 3) G.name("Desargues Graph") return G @@ -1942,7 +1930,7 @@ def DurerGraph(): EXAMPLES: The Dürer graph is named after Albrecht Dürer. It is a planar graph - with 12 vertices and 18 edges. :: + with 12 vertices and 18 edges:: sage: G = graphs.DurerGraph(); G Durer graph: Graph on 12 vertices @@ -1953,7 +1941,7 @@ def DurerGraph(): sage: G.size() 18 - The Dürer graph has chromatic number 3, diameter 4, and girth 3. :: + The Dürer graph has chromatic number 3, diameter 4, and girth 3:: sage: G.chromatic_number() 3 @@ -1962,7 +1950,7 @@ def DurerGraph(): sage: G.girth() 3 - Its automorphism group is isomorphic to `D_6`. :: + Its automorphism group is isomorphic to `D_6`:: sage: ag = G.automorphism_group() sage: ag.is_isomorphic(DihedralGroup(6)) @@ -2110,10 +2098,10 @@ def HortonGraph(): 96 sage: g.chromatic_number() 2 - sage: g.is_hamiltonian() # not tested -- veeeery long + sage: g.is_hamiltonian() # not tested -- veeeery long False """ - g = Graph(name = "Horton Graph") + g = Graph(name="Horton Graph") # Each group of the 6 groups of vertices is based on the same 3-regular # graph. @@ -2140,7 +2128,9 @@ def HortonGraph(): # Embedding for i in range(6): - g._circle_embedding([(i, j) for j in range(16)], center=(cos(2 * i * pi / 6), sin(2 * i * pi / 6)), radius=.3) + g._circle_embedding([(i, j) for j in range(16)], + center=(cos(2 * i * pi / 6), sin(2 * i * pi / 6)), + radius=.3) for i in range(3): g.delete_vertex((2 * i + 1, 15)) @@ -2167,23 +2157,23 @@ def EllinghamHorton54Graph(): It is 3-connected and bipartite:: - sage: g.vertex_connectivity() # not tested - too long + sage: g.vertex_connectivity() # not tested - too long 3 sage: g.is_bipartite() True It is not Hamiltonian:: - sage: g.is_hamiltonian() # not tested - too long + sage: g.is_hamiltonian() # not tested - too long False ... and it has a nice drawing :: - sage: g.show(figsize=[10, 10]) # not tested - too long + sage: g.show(figsize=[10, 10]) # not tested - too long TESTS:: - sage: g.show() # long time + sage: g.show() # long time """ edge_dict = { 0: [1, 11, 15], 1: [2, 47], 2: [3, 13], 3: [4, 8], 4: [5, 15], @@ -2243,23 +2233,23 @@ def EllinghamHorton78Graph(): It is 3-connected and bipartite:: - sage: g.vertex_connectivity() # not tested - too long + sage: g.vertex_connectivity() # not tested - too long 3 sage: g.is_bipartite() True It is not Hamiltonian:: - sage: g.is_hamiltonian() # not tested - too long + sage: g.is_hamiltonian() # not tested - too long False ... and it has a nice drawing :: - sage: g.show(figsize=[10,10]) # not tested - too long + sage: g.show(figsize=[10,10]) # not tested - too long TESTS:: - sage: g.show(figsize=[10, 10]) # not tested - too long + sage: g.show(figsize=[10, 10]) # not tested - too long """ g = Graph({ 0: [1, 5, 60], 1: [2, 12], 2: [3, 7], 3: [4, 14], 4: [5, 9], @@ -2297,12 +2287,10 @@ def EllinghamHorton78Graph(): g._line_embedding([60, 61, 62, 63], first=(-1, 2), last=(1, 2)) g._line_embedding([64, 65, 37], first=(-.5, 1.5), last=(1.2, 1.5)) - g._line_embedding([66, 73, 67, 68, 69], first=(1.2, -2), - last=(-.8, -2)) + g._line_embedding([66, 73, 67, 68, 69], first=(1.2, -2), last=(-.8, -2)) g._line_embedding([66, 70, 71], first=(.7, -1.5), last=(-1, -1.5)) g.name("Ellingham-Horton 78-graph") - return g def ErreraGraph(): @@ -2314,7 +2302,7 @@ def ErreraGraph(): EXAMPLES: The Errera graph is named after Alfred Errera. It is a planar graph on 17 - vertices and having 45 edges. :: + vertices and having 45 edges:: sage: G = graphs.ErreraGraph(); G Errera graph: Graph on 17 vertices @@ -2326,7 +2314,7 @@ def ErreraGraph(): 45 The Errera graph is Hamiltonian with radius 3, diameter 4, girth 3, and - chromatic number 4. :: + chromatic number 4:: sage: G.is_hamiltonian() True @@ -2341,14 +2329,14 @@ def ErreraGraph(): Each vertex degree is either 5 or 6. That is, if `f` counts the number of vertices of degree 5 and `s` counts the number of vertices of degree 6, then - `f + s` is equal to the order of the Errera graph. :: + `f + s` is equal to the order of the Errera graph:: sage: D = G.degree_sequence() sage: D.count(5) + D.count(6) == G.order() True The automorphism group of the Errera graph is isomorphic to the dihedral - group of order 20. :: + group of order 20:: sage: ag = G.automorphism_group() sage: ag.is_isomorphic(DihedralGroup(10)) @@ -2394,7 +2382,7 @@ def F26AGraph(): (x - 3) * (x + 3) * (x^4 - 5*x^2 + 3)^6 """ from sage.graphs.generators.families import LCFGraph - g= LCFGraph(26, [7,-7],13) + g= LCFGraph(26, [7, -7], 13) g.name("F26A Graph") return g @@ -2421,7 +2409,7 @@ def FlowerSnark(): Now show it:: - sage: F.show() # long time + sage: F.show() # long time """ pos_dict = {} for i in range(15): @@ -2432,11 +2420,10 @@ def FlowerSnark(): x = float(cos((pi/2) + ((2*pi)/5)*i)) y = float(sin((pi/2) + ((2*pi)/5)*i)) pos_dict[i] = (x,y) - return Graph({0:[1,14,15],1:[2,11],2:[3,7],3:[2,4,16],4:[5,14], \ - 5:[6,10],6:[5,7,17],8:[7,9,13],9:[10,18],11:[10,12], \ - 12:[13,19],13:[14],15:[19],16:[15,17],18:[17,19]}, \ - pos=pos_dict, name="Flower Snark") - + d = {0: [1, 14, 15], 1: [2, 11], 2: [3, 7], 3: [2, 4, 16], 4: [5, 14], + 5: [6, 10], 6: [5, 7, 17], 8: [7, 9, 13], 9: [10, 18], 11: [10, 12], + 12: [13, 19], 13: [14], 15: [19], 16: [15, 17], 18: [17, 19]} + return Graph(d, format="dict_of_lists", pos=pos_dict, name="Flower Snark") def FolkmanGraph(): """ @@ -2473,7 +2460,6 @@ def FolkmanGraph(): g.name("Folkman Graph") return g - def FosterGraph(): """ Return the Foster graph. @@ -2501,7 +2487,6 @@ def FosterGraph(): g.name("Foster Graph") return g - def FranklinGraph(): r""" Return the Franklin graph. @@ -2511,7 +2496,7 @@ def FranklinGraph(): EXAMPLES: The Franklin graph is named after Philip Franklin. It is a 3-regular graph - on 12 vertices and having 18 edges. :: + on 12 vertices and having 18 edges:: sage: G = graphs.FranklinGraph(); G Franklin graph: Graph on 12 vertices @@ -2523,7 +2508,7 @@ def FranklinGraph(): 18 The Franklin graph is a Hamiltonian, bipartite graph with radius 3, diameter - 3, and girth 4. :: + 3, and girth 4:: sage: G.is_hamiltonian() True @@ -2536,7 +2521,7 @@ def FranklinGraph(): sage: G.girth() 4 - It is a perfect, triangle-free graph having chromatic number 2. :: + It is a perfect, triangle-free graph having chromatic number 2:: sage: G.is_perfect() True @@ -2576,7 +2561,7 @@ def FruchtGraph(): Return a Frucht Graph. A Frucht graph has 12 nodes and 18 edges. It is the smallest cubic identity - graph. It is planar and it is Hamiltonian. See the :wikipedia:`Frucht_graph`. + graph. It is planar and Hamiltonian. See the :wikipedia:`Frucht_graph`. PLOTTING: Upon construction, the position dictionary is filled to override the spring-layout algorithm. By convention, the first seven nodes are on the @@ -2590,7 +2575,7 @@ def FruchtGraph(): Frucht graph: Graph on 12 vertices sage: FRUCHT.graph6_string() 'KhCKM?_EGK?L' - sage: (graphs.FruchtGraph()).show() # long time + sage: (graphs.FruchtGraph()).show() # long time TESTS:: @@ -2621,8 +2606,8 @@ def GoldnerHararyGraph(): EXAMPLES: - The Goldner-Harary graph is named after A. Goldner and Frank Harary. It is - a planar graph having 11 vertices and 27 edges. :: + The Goldner-Harary graph is named after A. Goldner and Frank Harary. It is + a planar graph having 11 vertices and 27 edges:: sage: G = graphs.GoldnerHararyGraph(); G Goldner-Harary graph: Graph on 11 vertices @@ -2633,8 +2618,7 @@ def GoldnerHararyGraph(): sage: G.size() 27 - The Goldner-Harary graph is chordal with radius 2, diameter 2, and girth - 3. :: + The Goldner-Harary graph is chordal with radius 2, diameter 2, and girth 3:: sage: G.is_chordal() True @@ -2646,7 +2630,7 @@ def GoldnerHararyGraph(): 3 Its chromatic number is 4 and its automorphism group is isomorphic to the - dihedral group `D_6`. :: + dihedral group `D_6`:: sage: G.chromatic_number() 4 @@ -2731,8 +2715,8 @@ def GrayGraph(embedding=1): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -2746,18 +2730,17 @@ def GrayGraph(embedding=1): sage: g.diameter() 6 sage: g.show(figsize=[10, 10]) # long time - sage: graphs.GrayGraph(embedding = 2).show(figsize=[10, 10]) # long time + sage: graphs.GrayGraph(embedding=2).show(figsize=[10, 10]) # long time TESTS:: - sage: graphs.GrayGraph(embedding = 3) + sage: graphs.GrayGraph(embedding=3) Traceback (most recent call last): ... ValueError: the value of embedding must be 1, 2, or 3 """ - from sage.graphs.generators.families import LCFGraph - g = LCFGraph(54, [-25,7,-7,13,-13,25], 9) + g = LCFGraph(54, [-25, 7, -7, 13, -13, 25], 9) g.name("Gray graph") if embedding == 1: @@ -2781,7 +2764,7 @@ def GrotzschGraph(): EXAMPLES: The Grötzsch graph is named after Herbert Grötzsch. It is a Hamiltonian - graph with 11 vertices and 20 edges. :: + graph with 11 vertices and 20 edges:: sage: G = graphs.GrotzschGraph(); G Grotzsch graph: Graph on 11 vertices @@ -2793,7 +2776,7 @@ def GrotzschGraph(): 20 The Grötzsch graph is triangle-free and having radius 2, diameter 2, and - girth 4. :: + girth 4:: sage: G.is_triangle_free() True @@ -2805,7 +2788,7 @@ def GrotzschGraph(): 4 Its chromatic number is 4 and its automorphism group is isomorphic to the - dihedral group `D_5`. :: + dihedral group `D_5`:: sage: G.chromatic_number() 4 @@ -2813,48 +2796,36 @@ def GrotzschGraph(): sage: ag.is_isomorphic(DihedralGroup(5)) True """ - g = Graph() - g.add_vertices(range(11)) - - edges = []; - for u in range(1,6): - edges.append( (0,u) ) - - edges.append( (10,6) ) - - for u in range(6,10): - edges.append( (u,u+1) ) - edges.append( (u,u-4) ) + edges = [(0, u) for u in range(1, 6)] + edges.append((10, 6)) + edges.append((10, 1)) + edges.append((6, 5)) - edges.append( (10,1) ) + for u in range(6, 10): + edges.append((u, u + 1)) + edges.append((u, u - 4)) - for u in range(7,11): - edges.append( (u,u-6) ) - - edges.append((6,5)) - - g.add_edges(edges) + for u in range(7, 11): + edges.append((u, u - 6)) pos = {} pos[0] = (0,0) - for u in range(1,6): - theta = (u-1)*2*pi/5 - pos[u] = (float(5*sin(theta)),float(5*cos(theta))) - pos[u+5] = (2*pos[u][0], 2*pos[u][1]) + for u in range(1, 6): + theta = (u - 1) * 2 * pi / 5 + pos[u] = (float(5 * sin(theta)), float(5 * cos(theta))) + pos[u + 5] = (2 * pos[u][0], 2 * pos[u][1]) - g.set_pos(pos) - g.name("Grotzsch graph") - return g + return Graph(edges, format='list_of_edges', pos=pos, name="Grotzsch graph") def HeawoodGraph(): """ Return a Heawood graph. The Heawood graph is a cage graph that has 14 nodes. It is a cubic symmetric - graph. (See also the Möbius-Kantor graph). It is nonplanar and - Hamiltonian. It has diameter = 3, radius = 3, girth = 6, chromatic number = - 2. It is 4-transitive but not 5-transitive. See the - :wikipedia:`Heawood_graph`. + graph. (See also the Möbius-Kantor graph, :meth:`~MobiusKantorGraph`). It is + nonplanar and Hamiltonian. It has diameter 3, radius 3, girth 6, and + chromatic number 2. It is 4-transitive but not 5-transitive. + See the :wikipedia:`Heawood_graph`. PLOTTING: Upon construction, the position dictionary is filled to override the spring-layout algorithm. By convention, the nodes are positioned in a @@ -2868,7 +2839,7 @@ def HeawoodGraph(): Heawood graph: Graph on 14 vertices sage: H.graph6_string() 'MhEGHC@AI?_PC@_G_' - sage: (graphs.HeawoodGraph()).show() # long time + sage: (graphs.HeawoodGraph()).show() # long time TESTS:: @@ -2895,7 +2866,7 @@ def HerschelGraph(): EXAMPLES: The Herschel graph is named after Alexander Stewart Herschel. It is a - planar, bipartite graph with 11 vertices and 18 edges. :: + planar, bipartite graph with 11 vertices and 18 edges:: sage: G = graphs.HerschelGraph(); G Herschel graph: Graph on 11 vertices @@ -2909,7 +2880,7 @@ def HerschelGraph(): 18 The Herschel graph is a perfect graph with radius 3, diameter 4, and girth - 4. :: + 4:: sage: G.is_perfect() True @@ -2921,7 +2892,7 @@ def HerschelGraph(): 4 Its chromatic number is 2 and its automorphism group is isomorphic to the - dihedral group `D_6`. :: + dihedral group `D_6`:: sage: G.chromatic_number() 2 @@ -2961,13 +2932,13 @@ def HigmanSimsGraph(relabel=True): The Higman-Sims graph is a remarkable strongly regular graph of degree 22 on 100 vertices. For example, it can be split into two sets of 50 vertices each, so that each half induces a subgraph isomorphic to the - Hoffman-Singleton graph (:meth:`~HoffmanSingletonGraph`). This can be done + Hoffman-Singleton graph (:meth:`~HoffmanSingletonGraph`). This can be done in 352 ways (see `Higman-Sims graph `_ by Andries E. Brouwer, accessed 24 October 2009.) Its most famous property is that the automorphism group has an index 2 - subgroup which is one of the 26 sporadic groups. [HS1968]_ + subgroup which is one of the 26 sporadic groups [HS1968]_. The construction used here follows [Haf2004]_. @@ -2975,11 +2946,12 @@ def HigmanSimsGraph(relabel=True): INPUT: - - ``relabel`` - default: ``True``. If ``True`` the vertices will be labeled - with consecutive integers. If ``False`` the labels are strings that are - three digits long. "xyz" means the vertex is in group x (zero through - three), pentagon or pentagram y (zero through four), and is vertex z (zero - through four) of that pentagon or pentagram. See [Haf2004]_ for more. + - ``relabel`` -- boolean (default: ``True``); whether to relabel the + vertices with consecutive integers. If ``False`` the labels are strings + that are three digits long. "xyz" means the vertex is in group `x` (zero + through three), pentagon or pentagram `y` (zero through four), and is + vertex `z` (zero through four) of that pentagon or pentagram. See + [Haf2004]_ for more. OUTPUT: @@ -2989,7 +2961,7 @@ def HigmanSimsGraph(relabel=True): A split into the first 50 and last 50 vertices will induce two copies of the Hoffman-Singleton graph, and we illustrate another such split, which is - obvious based on the construction used. :: + obvious based on the construction used:: sage: H = graphs.HigmanSimsGraph() sage: A = H.subgraph(range(0,50)) @@ -3003,7 +2975,7 @@ def HigmanSimsGraph(relabel=True): True The automorphism group contains only one nontrivial proper normal subgroup, - which is of index 2 and is simple. It is known as the Higman-Sims group. :: + which is of index 2 and is simple. It is known as the Higman-Sims group:: sage: H = graphs.HigmanSimsGraph() sage: G = H.automorphism_group() @@ -3025,66 +2997,65 @@ def HigmanSimsGraph(relabel=True): # Four groups of either five pentagons, or five pentagrams 4 x 5 x 5 = 100 # vertices # First digit is "group", second is "penta{gon|gram}", third is "vertex" - vlist = ['%d%d%d'%(g,p,v) + vlist = ['%d%d%d'%(g, p, v) for g in range(4) for p in range(5) for v in range(5)] - for avertex in vlist: - HS.add_vertex(avertex) + HS.add_vertices(vlist) # Edges: Within groups 0 and 2, joined as pentagons # Edges: Within groups 1 and 3, joined as pentagrams for g in range(4): shift = 1 - if g in [1,3]: + if g in [1, 3]: shift += 1 for p in range(5): for v in range(5): - HS.add_edge(('%d%d%d'%(g,p,v), '%d%d%d'%(g,p,(v+shift)%5))) + HS.add_edge(('%d%d%d'%(g, p, v), '%d%d%d'%(g, p, (v + shift) % 5))) # Edges: group 0 to group 1 for x in range(5): for m in range(5): for c in range(5): - y = (m*x+c)%5 - HS.add_edge(('0%d%d'%(x,y), '1%d%d'%(m,c))) + y = (m * x + c) % 5 + HS.add_edge(('0%d%d'%(x, y), '1%d%d'%(m, c))) # Edges: group 1 to group 2 for m in range(5): for A in range(5): for B in range(5): - c = (2*(m-A)*(m-A)+B)%5 - HS.add_edge(('1%d%d'%(m,c), '2%d%d'%(A,B))) + c = (2 * (m - A) * (m - A) + B) % 5 + HS.add_edge(('1%d%d'%(m, c), '2%d%d'%(A, B))) # Edges: group 2 to group 3 for A in range(5): for a in range(5): for b in range(5): - B = (2*A*A+3*a*A-a*a+b)%5 - HS.add_edge(('2%d%d'%(A,B), '3%d%d'%(a,b))) + B = (2*A*A + 3*a*A - a*a+b) % 5 + HS.add_edge(('2%d%d'%(A, B), '3%d%d'%(a, b))) # Edges: group 3 to group 0 for a in range(5): for b in range(5): for x in range(5): - y = ((x-a)*(x-a)+b)%5 - HS.add_edge(('3%d%d'%(a,b), '0%d%d'%(x,y))) + y = ((x - a) * (x - a) + b)%5 + HS.add_edge(('3%d%d'%(a, b), '0%d%d'%(x, y))) # Edges: group 0 to group 2 for x in range(5): for A in range(5): for B in range(5): - y = (3*x*x+A*x+B+1)%5 - HS.add_edge(('0%d%d'%(x,y), '2%d%d'%(A,B))) - y = (3*x*x+A*x+B-1)%5 - HS.add_edge(('0%d%d'%(x,y), '2%d%d'%(A,B))) + y = (3*x*x + A*x + B + 1) % 5 + HS.add_edge(('0%d%d'%(x, y), '2%d%d'%(A, B))) + y = (3*x*x + A*x + B - 1) % 5 + HS.add_edge(('0%d%d'%(x, y), '2%d%d'%(A, B))) # Edges: group 1 to group 3 for m in range(5): for a in range(5): for b in range(5): - c = (m*(m-a)+b+2)%5 - HS.add_edge(('1%d%d'%(m,c), '3%d%d'%(a,b))) - c = (m*(m-a)+b-2)%5 - HS.add_edge(('1%d%d'%(m,c), '3%d%d'%(a,b))) + c = (m*(m-a) + b + 2) % 5 + HS.add_edge(('1%d%d'%(m, c), '3%d%d'%(a, b))) + c = (m*(m-a) + b - 2) % 5 + HS.add_edge(('1%d%d'%(m, c), '3%d%d'%(a, b))) # Rename to integer vertex labels, creating dictionary # Or not, and create identity mapping @@ -3099,7 +3070,7 @@ def HigmanSimsGraph(relabel=True): for i in range(100): x = float(cos((pi/2) + ((2*pi)/100)*i)) y = float(sin((pi/2) + ((2*pi)/100)*i)) - pos_dict[vmap[vlist[i]]] = (x,y) + pos_dict[vmap[vlist[i]]] = (x, y) HS.set_pos(pos_dict) return HS @@ -3157,37 +3128,37 @@ def HoffmanSingletonGraph(): for j in range(5): for i in range(5): for k in range(5): - con = (i+j*k)%5 - H.add_edge(('q%d%d'%(k,con),'p%d%d'%(j,i))) + con = (i + j*k) % 5 + H.add_edge(('q%d%d'%(k, con),'p%d%d'%(j, i))) H.name('Hoffman-Singleton graph') from sage.combinat.permutation import Permutations from sage.misc.prandom import randint - P = Permutations([1,2,3,4]) - qpp = [0] + list(P[randint(0,23)]) - ppp = [0] + list(P[randint(0,23)]) - qcycle = lambda i,s : ['q%s%s'%(i,(j+s)%5) for j in qpp] - pcycle = lambda i,s : ['p%s%s'%(i,(j+s)%5) for j in ppp] + P = Permutations([1, 2, 3, 4]) + qpp = [0] + list(P[randint(0, 23)]) + ppp = [0] + list(P[randint(0, 23)]) + qcycle = lambda i, s: ['q%s%s'%(i, (j + s) % 5) for j in qpp] + pcycle = lambda i, s: ['p%s%s'%(i, (j + s) % 5) for j in ppp] l = 0 s = 0 D = [] while l < 5: - for q in qcycle(l,s): + for q in qcycle(l, s): D.append(q) - vv = 'p%s'%q[1] + vv = 'p%s' % q[1] s = int([v[-1] for v in H.neighbors(q) if v[:2] == vv][0]) - for p in pcycle(l,s): + for p in pcycle(l, s): D.append(p) - vv = 'q%s'%(int(p[1])+1) + vv = 'q%s' % (int(p[1]) + 1) v = [v[-1] for v in H.neighbors(p) if v[:2] == vv] if len(v): s = int(v[0]) - l+=1 + l += 1 map = H.relabel(range(50), return_map=True) pos_dict = {} for i in range(50): x = float(cos((pi/2) + ((2*pi)/50)*i)) y = float(sin((pi/2) + ((2*pi)/50)*i)) - pos_dict[map[D[i]]] = (x,y) + pos_dict[map[D[i]]] = (x, y) H.set_pos(pos_dict) return H @@ -3202,7 +3173,7 @@ def HoffmanGraph(): sage: g = graphs.HoffmanGraph() sage: g.is_bipartite() True - sage: g.is_hamiltonian() # long time + sage: g.is_hamiltonian() # long time True sage: g.radius() 3 @@ -3250,7 +3221,7 @@ def HoltGraph(): True sage: g.chromatic_number() 3 - sage: g.is_hamiltonian() # long time + sage: g.is_hamiltonian() # long time True sage: g.radius() 3 @@ -3261,18 +3232,18 @@ def HoltGraph(): sage: g.automorphism_group().cardinality() 54 """ - g = Graph(loops=False, name = "Holt graph", pos={}) + g = Graph(loops=False, name="Holt graph", pos={}) for x in range(9): for y in range(3): - g.add_edge((x,y),((4*x+1)%9,(y-1)%3)) - g.add_edge((x,y),((4*x-1)%9,(y-1)%3)) - g.add_edge((x,y),((7*x+7)%9,(y+1)%3)) - g.add_edge((x,y),((7*x-7)%9,(y+1)%3)) + g.add_edge((x, y), ((4 * x + 1) % 9, (y - 1) % 3)) + g.add_edge((x, y), ((4 * x - 1) % 9, (y - 1) % 3)) + g.add_edge((x, y), ((7 * x + 7) % 9, (y + 1) % 3)) + g.add_edge((x, y), ((7 * x - 7) % 9, (y + 1) % 3)) - for j in range(0,6,2): - g._line_embedding([(x,j/2) for x in range(9)], - first=(cos(2*j*pi/6),sin(2*j*pi/6)), - last=(cos(2*(j+1)*pi/6),sin(2*(j+1)*pi/6))) + for j in range(0, 6, 2): + g._line_embedding([(x, j / 2) for x in range(9)], + first=(cos(2 * j * pi / 6), sin(2 * j * pi / 6)), + last=(cos(2 * (j + 1) * pi / 6), sin(2 * (j + 1) * pi / 6))) return g @@ -3283,7 +3254,7 @@ def KrackhardtKiteGraph(): The Krackhardt kite graph was originally developed by David Krackhardt for the purpose of studying social networks (see [Kre2002]_ and the :wikipedia:`Krackhardt_kite_graph`). It is used to show the distinction - between: degree centrality, betweenness centrality, and closeness + between degree centrality, betweenness centrality, and closeness centrality. For more information read the plotting section below in conjunction with the example. @@ -3305,7 +3276,7 @@ def KrackhardtKiteGraph(): Construct and show a Krackhardt kite graph :: sage: g = graphs.KrackhardtKiteGraph() - sage: g.show() # long time + sage: g.show() # long time TESTS:: @@ -3314,19 +3285,20 @@ def KrackhardtKiteGraph(): sage: G.is_isomorphic(Graph(networkx.krackhardt_kite_graph())) True """ - edges = {0:[1, 2, 3, 5], 1:[3, 4, 6], 2:[3, 5], 3:[4, 5, 6], - 4:[6], 5:[6, 7], 6:[7], 7:[8], 8:[9]} - pos_dict = {0:(-1,4),1:(1,4),2:(-2,3),3:(0,3),4:(2,3),5:(-1,2),6:(1,2),7:(0,1),8:(0,0),9:(0,-1)} + edges = {0: [1, 2, 3, 5], 1: [3, 4, 6], 2: [3, 5], 3: [4, 5, 6], + 4: [6], 5: [6, 7], 6: [7], 7: [8], 8: [9]} + pos_dict = {0: (-1, 4), 1: (1, 4), 2: (-2, 3), 3: (0, 3), 4: (2, 3), + 5: (-1, 2), 6: (1, 2), 7: (0, 1), 8: (0, 0), 9: (0, -1)} return Graph(edges, pos=pos_dict, name="Krackhardt Kite Graph") def Klein3RegularGraph(): r""" Return the Klein 3-regular graph. - The cubic Klein graph has 56 vertices and can be embedded on a surface of - genus 3. It is the dual of - :meth:`~sage.graphs.graph_generators.GraphGenerators.Klein7RegularGraph`. For - more information, see the :wikipedia:`Klein_graphs`. + The cubic Klein graph has 56 vertices and can be embedded on a + surface of genus 3. It is the dual of + :meth:`~sage.graphs.graph_generators.GraphGenerators.Klein7RegularGraph`. + For more information, see the :wikipedia:`Klein_graphs`. EXAMPLES:: @@ -3341,7 +3313,7 @@ def Klein3RegularGraph(): sage: g.chromatic_number() 3 """ - g3 = Graph(':w`_GKWDBap`CMWFCpWsQUNdBwwuXPHrg`U`RIqypehVLqgHupYcFJyAv^Prk]'+ + g3 = Graph(':w`_GKWDBap`CMWFCpWsQUNdBwwuXPHrg`U`RIqypehVLqgHupYcFJyAv^Prk]' 'EcarHwIVHAKh|\\tLVUxT]`ZDTJ{Af[o_AuKs{r_?ef', loops=False, multiedges=False) g3._circle_embedding([0, 2, 3, 4, 6, 8, 14, 1, 37, 30, 34, 48, 55, 43, 40, @@ -3357,8 +3329,8 @@ def Klein7RegularGraph(): The 7-valent Klein graph has 24 vertices and can be embedded on a surface of genus 3. It is the dual of - :meth:`~sage.graphs.graph_generators.GraphGenerators.Klein3RegularGraph`. For - more information, see the :wikipedia:`Klein_graphs`. + :meth:`~sage.graphs.graph_generators.GraphGenerators.Klein3RegularGraph`. + For more information, see the :wikipedia:`Klein_graphs`. EXAMPLES:: @@ -3392,9 +3364,9 @@ def LocalMcLaughlinGraph(): EXAMPLES:: - sage: g = graphs.LocalMcLaughlinGraph(); g # long time # optional - gap_packages + sage: g = graphs.LocalMcLaughlinGraph(); g # long time # optional - gap_packages Local McLaughlin Graph: Graph on 162 vertices - sage: g.is_strongly_regular(parameters=True) # long time # optional - gap_packages + sage: g.is_strongly_regular(parameters=True) # long time # optional - gap_packages (162, 56, 10, 24) """ g = McLaughlinGraph() @@ -3417,8 +3389,8 @@ def LjubljanaGraph(embedding=1): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``1``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -3441,7 +3413,6 @@ def LjubljanaGraph(embedding=1): ... ValueError: the value of embedding must be 1 or 2 """ - L = [47, -23, -31, 39, 25, -21, -31, -41, 25, 15, 29, -41, -19, 15, -49, 33, 39, -35, -21, 17, -33, 49, 41, 31, -15, -29, 41, 31, -15, -25, 21, 31, -51, -25, 23, 9, -17, 51, 35, -29, 21, -51, @@ -3456,7 +3427,6 @@ def LjubljanaGraph(embedding=1): # Correspondence between the vertices of the Heawood Graph and 8-sets of # the Ljubljana Graph. - d = { 0: [1, 21, 39, 57, 51, 77, 95, 107], 1: [2, 22, 38, 58, 50, 78, 94, 106], @@ -3476,19 +3446,15 @@ def LjubljanaGraph(embedding=1): # The vertices of each 8-set are plotted on a circle, and the # circles are slowly shifted to obtain a symmetric drawing. - for i, (u, vertices) in enumerate(d.items()): g._circle_embedding(vertices, center=dh[u], radius=.1, shift=8.*i/14) - return g - - elif embedding == 2: - return g - - else: + elif embedding != 2: raise ValueError("the value of embedding must be 1 or 2") + return g + def LivingstoneGraph(): r""" Return the Livingstone Graph. @@ -3500,16 +3466,16 @@ def LivingstoneGraph(): EXAMPLES:: - sage: g = graphs.LivingstoneGraph() # optional - internet - sage: g.order() # optional - internet + sage: g = graphs.LivingstoneGraph() # optional - internet + sage: g.order() # optional - internet 266 - sage: g.size() # optional - internet + sage: g.size() # optional - internet 1463 - sage: g.girth() # optional - internet + sage: g.girth() # optional - internet 5 - sage: g.is_vertex_transitive() # optional - internet + sage: g.is_vertex_transitive() # optional - internet True - sage: g.is_distance_regular() # optional - internet + sage: g.is_distance_regular() # optional - internet True """ from sage.groups.perm_gps.permgroup_named import JankoGroup @@ -3583,18 +3549,19 @@ def MarkstroemGraph(): g = Graph(name="Markstroem Graph") g.add_cycle(list(range(9))) - g.add_path([0,9,10,11,2,1,11]) - g.add_path([3,12,13,14,5,4,14]) - g.add_path([6,15,16,17,8,7,17]) - g.add_cycle([10,9,18]) - g.add_cycle([12,13,19]) - g.add_cycle([15,16,20]) - g.add_cycle([21,22,23]) - g.add_edges([(19,22),(18,21),(20,23)]) - - g._circle_embedding(sum([[9+3*i+j for j in range(3)]+[0]*2 for i in range(3)],[]), radius=.6, shift=.7) - g._circle_embedding([18,19,20], radius=.35, shift=.25) - g._circle_embedding([21,22,23], radius=.15, shift=.25) + g.add_path([0, 9, 10, 11, 2, 1, 11]) + g.add_path([3, 12, 13, 14, 5, 4, 14]) + g.add_path([6, 15, 16, 17, 8, 7, 17]) + g.add_cycle([10, 9, 18]) + g.add_cycle([12, 13, 19]) + g.add_cycle([15, 16, 20]) + g.add_cycle([21, 22, 23]) + g.add_edges([(19, 22), (18, 21), (20, 23)]) + + g._circle_embedding(sum([[9 + 3*i + j for j in range(3)] + [0]*2 for i in range(3)], []), + radius=.6, shift=.7) + g._circle_embedding([18, 19, 20], radius=.35, shift=.25) + g._circle_embedding([21, 22, 23], radius=.15, shift=.25) g._circle_embedding(list(range(9))) return g @@ -3607,8 +3574,8 @@ def McGeeGraph(embedding=2): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``2``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -3622,7 +3589,7 @@ def McGeeGraph(embedding=2): sage: g.diameter() 4 sage: g.show() - sage: graphs.McGeeGraph(embedding=1).show() + sage: graphs.McGeeGraph(embedding=1).show() # long time TESTS:: @@ -3670,10 +3637,10 @@ def McLaughlinGraph(): EXAMPLES:: - sage: g = graphs.McLaughlinGraph() # optional gap_packages - sage: g.is_strongly_regular(parameters=True) # optional gap_packages + sage: g = graphs.McLaughlinGraph() # optional gap_packages + sage: g.is_strongly_regular(parameters=True) # optional gap_packages (275, 112, 30, 56) - sage: set(g.spectrum()) == {112, 2, -28} # optional gap_packages + sage: set(g.spectrum()) == {112, 2, -28} # optional gap_packages True """ from sage.combinat.designs.block_design import WittDesign @@ -3686,7 +3653,7 @@ def McLaughlinGraph(): C = [b for b in blocks if 0 not in b] g = Graph() for b in B: - for x in range(1,23): + for x in range(1, 23): if not x in b: g.add_edge(b, x) @@ -3719,8 +3686,8 @@ def MoebiusKantorGraph(): A Möbius-Kantor graph is a cubic symmetric graph. (See also the Heawood graph). It has 16 nodes and 24 edges. It is nonplanar and Hamiltonian. It - has diameter = 4, girth = 6, and chromatic number = 2. It is identical to - the Generalized Petersen graph, P[8,3]. + has diameter 4, girth 6, and chromatic number 2. It is identical to the + Generalized Petersen graph, P[8, 3]. For more details, see `Möbius-Kantor Graph - from Wolfram MathWorld `_. @@ -3734,10 +3701,10 @@ def MoebiusKantorGraph(): Moebius-Kantor Graph: Graph on 16 vertices sage: MK.graph6_string() 'OhCGKE?O@?ACAC@I?Q_AS' - sage: (graphs.MoebiusKantorGraph()).show() # long time + sage: (graphs.MoebiusKantorGraph()).show() # long time """ from sage.graphs.generators.families import GeneralizedPetersenGraph - G=GeneralizedPetersenGraph(8,3) + G = GeneralizedPetersenGraph(8, 3) G.name("Moebius-Kantor Graph") return G @@ -3814,8 +3781,8 @@ def NauruGraph(embedding=2): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``2``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -3829,7 +3796,7 @@ def NauruGraph(embedding=2): sage: g.diameter() 4 sage: g.show() - sage: graphs.NauruGraph(embedding=1).show() + sage: graphs.NauruGraph(embedding=1).show() # long time TESTS:: @@ -3871,16 +3838,16 @@ def PappusGraph(): """ pos_dict = {} for i in range(6): - pos_dict[i] = [float(cos(pi/2 + ((2*pi)/6)*i)),\ + pos_dict[i] = [float(cos(pi/2 + ((2*pi)/6)*i)), float(sin(pi/2 + ((2*pi)/6)*i))] - pos_dict[6 + i] = [(2/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)),\ + pos_dict[6 + i] = [(2/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)), (2/3.0)*float(sin(pi/2 + ((2*pi)/6)*i))] - pos_dict[12 + i] = [(1/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)),\ + pos_dict[12 + i] = [(1/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)), (1/3.0)*float(sin(pi/2 + ((2*pi)/6)*i))] - return Graph({0:[1,5,6],1:[2,7],2:[3,8],3:[4,9],4:[5,10],\ - 5:[11],6:[13,17],7:[12,14],8:[13,15],9:[14,16],\ - 10:[15,17],11:[12,16],12:[15],13:[16],14:[17]},\ - pos=pos_dict, name="Pappus Graph") + edges = {0: [1, 5, 6], 1: [2, 7], 2: [3, 8], 3: [4, 9], 4: [5, 10], 5: [11], + 6: [13, 17], 7: [12, 14], 8: [13, 15], 9: [14, 16], 10: [15, 17], + 11: [12, 16], 12: [15], 13: [16], 14: [17]} + return Graph(edges, pos=pos_dict, name="Pappus Graph") def PoussinGraph(): r""" @@ -3897,13 +3864,14 @@ def PoussinGraph(): sage: g.is_planar() True """ - g = Graph({2:[7,8,3,4],1:[7,6],0:[6,5,4],3:[5]},name="Poussin Graph") + g = Graph({2: [7, 8, 3, 4], 1: [7, 6], 0: [6, 5, 4], 3: [5]}, + name="Poussin Graph") g.add_cycle(list(range(3))) g.add_cycle(list(range(3, 9))) g.add_cycle(list(range(9, 14))) - g.add_path([8,12,7,11,6,10,5,9,3,13,8,12]) - g.add_edges([(14,i) for i in range(9,14)]) + g.add_path([8, 12, 7, 11, 6, 10, 5, 9, 3, 13, 8, 12]) + g.add_edges([(14, i) for i in range(9, 14)]) g._circle_embedding(list(range(3)), shift=.75) g._circle_embedding(list(range(3, 9)), radius=.4, shift=0) g._circle_embedding(list(range(9, 14)), radius=.2, shift=.4) @@ -3912,7 +3880,7 @@ def PoussinGraph(): return g def PetersenGraph(): - """ + r""" Return the Petersen Graph. The Petersen Graph is a named graph that consists of 10 vertices and 15 @@ -3924,15 +3892,18 @@ def PetersenGraph(): PLOTTING: See the plotting section for the generalized Petersen graphs. EXAMPLES: We compare below the Petersen graph with the default spring-layout - versus a planned position dictionary of [x,y] tuples:: + versus a planned position dictionary of `(x, y)` tuples:: - sage: petersen_spring = Graph({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,8,9], 7:[2,5,9], 8:[3,5,6], 9:[4,6,7]}) - sage: petersen_spring.show() # long time + sage: petersen_spring = Graph({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,8,9], 7:[2,5,9], 8:[3,5,6], + ....: 9:[4,6,7]}) + sage: petersen_spring.show() # long time sage: petersen_database = graphs.PetersenGraph() - sage: petersen_database.show() # long time + sage: petersen_database.show() # long time """ from sage.graphs.generators.families import GeneralizedPetersenGraph - P=GeneralizedPetersenGraph(5,2) + P = GeneralizedPetersenGraph(5, 2) P.name("Petersen graph") return P @@ -3996,7 +3967,6 @@ def RobertsonGraph(): g.name("Robertson Graph") return g - def SchlaefliGraph(): r""" Return the Schläfli graph. @@ -4031,13 +4001,14 @@ def SchlaefliGraph(): The neighborhood of each vertex is isomorphic to the complement of the Clebsch graph:: - sage: neighborhood = S.subgraph(vertices = S.neighbors(0)) + sage: neighborhood = S.subgraph(vertices=S.neighbors(0)) sage: graphs.ClebschGraph().complement().is_isomorphic(neighborhood) True """ from sage.graphs.graph import Graph G = Graph('ZBXzr|}^z~TTitjLth|dmkrmsl|if}TmbJMhrJX]YfFyTbmsseztKTvyhDvw') - order = [1,8,5,10,2,6,11,15,17,13,18,12,9,24,25,3,26,7,16,20,23,0,21,14,22,4,19] + order = [1, 8, 5, 10, 2, 6, 11, 15, 17, 13, 18, 12, 9, 24, 25, 3, 26, 7, + 16, 20, 23, 0, 21, 14, 22, 4, 19] G._circle_embedding(order) G.name("Schläfli graph") return G @@ -4162,7 +4133,7 @@ def SylvesterGraph(): True """ g = HoffmanSingletonGraph() - e = next(g.edge_iterator(labels = False)) + e = next(g.edge_iterator(labels=False)) g.delete_vertices(g.neighbors(e[0]) + g.neighbors(e[1])) g.relabel() ordering = [0, 1, 2, 4, 5, 9, 16, 35, 15, 18, 20, 30, 22, 6, 33, 32, 14, @@ -4172,7 +4143,6 @@ def SylvesterGraph(): g.name("Sylvester Graph") return g - def SimsGewirtzGraph(): r""" Return the Sims-Gewirtz Graph. @@ -4200,10 +4170,9 @@ def SimsGewirtzGraph(): 280 sage: g.is_strongly_regular(parameters = True) (56, 10, 0, 2) - """ g = HigmanSimsGraph() - e = next(g.edge_iterator(labels = False)) + e = next(g.edge_iterator(labels=False)) g.delete_vertices(g.neighbors(e[0]) + g.neighbors(e[1])) g.relabel() ordering = [0, 2, 3, 4, 6, 7, 8, 17, 1, 41, 49, 5, 22, 26, 11, 27, 15, 47, @@ -4245,13 +4214,13 @@ def SousselierGraph(): g = Graph(name="Sousselier Graph") g.add_cycle(list(range(15))) - g.add_path([12,8,3,14]) - g.add_path([9,5,0,11]) - g.add_edge(6,2) - g.add_edges([(15,i) for i in range(15) if i%3==1]) + g.add_path([12, 8, 3, 14]) + g.add_path([9, 5, 0, 11]) + g.add_edge(6, 2) + g.add_edges([(15, i) for i in range(15) if i % 3 == 1]) g._circle_embedding(list(range(15)), shift=-.25) - g.get_pos()[15] = (0,0) + g.get_pos()[15] = (0, 0) return g @@ -4288,7 +4257,8 @@ def SzekeresSnarkGraph(): g.add_edge((i, 6), ((i + 2) % 5, 2)) g._circle_embedding([(i, j) for j in range(9)], radius=.3, - center=(cos(2 * (i + .25) * pi / 5), sin( 2 * (i +.25) * pi / 5)), + center=(cos(2 * (i + .25) * pi / 5), + sin(2 * (i + .25) * pi / 5)), shift=5.45 + 1.8 * i) g._circle_embedding(c, radius=1, shift=.25) @@ -4296,7 +4266,6 @@ def SzekeresSnarkGraph(): g.relabel() return g - def ThomsenGraph(): """ Return the Thomsen Graph. @@ -4315,9 +4284,10 @@ def ThomsenGraph(): 'EFz_' sage: (graphs.ThomsenGraph()).show() # long time """ - edges = {0:[3, 4, 5], 1:[3, 4, 5], 2:[3, 4, 5]} - pos_dict = {0:(-1,1),1:(0,1),2:(1,1),3:(-1,0),4:(0,0),5:(1,0)} - return Graph(edges, pos=pos_dict, name="Thomsen graph") + from sage.graphs.generators.basic import CompleteBipartiteGraph + G = CompleteBipartiteGraph(3, 3) + G.name("Thomsen graph") + return G def TietzeGraph(): r""" @@ -4342,12 +4312,12 @@ def TietzeGraph(): sage: g.automorphism_group().is_isomorphic(groups.permutation.Dihedral(6)) True """ - g = Graph([(0,9),(3,10),(6,11),(1,5),(2,7),(4,8)], name="Tietze Graph") + g = Graph([(0, 9), (3, 10), (6, 11), (1, 5), (2, 7), (4, 8)], + name="Tietze Graph") g.add_cycle(list(range(9))) - g.add_cycle([9,10,11]) + g.add_cycle([9, 10, 11]) g._circle_embedding(list(range(9))) g._circle_embedding([9, 10, 11], radius=.5) - return g def TruncatedIcosidodecahedralGraph(): @@ -4367,7 +4337,7 @@ def TruncatedIcosidodecahedralGraph(): Traceback (most recent call last): ... ValueError: *Error: Numerical inconsistency is found. Use the GMP exact arithmetic. - sage: g.order(), g.size() # not tested + sage: g.order(), g.size() # not tested (120, 180) """ from sage.geometry.polyhedron.library import polytopes @@ -4434,8 +4404,8 @@ def TutteCoxeterGraph(embedding=2): INPUT: - - ``embedding`` -- two embeddings are available, and can be selected by - setting ``embedding`` to 1 or 2. + - ``embedding`` -- integer (default: ``2``); two embeddings are available, + and can be selected by setting ``embedding`` to 1 or 2 EXAMPLES:: @@ -4449,7 +4419,7 @@ def TutteCoxeterGraph(embedding=2): sage: g.diameter() 4 sage: g.show() - sage: graphs.TutteCoxeterGraph(embedding=1).show() + sage: graphs.TutteCoxeterGraph(embedding=1).show() # long time TESTS:: @@ -4458,7 +4428,6 @@ def TutteCoxeterGraph(embedding=2): ... ValueError: the value of embedding must be 1 or 2 """ - from sage.graphs.generators.families import LCFGraph g = LCFGraph(30, [-13, -9, 7, -7, 9, 13], 5) g.name("Tutte-Coxeter graph") @@ -4480,14 +4449,11 @@ def TutteCoxeterGraph(embedding=2): g._circle_embedding(d[4], center=(-1, -1), radius=.25, shift=2) g._circle_embedding(d[5], center=(1, -1), radius=.25) - return g - - elif embedding == 2: - return g - - else: + elif embedding != 2: raise ValueError("the value of embedding must be 1 or 2") + return g + def TutteGraph(): r""" Return the Tutte Graph. @@ -4505,7 +4471,7 @@ def TutteGraph(): 69 sage: g.is_planar() True - sage: g.vertex_connectivity() # long time + sage: g.vertex_connectivity() # long time 3 sage: g.girth() 4 @@ -4518,19 +4484,19 @@ def TutteGraph(): g.add_cycle([(i,j) for i in range(3) for j in range(3) ]) for i in range(3): - g.add_cycle([(i,j) for j in range(9)]) - g.add_cycle([(i,j) for j in range(9,14)]) - g.add_edge((i,5),0) - g.add_edge((i,13),(i,3)) - g.add_edge((i,12),(i,1)) - g.add_edge((i,11),(i,8)) - g.add_edge((i,10),(i,7)) - g.add_edge((i,6),(i,14)) - g.add_edge((i,4),(i,14)) - g.add_edge((i,9),(i,14)) + g.add_cycle([(i, j) for j in range(9)]) + g.add_cycle([(i, j) for j in range(9, 14)]) + g.add_edge((i, 5), 0) + g.add_edge((i, 13), (i, 3)) + g.add_edge((i, 12), (i, 1)) + g.add_edge((i, 11), (i, 8)) + g.add_edge((i, 10), (i, 7)) + g.add_edge((i, 6), (i, 14)) + g.add_edge((i, 4), (i, 14)) + g.add_edge((i, 9), (i, 14)) g._circle_embedding([(i, j) for i in range(3) for j in range(6)], shift=.5) - g._circle_embedding([(i, 14) for i in range(3) ], radius=.3, shift=.25) + g._circle_embedding([(i, 14) for i in range(3)], radius=.3, shift=.25) for i in range(3): g._circle_embedding([(i, j) for j in range(3, 9)] + [0]*5, @@ -4590,22 +4556,22 @@ def WatkinsSnarkGraph(): g = Graph(name="Watkins Snark Graph") for i in range(5): - g.add_cycle([(i,j) for j in range(9)]) - g._circle_embedding([(i,j) for j in range(4)]+[0]*2+[(i,4)]+[0]*2+[(i,j) for j in range(5,9)], + g.add_cycle([(i, j) for j in range(9)]) + g._circle_embedding([(i, j) for j in range(4)] + [0, 0, (i, 4), 0, 0] + + [(i, j) for j in range(5, 9)], radius=.3, center=(cos(2*(i+.25)*pi/5), sin(2*(i+.25)*pi/5)), shift=2.7*i+7.55) - g.add_edge((i,5),((i+1)%5,0)) - g.add_edge((i,8),((i+2)%5,3)) - g.add_edge((i,1),i) - g.add_edge((i,7),i) - g.add_edge((i,4),i) - g.add_edge((i,6),(i,2)) + g.add_edge((i, 5), ((i + 1) % 5, 0)) + g.add_edge((i, 8), ((i + 2) % 5, 3)) + g.add_edge((i, 1), i) + g.add_edge((i, 7), i) + g.add_edge((i, 4), i) + g.add_edge((i, 6), (i, 2)) g._circle_embedding(list(range(5)), shift=.25, radius=1.1) return g - def WienerArayaGraph(): r""" Return the Wiener-Araya Graph. @@ -4626,7 +4592,7 @@ def WienerArayaGraph(): 4 sage: g.is_planar() True - sage: g.is_hamiltonian() # not tested -- around 30s long + sage: g.is_hamiltonian() # not tested -- around 30s long False sage: g.delete_vertex(g.random_vertex()) sage: g.is_hamiltonian() @@ -4634,41 +4600,41 @@ def WienerArayaGraph(): """ g = Graph(name="Wiener-Araya Graph") - g.add_cycle([(0,i) for i in range(4)]) - g.add_cycle([(1,i) for i in range(12)]) - g.add_cycle([(2,i) for i in range(20)]) - g.add_cycle([(3,i) for i in range(6)]) + g.add_cycle([(0, i) for i in range(4)]) + g.add_cycle([(1, i) for i in range(12)]) + g.add_cycle([(2, i) for i in range(20)]) + g.add_cycle([(3, i) for i in range(6)]) g._circle_embedding([(0, i) for i in range(4)], shift=.5) - g._circle_embedding(sum([[(1,3*i),(1,3*i+1)]+[0]*3+[(1,3*i+2)]+[0]*3 for i in range(4)],[]), + g._circle_embedding(sum([[(1, 3 * i), (1, 3 * i + 1), 0, 0, 0, (1, 3 * i + 2), 0, 0, 0] + for i in range(4)], []), shift=4, radius=.65) g._circle_embedding([(2, i) for i in range(20)], radius=.5) g._circle_embedding([(3, i) for i in range(6)], radius=.3, shift=.5) for i in range(4): - g.delete_edge((1,3*i),(1,3*i+1)) - g.add_edge((1,3*i),(0,i)) - g.add_edge((1,3*i+1),(0,i)) - g.add_edge((2,5*i+2),(1,3*i)) - g.add_edge((2,5*i+3),(1,3*i+1)) - g.add_edge((2,(5*i+5)%20),(1,3*i+2)) - g.add_edge((2,(5*i+1)%20),(3,i+(i>=1)+(i>=3))) - g.add_edge((2,(5*i+4)%20),(3,i+(i>=1)+(i>=3))) - - g.delete_edge((3,1),(3,0)) - g.add_edge((3,1),(2,4)) - g.delete_edge((3,4),(3,3)) - g.add_edge((3,4),(2,14)) - g.add_edge((3,1),(3,4)) + g.delete_edge((1, 3 * i), (1, 3 * i + 1)) + g.add_edge((1, 3 * i), (0, i)) + g.add_edge((1, 3 * i + 1), (0, i)) + g.add_edge((2, 5 * i + 2), (1, 3 * i)) + g.add_edge((2, 5 * i + 3), (1, 3 * i + 1)) + g.add_edge((2, (5 * i + 5) % 20), (1, 3 * i + 2)) + g.add_edge((2, (5 * i + 1) % 20), (3, i + (i >= 1) + (i >= 3))) + g.add_edge((2, (5 * i + 4) % 20), (3, i + (i >= 1) + (i >= 3))) + + g.delete_edge((3, 1), (3, 0)) + g.add_edge((3, 1), (2, 4)) + g.delete_edge((3, 4), (3, 3)) + g.add_edge((3, 4), (2, 14)) + g.add_edge((3, 1), (3, 4)) g.get_pos().pop(0) g.relabel() return g - def _EllipticLinesProjectivePlaneScheme(k): r""" - Pseudo-cyclic association scheme for action of `O(3,2^k)` on elliptic lines + Pseudo-cyclic association scheme for action of `O(3,2^k)` on elliptic lines. The group `O(3,2^k)` acts naturally on the `q(q-1)/2` lines of `PG(2,2^k)` skew to the conic preserved by it, see Sect. 12.7.B of [BCN1989]_ and @@ -4679,7 +4645,7 @@ def _EllipticLinesProjectivePlaneScheme(k): INPUT: - - ``k`` (integer) -- the exponent of 2 to get the field size + - ``k`` -- integer; the exponent of 2 to get the field size TESTS:: @@ -4698,7 +4664,7 @@ def _EllipticLinesProjectivePlaneScheme(k): from sage.matrix.constructor import matrix from itertools import product q = 2**k - g0 = libgap.GeneralOrthogonalGroup(3,q) # invariant form x0^2+x1*x2 + g0 = libgap.GeneralOrthogonalGroup(3, q) # invariant form x0^2 + x1*x2 g = libgap.Group(libgap.List(g0.GeneratorsOfGroup(), libgap.TransposedMat)) W = libgap.FullRowSpace(libgap.GF(q), 3) l = sum(libgap.Elements(libgap.Basis(W))) @@ -4716,7 +4682,7 @@ def MathonStronglyRegularGraph(t): INPUT: - - ``t`` (integer) -- the number of the graph, from 0 to 2. + - ``t`` -- integer; the number of the graph, from 0 to 2 EXAMPLES:: @@ -4727,11 +4693,11 @@ def MathonStronglyRegularGraph(t): TESTS:: - sage: G = graphs.MathonStronglyRegularGraph(1) # long time - sage: G.is_strongly_regular(parameters=True) # long time + sage: G = graphs.MathonStronglyRegularGraph(1) # long time + sage: G.is_strongly_regular(parameters=True) # long time (784, 270, 98, 90) - sage: G = graphs.MathonStronglyRegularGraph(2) # long time - sage: G.is_strongly_regular(parameters=True) # long time + sage: G = graphs.MathonStronglyRegularGraph(2) # long time + sage: G.is_strongly_regular(parameters=True) # long time (784, 297, 116, 110) """ @@ -4741,7 +4707,7 @@ def MathonStronglyRegularGraph(t): def JankoKharaghaniGraph(v): r""" - Return a (936, 375, 150, 150)-srg or a (1800, 1029, 588, 588)-srg. + Return a `(936, 375, 150, 150)`-srg or a `(1800, 1029, 588, 588)`-srg. This functions returns a strongly regular graph for the two sets of parameters shown to be realizable in [JK2002]_. The paper also uses a @@ -4749,16 +4715,16 @@ def JankoKharaghaniGraph(v): INPUT: - - ``v`` (integer) -- one of 936 or 1800. + - ``v`` -- integer; one of 936 or 1800 EXAMPLES:: - sage: g = graphs.JankoKharaghaniGraph(936) # long time - sage: g.is_strongly_regular(parameters=True) # long time + sage: g = graphs.JankoKharaghaniGraph(936) # long time + sage: g.is_strongly_regular(parameters=True) # long time (936, 375, 150, 150) sage: g = graphs.JankoKharaghaniGraph(1800) # not tested (30s) - sage: g.is_strongly_regular(parameters=True) # not tested (30s) + sage: g.is_strongly_regular(parameters=True) # not tested (30s) (1800, 1029, 588, 588) """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF @@ -4767,22 +4733,22 @@ def JankoKharaghaniGraph(v): # The notations of [JK02] are rather tricky, and so this code attempts to # stick as much as possible to the paper's variable names. - assert v in [1800,936] + assert(v in [1800, 936]) J = matrix.ones I = matrix.identity # Definition of the 36x36 matrix H ([JK02], section 2) A = J(6) - B = ("111---","1---11","1--1-1","--111-","-1-11-","-11--1") - C = ("-1-1-1","1---11","--11-1","1-1-1-","-1-11-","111---") - D = ("--1-11","-11-1-","11-1--","--11-1","11---1","1--11-") - E = ("-1--11","1-1--1","-11-1-","---111","1-11--","11-1--") - F = ("-1-1-1","11--1-","--111-","1-11--","-11--1","1---11") - B,C,D,E,F = [matrix([map({'1':1,'-':-1}.get,r) for r in m]) - for m in [B,C,D,E,F]] - - H = [A,B,C,D,E,F] + B = ("111---", "1---11", "1--1-1", "--111-", "-1-11-", "-11--1") + C = ("-1-1-1", "1---11", "--11-1", "1-1-1-", "-1-11-", "111---") + D = ("--1-11", "-11-1-", "11-1--", "--11-1", "11---1", "1--11-") + E = ("-1--11", "1-1--1", "-11-1-", "---111", "1-11--", "11-1--") + F = ("-1-1-1", "11--1-", "--111-", "1-11--", "-11--1", "1---11") + B, C, D, E, F = [matrix([map({'1': 1, '-': -1}.get, r) for r in m]) + for m in [B, C, D, E, F]] + + H = [A, B, C, D, E, F] H = [[-x for x in H[6-i:]] + H[:6-i] for i in range(6)] H = matrix.block(H) @@ -4791,26 +4757,26 @@ def JankoKharaghaniGraph(v): m = 12 t = (2 if v == 936 else 4) k = m - q = m*t+1 - K = GF(q,'alpha') + q = m * t + 1 + K = GF(q, 'alpha') a = K.primitive_element() Ci= [[K(0)]] + [set(a**(k*j+i) for j in range(t)) for i in range(m)] - Kelem_to_Ci = {v:i for i,s in enumerate(Ci) for v in s} # maps v to [0,...,12] + Kelem_to_Ci = {v: i for i, s in enumerate(Ci) for v in s} # maps v to [0,...,12] - W = ([[0]+ [1]*(len(K))] + - [[1]+[Kelem_to_Ci[aj-ai] for aj in K] for ai in K]) + W = ([[0] + [1]*(len(K))] + + [[1] + [Kelem_to_Ci[aj-ai] for aj in K] for ai in K]) # The nonzero elements of W are considered as elements of C_12, generated by # a matrix Omega of order 12 n = 18 - U = matrix.circulant([int(i==1) for i in range(2*n)]) - N = matrix.diagonal([1 if i else -1 for i in range(2*n)]) - Omega = (U*N)**6 - assert Omega**12 == I(36) + U = matrix.circulant([int(i==1) for i in range(2 * n)]) + N = matrix.diagonal([1 if i else -1 for i in range(2 * n)]) + Omega = (U * N)**6 + assert(Omega**12 == I(36)) # The value w_{ij} is understood in the paper as matrix generated by Omega # acting on the left of a matrix L, which we now define. - M = H-I(6).tensor_product(J(6)) + M = H - I(6).tensor_product(J(6)) L = matrix(list(reversed(I(6).rows()))).tensor_product(I(6)) # w_ij represents in the paper the matrix w_{ij}*L. We perform this action while @@ -4824,18 +4790,18 @@ def JankoKharaghaniGraph(v): if v == 1800: abs = lambda M: matrix([[1 if x else 0 for x in R] for R in M.rows()]) - M = (J(6)+I(6)).tensor_product(J(6)) # we define M = (J(6)+I(6)) x J(6) - D2 = [[M*0 if w == 0 else M*abs((Omega**w)*L) for w in R] # '[ (J(6)+I(6)) x J(6) |w_{ij}| ]' + M = (J(6)+I(6)).tensor_product(J(6)) # we define M = (J(6)+I(6)) x J(6) + D2 = [[M*0 if w == 0 else M*abs((Omega**w)*L) for w in R] # '[ (J(6)+I(6)) x J(6) |w_{ij}| ]' for R in W] D = (D+matrix.block(D2))/2 - return Graph([e for e,v in D.dict().items() if v == 1], + return Graph([e for e, v in D.dict().items() if v == 1], multiedges=False, name="Janko-Kharaghani") def JankoKharaghaniTonchevGraph(): r""" - Return a (324,153,72,72)-strongly regular graph from [JKT2001]_. + Return a `(324,153,72,72)`-strongly regular graph from [JKT2001]_. Build the graph using the description given in [JKT2001]_, taking sets B1 and B163 in the text as adjacencies of vertices 1 and 163, respectively, and @@ -4851,45 +4817,58 @@ def JankoKharaghaniTonchevGraph(): from sage.combinat.permutation import Permutation as P from sage.libs.gap.libgap import libgap - m1=prod(P((9*x+k,9*x+k+3,9*x+k+6)) for k in range(1, 4) for x in range(36)) - m2=prod(P((3*x+1,3*x+2,3*x+3)) for x in range(108)) - t=prod(prod(map(P,[(9*x+2,9*x+3),(9*x+4,9*x+7),(9*x+5,9*x+9),(9*x+6,9*x+8)])) for - x in range(36)) - n1=prod(prod(map(P,[(1+x,19+x,37+x),(55+x,73+x,91+x),(109+x,127+x,145+x), - (163+x,181+x,199+x),(217+x,235+x,253+x),(271+x,289+x,307+x)])) - for x in range(18)) - n2=prod(prod(map(P,[(1+x,55+x,109+x),(19+x,73+x,127+x),(37+x,91+x,145+x), - (163+x,217+x,271+x),(181+x,235+x,289+x),(199+x,253+x,307+x)])) + m1 = prod(P((9 * x + k, 9 * x + k + 3, 9 * x + k + 6)) + for k in range(1, 4) for x in range(36)) + m2 = prod(P((3 * x + 1, 3 * x + 2, 3 * x + 3)) for x in range(108)) + t = prod(prod(map(P, [(9 * x + 2, 9 * x + 3), (9 * x + 4, 9 * x + 7), + (9 * x + 5, 9 * x + 9), (9 * x + 6, 9 * x + 8)])) + for x in range(36)) + n1 = prod(prod(map(P, [(1 + x, 19 + x, 37 + x), (55 + x, 73 + x, 91 + x), + (109 + x, 127 + x, 145 + x), (163 + x, 181 + x, 199 + x), + (217 + x, 235 + x, 253 + x), (271 + x, 289 + x, 307 + x)])) + for x in range(18)) + n2 = prod(prod(map(P, [(1 + x, 55 + x, 109 + x), (19 + x, 73 + x, 127 + x), + (37 + x, 91 + x, 145 + x), (163 + x, 217 + x, 271 + x), + (181 + x, 235 + x, 289 + x), (199 + x, 253 + x, 307 + x)])) + for x in range(18)) + s = prod(prod(map(P, [(19 + x, 37 + x), (55 + x, 109 + x), (73 + x, 145 + x), + (91 + x, 127 + x), (181 + x, 199 + x), (217 + x, 271 + x), + (235 + x, 307 + x), (253 + x, 289 + x)])) for x in range(18)) - s=prod(prod(map(P,[(19+x,37+x),(55+x,109+x),(73+x,145+x),(91+x,127+x), - (181+x,199+x),(217+x,271+x),(235+x,307+x),(253+x,289+x)])) - for x in range(18)) - k=prod(prod(map(P,[(18*x+1,18*x+10),(18*x+2,18*x+11),(18*x+3,18*x+12), - (18*x+4,18*x+13),(18*x+5,18*x+14),(18*x+6,18*x+15),(18*x+7,18*x+16), - (18*x+8,18*x+17),(18*x+9,18*x+18)])) + k = prod(prod(map(P, [(18 * x + 1, 18 * x + 10), (18 * x + 2, 18 * x + 11), + (18 * x + 3, 18 * x + 12), (18 * x + 4, 18 * x + 13), + (18 * x + 5, 18 * x + 14), (18 * x + 6, 18 * x + 15), + (18 * x + 7, 18 * x + 16), (18 * x + 8, 18 * x + 17), + (18 * x + 9, 18 * x + 18)])) for x in range(18)) G = libgap.Group([libgap.PermList(p) for p in [m1, m2, t, n1, n2, s, k]]) st = libgap.Group([libgap.PermList(p) for p in [t, s]]) - B1=(19,22,25,29,30,31,33,34,35,37,40,43,47,48,49,51,52,53,55,56,57,65, - 66,67,68,70,72,76,77,78,79,80,81,82,86,90,92,93,95,96,98,99,100,105,107, - 109,110,111,119,120,121,122,124,126,128,129,131,132,134,135,136,141,143, - 148,149,150,151,152,153,154,158,162,167,168,170,171,172,176,177,179,180, - 184,186,187,188,190,191,192,193,196,202,204,205,206,208,209,210,211,214, - 218,219,221,225,226,227,228,229,232,236,237,238,241,244,245,246,249,251, - 254,255,256,259,262,265,266,268,270,272,273,275,279,280,281,282,283,286, - 290,291,292,295,298,301,302,304,306,308,309,310,313,316,317,318,321,323) - B163=(5,6,8,9,10,14,15,17,18,22,24,25,26,28,29,30,31,34,40,42,43,44,46, - 47,48,49,52,56,57,59,63,64,65,66,67,70,74,75,76,79,82,83,84,87,89,92,93, - 94,97,100,103,104,106,108,110,111,113,117,118,119,120,121,124,128,129, - 130,133,136,139,140,142,144,146,147,148,151,154,155,156,159,161,181,185, - 189,191,192,194,195,197,198,199,203,207,209,210,212,213,215,216,217,222, - 224,229,230,231,232,233,234,236,237,238,240,241,242,244,245,246,254,255, - 256,257,259,261,262,265,268,271,276,278,283,284,285,286,287,288,290,291, - 292,293,295,297,298,301,304,308,309,310,312,313,314,316,317,318) - Gamma=Graph(multiedges=False,name='Janko-Kharaghani-Tonchev') - for i,b in ((1,B1),(163,B163)): + B1 = (19, 22, 25, 29, 30, 31, 33, 34, 35, 37, 40, 43, 47, 48, 49, 51, 52, + 53, 55, 56, 57, 65, 66, 67, 68, 70, 72, 76, 77, 78, 79, 80, 81, 82, + 86, 90, 92, 93, 95, 96, 98, 99, 100, 105, 107, 109, 110, 111, 119, + 120, 121, 122, 124, 126, 128, 129, 131, 132, 134, 135, 136, 141, 143, + 148, 149, 150, 151, 152, 153, 154, 158, 162, 167, 168, 170, 171, 172, + 176, 177, 179, 180, 184, 186, 187, 188, 190, 191, 192, 193, 196, 202, + 204, 205, 206, 208, 209, 210, 211, 214, 218, 219, 221, 225, 226, 227, + 228, 229, 232, 236, 237, 238, 241, 244, 245, 246, 249, 251, 254, 255, + 256, 259, 262, 265, 266, 268, 270, 272, 273, 275, 279, 280, 281, 282, + 283, 286, 290, 291, 292, 295, 298, 301, 302, 304, 306, 308, 309, 310, + 313, 316, 317, 318, 321, 323) + B163 = (5, 6, 8, 9, 10, 14, 15, 17, 18, 22, 24, 25, 26, 28, 29, 30, 31, 34, + 40, 42, 43, 44, 46, 47, 48, 49, 52, 56, 57, 59, 63, 64, 65, 66, 67, + 70, 74, 75, 76, 79, 82, 83, 84, 87, 89, 92, 93, 94, 97, 100, 103, + 104, 106, 108, 110, 111, 113, 117, 118, 119, 120, 121, 124, 128, + 129, 130, 133, 136, 139, 140, 142, 144, 146, 147, 148, 151, 154, + 155, 156, 159, 161, 181, 185, 189, 191, 192, 194, 195, 197, 198, + 199, 203, 207, 209, 210, 212, 213, 215, 216, 217, 222, 224, 229, + 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 245, + 246, 254, 255, 256, 257, 259, 261, 262, 265, 268, 271, 276, 278, + 283, 284, 285, 286, 287, 288, 290, 291, 292, 293, 295, 297, 298, + 301, 304, 308, 309, 310, 312, 313, 314, 316, 317, 318) + Gamma=Graph(multiedges=False, name='Janko-Kharaghani-Tonchev') + for i, b in ((1, B1), (163, B163)): for j in map(lambda x: x[0], st.OrbitsDomain(b)): - Gamma.add_edges(map(tuple,G.Orbit(libgap.Set([i,j]), libgap.OnSets))) + Gamma.add_edges(map(tuple,G.Orbit(libgap.Set([i, j]), libgap.OnSets))) Gamma.relabel(range(Gamma.order())) return Gamma @@ -4998,33 +4977,33 @@ def IoninKharaghani765Graph(): K = GF(3) # the four φ functions - phi = [lambda xy: 1*xy[0]+0*xy[1], - lambda xy: 0*xy[0]+1*xy[1], - lambda xy: 1*xy[0]+1*xy[1], - lambda xy: 1*xy[0]-1*xy[1]] + phi = [lambda xy: 1*xy[0] + 0*xy[1], + lambda xy: 0*xy[0] + 1*xy[1], + lambda xy: 1*xy[0] + 1*xy[1], + lambda xy: 1*xy[0] - 1*xy[1]] # Defining L_{i,j} - L = {(i,j):set() for i in range(4) for j in K} + L = {(i, j): set() for i in range(4) for j in K} from itertools import product - for p in product(K,K): + for p in product(K, K): for i in range(4): - L[i,phi[i](p)].add(p) + L[i, phi[i](p)].add(p) - L = {k:frozenset(v) for k,v in L.items()} + L = {k: frozenset(v) for k, v in L.items()} # Defining pi - pi = {L[i,j]:L[i,(j+1)%3] for (i,j) in L} + pi = {L[i, j]: L[i, (j + 1) % 3] for i, j in L} pi[frozenset()] = frozenset() # Defining A - A = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,0), (0,1), (1,-1), (1,0), (1,1)] + A = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1)] def M(S): S = set((K(x), K(y)) for x, y in S) def difference(xy, xxyy): return (K(xy[0] - xxyy[0]), K(xy[1] - xxyy[1])) - return matrix([[1 if difference(A[8-i],A[j]) in S else 0 + return matrix([[1 if difference(A[8-i], A[j]) in S else 0 for i in range(9)] for j in range(9)]) @@ -5042,26 +5021,25 @@ def N(Xi): # The matrix W, with off-diagonal entries equal to integers 1,...,15 # (instead of x^1,...,x^15) from sage.matrix.constructor import matrix - GF16 = GF(16,'x') - W = matrix( [[x+y for x in GF16] + [1] for y in GF16] + - [[1]*16+[0]]) + GF16 = GF(16, 'x') + W = matrix( [[x + y for x in GF16] + [1] for y in GF16] + + [[1]*16 + [0]]) x = GF16.primitive_element() - log_x = {x**i:i for i in range(15)} - W = W.apply_map(lambda x:log_x[x]+1 if x else 0) + log_x = {x**i: i for i in range(15)} + W = W.apply_map(lambda x: log_x[x] + 1 if x else 0) # Associate a matrix to every entry of W - int_to_matrix = {0:matrix.zero(45)} + int_to_matrix = {0: matrix.zero(45)} for i in range(15): - vec = [frozenset([]),L[0,0],L[1,0],L[2,0],L[3,0]] + vec = [frozenset([]), L[0, 0], L[1, 0], L[2, 0], L[3, 0]] vec = f_pow(pi_vec, i % 3, vec) vec = f_pow(sigma2, i % 5, vec) - int_to_matrix[i+1] = N(vec) + int_to_matrix[i + 1] = N(vec) M2 = matrix.block([[int_to_matrix[x] for x in R] for R in W.rows()]) g = Graph(M2, name="Ionin-Kharaghani") return g - def U42Graph216(): r""" Return a (216,40,4,8)-strongly regular graph from [CRS2016]_. @@ -5073,8 +5051,8 @@ def U42Graph216(): EXAMPLES:: - sage: G=graphs.U42Graph216() # optional - gap_packages (grape) - sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) + sage: G=graphs.U42Graph216() # optional - gap_packages (grape) + sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (216, 40, 4, 8) """ from sage.libs.gap.libgap import libgap @@ -5082,7 +5060,7 @@ def U42Graph216(): GapPackage("grape", spkg="gap_packages").require() - adj_list=libgap.function_factory("""function() + adj_list = libgap.function_factory("""function() local gg, hl, o216, a216, x, h, re, G; LoadPackage("grape"); gg:=SpecialUnitaryGroup(4,2); @@ -5101,8 +5079,8 @@ def U42Graph216(): end;""") adj = adj_list() # for each vertex, we get the list of vertices it is adjacent to - G = Graph(((i,int(j-1)) - for i,ni in enumerate(adj) for j in ni), + G = Graph(((i, int(j - 1)) + for i, ni in enumerate(adj) for j in ni), format='list_of_edges', multiedges=False) G.name('U42Graph216') return G @@ -5120,8 +5098,8 @@ def U42Graph540(): EXAMPLES:: - sage: G=graphs.U42Graph540() # optional - gap_packages (grape) - sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) + sage: G=graphs.U42Graph540() # optional - gap_packages (grape) + sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (540, 187, 58, 68) """ @@ -5143,9 +5121,9 @@ def U42Graph540(): return List([1..540],x->Adjacency(G,x)); end;""") - adj = adj_list() # for each vertex, we get the list of vertices it is adjacent to - G = Graph(((i,int(j-1)) - for i,ni in enumerate(adj) for j in ni), + adj = adj_list() # for each vertex, we get the list of vertices it is adjacent to + G = Graph(((i, int(j - 1)) + for i, ni in enumerate(adj) for j in ni), format='list_of_edges', multiedges=False) G.name('U42Graph540') return G diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index 6d5a32ab3b1..ba0c52e79f9 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -5,7 +5,7 @@ The methods defined here appear in :mod:`sage.graphs.graph_generators`. """ -########################################################################### +# **************************************************************************** # # Copyright (C) 2006 Robert L. Miller # and Emily A. Kirkman @@ -13,7 +13,7 @@ # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ -########################################################################### +# **************************************************************************** # import from Sage library from sage.graphs.graph import Graph @@ -22,16 +22,16 @@ def AfricaMap(continental=False, year=2018): """ Return African states as a graph of common border. - "African state" here is defined as an independent - state having the capital city in Africa. The graph - has an edge between those countries that have common - *land* border. + "African state" here is defined as an independent state having the capital + city in Africa. The graph has an edge between those countries that have + common *land* border. INPUT: - - ``continental``, a Boolean -- if set, only return states in - the continental Africa - - ``year`` -- reserved for future use + - ``continental`` -- boolean (default: ``False``); whether to only return + states in the continental Africa or all African states + + - ``year`` -- integer (default: ``2018``); reserved for future use EXAMPLES:: @@ -48,7 +48,7 @@ def AfricaMap(continental=False, year=2018): TESTS:: - sage: Africa.plot() + sage: Africa.plot() # long time Graphics object consisting of 159 graphics primitives """ if year != 2018: @@ -60,13 +60,15 @@ def AfricaMap(continental=False, year=2018): 'Benin': ['Burkina Faso', 'Niger', 'Nigeria', 'Togo'], 'Botswana': ['Namibia', 'South Africa', 'Zimbabwe'], 'Burkina Faso': ['Ghana', 'Ivory Coast', 'Mali', 'Niger', 'Togo'], - 'Cameroon': ['Central Africa', 'Chad', 'Equatorial Guinea', 'Gabon', 'Nigeria'], + 'Cameroon': ['Central Africa', 'Chad', 'Equatorial Guinea', 'Gabon', + 'Nigeria'], 'Central Africa': ['Chad', 'South Sudan', 'Sudan'], 'Chad': ['Libya', 'Niger', 'Nigeria', 'Sudan'], 'Republic of the Congo': ['Gabon', 'Cameroon', 'Central Africa', 'Angola', 'Democratic Republic of the Congo'], - 'Democratic Republic of the Congo': ['Zambia', 'South Sudan', 'Tanzania', 'Burundi', - 'Rwanda', 'Uganda', 'Central Africa', 'Angola'], + 'Democratic Republic of the Congo': ['Zambia', 'South Sudan', 'Tanzania', + 'Burundi', 'Rwanda', 'Uganda', + 'Central Africa', 'Angola'], 'Djibouti': ['Eritrea', 'Ethiopia', 'Somalia'], 'Ethiopia': ['Eritrea', 'Kenya', 'Somalia', 'South Sudan', 'Sudan'], 'Gabon': ['Equatorial Guinea'], @@ -87,7 +89,8 @@ def AfricaMap(continental=False, year=2018): 'Zambia': ['Malawi', 'Mozambique', 'Namibia', 'Zimbabwe'] } - no_land_border = ['Cape Verde', 'Seychelles', 'Mauritius', u'São Tomé and Príncipe', 'Madagascar', 'Comoros'] + no_land_border = ['Cape Verde', 'Seychelles', 'Mauritius', + u'São Tomé and Príncipe', 'Madagascar', 'Comoros'] G = Graph(common_border, format='dict_of_lists') @@ -100,21 +103,20 @@ def AfricaMap(continental=False, year=2018): return G - def EuropeMap(continental=False, year=2018): """ Return European states as a graph of common border. - "European state" here is defined as an independent - state having the capital city in Europe. The graph - has an edge between those countries that have common - *land* border. + "European state" here is defined as an independent state having the capital + city in Europe. The graph has an edge between those countries that have + common *land* border. INPUT: - - ``continental``, a Boolean -- if set, only return states in - the continental Europe - - ``year`` -- reserved for future use + - ``continental`` -- boolean (default: ``False``); whether to only return + states in the continental Europe or all European states + + - ``year`` -- integer (default: ``2018``); reserved for future use EXAMPLES:: @@ -133,24 +135,36 @@ def EuropeMap(continental=False, year=2018): raise ValueError("currently only year 2018 is implemented") common_border = { - 'Poland': ['Slovakia', 'Czech Republic', 'Lithuania', 'Russia', 'Ukraine', 'Germany'], - 'Germany': ['Czech Republic', 'Netherlands', 'Switzerland', 'Luxembourg', 'Denmark'], - 'Croatia': ['Bosnia and Herzegovina', 'Serbia', 'Hungary', 'Montenegro', 'Slovenia'], - 'Austria': ['Czech Republic', 'Germany', 'Switzerland', 'Slovenia', 'Liechtenstein'], - 'France': ['Germany', 'Italy', 'Switzerland', 'Monaco', 'Luxembourg', 'Andorra'], - 'Hungary': ['Slovakia', 'Serbia', 'Romania', 'Ukraine', 'Slovenia', 'Austria'], - 'Italy': ['Switzerland', 'Vatican City', 'San Marino', 'Slovenia', 'Austria'], - 'Belarus': ['Poland', 'Latvia', 'Lithuania', 'Russia', 'Ukraine'], - 'Montenegro': ['Bosnia and Herzegovina', 'Serbia', 'Albania'], - 'Belgium': ['Germany', 'Netherlands', 'Luxembourg', 'France'], - 'Russia': ['Finland', 'Lithuania', 'Estonia', 'Ukraine'], - 'Romania': ['Serbia', 'Moldova', 'Bulgaria', 'Ukraine'], - 'Latvia': ['Lithuania', 'Russia', 'Estonia'], - 'Slovakia': ['Czech Republic', 'Ukraine', 'Austria'], 'Switzerland': ['Liechtenstein'], - 'Spain': ['Portugal', 'Andorra', 'France'], 'Norway': ['Finland', 'Sweden', 'Russia'], - 'Ireland': ['United Kingdom'], 'Serbia': ['Bosnia and Herzegovina', 'Bulgaria'], - 'Greece': ['Macedonia', 'Bulgaria', 'Albania'], 'Ukraine': ['Moldova'], - 'Macedonia': ['Serbia', 'Bulgaria', 'Albania'], 'Sweden': ['Finland'] + 'Austria': ['Czech Republic', 'Germany', 'Liechtenstein', 'Slovenia', + 'Switzerland'], + 'Belarus': ['Latvia', 'Lithuania', 'Poland', 'Russia', 'Ukraine'], + 'Belgium': ['France', 'Germany', 'Luxembourg', 'Netherlands'], + 'Croatia': ['Bosnia and Herzegovina', 'Hungary', 'Montenegro', 'Serbia', + 'Slovenia'], + 'France': ['Andorra', 'Germany', 'Italy', 'Luxembourg', 'Monaco', + 'Switzerland'], + 'Germany': ['Czech Republic', 'Denmark', 'Luxembourg', 'Netherlands', + 'Switzerland'], + 'Greece': ['Albania', 'Bulgaria', 'Macedonia'], + 'Hungary': ['Austria', 'Romania', 'Serbia', 'Slovakia', 'Slovenia', + 'Ukraine'], + 'Ireland': ['United Kingdom'], + 'Italy': ['Austria', 'San Marino', 'Slovenia', 'Switzerland', + 'Vatican City'], + 'Latvia': ['Estonia', 'Lithuania', 'Russia'], + 'Macedonia': ['Albania', 'Bulgaria', 'Serbia'], + 'Montenegro': ['Albania', 'Bosnia and Herzegovina', 'Serbia'], + 'Norway': ['Finland', 'Russia', 'Sweden'], + 'Poland': ['Czech Republic', 'Germany', 'Lithuania', 'Russia', 'Slovakia', + 'Ukraine'], + 'Romania': ['Bulgaria', 'Moldova', 'Serbia', 'Ukraine'], + 'Russia': ['Estonia', 'Finland', 'Lithuania', 'Ukraine'], + 'Serbia': ['Bosnia and Herzegovina', 'Bulgaria'], + 'Slovakia': ['Austria', 'Czech Republic', 'Ukraine'], + 'Spain': ['Andorra', 'France', 'Portugal'], + 'Sweden': ['Finland'], + 'Switzerland': ['Liechtenstein'], + 'Ukraine': ['Moldova'] } no_land_border = ['Iceland', 'Malta'] @@ -165,19 +179,17 @@ def EuropeMap(continental=False, year=2018): return G - def USAMap(continental=False): """ Return states of USA as a graph of common border. - The graph has an edge between those states that have - common *land* border line or point. Hence for example - Colorado and Arizona are marked as neighbors, but - Michigan and Minnesota are not. + The graph has an edge between those states that have common *land* border + line or point. Hence for example Colorado and Arizona are marked as + neighbors, but Michigan and Minnesota are not. INPUT: - - ``continental``, a Boolean -- if set, exclude Alaska + - ``continental`` -- boolean (default: ``False``); whether to exclude Alaska and Hawaii EXAMPLES: @@ -185,7 +197,8 @@ def USAMap(continental=False): How many states are neighbor's neighbor for Pennsylvania:: sage: USA = graphs.USAMap() - sage: len([n2 for n2 in USA if USA.distance('Pennsylvania', n2) == 2]) + sage: distance = USA.shortest_path_lengths('Pennsylvania') + sage: len([n2 for n2, d in distance.items() if d == 2]) 7 Diameter for continental USA:: @@ -197,75 +210,90 @@ def USAMap(continental=False): states = { "Alabama": ["Florida", "Georgia", "Mississippi", "Tennessee"], "Arizona": ["California", "Colorado", "Nevada", "New Mexico", "Utah"], - "Arkansas": ["Louisiana", "Mississippi", "Missouri", "Oklahoma", "Tennessee", "Texas"], + "Arkansas": ["Louisiana", "Mississippi", "Missouri", "Oklahoma", + "Tennessee", "Texas"], "California": ["Arizona", "Nevada", "Oregon"], - "Colorado": ["Arizona", "Kansas", "Nebraska", "New Mexico", "Oklahoma", "Utah", "Wyoming"], + "Colorado": ["Arizona", "Kansas", "Nebraska", "New Mexico", "Oklahoma", + "Utah", "Wyoming"], "Connecticut": ["Massachusetts", "New York", "Rhode Island"], "Delaware": ["Maryland", "New Jersey", "Pennsylvania"], "Florida": ["Alabama", "Georgia"], - "Georgia": ["Alabama", "Florida", "North Carolina", "South Carolina", "Tennessee"], + "Georgia": ["Alabama", "Florida", "North Carolina", "South Carolina", + "Tennessee"], "Idaho": ["Montana", "Nevada", "Oregon", "Utah", "Washington", "Wyoming"], - "Illinois": ["Indiana", "Iowa", "Michigan", "Kentucky", "Missouri", "Wisconsin"], + "Illinois": ["Indiana", "Iowa", "Michigan", "Kentucky", "Missouri", + "Wisconsin"], "Indiana": ["Illinois", "Kentucky", "Michigan", "Ohio"], - "Iowa": ["Illinois", "Minnesota", "Missouri", "Nebraska", "South Dakota", "Wisconsin"], + "Iowa": ["Illinois", "Minnesota", "Missouri", "Nebraska", "South Dakota", + "Wisconsin"], "Kansas": ["Colorado", "Missouri", "Nebraska", "Oklahoma"], - "Kentucky": ["Illinois", "Indiana", "Missouri", "Ohio", "Tennessee", "Virginia", "West Virginia"], + "Kentucky": ["Illinois", "Indiana", "Missouri", "Ohio", "Tennessee", + "Virginia", "West Virginia"], "Louisiana": ["Arkansas", "Mississippi", "Texas"], "Maine": ["New Hampshire"], "Maryland": ["Delaware", "Pennsylvania", "Virginia", "West Virginia"], - "Massachusetts": ["Connecticut", "New Hampshire", "New York", "Rhode Island", "Vermont"], + "Massachusetts": ["Connecticut", "New Hampshire", "New York", + "Rhode Island", "Vermont"], "Michigan": ["Illinois", "Indiana", "Ohio", "Wisconsin"], "Minnesota": ["Iowa", "North Dakota", "South Dakota", "Wisconsin"], "Mississippi": ["Alabama", "Arkansas", "Louisiana", "Tennessee"], - "Missouri": ["Arkansas", "Illinois", "Iowa", "Kansas", "Kentucky", "Nebraska", "Oklahoma", "Tennessee"], + "Missouri": ["Arkansas", "Illinois", "Iowa", "Kansas", "Kentucky", + "Nebraska", "Oklahoma", "Tennessee"], "Montana": ["Idaho", "North Dakota", "South Dakota", "Wyoming"], - "Nebraska": ["Colorado", "Iowa", "Kansas", "Missouri", "South Dakota", "Wyoming"], + "Nebraska": ["Colorado", "Iowa", "Kansas", "Missouri", "South Dakota", + "Wyoming"], "Nevada": ["Arizona", "California", "Idaho", "Oregon", "Utah"], "New Hampshire": ["Maine", "Massachusetts", "Vermont"], "New Jersey": ["Delaware", "New York", "Pennsylvania"], "New Mexico": ["Arizona", "Colorado", "Oklahoma", "Texas", "Utah"], - "New York": ["Connecticut", "Massachusetts", "New Jersey", "Pennsylvania", "Vermont"], + "New York": ["Connecticut", "Massachusetts", "New Jersey", + "Pennsylvania", "Vermont"], "North Carolina": ["Georgia", "South Carolina", "Tennessee", "Virginia"], "North Dakota": ["Minnesota", "Montana", "South Dakota"], - "Ohio": ["Indiana", "Kentucky", "Michigan", "Pennsylvania", "West Virginia"], - "Oklahoma": ["Arkansas", "Colorado", "Kansas", "Missouri", "New Mexico", "Texas"], + "Ohio": ["Indiana", "Kentucky", "Michigan", "Pennsylvania", + "West Virginia"], + "Oklahoma": ["Arkansas", "Colorado", "Kansas", "Missouri", + "New Mexico", "Texas"], "Oregon": ["California", "Idaho", "Nevada", "Washington"], - "Pennsylvania": ["Delaware", "Maryland", "New Jersey", "New York", "Ohio", "West Virginia"], + "Pennsylvania": ["Delaware", "Maryland", "New Jersey", "New York", + "Ohio", "West Virginia"], "Rhode Island": ["Connecticut", "Massachusetts"], "South Carolina": ["Georgia", "North Carolina"], - "South Dakota": ["Iowa", "Minnesota", "Montana", "Nebraska", "North Dakota", "Wyoming"], - "Tennessee": ["Alabama", "Arkansas", "Georgia", "Kentucky", "Mississippi", "Missouri", "North Carolina", "Virginia"], + "South Dakota": ["Iowa", "Minnesota", "Montana", "Nebraska", + "North Dakota", "Wyoming"], + "Tennessee": ["Alabama", "Arkansas", "Georgia", "Kentucky", "Mississippi", + "Missouri", "North Carolina", "Virginia"], "Texas": ["Arkansas", "Louisiana", "New Mexico", "Oklahoma"], "Utah": ["Arizona", "Colorado", "Idaho", "Nevada", "New Mexico", "Wyoming"], "Vermont": ["Massachusetts", "New Hampshire", "New York"], - "Virginia": ["Kentucky", "Maryland", "North Carolina", "Tennessee", "West Virginia"], + "Virginia": ["Kentucky", "Maryland", "North Carolina", "Tennessee", + "West Virginia"], "Washington": ["Idaho", "Oregon"], - "West Virginia": ["Kentucky", "Maryland", "Ohio", "Pennsylvania", "Virginia"], + "West Virginia": ["Kentucky", "Maryland", "Ohio", "Pennsylvania", + "Virginia"], "Wisconsin": ["Illinois", "Iowa", "Michigan", "Minnesota"], - "Wyoming": ["Colorado", "Idaho", "Montana", "Nebraska", "South Dakota", "Utah"] + "Wyoming": ["Colorado", "Idaho", "Montana", "Nebraska", "South Dakota", + "Utah"] } - if not continental: + if continental: + name = "Continental USA Map" + else: states['Alaska'] = [] states['Hawaii'] = [] - G = Graph(states, format='dict_of_lists') - G.name(new="USA Map") - return G + name = "USA Map" - G = Graph(states, format='dict_of_lists') - G.name(new="Continental USA Map") - return G + return Graph(states, format='dict_of_lists', name=name) def WorldMap(): """ - Returns the Graph of all the countries, in which two countries are adjacent + Return the Graph of all the countries, in which two countries are adjacent in the graph if they have a common boundary. This graph has been built from the data available in The CIA World Factbook [CIA]_ (2009-08-21). - The returned graph ``G`` has a member ``G.gps_coordinates`` - equal to a dictionary containing the GPS coordinates - of each country's capital city. + The returned graph ``G`` has a member ``G.gps_coordinates`` equal to a + dictionary containing the GPS coordinates of each country's capital city. EXAMPLES:: @@ -279,429 +307,408 @@ def WorldMap(): TESTS:: - sage: 'Iceland' in graphs.WorldMap() # Trac 24488 - True + :trac:`24488`:: - REFERENCE: - - [CIA]_ + sage: 'Iceland' in graphs.WorldMap() + True """ edges = [ - ('Afghanistan', 'China', None), ('Afghanistan', 'Iran', None), - ('Afghanistan', 'Uzbekistan', None), ('Albania', 'Greece', None), - ('Albania', 'Kosovo', None), ('Albania', 'Macedonia', None), - ('Albania', 'Montenegro', None), ('Algeria', 'Morocco', None), - ('Algeria', 'Tunisia', None), ('Andorra', 'Spain', None), - ('Angola', 'Democratic Republic of the Congo', None), ('Angola', 'Namibia', None), - ('Angola', 'Zambia', None), ('Argentina', 'Bolivia', None), - ('Argentina', 'Brazil', None), ('Argentina', 'Chile', None), - ('Argentina', 'Paraguay', None), ('Argentina', 'Uruguay', None), - ('Armenia', 'Georgia', None), ('Armenia', 'Iran', None), - ('Austria', 'Germany', None), ('Azerbaijan', 'Armenia', None), - ('Azerbaijan', 'Georgia', None), ('Azerbaijan', 'Iran', None), - ('Azerbaijan', 'Russia', None), ('Azerbaijan', 'Turkey', None), - ('Bangladesh', 'Burma', None), ('Belgium', 'Germany', None), - ('Belgium', 'Netherlands', None), ('Belize', 'Mexico', None), - ('Benin', 'Burkina Faso', None), ('Benin', 'Niger', None), - ('Benin', 'Nigeria', None), ('Benin', 'Togo', None), - ('Bolivia', 'Brazil', None), ('Bolivia', 'Chile', None), - ('Bolivia', 'Paraguay', None), ('Bolivia', 'Peru', None), - ('Bosnia and Herzegovina', 'Croatia', None), ('Bosnia and Herzegovina', 'Montenegro', None), - ('Bosnia and Herzegovina', 'Serbia', None), ('Brazil', 'Colombia', None), - ('Brazil', 'Guyana', None), ('Brazil', 'Suriname', None), - ('Brazil', 'Venezuela', None), ('Bulgaria', 'Greece', None), - ('Bulgaria', 'Macedonia', None), ('Bulgaria', 'Romania', None), - ('Bulgaria', 'Serbia', None), ('Burkina Faso', 'Mali', None), - ('Burkina Faso', 'Niger', None), ('Burkina Faso', 'Togo', None), - ('Burundi', 'Democratic Republic of the Congo', None), ('Cambodia', 'Laos', None), - ('Cambodia', 'Thailand', None), ('Cambodia', 'Vietnam', None), - ('Cameroon', 'Central African Republic', None), ('Cameroon', 'Chad', None), - ('Cameroon', 'Equatorial Guinea', None), ('Cameroon', 'Nigeria', None), - ('Cameroon', 'Republic of the Congo', None), ('Canada', 'United States', None), - ('Central African Republic', 'Chad', None), ('Central African Republic', 'Democratic Republic of the Congo', None), - ('Central African Republic', 'Sudan', None), ('Chad', 'Niger', None), - ('Chad', 'Nigeria', None), ('Chad', 'Sudan', None), - ('China', 'Bhutan', None), ('China', 'Burma', None), - ('China', 'Hong Kong', None), ('China', 'Kazakhstan', None), - ('China', 'Kyrgyzstan', None), ('China', 'Mongolia', None), - ('China', 'Nepal', None), ('China', 'North Korea', None), - ('China', 'Russia', None), ('China', 'Vietnam', None), - ('Colombia', 'Venezuela', None), ('Costa Rica', 'Nicaragua', None), - ("Cote d'Ivoire", 'Burkina Faso', None), ("Cote d'Ivoire", 'Guinea', None), - ("Cote d'Ivoire", 'Mali', None), ('Cyprus', 'Akrotiri', None), - ('Cyprus', 'Dhekelia', None), ('Czech Republic', 'Austria', None), - ('Czech Republic', 'Germany', None), ('Czech Republic', 'Poland', None), - ('Democratic Republic of the Congo', 'Zambia', None), ('Denmark', 'Germany', None), - ('Djibouti', 'Eritrea', None), ('Dominican Republic', 'Haiti', None), - ('Ecuador', 'Colombia', None), ('El Salvador', 'Honduras', None), - ('Ethiopia', 'Djibouti', None), ('Ethiopia', 'Eritrea', None), - ('Ethiopia', 'Kenya', None), ('Ethiopia', 'Somalia', None), - ('Ethiopia', 'Sudan', None), ('Finland', 'Russia', None), - ('Finland', 'Sweden', None), ('France', 'Andorra', None), - ('France', 'Belgium', None), ('France', 'Brazil', None), - ('France', 'Germany', None), ('France', 'Italy', None), - ('France', 'Luxembourg', None), ('France', 'Spain', None), - ('France', 'Suriname', None), ('France', 'Switzerland', None), - ('Gabon', 'Cameroon', None), ('Gabon', 'Equatorial Guinea', None), - ('Gabon', 'Republic of the Congo', None), ('Gaza Strip', 'Egypt', None), - ('Gaza Strip', 'Israel', None), ('Ghana', 'Burkina Faso', None), - ('Ghana', "Cote d'Ivoire", None), ('Ghana', 'Togo', None), - ('Gibraltar', 'Spain', None), ('Guatemala', 'Belize', None), - ('Guatemala', 'El Salvador', None), ('Guatemala', 'Honduras', None), - ('Guatemala', 'Mexico', None), ('Guinea', 'Sierra Leone', None), - ('Guinea-Bissau', 'Guinea', None), ('Guinea-Bissau', 'Senegal', None), - ('Honduras', 'Nicaragua', None), ('Hungary', 'Austria', None), - ('Hungary', 'Croatia', None), ('Hungary', 'Serbia', None), - ('India', 'Bangladesh', None), ('India', 'Bhutan', None), - ('India', 'Burma', None), ('India', 'China', None), - ('India', 'Nepal', None), ('Indonesia', 'Papua New Guinea', None), - ('Iran', 'Iraq', None), ('Ireland', 'United Kingdom', None), - ('Israel', 'Egypt', None), ('Italy', 'Austria', None), - ('Jordan', 'Iraq', None), ('Jordan', 'Israel', None), - ('Jordan', 'Syria', None), ('Jordan', 'West Bank', None), - ('Kazakhstan', 'Kyrgyzstan', None), ('Kenya', 'Somalia', None), - ('Kenya', 'Sudan', None), ('Kenya', 'Uganda', None), - ('Kosovo', 'Macedonia', None), ('Kosovo', 'Serbia', None), - ('Kuwait', 'Iraq', None), ('Laos', 'Burma', None), - ('Laos', 'China', None), ('Laos', 'Thailand', None), - ('Laos', 'Vietnam', None), ('Latvia', 'Belarus', None), - ('Latvia', 'Estonia', None), ('Lebanon', 'Israel', None), - ('Lesotho', 'South Africa', None), ('Liberia', "Cote d'Ivoire", None), - ('Liberia', 'Guinea', None), ('Liberia', 'Sierra Leone', None), - ('Libya', 'Algeria', None), ('Libya', 'Chad', None), - ('Libya', 'Egypt', None), ('Libya', 'Niger', None), - ('Libya', 'Sudan', None), ('Libya', 'Tunisia', None), - ('Liechtenstein', 'Austria', None), ('Liechtenstein', 'Switzerland', None), - ('Lithuania', 'Belarus', None), ('Lithuania', 'Latvia', None), - ('Lithuania', 'Poland', None), ('Lithuania', 'Russia', None), - ('Luxembourg', 'Belgium', None), ('Luxembourg', 'Germany', None), - ('Macau', 'China', None), ('Macedonia', 'Greece', None), - ('Macedonia', 'Serbia', None), ('Malaysia', 'Brunei', None), - ('Malaysia', 'Indonesia', None), ('Malaysia', 'Thailand', None), - ('Mali', 'Algeria', None), ('Mali', 'Guinea', None), - ('Mali', 'Niger', None), ('Mali', 'Senegal', None), - ('Mauritania', 'Algeria', None), ('Mauritania', 'Mali', None), - ('Mauritania', 'Senegal', None), ('Mauritania', 'Western Sahara', None), - ('Monaco', 'France', None), ('Montenegro', 'Croatia', None), - ('Montenegro', 'Kosovo', None), ('Montenegro', 'Serbia', None), - ('Morocco', 'Spain', None), ('Mozambique', 'Malawi', None), - ('Mozambique', 'Zambia', None), ('Mozambique', 'Zimbabwe', None), - ('Namibia', 'Botswana', None), ('Namibia', 'Zambia', None), - ('Netherlands', 'Germany', None), ('Niger', 'Algeria', None), - ('Niger', 'Nigeria', None), ('Norway', 'Finland', None), - ('Norway', 'Russia', None), ('Norway', 'Sweden', None), - ('Oman', 'United Arab Emirates', None), ('Oman', 'Yemen', None), - ('Pakistan', 'Afghanistan', None), ('Pakistan', 'China', None), - ('Pakistan', 'India', None), ('Pakistan', 'Iran', None), - ('Panama', 'Colombia', None), ('Panama', 'Costa Rica', None), - ('Paraguay', 'Brazil', None), ('Peru', 'Brazil', None), - ('Peru', 'Chile', None), ('Peru', 'Colombia', None), - ('Peru', 'Ecuador', None), ('Poland', 'Belarus', None), - ('Poland', 'Germany', None), ('Portugal', 'Spain', None), - ('Republic of the Congo', 'Angola', None), ('Republic of the Congo', 'Central African Republic', None), - ('Republic of the Congo', 'Democratic Republic of the Congo', None), ('Romania', 'Hungary', None), - ('Romania', 'Moldova', None), ('Romania', 'Serbia', None), - ('Russia', 'Belarus', None), ('Russia', 'Estonia', None), - ('Russia', 'Georgia', None), ('Russia', 'Kazakhstan', None), - ('Russia', 'Latvia', None), ('Russia', 'Mongolia', None), - ('Russia', 'North Korea', None), ('Russia', 'Poland', None), - ('Rwanda', 'Burundi', None), ('Rwanda', 'Democratic Republic of the Congo', None), - ('Rwanda', 'Uganda', None), ('Saint Martin', 'Netherlands Antilles', None), - ('San Marino', 'Italy', None), ('Saudi Arabia', 'Iraq', None), - ('Saudi Arabia', 'Jordan', None), ('Saudi Arabia', 'Kuwait', None), - ('Saudi Arabia', 'Oman', None), ('Saudi Arabia', 'Qatar', None), - ('Saudi Arabia', 'United Arab Emirates', None), ('Saudi Arabia', 'Yemen', None), - ('Senegal', 'Guinea', None), ('Serbia', 'Croatia', None), - ('Slovakia', 'Austria', None), ('Slovakia', 'Czech Republic', None), - ('Slovakia', 'Hungary', None), ('Slovakia', 'Poland', None), - ('Slovakia', 'Ukraine', None), ('Slovenia', 'Austria', None), - ('Slovenia', 'Croatia', None), ('Slovenia', 'Hungary', None), - ('Slovenia', 'Italy', None), ('Somalia', 'Djibouti', None), - ('South Africa', 'Botswana', None), ('South Africa', 'Mozambique', None), - ('South Africa', 'Namibia', None), ('South Africa', 'Zimbabwe', None), - ('South Korea', 'North Korea', None), ('Sudan', 'Democratic Republic of the Congo', None), - ('Sudan', 'Egypt', None), ('Sudan', 'Eritrea', None), - ('Suriname', 'Guyana', None), ('Swaziland', 'Mozambique', None), - ('Swaziland', 'South Africa', None), ('Switzerland', 'Austria', None), - ('Switzerland', 'Germany', None), ('Switzerland', 'Italy', None), - ('Syria', 'Iraq', None), ('Syria', 'Israel', None), - ('Syria', 'Lebanon', None), ('Tajikistan', 'Afghanistan', None), - ('Tajikistan', 'China', None), ('Tajikistan', 'Kyrgyzstan', None), - ('Tajikistan', 'Uzbekistan', None), ('Tanzania', 'Burundi', None), - ('Tanzania', 'Democratic Republic of the Congo', None), ('Tanzania', 'Kenya', None), - ('Tanzania', 'Malawi', None), ('Tanzania', 'Mozambique', None), - ('Tanzania', 'Rwanda', None), ('Tanzania', 'Uganda', None), - ('Tanzania', 'Zambia', None), ('Thailand', 'Burma', None), - ('The Gambia', 'Senegal', None), ('Timor-Leste', 'Indonesia', None), - ('Turkey', 'Armenia', None), ('Turkey', 'Bulgaria', None), - ('Turkey', 'Georgia', None), ('Turkey', 'Greece', None), - ('Turkey', 'Iran', None), ('Turkey', 'Iraq', None), - ('Turkey', 'Syria', None), ('Turkmenistan', 'Afghanistan', None), - ('Turkmenistan', 'Iran', None), ('Turkmenistan', 'Kazakhstan', None), - ('Turkmenistan', 'Uzbekistan', None), ('Uganda', 'Democratic Republic of the Congo', None), - ('Uganda', 'Sudan', None), ('Ukraine', 'Belarus', None), - ('Ukraine', 'Hungary', None), ('Ukraine', 'Moldova', None), - ('Ukraine', 'Poland', None), ('Ukraine', 'Romania', None), - ('Ukraine', 'Russia', None), ('United States', 'Mexico', None), - ('Uruguay', 'Brazil', None), ('Uzbekistan', 'Kazakhstan', None), - ('Uzbekistan', 'Kyrgyzstan', None), ('Vatican City', 'Italy', None), - ('Venezuela', 'Guyana', None), ('West Bank', 'Israel', None), - ('Western Sahara', 'Algeria', None), ('Western Sahara', 'Morocco', None), - ('Zambia', 'Malawi', None), ('Zambia', 'Zimbabwe', None), - ('Zimbabwe', 'Botswana', None) + ('Afghanistan', 'China'), ('Afghanistan', 'Iran'), + ('Afghanistan', 'Uzbekistan'), ('Albania', 'Greece'), + ('Albania', 'Kosovo'), ('Albania', 'Macedonia'), + ('Albania', 'Montenegro'), ('Algeria', 'Morocco'), + ('Algeria', 'Tunisia'), ('Andorra', 'Spain'), + ('Angola', 'Democratic Republic of the Congo'), ('Angola', 'Namibia'), + ('Angola', 'Zambia'), ('Argentina', 'Bolivia'), ('Argentina', 'Brazil'), + ('Argentina', 'Chile'), ('Argentina', 'Paraguay'), + ('Argentina', 'Uruguay'), ('Armenia', 'Georgia'), ('Armenia', 'Iran'), + ('Austria', 'Germany'), ('Azerbaijan', 'Armenia'), + ('Azerbaijan', 'Georgia'), ('Azerbaijan', 'Iran'), + ('Azerbaijan', 'Russia'), ('Azerbaijan', 'Turkey'), + ('Bangladesh', 'Burma'), ('Belgium', 'Germany'), + ('Belgium', 'Netherlands'), ('Belize', 'Mexico'), + ('Benin', 'Burkina Faso'), ('Benin', 'Niger'), ('Benin', 'Nigeria'), + ('Benin', 'Togo'), ('Bolivia', 'Brazil'), ('Bolivia', 'Chile'), + ('Bolivia', 'Paraguay'), ('Bolivia', 'Peru'), + ('Bosnia and Herzegovina', 'Croatia'), + ('Bosnia and Herzegovina', 'Montenegro'), + ('Bosnia and Herzegovina', 'Serbia'), ('Brazil', 'Colombia'), + ('Brazil', 'Guyana'), ('Brazil', 'Suriname'), ('Brazil', 'Venezuela'), + ('Bulgaria', 'Greece'), ('Bulgaria', 'Macedonia'), + ('Bulgaria', 'Romania'), ('Bulgaria', 'Serbia'), + ('Burkina Faso', 'Mali'), ('Burkina Faso', 'Niger'), + ('Burkina Faso', 'Togo'), + ('Burundi', 'Democratic Republic of the Congo'), ('Cambodia', 'Laos'), + ('Cambodia', 'Thailand'), ('Cambodia', 'Vietnam'), + ('Cameroon', 'Central African Republic'), ('Cameroon', 'Chad'), + ('Cameroon', 'Equatorial Guinea'), ('Cameroon', 'Nigeria'), + ('Cameroon', 'Republic of the Congo'), ('Canada', 'United States'), + ('Central African Republic', 'Chad'), + ('Central African Republic', 'Democratic Republic of the Congo'), + ('Central African Republic', 'Sudan'), ('Chad', 'Niger'), + ('Chad', 'Nigeria'), ('Chad', 'Sudan'), ('China', 'Bhutan'), + ('China', 'Burma'), ('China', 'Hong Kong'), ('China', 'Kazakhstan'), + ('China', 'Kyrgyzstan'), ('China', 'Mongolia'), ('China', 'Nepal'), + ('China', 'North Korea'), ('China', 'Russia'), ('China', 'Vietnam'), + ('Colombia', 'Venezuela'), ('Costa Rica', 'Nicaragua'), + ("Cote d'Ivoire", 'Burkina Faso'), ("Cote d'Ivoire", 'Guinea'), + ("Cote d'Ivoire", 'Mali'), ('Cyprus', 'Akrotiri'), + ('Cyprus', 'Dhekelia'), ('Czech Republic', 'Austria'), + ('Czech Republic', 'Germany'), ('Czech Republic', 'Poland'), + ('Democratic Republic of the Congo', 'Zambia'), ('Denmark', 'Germany'), + ('Djibouti', 'Eritrea'), ('Dominican Republic', 'Haiti'), + ('Ecuador', 'Colombia'), ('El Salvador', 'Honduras'), + ('Ethiopia', 'Djibouti'), ('Ethiopia', 'Eritrea'), + ('Ethiopia', 'Kenya'), ('Ethiopia', 'Somalia'), ('Ethiopia', 'Sudan'), + ('Finland', 'Russia'), ('Finland', 'Sweden'), ('France', 'Andorra'), + ('France', 'Belgium'), ('France', 'Brazil'), ('France', 'Germany'), + ('France', 'Italy'), ('France', 'Luxembourg'), ('France', 'Spain'), + ('France', 'Suriname'), ('France', 'Switzerland'), + ('Gabon', 'Cameroon'), ('Gabon', 'Equatorial Guinea'), + ('Gabon', 'Republic of the Congo'), ('Gaza Strip', 'Egypt'), + ('Gaza Strip', 'Israel'), ('Ghana', 'Burkina Faso'), + ('Ghana', "Cote d'Ivoire"), ('Ghana', 'Togo'), ('Gibraltar', 'Spain'), + ('Guatemala', 'Belize'), ('Guatemala', 'El Salvador'), + ('Guatemala', 'Honduras'), ('Guatemala', 'Mexico'), + ('Guinea', 'Sierra Leone'), ('Guinea-Bissau', 'Guinea'), + ('Guinea-Bissau', 'Senegal'), ('Honduras', 'Nicaragua'), + ('Hungary', 'Austria'), ('Hungary', 'Croatia'), ('Hungary', 'Serbia'), + ('India', 'Bangladesh'), ('India', 'Bhutan'), ('India', 'Burma'), + ('India', 'China'), ('India', 'Nepal'), + ('Indonesia', 'Papua New Guinea'), ('Iran', 'Iraq'), + ('Ireland', 'United Kingdom'), ('Israel', 'Egypt'), + ('Italy', 'Austria'), ('Jordan', 'Iraq'), ('Jordan', 'Israel'), + ('Jordan', 'Syria'), ('Jordan', 'West Bank'), + ('Kazakhstan', 'Kyrgyzstan'), ('Kenya', 'Somalia'), ('Kenya', 'Sudan'), + ('Kenya', 'Uganda'), ('Kosovo', 'Macedonia'), ('Kosovo', 'Serbia'), + ('Kuwait', 'Iraq'), ('Laos', 'Burma'), ('Laos', 'China'), + ('Laos', 'Thailand'), ('Laos', 'Vietnam'), ('Latvia', 'Belarus'), + ('Latvia', 'Estonia'), ('Lebanon', 'Israel'), + ('Lesotho', 'South Africa'), ('Liberia', "Cote d'Ivoire"), + ('Liberia', 'Guinea'), ('Liberia', 'Sierra Leone'), + ('Libya', 'Algeria'), ('Libya', 'Chad'), ('Libya', 'Egypt'), + ('Libya', 'Niger'), ('Libya', 'Sudan'), ('Libya', 'Tunisia'), + ('Liechtenstein', 'Austria'), ('Liechtenstein', 'Switzerland'), + ('Lithuania', 'Belarus'), ('Lithuania', 'Latvia'), + ('Lithuania', 'Poland'), ('Lithuania', 'Russia'), + ('Luxembourg', 'Belgium'), ('Luxembourg', 'Germany'), + ('Macau', 'China'), ('Macedonia', 'Greece'), ('Macedonia', 'Serbia'), + ('Malaysia', 'Brunei'), ('Malaysia', 'Indonesia'), + ('Malaysia', 'Thailand'), ('Mali', 'Algeria'), ('Mali', 'Guinea'), + ('Mali', 'Niger'), ('Mali', 'Senegal'), ('Mauritania', 'Algeria'), + ('Mauritania', 'Mali'), ('Mauritania', 'Senegal'), + ('Mauritania', 'Western Sahara'), ('Monaco', 'France'), + ('Montenegro', 'Croatia'), ('Montenegro', 'Kosovo'), + ('Montenegro', 'Serbia'), ('Morocco', 'Spain'), + ('Mozambique', 'Malawi'), ('Mozambique', 'Zambia'), + ('Mozambique', 'Zimbabwe'), ('Namibia', 'Botswana'), + ('Namibia', 'Zambia'), ('Netherlands', 'Germany'), ('Niger', 'Algeria'), + ('Niger', 'Nigeria'), ('Norway', 'Finland'), ('Norway', 'Russia'), + ('Norway', 'Sweden'), ('Oman', 'United Arab Emirates'), + ('Oman', 'Yemen'), ('Pakistan', 'Afghanistan'), ('Pakistan', 'China'), + ('Pakistan', 'India'), ('Pakistan', 'Iran'), ('Panama', 'Colombia'), + ('Panama', 'Costa Rica'), ('Paraguay', 'Brazil'), ('Peru', 'Brazil'), + ('Peru', 'Chile'), ('Peru', 'Colombia'), ('Peru', 'Ecuador'), + ('Poland', 'Belarus'), ('Poland', 'Germany'), ('Portugal', 'Spain'), + ('Republic of the Congo', 'Angola'), + ('Republic of the Congo', 'Central African Republic'), + ('Republic of the Congo', 'Democratic Republic of the Congo'), + ('Romania', 'Hungary'), ('Romania', 'Moldova'), ('Romania', 'Serbia'), + ('Russia', 'Belarus'), ('Russia', 'Estonia'), ('Russia', 'Georgia'), + ('Russia', 'Kazakhstan'), ('Russia', 'Latvia'), ('Russia', 'Mongolia'), + ('Russia', 'North Korea'), ('Russia', 'Poland'), ('Rwanda', 'Burundi'), + ('Rwanda', 'Democratic Republic of the Congo'), ('Rwanda', 'Uganda'), + ('Saint Martin', 'Netherlands Antilles'), ('San Marino', 'Italy'), + ('Saudi Arabia', 'Iraq'), ('Saudi Arabia', 'Jordan'), + ('Saudi Arabia', 'Kuwait'), ('Saudi Arabia', 'Oman'), + ('Saudi Arabia', 'Qatar'), ('Saudi Arabia', 'United Arab Emirates'), + ('Saudi Arabia', 'Yemen'), ('Senegal', 'Guinea'), ('Serbia', 'Croatia'), + ('Slovakia', 'Austria'), ('Slovakia', 'Czech Republic'), + ('Slovakia', 'Hungary'), ('Slovakia', 'Poland'), + ('Slovakia', 'Ukraine'), ('Slovenia', 'Austria'), + ('Slovenia', 'Croatia'), ('Slovenia', 'Hungary'), ('Slovenia', 'Italy'), + ('Somalia', 'Djibouti'), ('South Africa', 'Botswana'), + ('South Africa', 'Mozambique'), ('South Africa', 'Namibia'), + ('South Africa', 'Zimbabwe'), ('South Korea', 'North Korea'), + ('Sudan', 'Democratic Republic of the Congo'), ('Sudan', 'Egypt'), + ('Sudan', 'Eritrea'), ('Suriname', 'Guyana'), + ('Swaziland', 'Mozambique'), ('Swaziland', 'South Africa'), + ('Switzerland', 'Austria'), ('Switzerland', 'Germany'), + ('Switzerland', 'Italy'), ('Syria', 'Iraq'), ('Syria', 'Israel'), + ('Syria', 'Lebanon'), ('Tajikistan', 'Afghanistan'), + ('Tajikistan', 'China'), ('Tajikistan', 'Kyrgyzstan'), + ('Tajikistan', 'Uzbekistan'), ('Tanzania', 'Burundi'), + ('Tanzania', 'Democratic Republic of the Congo'), ('Tanzania', 'Kenya'), + ('Tanzania', 'Malawi'), ('Tanzania', 'Mozambique'), + ('Tanzania', 'Rwanda'), ('Tanzania', 'Uganda'), ('Tanzania', 'Zambia'), + ('Thailand', 'Burma'), ('The Gambia', 'Senegal'), + ('Timor-Leste', 'Indonesia'), ('Turkey', 'Armenia'), + ('Turkey', 'Bulgaria'), ('Turkey', 'Georgia'), ('Turkey', 'Greece'), + ('Turkey', 'Iran'), ('Turkey', 'Iraq'), ('Turkey', 'Syria'), + ('Turkmenistan', 'Afghanistan'), ('Turkmenistan', 'Iran'), + ('Turkmenistan', 'Kazakhstan'), ('Turkmenistan', 'Uzbekistan'), + ('Uganda', 'Democratic Republic of the Congo'), ('Uganda', 'Sudan'), + ('Ukraine', 'Belarus'), ('Ukraine', 'Hungary'), ('Ukraine', 'Moldova'), + ('Ukraine', 'Poland'), ('Ukraine', 'Romania'), ('Ukraine', 'Russia'), + ('United States', 'Mexico'), ('Uruguay', 'Brazil'), + ('Uzbekistan', 'Kazakhstan'), ('Uzbekistan', 'Kyrgyzstan'), + ('Vatican City', 'Italy'), ('Venezuela', 'Guyana'), + ('West Bank', 'Israel'), ('Western Sahara', 'Algeria'), + ('Western Sahara', 'Morocco'), ('Zambia', 'Malawi'), + ('Zambia', 'Zimbabwe'), ('Zimbabwe', 'Botswana') ] gps_coordinates = { - 'Canada': [[60, 'N'], [95, 'W']], - 'Saint Martin': [[18, 'N'], [63, 'W']], - 'Sao Tome and Principe': [[1, 'N'], [7, 'E']], - 'Turkmenistan': [[40, 'N'], [60, 'E']], - 'Saint Helena': [[15, 'S'], [5, 'W']], - 'Lithuania': [[56, 'N'], [24, 'E']], - 'Cambodia': [[13, 'N'], [105, 'E']], - 'Saint Kitts and Nevis': [[17, 'N'], [62, 'W']], - 'Ethiopia': [[8, 'N'], [38, 'E']], - 'The Gambia': [[13, 'N'], [16, 'W']], - 'Aruba': [[12, 'N'], [69, 'W']], - 'Swaziland': [[26, 'S'], [31, 'E']], - 'Guinea-Bissau': [[12, 'N'], [15, 'W']], - 'Argentina': [[34, 'S'], [64, 'W']], - 'Bolivia': [[17, 'S'], [65, 'W']], - 'Bahamas, The': [[24, 'N'], [76, 'W']], - 'Spratly Islands': [[8, 'N'], [111, 'E']], - 'Ghana': [[8, 'N'], [2, 'W']], - 'Saudi Arabia': [[25, 'N'], [45, 'E']], + "Cote d'Ivoire": [[8, 'N'], [5, 'W']], + 'Afghanistan': [[33, 'N'], [65, 'E']], + 'Akrotiri': [[34, 'N'], [32, 'E']], + 'Albania': [[41, 'N'], [20, 'E']], + 'Algeria': [[28, 'N'], [3, 'E']], 'American Samoa': [[14, 'S'], [170, 'W']], - 'Cocos (Keeling) Islands': [[12, 'S'], [96, 'E']], - 'Slovenia': [[46, 'N'], [14, 'E']], - 'Guatemala': [[15, 'N'], [90, 'W']], - 'Bosnia and Herzegovina': [[44, 'N'], [18, 'E']], - 'Kuwait': [[29, 'N'], [45, 'E']], - 'Jordan': [[31, 'N'], [36, 'E']], - 'Saint Barthelemy': [[17, 'N'], [62, 'W']], + 'Andorra': [[42, 'N'], [1, 'E']], + 'Angola': [[12, 'S'], [18, 'E']], + 'Anguilla': [[18, 'N'], [63, 'W']], + 'Antarctica': [[90, 'S'], [0, 'E']], + 'Antigua and Barbuda': [[17, 'N'], [61, 'W']], + 'Argentina': [[34, 'S'], [64, 'W']], + 'Armenia': [[40, 'N'], [45, 'E']], + 'Aruba': [[12, 'N'], [69, 'W']], 'Ashmore and Cartier Islands': [[12, 'S'], [123, 'E']], - 'Dominica': [[15, 'N'], [61, 'W']], - 'Liberia': [[6, 'N'], [9, 'W']], - 'Maldives': [[3, 'N'], [73, 'E']], - 'Micronesia, Federated States of': [[6, 'N'], [158, 'E']], - 'Pakistan': [[30, 'N'], [70, 'E']], - 'Oman': [[21, 'N'], [57, 'E']], - 'Tanzania': [[6, 'S'], [35, 'E']], - 'Albania': [[41, 'N'], [20, 'E']], - 'Gabon': [[1, 'S'], [11, 'E']], - 'Niue': [[19, 'S'], [169, 'W']], - 'Monaco': [[43, 'N'], [7, 'E']], - 'Wallis and Futuna': [[13, 'S'], [176, 'W']], - 'New Zealand': [[41, 'S'], [174, 'E']], - 'Yemen': [[15, 'N'], [48, 'E']], - 'Jersey': [[49, 'N'], [2, 'W']], - 'Jamaica': [[18, 'N'], [77, 'W']], - 'Greenland': [[72, 'N'], [40, 'W']], - 'West Bank': [[32, 'N'], [35, 'E']], - 'Macau': [[22, 'N'], [113, 'E']], - 'Jan Mayen': [[71, 'N'], [8, 'W']], - 'United Arab Emirates': [[24, 'N'], [54, 'E']], - 'Guam': [[13, 'N'], [144, 'E']], - 'Uruguay': [[33, 'S'], [56, 'W']], - 'India': [[20, 'N'], [77, 'E']], + 'Australia': [[27, 'S'], [133, 'E']], + 'Austria': [[47, 'N'], [13, 'E']], 'Azerbaijan': [[40, 'N'], [47, 'E']], - 'Lesotho': [[29, 'S'], [28, 'E']], - 'Saint Vincent and the Grenadines': [[13, 'N'], [61, 'W']], - 'Kenya': [[1, 'N'], [38, 'E']], - 'South Korea': [[37, 'N'], [127, 'E']], - 'Tajikistan': [[39, 'N'], [71, 'E']], - 'Turkey': [[39, 'N'], [35, 'E']], - 'Afghanistan': [[33, 'N'], [65, 'E']], - 'Paraguay': [[23, 'S'], [58, 'W']], + 'Bahamas, The': [[24, 'N'], [76, 'W']], + 'Bahrain': [[26, 'N'], [50, 'E']], 'Bangladesh': [[24, 'N'], [90, 'E']], - 'Mauritania': [[20, 'N'], [12, 'W']], - 'Solomon Islands': [[8, 'S'], [159, 'E']], - 'Saint Pierre and Miquelon': [[46, 'N'], [56, 'W']], - 'Gaza Strip': [[31, 'N'], [34, 'E']], - 'San Marino': [[43, 'N'], [12, 'E']], - 'French Polynesia': [[15, 'S'], [140, 'W']], - 'France': [[46, 'N'], [2, 'E']], - 'Fiji': [[18, 'S'], [175, 'E']], - 'Rwanda': [[2, 'S'], [30, 'E']], - 'Slovakia': [[48, 'N'], [19, 'E']], - 'Somalia': [[10, 'N'], [49, 'E']], - 'Peru': [[10, 'S'], [76, 'W']], - 'Laos': [[18, 'N'], [105, 'E']], - 'Nauru': [[0, 'S'], [166, 'E']], - 'Seychelles': [[4, 'S'], [55, 'E']], - 'Norway': [[62, 'N'], [10, 'E']], - "Cote d'Ivoire": [[8, 'N'], [5, 'W']], - 'Cook Islands': [[21, 'S'], [159, 'W']], + 'Barbados': [[13, 'N'], [59, 'W']], + 'Belarus': [[53, 'N'], [28, 'E']], + 'Belgium': [[50, 'N'], [4, 'E']], + 'Belize': [[17, 'N'], [88, 'W']], 'Benin': [[9, 'N'], [2, 'E']], - 'Western Sahara': [[24, 'N'], [13, 'W']], - 'Cuba': [[21, 'N'], [80, 'W']], - 'Cameroon': [[6, 'N'], [12, 'E']], - 'Montenegro': [[42, 'N'], [19, 'E']], - 'Republic of the Congo': [[1, 'S'], [15, 'E']], + 'Bermuda': [[32, 'N'], [64, 'W']], + 'Bhutan': [[27, 'N'], [90, 'E']], + 'Bolivia': [[17, 'S'], [65, 'W']], + 'Bosnia and Herzegovina': [[44, 'N'], [18, 'E']], + 'Botswana': [[22, 'S'], [24, 'E']], + 'Bouvet Island': [[54, 'S'], [3, 'E']], + 'Brazil': [[10, 'S'], [55, 'W']], + 'British Indian Ocean Territory': [[6, 'S'], [71, 'E']], + 'British Virgin Islands': [[18, 'N'], [64, 'W']], + 'Brunei': [[4, 'N'], [114, 'E']], + 'Bulgaria': [[43, 'N'], [25, 'E']], 'Burkina Faso': [[13, 'N'], [2, 'W']], - 'Togo': [[8, 'N'], [1, 'E']], - 'Virgin Islands': [[18, 'N'], [64, 'W']], - 'China': [[35, 'N'], [105, 'E']], - 'Armenia': [[40, 'N'], [45, 'E']], - 'Timor-Leste': [[8, 'S'], [125, 'E']], - 'Dominican Republic': [[19, 'N'], [70, 'W']], - 'Ukraine': [[49, 'N'], [32, 'E']], - 'Bahrain': [[26, 'N'], [50, 'E']], - 'Tonga': [[20, 'S'], [175, 'W']], - 'Finland': [[64, 'N'], [26, 'E']], - 'Libya': [[25, 'N'], [17, 'E']], + 'Burma': [[22, 'N'], [98, 'E']], + 'Burundi': [[3, 'S'], [30, 'E']], + 'Cambodia': [[13, 'N'], [105, 'E']], + 'Cameroon': [[6, 'N'], [12, 'E']], + 'Canada': [[60, 'N'], [95, 'W']], + 'Cape Verde': [[16, 'N'], [24, 'W']], 'Cayman Islands': [[19, 'N'], [80, 'W']], 'Central African Republic': [[7, 'N'], [21, 'E']], - 'New Caledonia': [[21, 'S'], [165, 'E']], - 'Mauritius': [[20, 'S'], [57, 'E']], - 'Liechtenstein': [[47, 'N'], [9, 'E']], - 'Vietnam': [[16, 'N'], [107, 'E']], - 'British Virgin Islands': [[18, 'N'], [64, 'W']], - 'Mali': [[17, 'N'], [4, 'W']], - 'Vatican City': [[41, 'N'], [12, 'E']], - 'Russia': [[60, 'N'], [100, 'E']], - 'Bulgaria': [[43, 'N'], [25, 'E']], - 'United States': [[38, 'N'], [97, 'W']], - 'Romania': [[46, 'N'], [25, 'E']], - 'Angola': [[12, 'S'], [18, 'E']], 'Chad': [[15, 'N'], [19, 'E']], - 'South Africa': [[29, 'S'], [24, 'E']], - 'Tokelau': [[9, 'S'], [172, 'W']], - 'Turks and Caicos Islands': [[21, 'N'], [71, 'W']], - 'South Georgia and the South Sandwich Islands': [[54, 'S'], [37, 'W']], - 'Sweden': [[62, 'N'], [15, 'E']], - 'Qatar': [[25, 'N'], [51, 'E']], - 'Malaysia': [[2, 'N'], [112, 'E']], - 'Senegal': [[14, 'N'], [14, 'W']], - 'Latvia': [[57, 'N'], [25, 'E']], + 'Chile': [[30, 'S'], [71, 'W']], + 'China': [[35, 'N'], [105, 'E']], + 'Christmas Island': [[10, 'S'], [105, 'E']], 'Clipperton Island': [[10, 'N'], [109, 'W']], - 'Uganda': [[1, 'N'], [32, 'E']], - 'Japan': [[36, 'N'], [138, 'E']], - 'Niger': [[16, 'N'], [8, 'E']], - 'Brazil': [[10, 'S'], [55, 'W']], - 'Faroe Islands': [[62, 'N'], [7, 'W']], - 'Guinea': [[11, 'N'], [10, 'W']], - 'Panama': [[9, 'N'], [80, 'W']], + 'Cocos (Keeling) Islands': [[12, 'S'], [96, 'E']], + 'Colombia': [[4, 'N'], [72, 'W']], + 'Comoros': [[12, 'S'], [44, 'E']], + 'Cook Islands': [[21, 'S'], [159, 'W']], + 'Coral Sea Islands': [[18, 'S'], [152, 'E']], 'Costa Rica': [[10, 'N'], [84, 'W']], - 'Luxembourg': [[49, 'N'], [6, 'E']], - 'Cape Verde': [[16, 'N'], [24, 'W']], - 'Andorra': [[42, 'N'], [1, 'E']], - 'Gibraltar': [[36, 'N'], [5, 'W']], - 'Ireland': [[53, 'N'], [8, 'W']], - 'Syria': [[35, 'N'], [38, 'E']], - 'Palau': [[7, 'N'], [134, 'E']], - 'Nigeria': [[10, 'N'], [8, 'E']], + 'Croatia': [[45, 'N'], [15, 'E']], + 'Cuba': [[21, 'N'], [80, 'W']], + 'Cyprus': [[35, 'N'], [33, 'E']], + 'Czech Republic': [[49, 'N'], [15, 'E']], + 'Democratic Republic of the Congo': [[0, 'N'], [25, 'E']], + 'Denmark': [[56, 'N'], [10, 'E']], + 'Dhekelia': [[34, 'N'], [33, 'E']], + 'Djibouti': [[11, 'N'], [43, 'E']], + 'Dominica': [[15, 'N'], [61, 'W']], + 'Dominican Republic': [[19, 'N'], [70, 'W']], 'Ecuador': [[2, 'S'], [77, 'W']], - 'Northern Mariana Islands': [[15, 'N'], [145, 'E']], - 'Brunei': [[4, 'N'], [114, 'E']], - 'Mozambique': [[18, 'S'], [35, 'E']], - 'Australia': [[27, 'S'], [133, 'E']], - 'Iran': [[32, 'N'], [53, 'E']], - 'Algeria': [[28, 'N'], [3, 'E']], - 'Svalbard': [[78, 'N'], [20, 'E']], + 'Egypt': [[27, 'N'], [30, 'E']], 'El Salvador': [[13, 'N'], [88, 'W']], - 'Tuvalu': [[8, 'S'], [178, 'E']], - 'Pitcairn Islands': [[25, 'S'], [130, 'W']], - 'Czech Republic': [[49, 'N'], [15, 'E']], - 'Marshall Islands': [[9, 'N'], [168, 'E']], - 'Chile': [[30, 'S'], [71, 'W']], - 'Puerto Rico': [[18, 'N'], [66, 'W']], - 'Belgium': [[50, 'N'], [4, 'E']], - 'Kiribati': [[1, 'N'], [173, 'E']], + 'Equatorial Guinea': [[2, 'N'], [10, 'E']], + 'Eritrea': [[15, 'N'], [39, 'E']], + 'Estonia': [[59, 'N'], [26, 'E']], + 'Ethiopia': [[8, 'N'], [38, 'E']], + 'Falkland Islands (Islas Malvinas)': [[51, 'S'], [59, 'W']], + 'Faroe Islands': [[62, 'N'], [7, 'W']], + 'Fiji': [[18, 'S'], [175, 'E']], + 'Finland': [[64, 'N'], [26, 'E']], + 'France': [[46, 'N'], [2, 'E']], + 'French Polynesia': [[15, 'S'], [140, 'W']], + 'Gabon': [[1, 'S'], [11, 'E']], + 'Gaza Strip': [[31, 'N'], [34, 'E']], + 'Georgia': [[42, 'N'], [43, 'E']], + 'Germany': [[51, 'N'], [9, 'E']], + 'Ghana': [[8, 'N'], [2, 'W']], + 'Gibraltar': [[36, 'N'], [5, 'W']], + 'Greece': [[39, 'N'], [22, 'E']], + 'Greenland': [[72, 'N'], [40, 'W']], + 'Grenada': [[12, 'N'], [61, 'W']], + 'Guam': [[13, 'N'], [144, 'E']], + 'Guatemala': [[15, 'N'], [90, 'W']], + 'Guernsey': [[49, 'N'], [2, 'W']], + 'Guinea': [[11, 'N'], [10, 'W']], + 'Guinea-Bissau': [[12, 'N'], [15, 'W']], + 'Guyana': [[5, 'N'], [59, 'W']], 'Haiti': [[19, 'N'], [72, 'W']], - 'Belize': [[17, 'N'], [88, 'W']], + 'Heard Island and McDonald Islands': [[53, 'S'], [72, 'E']], + 'Honduras': [[15, 'N'], [86, 'W']], 'Hong Kong': [[22, 'N'], [114, 'E']], - 'Saint Lucia': [[13, 'N'], [60, 'W']], - 'Georgia': [[42, 'N'], [43, 'E']], + 'Hungary': [[47, 'N'], [20, 'E']], + 'Iceland': [[65, 'N'], [18, 'W']], + 'India': [[20, 'N'], [77, 'E']], + 'Indonesia': [[5, 'S'], [120, 'E']], + 'Iran': [[32, 'N'], [53, 'E']], + 'Iraq': [[33, 'N'], [44, 'E']], + 'Ireland': [[53, 'N'], [8, 'W']], + 'Isle of Man': [[54, 'N'], [4, 'W']], + 'Israel': [[31, 'N'], [34, 'E']], + 'Italy': [[42, 'N'], [12, 'E']], + 'Jamaica': [[18, 'N'], [77, 'W']], + 'Jan Mayen': [[71, 'N'], [8, 'W']], + 'Japan': [[36, 'N'], [138, 'E']], + 'Jersey': [[49, 'N'], [2, 'W']], + 'Jordan': [[31, 'N'], [36, 'E']], + 'Kazakhstan': [[48, 'N'], [68, 'E']], + 'Kenya': [[1, 'N'], [38, 'E']], + 'Kiribati': [[1, 'N'], [173, 'E']], + 'Kosovo': [[42, 'N'], [21, 'E']], + 'Kuwait': [[29, 'N'], [45, 'E']], + 'Kyrgyzstan': [[41, 'N'], [75, 'E']], + 'Laos': [[18, 'N'], [105, 'E']], + 'Latvia': [[57, 'N'], [25, 'E']], + 'Lebanon': [[33, 'N'], [35, 'E']], + 'Lesotho': [[29, 'S'], [28, 'E']], + 'Liberia': [[6, 'N'], [9, 'W']], + 'Libya': [[25, 'N'], [17, 'E']], + 'Liechtenstein': [[47, 'N'], [9, 'E']], + 'Lithuania': [[56, 'N'], [24, 'E']], + 'Luxembourg': [[49, 'N'], [6, 'E']], + 'Macau': [[22, 'N'], [113, 'E']], + 'Macedonia': [[41, 'N'], [22, 'E']], + 'Madagascar': [[20, 'S'], [47, 'E']], + 'Malawi': [[13, 'S'], [34, 'E']], + 'Malaysia': [[2, 'N'], [112, 'E']], + 'Maldives': [[3, 'N'], [73, 'E']], + 'Mali': [[17, 'N'], [4, 'W']], + 'Malta': [[35, 'N'], [14, 'E']], + 'Marshall Islands': [[9, 'N'], [168, 'E']], + 'Mauritania': [[20, 'N'], [12, 'W']], + 'Mauritius': [[20, 'S'], [57, 'E']], + 'Mayotte': [[12, 'S'], [45, 'E']], 'Mexico': [[23, 'N'], [102, 'W']], - 'Denmark': [[56, 'N'], [10, 'E']], - 'Poland': [[52, 'N'], [20, 'E']], + 'Micronesia, Federated States of': [[6, 'N'], [158, 'E']], 'Moldova': [[47, 'N'], [29, 'E']], + 'Monaco': [[43, 'N'], [7, 'E']], + 'Mongolia': [[46, 'N'], [105, 'E']], + 'Montenegro': [[42, 'N'], [19, 'E']], + 'Montserrat': [[16, 'N'], [62, 'W']], 'Morocco': [[32, 'N'], [5, 'W']], + 'Mozambique': [[18, 'S'], [35, 'E']], 'Namibia': [[22, 'S'], [17, 'E']], - 'Mongolia': [[46, 'N'], [105, 'E']], - 'Guernsey': [[49, 'N'], [2, 'W']], - 'Thailand': [[15, 'N'], [100, 'E']], - 'Switzerland': [[47, 'N'], [8, 'E']], - 'Grenada': [[12, 'N'], [61, 'W']], + 'Nauru': [[0, 'S'], [166, 'E']], 'Navassa Island': [[18, 'N'], [75, 'W']], - 'Isle of Man': [[54, 'N'], [4, 'W']], - 'Portugal': [[39, 'N'], [8, 'W']], - 'Estonia': [[59, 'N'], [26, 'E']], - 'Kosovo': [[42, 'N'], [21, 'E']], + 'Nepal': [[28, 'N'], [84, 'E']], + 'Netherlands Antilles': [[12, 'N'], [69, 'W']], + 'Netherlands': [[52, 'N'], [5, 'E']], + 'New Caledonia': [[21, 'S'], [165, 'E']], + 'New Zealand': [[41, 'S'], [174, 'E']], + 'Nicaragua': [[13, 'N'], [85, 'W']], + 'Niger': [[16, 'N'], [8, 'E']], + 'Nigeria': [[10, 'N'], [8, 'E']], + 'Niue': [[19, 'S'], [169, 'W']], 'Norfolk Island': [[29, 'S'], [167, 'E']], - 'Bouvet Island': [[54, 'S'], [3, 'E']], - 'Lebanon': [[33, 'N'], [35, 'E']], + 'North Korea': [[40, 'N'], [127, 'E']], + 'Northern Mariana Islands': [[15, 'N'], [145, 'E']], + 'Norway': [[62, 'N'], [10, 'E']], + 'Oman': [[21, 'N'], [57, 'E']], + 'Pakistan': [[30, 'N'], [70, 'E']], + 'Palau': [[7, 'N'], [134, 'E']], + 'Panama': [[9, 'N'], [80, 'W']], + 'Papua New Guinea': [[6, 'S'], [147, 'E']], + 'Paracel Islands': [[16, 'N'], [112, 'E']], + 'Paraguay': [[23, 'S'], [58, 'W']], + 'Peru': [[10, 'S'], [76, 'W']], + 'Philippines': [[13, 'N'], [122, 'E']], + 'Pitcairn Islands': [[25, 'S'], [130, 'W']], + 'Poland': [[52, 'N'], [20, 'E']], + 'Portugal': [[39, 'N'], [8, 'W']], + 'Puerto Rico': [[18, 'N'], [66, 'W']], + 'Qatar': [[25, 'N'], [51, 'E']], + 'Republic of the Congo': [[1, 'S'], [15, 'E']], + 'Romania': [[46, 'N'], [25, 'E']], + 'Russia': [[60, 'N'], [100, 'E']], + 'Rwanda': [[2, 'S'], [30, 'E']], + 'Saint Barthelemy': [[17, 'N'], [62, 'W']], + 'Saint Helena': [[15, 'S'], [5, 'W']], + 'Saint Kitts and Nevis': [[17, 'N'], [62, 'W']], + 'Saint Lucia': [[13, 'N'], [60, 'W']], + 'Saint Martin': [[18, 'N'], [63, 'W']], + 'Saint Pierre and Miquelon': [[46, 'N'], [56, 'W']], + 'Saint Vincent and the Grenadines': [[13, 'N'], [61, 'W']], + 'Samoa': [[13, 'S'], [172, 'W']], + 'San Marino': [[43, 'N'], [12, 'E']], + 'Sao Tome and Principe': [[1, 'N'], [7, 'E']], + 'Saudi Arabia': [[25, 'N'], [45, 'E']], + 'Senegal': [[14, 'N'], [14, 'W']], + 'Serbia': [[44, 'N'], [21, 'E']], + 'Seychelles': [[4, 'S'], [55, 'E']], 'Sierra Leone': [[8, 'N'], [11, 'W']], - 'Uzbekistan': [[41, 'N'], [64, 'E']], - 'Tunisia': [[34, 'N'], [9, 'E']], - 'Djibouti': [[11, 'N'], [43, 'E']], - 'Heard Island and McDonald Islands': [[53, 'S'], [72, 'E']], - 'Antigua and Barbuda': [[17, 'N'], [61, 'W']], + 'Singapore': [[1, 'N'], [103, 'E']], + 'Slovakia': [[48, 'N'], [19, 'E']], + 'Slovenia': [[46, 'N'], [14, 'E']], + 'Solomon Islands': [[8, 'S'], [159, 'E']], + 'Somalia': [[10, 'N'], [49, 'E']], + 'South Africa': [[29, 'S'], [24, 'E']], + 'South Georgia and the South Sandwich Islands': [[54, 'S'], [37, 'W']], + 'South Korea': [[37, 'N'], [127, 'E']], 'Spain': [[40, 'N'], [4, 'W']], - 'Colombia': [[4, 'N'], [72, 'W']], - 'Burundi': [[3, 'S'], [30, 'E']], - 'Taiwan': [[23, 'N'], [121, 'E']], - 'Cyprus': [[35, 'N'], [33, 'E']], - 'Barbados': [[13, 'N'], [59, 'W']], - 'Falkland Islands (Islas Malvinas)': [[51, 'S'], [59, 'W']], - 'Madagascar': [[20, 'S'], [47, 'E']], - 'Italy': [[42, 'N'], [12, 'E']], - 'Bhutan': [[27, 'N'], [90, 'E']], + 'Spratly Islands': [[8, 'N'], [111, 'E']], + 'Sri Lanka': [[7, 'N'], [81, 'E']], 'Sudan': [[15, 'N'], [30, 'E']], - 'Vanuatu': [[16, 'S'], [167, 'E']], - 'Malta': [[35, 'N'], [14, 'E']], - 'Hungary': [[47, 'N'], [20, 'E']], - 'Democratic Republic of the Congo': [[0, 'N'], [25, 'E']], - 'Netherlands': [[52, 'N'], [5, 'E']], - 'Bermuda': [[32, 'N'], [64, 'W']], 'Suriname': [[4, 'N'], [56, 'W']], - 'Anguilla': [[18, 'N'], [63, 'W']], + 'Svalbard': [[78, 'N'], [20, 'E']], + 'Swaziland': [[26, 'S'], [31, 'E']], + 'Sweden': [[62, 'N'], [15, 'E']], + 'Switzerland': [[47, 'N'], [8, 'E']], + 'Syria': [[35, 'N'], [38, 'E']], + 'Taiwan': [[23, 'N'], [121, 'E']], + 'Tajikistan': [[39, 'N'], [71, 'E']], + 'Tanzania': [[6, 'S'], [35, 'E']], + 'Thailand': [[15, 'N'], [100, 'E']], + 'The Gambia': [[13, 'N'], [16, 'W']], + 'Timor-Leste': [[8, 'S'], [125, 'E']], + 'Togo': [[8, 'N'], [1, 'E']], + 'Tokelau': [[9, 'S'], [172, 'W']], + 'Tonga': [[20, 'S'], [175, 'W']], + 'Trinidad and Tobago': [[11, 'N'], [61, 'W']], + 'Tunisia': [[34, 'N'], [9, 'E']], + 'Turkey': [[39, 'N'], [35, 'E']], + 'Turkmenistan': [[40, 'N'], [60, 'E']], + 'Turks and Caicos Islands': [[21, 'N'], [71, 'W']], + 'Tuvalu': [[8, 'S'], [178, 'E']], + 'Uganda': [[1, 'N'], [32, 'E']], + 'Ukraine': [[49, 'N'], [32, 'E']], + 'United Arab Emirates': [[24, 'N'], [54, 'E']], + 'United Kingdom': [[54, 'N'], [2, 'W']], + 'United States': [[38, 'N'], [97, 'W']], + 'Uruguay': [[33, 'S'], [56, 'W']], + 'Uzbekistan': [[41, 'N'], [64, 'E']], + 'Vanuatu': [[16, 'S'], [167, 'E']], + 'Vatican City': [[41, 'N'], [12, 'E']], 'Venezuela': [[8, 'N'], [66, 'W']], - 'Netherlands Antilles': [[12, 'N'], [69, 'W']], - 'Israel': [[31, 'N'], [34, 'E']], - 'Paracel Islands': [[16, 'N'], [112, 'E']], + 'Vietnam': [[16, 'N'], [107, 'E']], + 'Virgin Islands': [[18, 'N'], [64, 'W']], 'Wake Island': [[19, 'N'], [166, 'E']], - 'Indonesia': [[5, 'S'], [120, 'E']], - 'Iceland': [[65, 'N'], [18, 'W']], + 'Wallis and Futuna': [[13, 'S'], [176, 'W']], + 'West Bank': [[32, 'N'], [35, 'E']], + 'Western Sahara': [[24, 'N'], [13, 'W']], + 'Yemen': [[15, 'N'], [48, 'E']], 'Zambia': [[15, 'S'], [30, 'E']], - 'Samoa': [[13, 'S'], [172, 'W']], - 'Austria': [[47, 'N'], [13, 'E']], - 'Papua New Guinea': [[6, 'S'], [147, 'E']], - 'Malawi': [[13, 'S'], [34, 'E']], - 'Zimbabwe': [[20, 'S'], [30, 'E']], - 'Germany': [[51, 'N'], [9, 'E']], - 'Dhekelia': [[34, 'N'], [33, 'E']], - 'Kazakhstan': [[48, 'N'], [68, 'E']], - 'Philippines': [[13, 'N'], [122, 'E']], - 'Eritrea': [[15, 'N'], [39, 'E']], - 'Kyrgyzstan': [[41, 'N'], [75, 'E']], - 'Mayotte': [[12, 'S'], [45, 'E']], - 'Iraq': [[33, 'N'], [44, 'E']], - 'Montserrat': [[16, 'N'], [62, 'W']], - 'Coral Sea Islands': [[18, 'S'], [152, 'E']], - 'Macedonia': [[41, 'N'], [22, 'E']], - 'British Indian Ocean Territory': [[6, 'S'], [71, 'E']], - 'North Korea': [[40, 'N'], [127, 'E']], - 'Trinidad and Tobago': [[11, 'N'], [61, 'W']], - 'Akrotiri': [[34, 'N'], [32, 'E']], - 'Guyana': [[5, 'N'], [59, 'W']], - 'Belarus': [[53, 'N'], [28, 'E']], - 'Nepal': [[28, 'N'], [84, 'E']], - 'Burma': [[22, 'N'], [98, 'E']], - 'Honduras': [[15, 'N'], [86, 'W']], - 'Equatorial Guinea': [[2, 'N'], [10, 'E']], - 'Egypt': [[27, 'N'], [30, 'E']], - 'Nicaragua': [[13, 'N'], [85, 'W']], - 'Singapore': [[1, 'N'], [103, 'E']], - 'Serbia': [[44, 'N'], [21, 'E']], - 'Botswana': [[22, 'S'], [24, 'E']], - 'United Kingdom': [[54, 'N'], [2, 'W']], - 'Antarctica': [[90, 'S'], [0, 'E']], - 'Christmas Island': [[10, 'S'], [105, 'E']], - 'Greece': [[39, 'N'], [22, 'E']], - 'Sri Lanka': [[7, 'N'], [81, 'E']], - 'Croatia': [[45, 'N'], [15, 'E']], - 'Comoros': [[12, 'S'], [44, 'E']] + 'Zimbabwe': [[20, 'S'], [30, 'E']] } g = Graph() g.add_edges(edges) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 534b4954ed6..6ecb7bf19d6 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -20546,39 +20546,44 @@ def graphviz_string(self, **options): Edge-specific options can also be specified by providing a function (or tuple thereof) which maps each edge to a dictionary of options. Valid - options are ``"color"``, ``"backward"`` (a boolean), ``"dot"`` (a string - containing a sequence of options in ``dot`` format), ``"label"`` (a - string), ``"label_style"`` (``"string"`` or ``"latex"``), - ``"edge_string"`` (``"--"`` or ``"->"``). Here we state that the graph - should be laid out so that edges starting from ``1`` are going backward - (e.g. going up instead of down):: + options are + + - ``"color"`` + - ``"dot"`` (a string containing a sequence of options in ``dot`` format) + - ``"label"`` (a string) + - ``"label_style"`` (``"string"`` or ``"latex"``) + - ``"edge_string"`` (``"--"`` or ``"->"``) + - ``"dir"`` (``"forward"``, ``"back"``, ``"both"`` or ``"none"``) + + Here we state that the graph should be laid out so that edges + starting from ``1`` are going backward (e.g. going up instead of + down):: sage: def edge_options(data): ....: u, v, label = data - ....: return {"backward": u == 1} + ....: return {"dir":"back"} if u == 1 else {} sage: print(G.graphviz_string(edge_options=edge_options)) # random digraph { - node_10 [label="1"]; - node_11 [label="2"]; - node_3 [label="-1/2"]; - node_6 [label="1/2"]; - node_7 [label="1/2"]; - node_5 [label="1/3"]; - node_8 [label="2/3"]; + node_0 [label="-1"]; + node_1 [label="-1/2"]; + node_2 [label="1/2"]; + node_3 [label="-2"]; node_4 [label="1/4"]; - node_1 [label="-2"]; - node_9 [label="4/5"]; - node_0 [label="-4"]; - node_2 [label="-1"]; + node_5 [label="-4"]; + node_6 [label="1/3"]; + node_7 [label="2/3"]; + node_8 [label="4/5"]; + node_9 [label="1"]; + node_10 [label="2"]; - node_2 -> node_10 [dir=back]; - node_6 -> node_10 [dir=back]; - node_11 -> node_3; - node_11 -> node_5; - node_7 -> node_1; - node_7 -> node_8; - node_4 -> node_0; - node_4 -> node_9; + node_2 -> node_3; + node_2 -> node_7; + node_4 -> node_5; + node_4 -> node_8; + node_9 -> node_0 [dir=back]; + node_9 -> node_2 [dir=back]; + node_10 -> node_1; + node_10 -> node_6; } We now test all options:: @@ -20589,32 +20594,54 @@ def graphviz_string(self, **options): ....: if (u,v) == (1/2, -2): options["label"] = "coucou"; options["label_style"] = "string" ....: if (u,v) == (1/2,2/3): options["dot"] = "x=1,y=2" ....: if (u,v) == (1, -1): options["label_style"] = "latex" - ....: if (u,v) == (1, 1/2): options["edge_string"] = "<-" - ....: if (u,v) == (1/2, 1): options["backward"] = True + ....: if (u,v) == (1, 1/2): options["dir"] = "back" ....: return options sage: print(G.graphviz_string(edge_options=edge_options)) # random digraph { - node_10 [label="1"]; - node_11 [label="2"]; - node_3 [label="-1/2"]; - node_6 [label="1/2"]; - node_7 [label="1/2"]; - node_5 [label="1/3"]; - node_8 [label="2/3"]; + node_0 [label="-1"]; + node_1 [label="-1/2"]; + node_2 [label="1/2"]; + node_3 [label="-2"]; node_4 [label="1/4"]; - node_1 [label="-2"]; - node_9 [label="4/5"]; - node_0 [label="-4"]; - node_2 [label="-1"]; + node_5 [label="-4"]; + node_6 [label="1/3"]; + node_7 [label="2/3"]; + node_8 [label="4/5"]; + node_9 [label="1"]; + node_10 [label="2"]; - node_10 -> node_2 [label=" ", texlbl="$x \ {\mapsto}\ -\frac{1}{x}$", color = "red"]; - node_10 <- node_6 [color = "blue"]; - node_11 -> node_3 [color = "red"]; - node_11 -> node_5 [color = "blue"]; - node_7 -> node_1 [label="coucou", color = "red"]; - node_7 -> node_8 [x=1,y=2, color = "blue"]; - node_4 -> node_0 [color = "red"]; - node_4 -> node_9 [color = "blue"]; + node_2 -> node_3 [label="coucou", color = "red"]; + node_2 -> node_7 [x=1,y=2, color = "blue"]; + node_4 -> node_5 [color = "red"]; + node_4 -> node_8 [color = "blue"]; + node_9 -> node_0 [label=" ", texlbl="$x \ {\mapsto}\ -\frac{1}{x}$", color = "red"]; + node_9 -> node_2 [color = "blue", dir=back]; + node_10 -> node_1 [color = "red"]; + node_10 -> node_6 [color = "blue"]; + } + + We test the possible values of the ``'dir'`` edge option:: + + sage: edges = [(0,1,'a'), (1,2,'b'), (2,3,'c'), (3,4,'d')] + sage: G = DiGraph(edges) + sage: def edge_options(data): + ....: u,v,label = data + ....: if label == 'a': return {'dir':'forward'} + ....: if label == 'b': return {'dir':'back'} + ....: if label == 'c': return {'dir':'none'} + ....: if label == 'd': return {'dir':'both'} + sage: print(G.graphviz_string(edge_options=edge_options)) + digraph { + node_0 [label="0"]; + node_1 [label="1"]; + node_2 [label="2"]; + node_3 [label="3"]; + node_4 [label="4"]; + + node_0 -> node_1; + node_1 -> node_2 [dir=back]; + node_2 -> node_3 [dir=none]; + node_3 -> node_4 [dir=both]; } TESTS: @@ -20716,15 +20743,59 @@ def graphviz_string(self, **options): \draw [strokecolor,] (node_0) ... (node_1); ... \end{tikzpicture} + + An error is raised if the value of the edge option ``dir`` is + misspelled (:trac:`31381`):: + + sage: edges = [(0,1,'a'), (1,2,'b'), (2,3,'c'), (3,4,'d')] + sage: G = DiGraph(edges) + sage: def edge_options(data): + ....: u,v,label = data + ....: return {'dir':'forwward'} if label == 'a' else {} + sage: _ = G.graphviz_string(edge_options=edge_options) + Traceback (most recent call last): + ... + ValueError: dir(='forwward') in edge_options dict for the edge + (0, 1) should be 'forward', 'back', 'both', or 'none' + + An error is raised if the value of the edge option ``edge_string`` + is invalid (:trac:`31381`):: + + sage: edges = [(0,1,'a'), (1,2,'b'), (2,3,'c'), (3,4,'d')] + sage: G = DiGraph(edges) + sage: def edge_options(data): + ....: u,v,label = data + ....: return {'edge_string':'<-'} if label == 'a' else {} + sage: _ = G.graphviz_string(edge_options=edge_options) + Traceback (most recent call last): + ... + ValueError: edge_string(='<-') in edge_options dict for the edge + (0, 1) should be '--' or '->' + + The ``'backward'`` parameter of an edge option is deprecated since + :trac:`31381`:: + + sage: edges = [(0,1,'a'), (1,2,'b'), (2,3,'c'), (3,4,'d')] + sage: G = DiGraph(edges) + sage: def edge_options(data): + ....: u,v,label = data + ....: return {'backward':True} if label == 'a' else {} + sage: _ = G.graphviz_string(edge_options=edge_options) + doctest:...: DeprecationWarning: parameter {'backward':True} (in edge_options) + is deprecated. Use {'dir':'back'} instead. + See https://trac.sagemath.org/31381 for details. + """ from sage.graphs.dot2tex_utils import quoted_latex, quoted_str if self.is_directed(): graph_string = "digraph" default_edge_string = "->" + default_edge_dir = "forward" else: graph_string = "graph" default_edge_string = "--" + default_edge_dir = "none" edge_option_functions = options['edge_options'] if not isinstance(edge_option_functions, (tuple, list)): @@ -20804,7 +20875,7 @@ def graphviz_string(self, **options): # edges for loop for u, v, label in self.edge_iterator(): edge_options = { - 'backward': False, + 'dir': default_edge_dir, 'dot': None, 'edge_string': default_edge_string, 'color' : default_color, @@ -20814,6 +20885,17 @@ def graphviz_string(self, **options): for f in edge_option_functions: edge_options.update(f((u, v,label))) + if not edge_options['edge_string'] in ['--', '->']: + raise ValueError("edge_string(='{}') in edge_options dict for the " + "edge ({}, {}) should be '--' " + "or '->'".format(edge_options['edge_string'], u, v)) + + if 'backward' in edge_options and edge_options['backward']: + deprecation(31381, "parameter {'backward':True} (in edge_options) is" + " deprecated. Use {'dir':'back'} instead.") + del edge_options['backward'] + edge_options['dir'] = 'back' + dot_options = [] if edge_options['dot'] is not None: @@ -20835,9 +20917,14 @@ def graphviz_string(self, **options): dot_options.append('color = "%s"' % col) - if edge_options['backward']: - v,u = u,v - dot_options.append('dir=back') + if edge_options['dir'] == default_edge_dir: + pass + elif edge_options['dir'] in ['forward', 'back', 'both', 'none']: + dot_options.append('dir={}'.format(edge_options['dir'])) + else: + raise ValueError("dir(='{}') in edge_options dict for the" + " edge ({}, {}) should be 'forward', 'back', 'both'," + " or 'none'".format(edge_options['dir'], u, v)) s+= ' %s %s %s' % (key(u), edge_options['edge_string'], key(v)) if dot_options: diff --git a/src/sage/graphs/graph_decompositions/tdlib/sage_tdlib.cpp b/src/sage/graphs/graph_decompositions/sage_tdlib.cpp similarity index 100% rename from src/sage/graphs/graph_decompositions/tdlib/sage_tdlib.cpp rename to src/sage/graphs/graph_decompositions/sage_tdlib.cpp diff --git a/src/sage/graphs/graph_decompositions/tdlib.pyx b/src/sage/graphs/graph_decompositions/tdlib.pyx index 3c495c78176..0fcf682ffe2 100644 --- a/src/sage/graphs/graph_decompositions/tdlib.pyx +++ b/src/sage/graphs/graph_decompositions/tdlib.pyx @@ -67,7 +67,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.sets.set import Set from sage.graphs.graph import Graph -cdef extern from "tdlib/sage_tdlib.cpp": +cdef extern from "sage_tdlib.cpp": int sage_exact_decomposition(vector[unsigned int] &V_G, vector[unsigned int] &E_G, vector[vector[int]] &V_T, vector[unsigned int] &E_T, int lb) ############################################################## diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 998c03c3cef..b8f1d10e7c6 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -2075,6 +2075,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None DipoleGraph = staticmethod(families.DipoleGraph) distance_regular_graph = staticmethod(distance_regular.distance_regular_graph) DorogovtsevGoltsevMendesGraph = staticmethod(families.DorogovtsevGoltsevMendesGraph) + DoubleGeneralizedPetersenGraph = staticmethod(families.DoubleGeneralizedPetersenGraph) DoubleGrassmannGraph = staticmethod(distance_regular.DoubleGrassmannGraph) DoubleOddGraph = staticmethod(distance_regular.DoubleOddGraph) EgawaGraph = staticmethod(families.EgawaGraph) @@ -2095,6 +2096,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None HararyGraph = staticmethod(families.HararyGraph) HermitianFormsGraph = staticmethod(distance_regular.HermitianFormsGraph) HyperStarGraph = staticmethod(families.HyperStarGraph) + IGraph = staticmethod(families.IGraph) JohnsonGraph = staticmethod(families.JohnsonGraph) KneserGraph = staticmethod(families.KneserGraph) LCFGraph = staticmethod(families.LCFGraph) @@ -2112,10 +2114,12 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None PasechnikGraph = staticmethod(families.PasechnikGraph) petersen_family = staticmethod(families.petersen_family) RingedTree = staticmethod(families.RingedTree) + RoseWindowGraph = staticmethod(families.RoseWindowGraph) SierpinskiGasketGraph = staticmethod(families.SierpinskiGasketGraph) SquaredSkewHadamardMatrixGraph = staticmethod(families.SquaredSkewHadamardMatrixGraph) SwitchedSquaredSkewHadamardMatrixGraph = staticmethod(families.SwitchedSquaredSkewHadamardMatrixGraph) strongly_regular_graph = staticmethod(strongly_regular_db.strongly_regular_graph) + TabacjnGraph = staticmethod(families.TabacjnGraph) TadpoleGraph = staticmethod(families.TadpoleGraph) trees = staticmethod(families.trees) TuranGraph = staticmethod(families.TuranGraph) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 29fdf2995e9..b50dd0f8f5f 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -11,7 +11,7 @@ Using Andries Brouwer's `database of strongly regular graphs non-existence results. Note that some constructions are missing, and that some strongly regular graphs that exist in the database cannot be automatically built by Sage. Help us if you know any. -An outline of the implementation can be found in [CP16]_. +An outline of the implementation can be found in [CP2016]_. .. NOTE:: @@ -1743,7 +1743,7 @@ def eigenmatrix(int v, int k, int l, int mu): The most interesting feature of `vP^{-1}` is that it is the 1st eigenmatrix of the dual of `C[A]` if the dual is generated by the adjacency matrix of a - strongly regular graph. See [BH12]_ and [BI1984]_ for details. + strongly regular graph. See [BH2012]_ and [BI1984]_ for details. If the set of parameters is not feasible, or if they correspond to a conference graph, the function returns ``None``. Its output is stable, assuming @@ -1811,7 +1811,7 @@ cpdef latin_squares_graph_parameters(int v,int k, int l,int mu): Check whether (v,k,l,mu)-strongly regular graph has parameters of an `L_g(n)` s.r.g. Also known as pseudo-OA(n,g) case, i.e. s.r.g. with parameters of an OA(n,g)-graph. - Return g and n, if they exist. See Sect. 9.1 of [BH12]_ for details. + Return g and n, if they exist. See Sect. 9.1 of [BH2012]_ for details. INPUT: @@ -3036,7 +3036,7 @@ def _build_small_srg_database(): graph of the projective 2-intersection set associated with a 2-weight code `C`, and the usual theory of duality in association schemes to compute the parameters of the graph of words of `C`. Another relevant reference is - Sect.9.8.3 of [BH12]_. + Sect.9.8.3 of [BH2012]_. EXAMPLES:: diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 285b5378bb3..74bcc74de1e 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -326,6 +326,14 @@ def _element_constructor_(self, x, check=True): (2) sage: G(a) f2 + + TESTS: + + Document that :trac:`31428` is fixed:: + + sage: A = AbelianGroupGap([]) + sage: A([]) == A.one() + True """ if isinstance(x, AbelianGroupElement_gap): try: @@ -355,7 +363,7 @@ def _element_constructor_(self, x, check=True): orders = self.gens_orders() if len(exp) != len(gens_gap): raise ValueError("input does not match the number of generators") - x = gens_gap[0]**0 + x = self.one() for i in range(len(exp)): x *= gens_gap[i]**(exp[i] % orders[i]) x = x.gap() diff --git a/src/sage/groups/galois_group.py b/src/sage/groups/galois_group.py new file mode 100644 index 00000000000..d019082dc6d --- /dev/null +++ b/src/sage/groups/galois_group.py @@ -0,0 +1,304 @@ +r""" +Galois groups of field extensions + +AUTHORS: + +- David Roe (2019): initial version +""" + +from sage.groups.perm_gps.permgroup import PermutationGroup_generic +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.category_object import normalize_names + +def _alg_key(self, algorithm=None, recompute=False): + r""" + Return a key for use in cached_method calls. + + If recompute is false, will cache using ``None`` as the key, so no recomputation will be done. + + If recompute is true, will cache by algorithm, yielding a recomputation for each different algorithm. + + EXAMPLES:: + + sage: from sage.groups.galois_group import _alg_key + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: _alg_key(G, algorithm="pari", recompute=True) + 'pari' + """ + if recompute: + algorithm = self._get_algorithm(algorithm) + return algorithm + +class GaloisGroup(PermutationGroup_generic): + r""" + The group of automorphisms of a Galois closure of a given field. + + INPUT: + + - ``field`` -- a field, separable over its base + + - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field + + - ``gc_numbering`` -- boolean, whether to express permutations in terms of the + roots of the defining polynomial of the splitting field (versus the defining polynomial + of the original extension). The default value may vary based on the type of field. + """ + # Subclasses should implement the following methods and lazy attributes + + # methods (taking algorithm and recompute as arguments): + # * transitive_number + # * order + # * _element_constructor_ -- for creating elements + + # lazy_attributes + # * _gcdata -- a pair, the Galois closure and an embedding of the top field into it + # * _gens -- the list of generators of this group, as elements. This is not computed during __init__ for speed + # * _elts -- the list of all elements of this group. + + # * Element (for coercion) + + def __init__(self, field, algorithm=None, names=None, gc_numbering=False): + r""" + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: TestSuite(G).run() + """ + self._field = field + self._default_algorithm = algorithm + self._base = field.base_field() + self._gc_numbering = gc_numbering + if names is None: + # add a c for Galois closure + names = field.variable_name() + 'c' + self._gc_names = normalize_names(1, names) + # We do only the parts of the initialization of PermutationGroup_generic + # that don't depend on _gens + from sage.categories.permutation_groups import PermutationGroups + category = PermutationGroups().FinitelyGenerated().Finite() + # Note that we DON'T call the __init__ method for PermutationGroup_generic + # Instead, the relevant attributes are computed lazily + super(PermutationGroup_generic, self).__init__(category=category) + + def _repr_(self): + """ + String representation of this Galois group + + EXAMPLES:: + + sage: from sage.groups.galois_group import GaloisGroup + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: GaloisGroup._repr_(G) + 'Galois group of x^3 + 2*x + 2' + """ + f = self._field.defining_polynomial() + return "Galois group of %s" % f + + def _get_algorithm(self, algorithm): + r""" + Allows overriding the default algorithm specified at object creation. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: G._get_algorithm(None) + 'pari' + sage: G._get_algorithm('magma') + 'magma' + """ + return self._default_algorithm if algorithm is None else algorithm + + def top_field(self): + r""" + Return the larger of the two fields in the extension defining this Galois group. + + Note that this field may not be Galois. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: L = K.galois_closure('b') + sage: GK = K.galois_group() + sage: GK.top_field() is K + True + sage: GL = L.galois_group() + sage: GL.top_field() is L + True + """ + return self._field + + def transitive_label(self): + r""" + Return the transitive label for the action of this Galois group on the roots of + the defining polynomial of the field extension. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^8 - x^5 + x^4 - x^3 + 1) + sage: G = K.galois_group() + sage: G.transitive_label() + '8T44' + """ + try: + return "%sT%s" % (self._field.degree(), self.transitive_number()) + except NotImplementedError: # relative number fields don't support degree + return "%sT%s" % (self._field.relative_degree(), self.transitive_number()) + + def is_galois(self): + r""" + Return whether the top field is Galois over its base. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^8 - x^5 + x^4 - x^3 + 1) + sage: G = K.galois_group() + sage: from sage.groups.galois_group import GaloisGroup + sage: GaloisGroup.is_galois(G) + False + """ + return self.order() == self._field.degree() + + @lazy_attribute + def _galois_closure(self): + r""" + The Galois closure of the top field. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group(names='b') + sage: G._galois_closure + Number Field in b with defining polynomial x^6 + 12*x^4 + 36*x^2 + 140 + """ + return self._gcdata[0] + + def splitting_field(self): + r""" + The Galois closure of the top field. + + EXAMPLES:: + + sage: K = NumberField(x^3 - x + 1, 'a') + sage: K.galois_group(names='b').splitting_field() + Number Field in b with defining polynomial x^6 - 6*x^4 + 9*x^2 + 23 + sage: L = QuadraticField(-23, 'c'); L.galois_group().splitting_field() is L + True + """ + return self._galois_closure + + @lazy_attribute + def _gc_map(self): + r""" + The inclusion of the top field into the Galois closure. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group(names='b') + sage: G._gc_map + Ring morphism: + From: Number Field in a with defining polynomial x^3 + 2*x + 2 + To: Number Field in b with defining polynomial x^6 + 12*x^4 + 36*x^2 + 140 + Defn: a |--> 1/36*b^4 + 5/18*b^2 - 1/2*b + 4/9 + """ + return self._gcdata[1] + + @lazy_attribute + def _deg(self): + r""" + The number of moved points in the permutation representation. + + This will be the degree of the original number field if `_gc_numbering`` + is ``False``, or the degree of the Galois closure otherwise. + + EXAMPES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5-2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._deg + 5 + sage: G = K.galois_group(gc_numbering=True); G._deg + 20 + """ + if self._gc_numbering: + return self.order() + else: + try: + return self._field.degree() + except NotImplementedError: # relative number fields don't support degree + return self._field.relative_degree() + + @lazy_attribute + def _domain(self): + r""" + The integers labeling the roots on which this Galois group acts. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5-2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._domain + {1, 2, 3, 4, 5} + sage: G = K.galois_group(gc_numbering=True); G._domain + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + """ + return FiniteEnumeratedSet(range(1, self._deg+1)) + + @lazy_attribute + def _domain_to_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5-2) + sage: G = K.galois_group(gc_numbering=False) + sage: G._domain_to_gap[5] + 5 + """ + return dict((key, i+1) for i, key in enumerate(self._domain)) + + @lazy_attribute + def _domain_from_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5-2) + sage: G = K.galois_group(gc_numbering=True) + sage: G._domain_from_gap[20] + 20 + """ + return dict((i+1, key) for i, key in enumerate(self._domain)) + + def ngens(self): + r""" + Number of generators of this Galois group + + EXAMPLES:: + + sage: QuadraticField(-23, 'a').galois_group().ngens() + 1 + """ + return len(self._gens) diff --git a/src/sage/groups/pari_group.py b/src/sage/groups/pari_group.py index ea559599be8..128fc1b217d 100644 --- a/src/sage/groups/pari_group.py +++ b/src/sage/groups/pari_group.py @@ -25,6 +25,14 @@ def __init__(self, x, degree): self.__degree = Integer(degree) def __repr__(self): + """ + String representation of this group + + EXAMPLES:: + + sage: PariGroup([6, -1, 2, "S3"], 3) + PARI group [6, -1, 2, S3] of degree 3 + """ return "PARI group %s of degree %s" % (self.__x, self.__degree) def __eq__(self, other): @@ -76,7 +84,7 @@ def __pari__(self): def degree(self): """ - Return the degree of ``self``. + Return the degree of this group. EXAMPLES:: @@ -88,6 +96,48 @@ def degree(self): """ return self.__degree + def signature(self): + """ + Return 1 if contained in the alternating group, -1 otherwise. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^4 - 17*x^3 - 2*x + 1 + sage: G1 = f1.galois_group(pari_group=True) + sage: G1.signature() + -1 + """ + return Integer(self.__x[1]) + + def transitive_number(self): + """ + If the transitive label is nTk, return `k`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^4 - 17*x^3 - 2*x + 1 + sage: G1 = f1.galois_group(pari_group=True) + sage: G1.transitive_number() + 5 + """ + return Integer(self.__x[2]) + + def label(self): + """ + Return the human readable description for this group generated by Pari. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^4 - 17*x^3 - 2*x + 1 + sage: G1 = f1.galois_group(pari_group=True) + sage: G1.label() + 'S4' + """ + return str(self.__x[3]) + def order(self): """ Return the order of ``self``. @@ -105,6 +155,17 @@ def order(self): cardinality = order def permutation_group(self): + """ + Return the corresponding GAP transitive group + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = x^8 - x^5 + x^4 - x^3 + 1 + sage: G = f.galois_group(pari_group=True) + sage: G.permutation_group() + Transitive group number 44 of degree 8 + """ return TransitiveGroup(self.__degree, self.__x[2]) _permgroup_ = permutation_group diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index f78e5f26e66..e07c2eefec6 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -2113,7 +2113,7 @@ def order(self): subgroup_order = self._order() if subgroup_order is not None: - return subgroup_order + return subgroup_order return Integer(self.gap().Size()) diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index a9dec7342a8..0bc4baa4c36 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -1821,8 +1821,8 @@ def __init__(self, d, n): ... ValueError: Index n must be in {1,..,1} """ - d = Integer(d) - n = Integer(n) + self._d = d = Integer(d) + self._n = n = Integer(n) if d < 0: raise ValueError("Degree d must not be negative") max_n = TransitiveGroups(d).cardinality() @@ -1834,8 +1834,28 @@ def __init__(self, d, n): gap_group = libgap.TransitiveGroup(d, n) PermutationGroup_generic.__init__(self, gap_group=gap_group) - self._d = d - self._n = n + + def transitive_number(self): + """ + Return the index of this group in the GAP database, starting at 1 + + EXAMPLES:: + + sage: TransitiveGroup(8, 44).transitive_number() + 44 + """ + return self._n + + def degree(self): + """ + Return the degree of this permutation group + + EXAMPLES:: + + sage: TransitiveGroup(8, 44).degree() + 8 + """ + return self._d def _repr_(self): """ diff --git a/src/sage/interacts/debugger.py b/src/sage/interacts/debugger.py deleted file mode 100644 index 903c70dc917..00000000000 --- a/src/sage/interacts/debugger.py +++ /dev/null @@ -1,294 +0,0 @@ -""" -Interactive Debugger for the Sage Notebook - -Start the debugger in the Sage Notebook by running the ``debug()`` -function. You can run several debuggers at once. - -AUTHOR: - -- William Stein (2012) -""" - -from sage.misc.superseded import deprecation -deprecation(27531, "sage.interacts.debugger is deprecated because it is meant for the deprecated Sage Notebook") - -# Below all tests are done using sage0, which is a pexpect interface -# to Sage itself. This allows us to test exploring a stack traceback -# using the doctest framework. - -def test_function2(a, b): - """ - Used for doctesting the notebook debugger. - - EXAMPLES:: - - >>> import sage.interacts.debugger - doctest:...: DeprecationWarning: sage.interacts.debugger is deprecated because it is meant for the deprecated Sage Notebook - See https://trac.sagemath.org/27531 for details. - >>> sage.interacts.debugger.test_function2(2, 3) # using normal prompt would confuse tests below. - (5, 6, True, False) - """ - x = a + b - y = a * b - return x, y, xy # < to ensure HTML is properly escaped - -def test_function(n, m,level=10): - """ - Used for doctesting the notebook debugger. - - EXAMPLES:: - - >>> sage.interacts.debugger.test_function(2, 3) - (5, 6, True, False) - """ - # call another function so the stack is bigger - if level > 0: - return test_function(n,m,level=level-1) - else: - return test_function2(m, n) - - -class Debug(object): - """ - Create a debugger for the most recent stack trace. - - .. NOTE:: - - - Input is not preparsed. - - You can define and work with many debug interacts at the same time. - - TESTS: - - The current position in the stack frame is self._curframe_index:: - - sage: a = sage0.eval("import sage.interacts.debugger") - sage: a = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: d = sage0('sage.interacts.debugger.Debug()') - sage: d._curframe_index - 8 - """ - def __init__(self): - """ - Create the debugger object from the most recent traceback. - - TESTS:: - - sage: a = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: sage0('sage.interacts.debugger.Debug()') - - """ - import inspect - import sys - import traceback - try: - tb = sys.last_traceback - #we strip off the 5 outermost frames, since those relate only to - #the notebook, not user code - for i in range(5): - tb = tb.tb_next - self._stack = inspect.getinnerframes(tb) - except AttributeError: - raise RuntimeError("no traceback has been produced; nothing to debug") - self._curframe_index = len(self._stack) - 1 - - def curframe(self): - """ - Return the current frame object. This defines the local and - global variables at a point in the stack trace, and the code. - - OUTPUT: - - - frame object - - TESTS:: - - sage: a = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: d = sage0('sage.interacts.debugger.Debug()') - sage: d.curframe() # py2 - - """ - return self._stack[self._curframe_index][0] - - def evaluate(self, line): - """ - Evaluate the input string ``line`` in the scope of the current - position in the stack. - - INPUT: - - - ``line`` -- string; the code to exec - - OUTPUT: - - - string (the output) - - TESTS:: - - sage: _ = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: _ = sage0.eval('d = sage.interacts.debugger.Debug()') - sage: sage0.eval("d.evaluate('print(a);print(b)')") - 'm\nn' - """ - locals = self.curframe().f_locals - globals = self.curframe().f_globals - try: - code = compile(line + '\n', '', 'single') - exec(code, globals, locals) - except Exception: - import sys - t, v = sys.exc_info()[:2] - if isinstance(t, type('')): - exc_type_name = t - else: - exc_type_name = t.__name__ - print('*** {}: {}'.format(exc_type_name, v)) - - def listing(self, n=5): - """ - Return HTML display of the lines (with numbers and a pointer - at the current line) in the code listing for the current - frame, with `n` lines before and after of context. - - INPUT: - - - `n` -- integer (default: 5) - - OUTPUT: - - - list of strings - - TESTS:: - - sage: _ = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: _ = sage0.eval('d = sage.interacts.debugger.Debug()') - sage: print(sage0("d.listing(1)")) - ... x = a + b - --> ... y = a * b - ....: return x, y, x<y, x>y # < to ensure HTML is properly escaped -
>
src/sage/interacts/debugger.py - sage: print(sage0("d.listing()")) - ... - ... - ....: x = a + b - --> ... y = a * b - ....: return x, y, x<y, x>y # < to ensure HTML is properly escaped - ... - sage: _ = sage0.eval('d._curframe_index -= 1') - sage: print(sage0("d.listing(1)")) - 4... else: - --> ... test_function2(m, n) - ... -
> src/sage/interacts/debugger.py - """ - # TODO: Currently, just as with ipdb on the command line, - # there is no support for displaying code in Cython files. - # This shouldn't be too hard to add. - curframe = self.curframe() - filename = curframe.f_code.co_filename - lineno = curframe.f_lineno - import linecache - w = [] - for i in range(lineno-n, lineno+n+1): - z = linecache.getline(filename, i, curframe.f_globals) - if z: w.append(('--> ' if i == lineno else ' ') + '%-5s'%i + z) - code = ''.join(w) - if not code.strip(): - code = '(code not available)' - - # This is a hideous hack to get around how the notebook "works". - # If the output of anything contains the string TRACEBACK then - # it will get mangled. So we replace TRACEBACK in our code block - # by the harmless version with the colon missing. This sucks. - TRACEBACK = 'Traceback (most recent call last):' - code = code.replace(TRACEBACK, TRACEBACK[:-1]) - - # Create a hyperlink to the file, if possible. - i = filename.rfind('site-packages/sage') - if i != -1: - fname = filename[i+len('site-packages/sage')+1:].rstrip('/') - file = 'src/sage/%s'%(fname,fname) - else: - file = filename - - import html - t = """%s
> %s"""%(html.escape(code), file) - return t - - def interact(self): - """ - Start the interact debugger. - - TESTS:: - - sage: _ = sage0.eval("sage.interacts.debugger.test_function('n', 'm')") - sage: _ = sage0.eval('d = sage.interacts.debugger.Debug()') - sage: _ = sage0.eval('d.interact()') # only works in the notebook - """ - # We use a library_interact instead of a normal interact here, - # since this is an interact in the library, and a normal - # "@interact" is all mangled. - - from sage.interacts.library import library_interact - from sagenb.notebook.interact import slider, input_box, selector - - # self._last holds the last state of all controls. This allows - # us to deduce which control changed to cause the update, or that - # nothing changed, in which case we assume the user requested to - # re-evaluate the input box (for some reason -- currently there is - # no point in doing so). It is a shortcoming of @interact that - # we have to do this. - self._last = None - - # two sliders and a box to put in commands with an evaluate button. - @library_interact - def dbg(frame = slider(vmin=0, vmax=len(self._stack)-1, step_size=1, default=len(self._stack)-1, label='stack frame'), - lines = slider(vmin=3, vmax=99, step_size=2, default=11, label='lines of context'), - command = input_box("", label="", type=str), - button = selector(['Evaluate'], label='', buttons=True) - ): - - if self._last is None: - self._last = {'command':command, 'button':button, 'lines':lines, 'frame':frame} - - if self._last['lines'] != lines: - # they dragged the number-of-lines slider, so done - pass - elif self._last['command'] != command and command.strip(): - # they changed the command, so evaluate that - self.evaluate(command) - elif self._last['frame'] != frame: - # they dragged the frame slider. - self._curframe_index = frame - elif command: - # must have hit the evaluate button - self.evaluate(command) - - print('
{}'.format(self.listing(lines//2))) - # save control state for next time around - self._last = {'command':command, 'button':button, 'lines':lines, 'frame':frame} - - dbg() - -def debug(): - """ - If you get a traceback in the Sage notebook, use the ``debug()`` - command to start an interactive debugger. Using %debug on the - command line. - - Using the debugger you can move up and down the stack frame and - evaluate arbitrary code anywhere in the call stack. - - .. warning:: - - If you type in a command, execute it, switch to a different - frame and press enter again, nothing happens: it doesn't - execute the command in the new frame. You can get the command - to execute by hitting the Evaluate button though. This is a - fundamental limitation of the Sage notebook in Sage-5.0. - - EXAMPLES:: - - sage: debug() # only works in the notebook - You should use %debug on the command line. - """ - print("You should use %debug on the command line.") diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 5df87e96206..9c5f201a073 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -283,7 +283,7 @@ def __init__(self, name='fricas', command='fricas -nosman', sage: fricas(I*x).sage() # optional - fricas I*x """ - eval_using_file_cutoff = 4096-5 # magic number from Expect._eval_line (there might be a bug) + eval_using_file_cutoff = 4096 - 5 # magic number from Expect._eval_line (there might be a bug) assert max(len(c) for c in FRICAS_INIT_CODE) < eval_using_file_cutoff self.__eval_using_file_cutoff = eval_using_file_cutoff self._COMMANDS_CACHE = '%s/%s_commandlist_cache.sobj' % (DOT_SAGE, name) @@ -434,8 +434,8 @@ def _tab_completion(self, verbose=True, use_disk_cache=True): names = [x for x in v if valid.search(x) is None] # replace trailing ? with _q and trailing ! with _e - names += [x[:-1]+"_q" for x in v if x.endswith("?")] - names += [x[:-1]+"_e" for x in v if x.endswith("!")] + names += [x[:-1] + "_q" for x in v if x.endswith("?")] + names += [x[:-1] + "_e" for x in v if x.endswith("!")] self.__tab_completion = names if len(v) > 200: @@ -1335,7 +1335,7 @@ def _parse_other(s, start=0, make_fun=False): e = symbol_table["fricas"][e] except KeyError: e = var(e.replace("%", "_")) - return e, a-1 + return e, a - 1 @staticmethod def _parse_string(s, start=0): @@ -1560,6 +1560,15 @@ def _sage_expression(fricas_InputForm): sage: fricas.set("F", "operator 'f") # optional - fricas sage: fricas("eval(D(F(x,y), [x, y], [2, 1]), x=x+y)").sage() # optional - fricas D[0, 0, 1](f)(x + y, y) + + Conversion of hypergeometric functions (:trac:`31298`):: + + sage: a,b,c = var("a b c") + sage: A = hypergeometric([a, b], [c], x) + sage: fricas(A).sage() - A # optional - fricas + 0 + sage: fricas(A).D(x).sage() - diff(A, x) # optional - fricas + 0 """ from sage.libs.pynac.pynac import register_symbol from sage.symbolic.constants import e, pi, I @@ -1594,10 +1603,12 @@ def _sage_expression(fricas_InputForm): register_symbol(lambda x, y: x / y, {'fricas': '/'}) register_symbol(lambda x, y: x ** y, {'fricas': '^'}) register_symbol(lambda f, x: diff(f, x), {'fricas': 'D'}) - register_symbol(lambda x, y: x + y*I, {'fricas': 'complex'}) - register_symbol(lambda x: dilog(1-x), {'fricas': 'dilog'}) + register_symbol(lambda x, y: x + y * I, {'fricas': 'complex'}) + register_symbol(lambda x: dilog(1 - x), {'fricas': 'dilog'}) register_symbol(lambda z: lambert_w(z), {'fricas': 'lambertW'}) register_symbol(abs, {'fricas': 'abs'}) + # construct occurs in the InputForm of hypergeometricF + register_symbol(lambda *x: x, {'fricas': 'construct'}) # the following is a hack to deal with # integrate(sin((x^2+1)/x),x)::INFORM giving # (integral (sin (/ (+ (^ x 2) 1) x)) (:: x Symbol)) @@ -1882,7 +1893,7 @@ def _sage_(self): return self.numer().sage() / self.denom().sage() if head == "Complex": - return self.real().sage() + self.imag().sage()*I + return self.real().sage() + self.imag().sage() * I if head == "Factored": l = P.new('[[f.factor, f.exponent] for f in factors(%s)]' % self._name).sage() @@ -1916,7 +1927,7 @@ def _sage_(self): prec = max(P.new("length mantissa(%s)" % self._name).sage(), 53) R = RealField(prec) x, e, b = unparsed_InputForm.lstrip('float(').rstrip(')').split(',') - return R(ZZ(x)*ZZ(b)**ZZ(e)) + return R(ZZ(x) * ZZ(b)**ZZ(e)) if head == "DoubleFloat": return RDF(unparsed_InputForm) diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 582391d3a60..ff19b484748 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -38,8 +38,8 @@ 401 sage: giac.fsolve('x^2=cos(x)+4', 'x','0..5') [1.9140206190... - sage: giac.factor('x^5 - y^5') - (x-y)*(x^4+x^3*y+x^2*y^2+x*y^3+y^4) + sage: giac.factor('x^4 - y^4') + (x-y)*(x+y)*(x^2+y^2) sage: R.=QQ[];f=(x+y)^5;f2=giac(f);(f-f2).normal() 0 sage: x,y=giac('x,y'); giac.int(y/(cos(2*x)+cos(x)),x) # random @@ -74,14 +74,14 @@ :: - factor( (x^5-1)); + factor( (x^4-1)); We can write that in sage as :: - sage: giac('factor(x^5-1)') - (x-1)*(x^4+x^3+x^2+x+1) + sage: giac('factor(x^4-1)') + (x-1)*(x+1)*(x^2+1) Notice, there is no need to use a semicolon. @@ -92,8 +92,8 @@ :: - sage: giac('(x^5-1)').factor() - (x-1)*(x^4+x^3+x^2+x+1) + sage: giac('(x^4-1)').factor() + (x-1)*(x+1)*(x^2+1) where ``expression.command()`` means the same thing as ``command(expression)`` in Giac. We will use this @@ -623,7 +623,7 @@ def eval(self, code, strip=True, **kwds): '4\n3' sage: s='g(x):={\nx+1;\nx+2;\n}' sage: giac(s) - (x)->[x+1,x+2] + ...x+1...x+2... sage: giac.g(5) 7 """ diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 3eaf12e6ad1..6b0af7ee6ca 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -306,6 +306,17 @@ sage: singular.eval("ring testgf9 = (9,x),(a,b,c,d,e,f),(M((1,2,3,0)),wp(2,3),lp);") '' + +Verify that :trac:`17720` is fixed:: + + sage: R.

= QQ[] + sage: K.

= QQ.extension(p^2 - p - 1) + sage: r. = K[] + sage: I = r.ideal(z) + sage: I.primary_decomposition() + [Ideal (z) of Multivariate Polynomial Ring in x, z over Number Field in p with defining polynomial p^2 - p - 1] + sage: [ J.gens() for J in I.primary_decomposition("gtz")] + [[z]] """ # **************************************************************************** @@ -1955,18 +1966,16 @@ def _sage_(self, R=None): sage: A.sage(ZZ) # indirect doctest [0 0] [0 0] - sage: A = random_matrix(ZZ,3,3); A + sage: A = random_matrix(ZZ,3,3); A # random [ -8 2 0] [ 0 1 -1] [ 2 1 -95] - sage: As = singular(A); As + sage: As = singular(A); As # random -8 2 0 0 1 -1 2 1 -95 - sage: As.sage() - [ -8 2 0] - [ 0 1 -1] - [ 2 1 -95] + sage: As.sage() == A + True :: diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index f92c561d5c9..43a41c0c97b 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -1,4 +1,9 @@ -# distutils: libraries = ecl gmp +# distutils: extra_compile_args = ECL_CFLAGS +# distutils: include_dirs = ECL_INCDIR +# distutils: libraries = ECL_LIBRARIES +# distutils: library_dirs = ECL_LIBDIR +# distutils: extra_link_args = ECL_LIBEXTRA + ############################################################################### # Sage: Open Source Mathematical Software # Copyright (C) 2009 Nils Bruin @@ -153,4 +158,4 @@ cdef extern from "ecl/ecl.h": # symbols - cl_object ecl_make_symbol(const char *name, const char *package) \ No newline at end of file + cl_object ecl_make_symbol(const char *name, const char *package) diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index 7ff4ac13fa9..3f99f998a50 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -1,5 +1,9 @@ # distutils: language = c++ -# distutils: libraries = ec ntl pari gmp m +# distutils: libraries = ec NTL_LIBRARIES pari gmp m +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA from libcpp.map cimport map @@ -141,7 +145,7 @@ cdef extern from "eclib/newforms.h": newforms(long n, int disp) - void createfromcurve(int sign, CurveRed CR) + void createfromcurve(int sign, CurveRed CR, int nap) void display() # Here i is the index of the relevant newform in the space, # which for us will always be 0: diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index 8e655f692f0..b50b6061aa2 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -126,16 +126,20 @@ cdef class ECModularSymbol: sage: ECModularSymbol.__new__(ECModularSymbol) Modular symbol with sign 0 over Rational Field attached to None """ - def __init__(self, E, sign=1): + def __init__(self, E, sign=1, nap=1000): """ Construct the modular symbol object from an elliptic curve. INPUT: - - ``E``- an elliptic curve defined over Q + - ``E``- an elliptic curve defined over Q. + - ``sign`` (int) -- 0 or +1. If +1, only plus modular symbols - of this sign are available. If 0, modular symbols of both - signs are available but the construction is more expensive. + of this sign are available. If 0, modular symbols of both + signs are available but the construction is more expensive. + + - ``nap`` - (int, default 1000): the number of ap of E to use + in determining the normalisation of the modular symbols. EXAMPLES:: @@ -211,7 +215,7 @@ cdef class ECModularSymbol: self.sign = sign self.nfs = new newforms(n, 0) - self.nfs.createfromcurve(sign,CR) + self.nfs.createfromcurve(sign, CR, nap) sig_off() def __dealloc__(self): @@ -245,9 +249,7 @@ cdef class ECModularSymbol: - ``sign`` (int) - either +1, -1 or 0. If the sign of the space is +1, only sign +1 is allowed. Default: self.sign, or +1 when self.sign=0. - - - - ``base_at_infinity`` (bool) - if True, evaluates + - ``base_at_infinity`` (bool) - if True, evaluates {oo,r}. otherwise (default) evaluates {0,r}. OUTPUT: diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi index 4479865d8ad..51c5fa196cb 100644 --- a/src/sage/libs/flint/nmod_poly_linkage.pxi +++ b/src/sage/libs/flint/nmod_poly_linkage.pxi @@ -318,10 +318,11 @@ cdef inline int celement_mul_scalar(nmod_poly_t res, nmod_poly_t p, sage: P. = GF(32003)[] sage: p = P.random_element(degree=2) - sage: 389*p - 10573*x^2 + 12219*x + 2340 - sage: p*983 - 26142*x^2 + 29561*x + 18665 + sage: (389*p).coefficients() == [389*x for x in p.coefficients()] + True + sage: p = P.random_element(degree=8) + sage: (p*9836).coefficients() == [x*9836 for x in p.coefficients()] + True """ nmod_poly_scalar_mul_nmod(res, p, (c)%n) @@ -552,36 +553,24 @@ cdef inline int celement_gcd(nmod_poly_t res, nmod_poly_t a, nmod_poly_t b, unsi EXAMPLES:: sage: P. = GF(32003)[] - sage: f = P.random_element(degree=4); f - 5847*x^4 + 16660*x^3 + 10640*x^2 + 1430*x + 16460 - sage: g = P.random_element(degree=3); g - 28238*x^3 + 18622*x^2 + 28452*x + 2561 - sage: h = P.random_element(degree=2); h - 27698*x^2 + 27711*x + 4790 - sage: F = f*g; F - 4109*x^7 + 9608*x^6 + 20844*x^5 + 10711*x^4 + 8036*x^3 + 18420*x^2 + 1906*x + 6109 - sage: G = f*h; G - 15026*x^6 + 24454*x^5 + 17583*x^4 + 7748*x^3 + 18182*x^2 + 17362*x + 20011 - sage: d = (F).gcd(G); d - x^4 + 10468*x^3 + 28469*x^2 + 668*x + 24250 + sage: f = P.random_element(degree=4) + sage: g = P.random_element(degree=3) + sage: h = P.random_element(degree=2) + sage: F = f*g + sage: G = f*h + sage: d = (F).gcd(G) sage: (F//d)*d == F True sage: (G//d)*d == G True sage: Q. = GF(7)[] - sage: f = Q.random_element(degree=4); f - 2*x^4 + 5*x^3 + 2*x^2 + 3*x + 5 - sage: g = Q.random_element(degree=3); g - 2*x^3 + 4*x^2 + 4*x + 4 - sage: h = Q.random_element(degree=2); h - 6*x^2 + 5*x + 6 - sage: F = f*g; F - 4*x^7 + 4*x^6 + 4*x^5 + x^3 + 5*x^2 + 4*x + 6 - sage: G = f*h; G - 5*x^6 + 5*x^5 + 2*x^3 + x^2 + x + 2 - sage: d = (F).gcd(G); d - x^4 + 6*x^3 + x^2 + 5*x + 6 + sage: f = Q.random_element(degree=4) + sage: g = Q.random_element(degree=3) + sage: h = Q.random_element(degree=2) + sage: F = f*g + sage: G = f*h + sage: d = (F).gcd(G) sage: (F//d)*d == F True sage: (G//d)*d == G @@ -602,36 +591,24 @@ cdef inline int celement_xgcd(nmod_poly_t res, nmod_poly_t s, nmod_poly_t t, nmo EXAMPLES:: sage: P. = GF(32003)[] - sage: f = P.random_element(degree=4); f - 5847*x^4 + 16660*x^3 + 10640*x^2 + 1430*x + 16460 - sage: g = P.random_element(degree=3); g - 28238*x^3 + 18622*x^2 + 28452*x + 2561 - sage: h = P.random_element(degree=2); h - 27698*x^2 + 27711*x + 4790 - sage: F = f*g; F - 4109*x^7 + 9608*x^6 + 20844*x^5 + 10711*x^4 + 8036*x^3 + 18420*x^2 + 1906*x + 6109 - sage: G = f*h; G - 15026*x^6 + 24454*x^5 + 17583*x^4 + 7748*x^3 + 18182*x^2 + 17362*x + 20011 - sage: d,s,t = (F).xgcd(G); d - x^4 + 10468*x^3 + 28469*x^2 + 668*x + 24250 + sage: f = P.random_element(degree=4) + sage: g = P.random_element(degree=3) + sage: h = P.random_element(degree=2) + sage: F = f*g + sage: G = f*h + sage: d,s,t = (F).xgcd(G) sage: (F//d)*d == F True sage: (G//d)*d == G True sage: Q. = GF(7)[] - sage: f = Q.random_element(degree=4); f - 2*x^4 + 5*x^3 + 2*x^2 + 3*x + 5 - sage: g = Q.random_element(degree=3); g - 2*x^3 + 4*x^2 + 4*x + 4 - sage: h = Q.random_element(degree=2); h - 6*x^2 + 5*x + 6 - sage: F = f*g; F - 4*x^7 + 4*x^6 + 4*x^5 + x^3 + 5*x^2 + 4*x + 6 - sage: G = f*h; G - 5*x^6 + 5*x^5 + 2*x^3 + x^2 + x + 2 - sage: d,s,t = (F).xgcd(G); d - x^4 + 6*x^3 + x^2 + 5*x + 6 + sage: f = Q.random_element(degree=4) + sage: g = Q.random_element(degree=3) + sage: h = Q.random_element(degree=2) + sage: F = f*g + sage: G = f*h + sage: d,s,t = (F).xgcd(G) sage: (F//d)*d == F True sage: (G//d)*d == G @@ -645,10 +622,24 @@ cdef factor_helper(Polynomial_zmod_flint poly, bint squarefree=False): EXAMPLES:: sage: P. = GF(1009)[] - sage: (prod(P.random_element(degree=2) for i in range(5))).factor() - (224) * (x + 141) * (x + 326) * (x + 654) * (x + 801) * (x^2 + 40*x + 888) * (x^2 + 639*x + 134) * (x^2 + 723*x + 116) - sage: (prod(P.random_element()^i for i in range(5))).squarefree_decomposition() - (435) * (x + 595) * (x^2 + 375*x + 415)^3 * (x^2 + 617*x + 569)^4 + sage: factors = (prod(P.random_element(degree=2) for i in range(5))).factor() + sage: all(factor.is_irreducible() for factor, mult in factors) + True + + sage: def nonzero_random(P): + ....: r = P.random_element() + ....: while r == 0: + ....: r = P.random_element() + ....: return r + + sage: p = (prod(nonzero_random(P)^i for i in range(5))) + sage: decomp = p.squarefree_decomposition() + sage: any(factor.is_square() for factor, mult in decomp) + False + sage: start = 0 + sage: for factor, mult in decomp: + ....: assert mult > start + ....: start = mult """ cdef nmod_poly_factor_t factors_c nmod_poly_factor_init(factors_c) diff --git a/src/sage/libs/giac/__init__.py b/src/sage/libs/giac/__init__.py index ffcd5cbc243..7346cc5ff63 100644 --- a/src/sage/libs/giac/__init__.py +++ b/src/sage/libs/giac/__init__.py @@ -198,8 +198,8 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, sage: P = PolynomialRing(QQ,5, 'x') sage: I = ideal([P.random_element(3,7) for j in range(5)]) sage: B1 = gb_giac(I.gens(),1e-16) # long time (1s) - ...Running a probabilistic check for the reconstructed Groebner basis. - If successfull, error probability is less than 1e-16 ... + ... + If successful..., error probability is less than 1e-16 ... sage: sage.structure.proof.all.polynomial(True) sage: B2 = gb_giac(I.gens()) # long time (4s) @@ -214,7 +214,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, sage: P = PolynomialRing(QQ, 8, 'x') sage: I = sage.rings.ideal.Cyclic(P) sage: time B = gb_giac(I.gens(),1e-6,threads=2) # doctest: +SKIP - Running a probabilistic check for the reconstructed Groebner basis... + ... Time: CPU 168.98 s, Wall: 94.13 s You can get detailled information by setting ``prot=True`` @@ -237,7 +237,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, checking pairs for i=73, j= Number of critical pairs to check 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++... - Successfull check of 373 critical pairs + Successful... check of 373 critical pairs 12380865 end final check Polynomial Sequence with 74 Polynomials in 8 Variables @@ -354,6 +354,6 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, gb_giac = F.gbasis(list(var_names), giac_order) else: - gb_giac = F.eliminate(list(elim_variables)) + gb_giac = F.eliminate(list(elim_variables), 'gbasis') return PolynomialSequence(gb_giac, P, immutable=True) diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pyx b/src/sage/libs/lcalc/lcalc_Lfunction.pyx index e726b653a25..16cc67cf5d6 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pyx +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pyx @@ -1,5 +1,8 @@ -# distutils: libraries = m ntl Lfunction -# distutils: extra_compile_args = -O3 -ffast-math +# distutils: libraries = m NTL_LIBRARIES Lfunction +# distutils: extra_compile_args = NTL_CFLAGS -O3 -ffast-math +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ r""" Rubinstein's lcalc library diff --git a/src/sage/libs/m4ri.pxd b/src/sage/libs/m4ri.pxd index a1fe59481d8..cae979eb618 100644 --- a/src/sage/libs/m4ri.pxd +++ b/src/sage/libs/m4ri.pxd @@ -1,4 +1,5 @@ -# distutils: extra_compile_args = -std=c99 +# distutils: extra_compile_args = -std=c++11 +# distutils: language = c++ cdef extern from "m4ri/m4ri.h": ctypedef int rci_t diff --git a/src/sage/libs/ntl/convert.pyx b/src/sage/libs/ntl/convert.pyx index 19380ded8ea..d06270d5077 100644 --- a/src/sage/libs/ntl/convert.pyx +++ b/src/sage/libs/ntl/convert.pyx @@ -1,5 +1,9 @@ # distutils: depends = NTL/ZZ.h -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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++ """ diff --git a/src/sage/libs/ntl/error.pyx b/src/sage/libs/ntl/error.pyx index 99d6ca92a0c..ac6cef82910 100644 --- a/src/sage/libs/ntl/error.pyx +++ b/src/sage/libs/ntl/error.pyx @@ -1,4 +1,9 @@ # distutils: libraries = ntl gmp +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: libraries = NTL_LIBRARIES +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ """ diff --git a/src/sage/libs/ntl/ntl_GF2.pyx b/src/sage/libs/ntl/ntl_GF2.pyx index 1a03ed5ebd7..2a03e7723b4 100644 --- a/src/sage/libs/ntl/ntl_GF2.pyx +++ b/src/sage/libs/ntl/ntl_GF2.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index 99cff223907..748449f9c25 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** @@ -55,8 +59,8 @@ def ntl_GF2E_random(ntl_GF2EContext_class ctx): EXAMPLES:: sage: ctx = ntl.GF2EContext([1,1,0,1,1,0,0,0,1]) - sage: ntl.GF2E_random(ctx) - [1 1 0 0 1 0 1 1] + sage: ntl.GF2E_random(ctx).modulus_context() is ctx + True """ current_randstate().set_seed_ntl(False) diff --git a/src/sage/libs/ntl/ntl_GF2EContext.pyx b/src/sage/libs/ntl/ntl_GF2EContext.pyx index 65c26f5627c..d31f8fc6e10 100644 --- a/src/sage/libs/ntl/ntl_GF2EContext.pyx +++ b/src/sage/libs/ntl/ntl_GF2EContext.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_GF2EX.pyx b/src/sage/libs/ntl/ntl_GF2EX.pyx index 4a3ac4a3c0e..be37ec0313a 100644 --- a/src/sage/libs/ntl/ntl_GF2EX.pyx +++ b/src/sage/libs/ntl/ntl_GF2EX.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index b80766b4ec1..63c301e2767 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ # **************************************************************************** diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 8a21990db1c..16d04663efa 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** @@ -433,6 +437,7 @@ def ntl_setSeed(x=None): This is automatically seeded from the main Sage random number seed:: + sage: set_random_seed(0) sage: ntl.ZZ_random(1000) 979 @@ -458,7 +463,7 @@ ntl_setSeed() def randomBnd(q): r""" - Return a random number in the range [0,n). + Return a random number in the range `[0, n)`. According to the NTL documentation, these numbers are "cryptographically strong"; of course, that depends in part on @@ -466,8 +471,12 @@ def randomBnd(q): EXAMPLES:: - sage: [ntl.ZZ_random(99999) for i in range(5)] - [30675, 84282, 80559, 6939, 44798] + sage: n = 99999 + sage: l = [ntl.ZZ_random(n) for i in range(5)] + sage: all(type(m) is sage.libs.ntl.ntl_ZZ.ntl_ZZ for m in l) + True + sage: all(0 <= m < n for m in l) + True AUTHOR: @@ -489,12 +498,16 @@ def randomBnd(q): def randomBits(long n): r""" - Return a pseudo-random number between 0 and `2^n-1`. + Return a pseudo-random number in the range `[0, 2^n)`. EXAMPLES:: - sage: [ntl.ZZ_random_bits(20) for i in range(3)] - [948179, 477498, 1020180] + sage: l = [ntl.ZZ_random_bits(20) for i in range(3)] + sage: all(0 <= m < 2^20 for m in l) + True + sage: l = [ntl.ZZ_random_bits(3) for i in range(10)] + sage: all(0 <= m < 8 for m in l) + True AUTHOR: -- Didier Deshommes diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 4d602df9032..3aea4aca9fc 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pyx b/src/sage/libs/ntl/ntl_ZZ_p.pyx index 7444c7c4cff..4fab1d1a7cb 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_p.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** @@ -47,8 +51,11 @@ def ntl_ZZ_p_random_element(v): EXAMPLES:: - sage: sage.libs.ntl.ntl_ZZ_p.ntl_ZZ_p_random_element(17) - 9 + sage: a = sage.libs.ntl.ntl_ZZ_p.ntl_ZZ_p_random_element(17) + sage: type(a) + + sage: a.modulus() + 17 """ current_randstate().set_seed_ntl(False) diff --git a/src/sage/libs/ntl/ntl_ZZ_pContext.pyx b/src/sage/libs/ntl/ntl_ZZ_pContext.pyx index 8c351111cd8..0c8995608fd 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pContext.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pContext.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_ZZ_pE.pyx b/src/sage/libs/ntl/ntl_ZZ_pE.pyx index ad236ebe733..f46b28f95b5 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pE.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pE.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx index 50bf8cf6cbb..69dcbff7469 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index f51d4f606b0..983bbbe826a 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 8821df416ef..d7fb124c62a 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ # **************************************************************************** diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index aac832eae98..2152f299967 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ diff --git a/src/sage/libs/ntl/ntl_lzz_pContext.pyx b/src/sage/libs/ntl/ntl_lzz_pContext.pyx index 30667a452d2..9f69dce88b0 100644 --- a/src/sage/libs/ntl/ntl_lzz_pContext.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pContext.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index c08ad28491a..70d8f103fe8 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index 111010f8d14..6967d469cef 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ @@ -64,25 +68,22 @@ cdef class ntl_mat_GF2(object): [0 0 0 0] ] - sage: A = random_matrix(GF(2),4,4); A + sage: A = random_matrix(GF(2),4,4); A # random [0 1 0 1] [0 1 1 1] [0 0 0 1] [0 1 1 0] - sage: B = ntl.mat_GF2(A); B + sage: B = ntl.mat_GF2(A); B # random [[0 1 0 1] [0 1 1 1] [0 0 0 1] [0 1 1 0] ] - sage: B = ntl.mat_GF2(4, 4, A.list()); B - [[0 1 0 1] - [0 1 1 1] - [0 0 0 1] - [0 1 1 0] - ] + sage: B = ntl.mat_GF2(4, 4, A.list()) + sage: B == A + True """ cdef Py_ssize_t _nrows, _ncols cdef Py_ssize_t i, j @@ -144,12 +145,9 @@ cdef class ntl_mat_GF2(object): EXAMPLES:: sage: A = random_matrix(GF(2),4,4) - sage: B = ntl.mat_GF2(A); B # indirect doctest - [[0 1 0 1] - [0 1 1 1] - [0 0 0 1] - [0 1 1 0] - ] + sage: B = ntl.mat_GF2(A) + sage: B.__repr__()[1:-2] == A.__repr__() + True """ return ccrepr(self.x) @@ -159,18 +157,9 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2),4,4) sage: B = random_matrix(GF(2),4,4) - sage: ntl.mat_GF2(A)*ntl.mat_GF2(B) - [[0 0 1 0] - [1 1 0 1] - [0 0 0 1] - [1 1 0 0] - ] - - sage: A*B - [0 0 1 0] - [1 1 0 1] - [0 0 0 1] - [1 1 0 0] + sage: c = ntl.mat_GF2(A)*ntl.mat_GF2(B) + sage: c._sage_() == A*B + True """ cdef ntl_mat_GF2 r = self._new() if not isinstance(other, ntl_mat_GF2): @@ -186,18 +175,9 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2),4,4) sage: B = random_matrix(GF(2),4,4) - sage: ntl.mat_GF2(A) - ntl.mat_GF2(B) - [[0 1 0 0] - [0 1 0 0] - [1 1 1 0] - [0 1 1 1] - ] - - sage: A - B - [0 1 0 0] - [0 1 0 0] - [1 1 1 0] - [0 1 1 1] + sage: c = ntl.mat_GF2(A) - ntl.mat_GF2(B) + sage: c._sage_() == A - B + True """ cdef ntl_mat_GF2 r = self._new() if not isinstance(other, ntl_mat_GF2): @@ -213,19 +193,9 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2),4,4) sage: B = random_matrix(GF(2),4,4) - sage: ntl.mat_GF2(A) + ntl.mat_GF2(B) - [[0 1 0 0] - [0 1 0 0] - [1 1 1 0] - [0 1 1 1] - ] - - sage: A + B - [0 1 0 0] - [0 1 0 0] - [1 1 1 0] - [0 1 1 1] - + sage: c = ntl.mat_GF2(A) + ntl.mat_GF2(B) + sage: c._sage_() == A + B + True """ cdef ntl_mat_GF2 r = self._new() if not isinstance(other, ntl_mat_GF2): @@ -240,18 +210,8 @@ cdef class ntl_mat_GF2(object): EXAMPLES:: sage: A = random_matrix(GF(2),4,4) - sage: -ntl.mat_GF2(A) - [[0 1 0 1] - [0 1 1 1] - [0 0 0 1] - [0 1 1 0] - ] - - sage: -A - [0 1 0 1] - [0 1 1 1] - [0 0 0 1] - [0 1 1 0] + sage: (-ntl.mat_GF2(A))._sage_() == -A + True """ cdef ntl_mat_GF2 r = self._new() sig_on() @@ -264,31 +224,15 @@ cdef class ntl_mat_GF2(object): EXAMPLES:: sage: A = random_matrix(GF(2),4,4) - sage: ntl.mat_GF2(A)^0 - [[1 0 0 0] - [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - ] - - sage: A^0 - [1 0 0 0] - [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - - sage: ntl.mat_GF2(A)^3 - [[0 1 1 0] - [0 0 0 0] - [0 1 1 0] - [0 1 1 0] - ] - - sage: A^3 - [0 1 1 0] - [0 0 0 0] - [0 1 1 0] - [0 1 1 0] + sage: Abar = ntl.mat_GF2(A) + sage: (Abar^0)._sage_() == A^0 + True + sage: (Abar^1)._sage_() == A^1 + True + sage: (Abar^2)._sage_() == A^2 + True + sage: (Abar^3)._sage_() == A^3 + True """ cdef ntl_mat_GF2 r = self._new() sig_on() @@ -447,24 +391,9 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2), 10, 10) sage: Abar = ntl.mat_GF2(A) - sage: A.echelon_form() - [1 0 0 0 0 0 1 0 1 0] - [0 1 0 0 0 0 0 0 0 0] - [0 0 1 0 0 0 1 0 1 0] - [0 0 0 1 0 0 1 0 1 0] - [0 0 0 0 1 0 1 0 0 0] - [0 0 0 0 0 1 1 0 0 0] - [0 0 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 0 0 0] - [0 0 0 0 0 0 0 0 0 0] - sage: A.rank() - 8 - - sage: Abar.gauss() - 8 - - sage: Abar + sage: A.rank() == Abar.gauss() + True + sage: Abar # random [[1 1 1 1 0 1 0 1 1 0] [0 1 1 1 0 1 1 0 0 1] [0 0 1 1 1 1 0 0 0 0] @@ -476,6 +405,17 @@ cdef class ntl_mat_GF2(object): [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] ] + + ``Abar`` is in row echolon form now:: + + sage: first_nonzero_indices = [Abar._sage_().row(i).nonzero_positions()[0] for i in range(A.rank())] + sage: all(first_nonzero_indices[i] < first_nonzero_indices[i+1] for i in range(A.rank()-1)) + True + + ``Abar`` is not reduced:: + + sage: all(Abar._sage_().row(i).nonzero_positions() == [] for i in range(A.rank(), Abar.NumRows())) + True """ if ncols == -1: ncols = self.x.NumCols() @@ -489,12 +429,8 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2), 4, 4) sage: Abar = ntl.mat_GF2(A) - sage: A.list() - [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0] - - sage: Abar.list() - [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0] - + sage: A.list() == Abar.list() + True """ cdef Py_ssize_t i, j return [self[i,j] for i in range(self.NumRows()) for j in range(self.x.NumCols())] @@ -525,30 +461,10 @@ cdef class ntl_mat_GF2(object): EXAMPLES:: - sage: A = random_matrix(GF(2), 6, 6); A - [0 1 0 1 1 0] - [0 1 1 1 0 1] - [0 0 0 1 0 1] - [0 1 1 0 0 1] - [0 0 0 1 1 1] - [0 0 1 1 1 1] - - sage: Abar = ntl.mat_GF2(A); Abar - [[0 1 0 1 1 0] - [0 1 1 1 0 1] - [0 0 0 1 0 1] - [0 1 1 0 0 1] - [0 0 0 1 1 1] - [0 0 1 1 1 1] - ] - - sage: Abar._sage_() - [0 1 0 1 1 0] - [0 1 1 1 0 1] - [0 0 0 1 0 1] - [0 1 1 0 0 1] - [0 0 0 1 1 1] - [0 0 1 1 1 1] + sage: A = random_matrix(GF(2), 6, 6) + sage: Abar = ntl.mat_GF2(A) + sage: Abar._sage_() == A + True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.matrix.constructor import Matrix @@ -568,31 +484,11 @@ cdef class ntl_mat_GF2(object): EXAMPLES:: sage: A = random_matrix(GF(2), 10, 10) - sage: Abar = ntl.mat_GF2(A); Abar - [[0 1 0 1 1 0 0 0 1 1] - [0 1 1 1 0 1 1 0 0 1] - [0 0 0 1 0 1 0 0 1 0] - [0 1 1 0 0 1 0 1 1 0] - [0 0 0 1 1 1 1 0 1 1] - [0 0 1 1 1 1 0 0 0 0] - [1 1 1 1 0 1 0 1 1 0] - [0 0 0 1 1 0 0 0 1 1] - [1 0 0 0 1 1 1 0 1 1] - [1 0 0 1 1 0 1 0 0 0] - ] - - sage: Abar.transpose() - [[0 0 0 0 0 0 1 0 1 1] - [1 1 0 1 0 0 1 0 0 0] - [0 1 0 1 0 1 1 0 0 0] - [1 1 1 0 1 1 1 1 0 1] - [1 0 0 0 1 1 0 1 1 1] - [0 1 1 1 1 1 1 0 1 0] - [0 1 0 0 1 0 0 0 1 1] - [0 0 0 1 0 0 1 0 0 0] - [1 0 1 1 1 0 1 1 1 0] - [1 1 0 0 1 0 0 1 1 0] - ] + sage: Abar = ntl.mat_GF2(A) + sage: Abar_t = Abar.transpose() + sage: A_t = A.transpose() + sage: A_t == Abar_t._sage_() + True """ cdef ntl_mat_GF2 r = self._new() sig_on() @@ -662,36 +558,23 @@ cdef class ntl_mat_GF2(object): def image(self): """ If A is this matrix and X the matrix returned by this function - then, the rows of X are computed as basis of A's row space. X - is in row echelon form. + then, the rows of X are computed as basis of A's row space. + X is in row echelon form. EXAMPLES:: sage: A = random_matrix(GF(2),10,10) sage: Abar = ntl.mat_GF2(A) - sage: A.image() - Vector space of degree 10 and dimension 8 over Finite Field of size 2 - Basis matrix: - [1 0 0 0 0 0 1 0 1 0] - [0 1 0 0 0 0 0 0 0 0] - [0 0 1 0 0 0 1 0 1 0] - [0 0 0 1 0 0 1 0 1 0] - [0 0 0 0 1 0 1 0 0 0] - [0 0 0 0 0 1 1 0 0 0] - [0 0 0 0 0 0 0 1 0 0] - [0 0 0 0 0 0 0 0 0 1] + sage: A_image = A.image().matrix() + sage: Abar_image = Abar.image()._sage_() + sage: A_image.row_space() == Abar_image.row_space() + True + X is in row echolon form:: - sage: Abar.image() - [[1 1 1 1 0 1 0 1 1 0] - [0 1 1 1 0 1 1 0 0 1] - [0 0 1 1 1 1 0 0 0 0] - [0 0 0 1 0 0 1 1 1 1] - [0 0 0 0 1 1 0 1 0 0] - [0 0 0 0 0 1 1 1 0 1] - [0 0 0 0 0 0 0 1 0 1] - [0 0 0 0 0 0 0 0 0 1] - ] + sage: first_nonzero_indices = [row.nonzero_positions()[0] for row in Abar_image.rows()] + sage: all(first_nonzero_indices[i] < first_nonzero_indices[i+1] for i in range(Abar_image.nrows() - 1)) + True """ cdef ntl_mat_GF2 X = self._new() sig_on() @@ -708,15 +591,15 @@ cdef class ntl_mat_GF2(object): sage: A = random_matrix(GF(2),10,10) sage: Abar = ntl.mat_GF2(A) - sage: A.kernel() - Vector space of degree 10 and dimension 2 over Finite Field of size 2 - Basis matrix: - [1 1 1 0 1 1 0 1 0 0] - [0 0 0 1 1 0 1 0 1 0] - sage: Abar.kernel() + sage: K_abar = Abar.kernel(); K_abar # random [[0 0 0 1 1 0 1 0 1 0] [1 1 1 0 1 1 0 1 0 0] ] + sage: (K_abar*Abar).IsZero() + True + sage: K_a = A.kernel().matrix() + sage: K_a.row_space() == K_abar._sage_().row_space() + True """ cdef ntl_mat_GF2 X = self._new() sig_on() diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index 4e7b6c13082..8240a10fef5 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** @@ -476,8 +480,13 @@ cdef class ntl_mat_GF2E(object): sage: ctx = ntl.GF2EContext([1,1,0,1,1,0,0,0,1]) sage: m = ntl.mat_GF2E(ctx, 2,2,[ntl.GF2E_random(ctx) for x in range(2*2)]) sage: ntl.GF2XHexOutput(0) - sage: m.list() + sage: l = m.list(); l # random [[1 1 0 0 1 0 1 1], [1 1 1 0 1 1 1], [0 1 1 1 1 0 0 1], [0 1 0 1 1 1]] + sage: len(l) == 4 + True + sage: all(a.modulus_context() is ctx for a in l) + True + """ return [self[i,j] for i in range(self.NumRows()) for j in range(self.x.NumCols())] @@ -671,7 +680,7 @@ cdef class ntl_mat_GF2E(object): sage: ntl.GF2XHexOutput(1) sage: A = ntl.mat_GF2E(ctx, 100,100) sage: A.randomize() - sage: len([e for e in A.list() if e!=0]) + sage: len([e for e in A.list() if e!=0]) # rel tol 1e-1 9346 sage: A = ntl.mat_GF2E(ctx, 100,100) @@ -681,8 +690,8 @@ cdef class ntl_mat_GF2E(object): sage: A = ntl.mat_GF2E(ctx, 100,100) sage: A.randomize(nonzero=True, density=0.1) - sage: len([e for e in A.list() if e!=0]) - 994 + sage: len([e for e in A.list() if e!=0]) # rel tol 2e-1 + 1000 """ cdef long i,j diff --git a/src/sage/libs/ntl/ntl_mat_ZZ.pyx b/src/sage/libs/ntl/ntl_mat_ZZ.pyx index 5c3e5e70746..db14f86b71d 100644 --- a/src/sage/libs/ntl/ntl_mat_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_mat_ZZ.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ #***************************************************************************** @@ -369,7 +373,7 @@ cdef class ntl_mat_ZZ(object): TypeError: cannot take determinant of non-square matrix. sage: ntl.mat_ZZ(4,4,[next_prime(2**i) for i in range(16)]).determinant() -10248 - sage: ntl.mat_ZZ(4,4,[ ZZ.random_element() for _ in range(16) ]).determinant() + sage: ntl.mat_ZZ(4,4,[ ZZ.random_element() for _ in range(16) ]).determinant() # random 678 """ if self.__nrows != self.__ncols: diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index eb1c04f1e6f..dd7f8e9bf86 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -20,8 +20,8 @@ sage: R. = QQ[] sage: K. = NumberField(theta^2 + 1) - sage: K.galois_group(type='pari') - Galois group PARI group [2, -1, 1, "S2"] of degree 2 of the Number Field in a with defining polynomial theta^2 + 1 + sage: K.absolute_polynomial().galois_group(pari_group=True) + PARI group [2, -1, 1, "S2"] of degree 2 Before :trac:`15654`, this used to take a very long time. Now it takes much less than a second:: diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 94db9fd9f09..174048d41b4 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -3,6 +3,7 @@ # distutils: libraries = SINGULAR_LIBRARIES # distutils: library_dirs = SINGULAR_LIBDIR # distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 """ Declarations of Singular's C/C++ Functions diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 0fea70ad259..bf03efe222b 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -1733,8 +1733,8 @@ def singular_function(name): sage: matrix = Matrix(P,2,2) sage: matrix.randomize(terms=1) sage: det = singular_function("det") - sage: det(matrix) - -3/5*x*y*z + sage: det(matrix) == matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0] + True sage: coeffs = singular_function("coeffs") sage: coeffs(x*y+y+1,y) [ 1] diff --git a/src/sage/manifolds/calculus_method.py b/src/sage/manifolds/calculus_method.py index 3f3fec039ca..e4d3919e67c 100644 --- a/src/sage/manifolds/calculus_method.py +++ b/src/sage/manifolds/calculus_method.py @@ -107,7 +107,7 @@ def _Sympy_to_SR(expression): # sympy abstract function a = expression._sage_() # As all sage objects have a ._sage_ operator, they have to be - # catched + # caught if type(a) is type(expression): raise TypeError return a diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 97357c7a620..5a2b7070867 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -2242,14 +2242,14 @@ def valid_coordinates_numerical(self, *coordinates): else: raise ValueError("restrictions must be in CNF (list of tuples)") list_of_fast_callable = [] - for litteral in clause: - if not isinstance(litteral, Expression): + for literal in clause: + if not isinstance(literal, Expression): raise ValueError("Restrictions must be in CNF (list of tuples)") # End of checks - fl = fast_callable(litteral.lhs(), vars=self[:], domain=float) - fr = fast_callable(litteral.rhs(), vars=self[:], domain=float) - op = litteral.operator() + fl = fast_callable(literal.lhs(), vars=self[:], domain=float) + fr = fast_callable(literal.rhs(), vars=self[:], domain=float) + op = literal.operator() list_of_fast_callable.append((fl, fr, op)) list_of_clause.append(list_of_fast_callable) @@ -3086,7 +3086,7 @@ def __eq__(self, other): def __ne__(self, other): r""" - Unequality operator. + Non-equality operator. TESTS:: diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index 231e3d3f70e..e821af80367 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -33,7 +33,7 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.element import AlgebraElement +from sage.structure.element import AlgebraElement, ModuleElementWithMutability from sage.structure.parent import Parent from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation @@ -41,10 +41,11 @@ from sage.manifolds.utilities import ExpressionNice from sage.misc.cachefunc import cached_method from sage.symbolic.ring import SR +from sage.structure.mutability import Mutability import sympy -class ChartFunction(AlgebraElement): +class ChartFunction(AlgebraElement, ModuleElementWithMutability): r""" Function of coordinates of a given chart. @@ -357,7 +358,7 @@ def __init__(self, parent, expression=None, calc_method=None, sage: TestSuite(g).run() """ - AlgebraElement.__init__(self, parent) + ModuleElementWithMutability.__init__(self, parent) self._chart = parent._chart self._nc = len(self._chart[:]) self._express = {} @@ -425,7 +426,6 @@ def chart(self): """ return self._chart - def scalar_field(self, name=None, latex_name=None): r""" Construct the scalar field that has ``self`` as coordinate expression. @@ -597,6 +597,9 @@ def set_expr(self, calc_method, expression): ValueError: Expressions are not equal """ + if self.is_immutable(): + raise ValueError("the expressions of an immutable element cannot " + "be changed") for vv in self._express.values(): if not bool(self._calc_method._tranf[calc_method](expression) == self._calc_method._tranf[calc_method](vv)): @@ -2803,7 +2806,9 @@ def zero(self): elt = SR.zero() else: elt = self._chart.manifold().base_field().zero() - return self.element_class(self, elt) + res = self.element_class(self, elt) + res.set_immutable() + return res @cached_method def one(self): @@ -2827,12 +2832,14 @@ def one(self): elt = SR.one() else: elt = self._chart.manifold().base_field().one() - return self.element_class(self, elt) + res = self.element_class(self, elt) + res.set_immutable() + return res is_field = is_integral_domain -class MultiCoordFunction(SageObject): +class MultiCoordFunction(SageObject, Mutability): r""" Coordinate function to some Cartesian power of the base field. @@ -2942,6 +2949,7 @@ def __init__(self, chart, expressions): self._nf = len(expressions) # number of functions self._functions = tuple(chart.function(express) for express in expressions) + Mutability.__init__(self) def _repr_(self): r""" @@ -3316,3 +3324,32 @@ def jacobian_det(self): func = self._functions[0] return type(func)(func.parent(), func._calc_method.simplify(det, method='SR'), calc_method=self._chart._calc_method._current) + + def set_immutable(self): + r""" + Set ``self`` and all chart functions of ``self`` immutable. + + EXAMPLES: + + Declare a coordinate function immutable:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: X. = M.chart() + sage: f = X.multifunction(x+y+z, x*y*z) + sage: f.is_immutable() + False + sage: f.set_immutable() + sage: f.is_immutable() + True + + The chart functions are now immutable, too:: + + sage: f[0].parent() + Ring of chart functions on Chart (M, (x, y, z)) + sage: f[0].is_immutable() + True + + """ + for func in self._functions: + func.set_immutable() + Mutability.set_immutable(self) diff --git a/src/sage/manifolds/differentiable/bundle_connection.py b/src/sage/manifolds/differentiable/bundle_connection.py index f9debfb64f0..c6c5e6ef016 100644 --- a/src/sage/manifolds/differentiable/bundle_connection.py +++ b/src/sage/manifolds/differentiable/bundle_connection.py @@ -1223,7 +1223,7 @@ def __setitem__(self, args, value): raise TypeError("in case of [:] syntax, the list/tuple " "of value must contain lists/tuples") else: - # check lenghts: + # check lengths: rk = vb._rank if len(value) != rk: raise ValueError("value must have " diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 789a2bb774c..396792d3792 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -303,7 +303,7 @@ def transition_map(self, other, transformations, intersection_name=None, on `U\cap V`. By definition, the transition map `\psi\circ\varphi^{-1}` must be - of classe `C^k`, where `k` is the degree of differentiability of the + of class `C^k`, where `k` is the degree of differentiability of the manifold (cf. :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_degree`). @@ -1074,7 +1074,7 @@ class DiffCoordChange(CoordChange): charts intersect, i.e. on `U\cap V`. By definition, the transition map `\psi\circ\varphi^{-1}` must be - of classe `C^k`, where `k` is the degree of differentiability of the + of class `C^k`, where `k` is the degree of differentiability of the manifold (cf. :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_degree`). diff --git a/src/sage/manifolds/differentiable/degenerate_submanifold.py b/src/sage/manifolds/differentiable/degenerate_submanifold.py index eae84a65fa8..a3d37fbece6 100644 --- a/src/sage/manifolds/differentiable/degenerate_submanifold.py +++ b/src/sage/manifolds/differentiable/degenerate_submanifold.py @@ -426,7 +426,8 @@ def list_of_screens(self): def set_transverse(self, rigging=None, normal=None): r""" For setting a transversal distribution of the degenerate submanifold. - according to the type of the submanifold amoung the 4 possible types, + + According to the type of the submanifold among the 4 possible types, one must enter a list of normal transversal vector fields and/or a list of transversal and not normal vector fields spanning a transverse distribution. @@ -502,7 +503,7 @@ def set_transverse(self, rigging=None, normal=None): rig.append(u) l2 += 1 if l1+l2!=self._codim: - raise ValueError("lenght of the transverse must be {}".format(self._codim)) + raise ValueError("length of the transverse must be {}".format(self._codim)) self._transverse['normal'] = tuple(nor) self._transverse['rigging'] = tuple(rig) @@ -565,7 +566,7 @@ def screen(self, name, screen, rad, latex_name=None): raise ValueError("a different screen distribution with the " "same name had already been set") if len(screen)+len(rad)!=self._dim: - raise ValueError("total lenght screen+rad must be {}".format(self._dim)) + raise ValueError("total length screen+rad must be {}".format(self._dim)) frame = self.default_frame() im = self.immersion() g = self.ambient_metric().along(im) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index 91df2c7ec2a..aaf03efcd4a 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -602,7 +602,7 @@ def degree(self): """ return self._tensor_rank - def hodge_dual(self, metric): + def hodge_dual(self, metric=None): r""" Compute the Hodge dual of the differential form with respect to some metric. @@ -625,7 +625,9 @@ def hodge_dual(self, metric): - ``metric``: a pseudo-Riemannian metric defined on the same manifold as the current differential form; must be an instance of - :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` + :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric`. + If none is provided, the ambient domain of ``self`` is supposed to be endowed + with a default metric and this metric is then used. OUTPUT: @@ -705,7 +707,32 @@ def hodge_dual(self, metric): on U: (x, y) |--> 1 on V: (u, v) |--> 1 + Hodge dual of a 1-form in the Euclidean space `R^3`:: + + sage: M = Manifold(3, 'M', start_index=1) + sage: X. = M.chart() + sage: g = M.metric('g') # the Euclidean metric + sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 + sage: var('Ax Ay Az') + (Ax, Ay, Az) + sage: a = M.one_form(Ax, Ay, Az, name='A') + sage: sa = a.hodge_dual(g) ; sa + 2-form *A on the 3-dimensional differentiable manifold M + sage: sa.display() + *A = Az dx/\dy - Ay dx/\dz + Ax dy/\dz + sage: ssa = sa.hodge_dual(g) ; ssa + 1-form **A on the 3-dimensional differentiable manifold M + sage: ssa.display() + **A = Ax dx + Ay dy + Az dz + sage: ssa == a # must hold for a Riemannian metric in dimension 3 + True + + See the documentation of + :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.hodge_star` + for more examples. """ + if metric is None: + metric = self._vmodule._ambient_domain.metric() return metric.hodge_star(self) def interior_product(self, qvect): @@ -860,7 +887,7 @@ def interior_product(self, qvect): # ***************************************************************************** -class DiffFormParal(FreeModuleAltForm, TensorFieldParal): +class DiffFormParal(FreeModuleAltForm, TensorFieldParal, DiffForm): r""" Differential form with values on a parallelizable manifold. @@ -1478,72 +1505,6 @@ def wedge(self, other): other_r = other.restrict(dom_resu) return FreeModuleAltForm.wedge(self_r, other_r) - def hodge_dual(self, metric): - r""" - Compute the Hodge dual of the differential form with respect to some - metric. - - If the differential form is a `p`-form `A`, its *Hodge dual* with - respect to a pseudo-Riemannian metric `g` is the - `(n-p)`-form `*A` defined by - - .. MATH:: - - *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A_{k_1\ldots k_p} - \epsilon^{k_1\ldots k_p}_{\qquad\ i_1\ldots i_{n-p}} - - where `n` is the manifold's dimension, `\epsilon` is the volume - `n`-form associated with `g` (see - :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.volume_form`) - and the indices `k_1,\ldots, k_p` are raised with `g`. - - INPUT: - - - ``metric``: a pseudo-Riemannian metric defined on the same manifold - as the current differential form; must be an instance of - :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` - - OUTPUT: - - - the `(n-p)`-form `*A` - - EXAMPLES: - - Hodge dual of a 1-form in the Euclidean space `R^3`:: - - sage: M = Manifold(3, 'M', start_index=1) - sage: X. = M.chart() - sage: g = M.metric('g') # the Euclidean metric - sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 - sage: var('Ax Ay Az') - (Ax, Ay, Az) - sage: a = M.one_form(Ax, Ay, Az, name='A') - sage: sa = a.hodge_dual(g) ; sa - 2-form *A on the 3-dimensional differentiable manifold M - sage: sa.display() - *A = Az dx/\dy - Ay dx/\dz + Ax dy/\dz - sage: ssa = sa.hodge_dual(g) ; ssa - 1-form **A on the 3-dimensional differentiable manifold M - sage: ssa.display() - **A = Ax dx + Ay dy + Az dz - sage: ssa == a # must hold for a Riemannian metric in dimension 3 - True - - Instead of calling the method :meth:`hodge_dual` on the differential - form, one can invoke the method - :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.hodge_star` - of the metric:: - - sage: a.hodge_dual(g) == g.hodge_star(a) - True - - See the documentation of - :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.hodge_star` - for more examples. - - """ - return metric.hodge_star(self) - def interior_product(self, qvect): r""" Interior product with a multivector field. diff --git a/src/sage/manifolds/differentiable/examples/euclidean.py b/src/sage/manifolds/differentiable/examples/euclidean.py index d909bc7eaa9..de84da10c48 100644 --- a/src/sage/manifolds/differentiable/examples/euclidean.py +++ b/src/sage/manifolds/differentiable/examples/euclidean.py @@ -791,7 +791,7 @@ def __init__(self, n, name=None, latex_name=None, if coordinates == 'Cartesian': symbols = 'x' else: - raise TypeError("unkown coordinate type") + raise TypeError("unknown coordinate type") elif n > 3: if coordinates == 'Cartesian': symbols = '' @@ -799,7 +799,7 @@ def __init__(self, n, name=None, latex_name=None, symbols += "x{}".format(i) + r":x_{" + str(i) + r"} " symbols = symbols[:-1] else: - raise TypeError("unkown coordinate type") + raise TypeError("unknown coordinate type") else: raise NotImplementedError("dimension not implemented yet") self._cartesian_chart = None # to be constructed later if necessary @@ -1197,7 +1197,7 @@ def __init__(self, name=None, latex_name=None, coordinates='Cartesian', """ if coordinates not in ['Cartesian', 'polar']: - raise TypeError("unkown coordinate type") + raise TypeError("unknown coordinate type") if symbols is None: if coordinates == 'Cartesian': symbols = 'x y' @@ -1726,7 +1726,7 @@ def __init__(self, name=None, latex_name=None, coordinates='Cartesian', """ if coordinates not in ['Cartesian', 'spherical', 'cylindrical']: - raise TypeError("unkown coordinate type") + raise TypeError("unknown coordinate type") if symbols is None: if coordinates == 'Cartesian': symbols = 'x y z' diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 6c06aab1fd5..95fbdd99086 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -2949,7 +2949,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): line of latitude. Now, set an affine connection with respect to such fields that are - parallely transported in all directions, that is: + parallelly transported in all directions, that is: `\nabla \hat{e}_{\theta} = \nabla \hat{e}_{\phi} = 0`. This is equivalent to setting all the connection coefficients to zero with respect to this frame:: diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index b0ad7fdb88b..61d94a414f8 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -952,7 +952,7 @@ def diff_map(self, codomain, coord_functions=None, chart1=None, coord_functions = {(chart1, chart2): coord_functions} return homset(coord_functions, name=name, latex_name=latex_name) - def diffeomorphism(self, codomain, coord_functions=None, chart1=None, + def diffeomorphism(self, codomain=None, coord_functions=None, chart1=None, chart2=None, name=None, latex_name=None): r""" Define a diffeomorphism between the current manifold and another one. @@ -962,8 +962,8 @@ def diffeomorphism(self, codomain, coord_functions=None, chart1=None, INPUT: - - ``codomain`` -- codomain of the diffeomorphism (the arrival manifold - or some subset of it) + - ``codomain`` -- (default: ``None``) codomain of the diffeomorphism (the arrival manifold + or some subset of it). If ``None``, the current manifold is taken. - ``coord_functions`` -- (default: ``None``) if not ``None``, must be either @@ -1030,6 +1030,9 @@ def diffeomorphism(self, codomain, coord_functions=None, chart1=None, examples. """ + if codomain is None: + codomain = self + homset = Hom(self, codomain) if coord_functions is None: coord_functions = {} @@ -3073,7 +3076,7 @@ def vector_frame(self, *args, **kwargs): Vector field f_0 on the 2-dimensional differentiable manifold M Thanks to the keywords ``dest_map`` and ``from_frame``, one can also - define a vector frame from one prexisting on another manifold, via a + define a vector frame from one preexisting on another manifold, via a differentiable map (here provided by the curve ``c``):: sage: fc = I.vector_frame(dest_map=c, from_frame=f); fc diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 9901f51ff70..e9fdcdb9916 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -982,7 +982,7 @@ def exterior_derivative(self): resu = self._new_instance() resu[0] = self._domain.zero_scalar_field() resu[1:] = [self[j].exterior_derivative() - for j in range(0, self._max_deg)] + for j in range(self._max_deg)] # Compose name: from sage.tensor.modules.format_utilities import (format_unop_txt, format_unop_latex) diff --git a/src/sage/manifolds/differentiable/scalarfield.py b/src/sage/manifolds/differentiable/scalarfield.py index 7b660405998..a2dbd8266dc 100644 --- a/src/sage/manifolds/differentiable/scalarfield.py +++ b/src/sage/manifolds/differentiable/scalarfield.py @@ -525,7 +525,7 @@ class DiffScalarField(ScalarField): (x, y) |--> x*y*H(x, y) on W: (u, v) |--> u*v*H(u/(u^2 + v^2), v/(u^2 + v^2))/(u^4 + 2*u^2*v^2 + v^4) - Thanks to the coercion `C^k(M)\rightarrow C^k(U)` mentionned + Thanks to the coercion `C^k(M)\rightarrow C^k(U)` mentioned above, it is possible to multiply a scalar field defined on `M` by a scalar field defined on `U`, the result being a scalar field defined on `U`:: diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 44d47895f98..4771f374500 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -3983,10 +3983,10 @@ def down(self, metric, pos=None): False """ - n_con = self._tensor_type[0] # number of contravariant indices = k + n_con = self._tensor_type[0] # number of contravariant indices = k if pos is None: result = self - for p in range(0, n_con): + for p in range(n_con): k = result._tensor_type[0] result = result.down(metric, k-1) return result diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index 72a0e33349f..ba10dde44d9 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -2368,8 +2368,8 @@ def truncate(self, symbol, order): [ 0 0 e 1] """ - ser = self.series_expansion(symbol, order) - return sum(symbol**i*s for (i, s) in enumerate(ser)) + series = self.series_expansion(symbol, order) + return sum(symbol**i * s for i, s in enumerate(series)) def set_calc_order(self, symbol, order, truncate=False): r""" diff --git a/src/sage/manifolds/differentiable/vectorframe.py b/src/sage/manifolds/differentiable/vectorframe.py index 3482c9872dd..355ee322021 100644 --- a/src/sage/manifolds/differentiable/vectorframe.py +++ b/src/sage/manifolds/differentiable/vectorframe.py @@ -479,7 +479,7 @@ class VectorFrame(FreeModuleBasis): immersion and `\Phi` being a curve in `M` (`U` is then an open interval of `\RR`). - For each instanciation of a vector frame, a coframe is automatically + For each instantiation of a vector frame, a coframe is automatically created, as an instance of the class :class:`CoFrame`. It is returned by the method :meth:`coframe`. diff --git a/src/sage/manifolds/local_frame.py b/src/sage/manifolds/local_frame.py index 0b29c9ab2f6..5ea1e016051 100644 --- a/src/sage/manifolds/local_frame.py +++ b/src/sage/manifolds/local_frame.py @@ -422,7 +422,7 @@ class LocalFrame(FreeModuleBasis): of the base space `M`, such that `e(p)` is a basis of the fiber `E_p` for any `p \in U`. - For each instanciation of a local frame, a local coframe is automatically + For each instantiation of a local frame, a local coframe is automatically created, as an instance of the class :class:`LocalCoFrame`. It is returned by the method :meth:`coframe`. diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index 59eeb47cdb8..e01fbbb99fa 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -649,7 +649,7 @@ def __eq__(self, other): common_chart = chart break if common_chart is None: - # A commont chart is searched via a coordinate transformation, + # A common chart is searched via a coordinate transformation, # privileging the default chart if def_chart in self._coordinates: try: @@ -665,7 +665,7 @@ def __eq__(self, other): except ValueError: pass if common_chart is None: - # At this stage, a commont chart is searched via a coordinate + # At this stage, a common chart is searched via a coordinate # transformation from any chart for chart in self._coordinates: try: diff --git a/src/sage/manifolds/topological_submanifold.py b/src/sage/manifolds/topological_submanifold.py index 3baf09a7f2c..a5613578821 100644 --- a/src/sage/manifolds/topological_submanifold.py +++ b/src/sage/manifolds/topological_submanifold.py @@ -434,7 +434,7 @@ def set_embedding(self, phi, inverse=None, var=None, sage: N.set_embedding(phi, inverse=phi_inv, var=t, ....: t_inverse={t: phi_inv_t}) - Now ``N`` appears as an embbeded submanifold:: + Now ``N`` appears as an embedded submanifold:: sage: N 2-dimensional topological submanifold N embedded in the diff --git a/src/sage/manifolds/trivialization.py b/src/sage/manifolds/trivialization.py index ac18adb9099..bdd8587e80f 100644 --- a/src/sage/manifolds/trivialization.py +++ b/src/sage/manifolds/trivialization.py @@ -733,7 +733,7 @@ def __eq__(self, other): def __ne__(self, other): r""" - Unequality operator. + Non-equality operator. TESTS:: diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 614922292e0..c6712f37e3f 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -457,7 +457,7 @@ def matrix(*args, **kwds): Check conversion from numpy:: sage: import numpy - sage: n = numpy.array([[numpy.complex(0,1),numpy.complex(0,2)],[3,4]],complex) + sage: n = numpy.array([[complex(0,1),complex(0,2)],[3,4]],complex) sage: m = matrix(n); m; m.parent() [1.0*I 2.0*I] [ 3.0 4.0] diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 18cacd03f66..885fe8572e3 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -2700,6 +2700,8 @@ cdef class Matrix(Matrix1): ALGORITHM: + If the base ring has a method `_matrix_charpoly`, we use it. + In the generic case of matrices over a ring (commutative and with unity), there is a division-free algorithm, which can be accessed using ``"df"``, with complexity `O(n^4)`. Alternatively, by @@ -2862,18 +2864,16 @@ cdef class Matrix(Matrix1): if f is not None: return f.change_variable_name(var) - if algorithm is None: - from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing + R = self._base_ring - R = self._base_ring - if is_NumberField(R): - f = self._charpoly_over_number_field(var) - elif is_IntegerModRing(R): - f = self.lift().charpoly(var).change_ring(R) - elif R in _Fields and R.is_exact(): - f = self._charpoly_hessenberg(var) - else: - f = self._charpoly_df(var) + if algorithm is None: + if hasattr(R, '_matrix_charpoly'): + f = R._matrix_charpoly(self, var) + if f is None: + if R in _Fields and R.is_exact(): + f = self._charpoly_hessenberg(var) + else: + f = self._charpoly_df(var) else: if algorithm == "hessenberg": f = self._charpoly_hessenberg(var) @@ -3049,53 +3049,6 @@ cdef class Matrix(Matrix1): return f - def _charpoly_over_number_field(self, var='x'): - r""" - Use PARI to compute the characteristic polynomial of self as a - polynomial over the base ring. - - EXAMPLES:: - - sage: x = QQ['x'].gen() - sage: K. = NumberField(x^2 - 2) - sage: m = matrix(K, [[a-1, 2], [a, a+1]]) - sage: m._charpoly_over_number_field('Z') - Z^2 - 2*a*Z - 2*a + 1 - sage: m._charpoly_over_number_field('a')(m) == 0 - True - sage: m = matrix(K, [[0, a, 0], [-a, 0, 0], [0, 0, 0]]) - sage: m._charpoly_over_number_field('Z') - Z^3 + 2*Z - - The remaining tests are indirect:: - - sage: L. = K.extension(x^3 - a) - sage: m = matrix(L, [[b+a, 1], [a, b^2-2]]) - sage: m.charpoly('Z') - Z^2 + (-b^2 - b - a + 2)*Z + a*b^2 - 2*b - 2*a - sage: m.charpoly('a') - a^2 + (-b^2 - b - a + 2)*a + a*b^2 - 2*b - 2*a - sage: m.charpoly('a')(m) == 0 - True - - :: - - sage: M. = L.extension(x^2 - a*x + b) - sage: m = matrix(M, [[a+b+c, 0, b], [0, c, 1], [a-1, b^2+1, 2]]) - sage: f = m.charpoly('Z'); f - Z^3 + (-2*c - b - a - 2)*Z^2 + ((b + 2*a + 4)*c - b^2 + (-a + 2)*b + 2*a - 1)*Z + (b^2 + (a - 3)*b - 4*a + 1)*c + a*b^2 + 3*b + 2*a - sage: f(m) == 0 - True - sage: f.base_ring() is M - True - """ - K = self.base_ring() - if not is_NumberField(K): - raise ValueError("_charpoly_over_number_field called with base ring (%s) not a number field" % K) - - paripoly = self.__pari__().charpoly() - return K[var](paripoly) - def fcp(self, var='x'): """ Return the factorization of the characteristic polynomial of self. @@ -3373,11 +3326,16 @@ cdef class Matrix(Matrix1): if not self.is_square(): raise TypeError("self must be square") + self.check_mutability() + + base = self._base_ring + if hasattr(base, '_matrix_hessenbergize'): + base._matrix_hessenbergize(self) + return + if self._base_ring not in _Fields: raise TypeError("Hessenbergize only possible for matrices over a field") - self.check_mutability() - zero = self._base_ring(0) one = self._base_ring(1) for m from 1 <= m < n-1: @@ -13473,6 +13431,10 @@ cdef class Matrix(Matrix1): This makes it an appropriate candidate for solving systems with symmetric (or Hermitian) coefficient matrices. + .. SEEALSO:: + + :meth:`block_ldlt` + EXAMPLES: There is no requirement that a matrix be positive definite, as @@ -13586,6 +13548,631 @@ cdef class Matrix(Matrix1): raise ValueError(msg.format(d)) return L, vector(L.base_ring(), d) + def _block_ldlt(self): + r""" + Perform a user-unfriendly block-`LDL^{T}` factorization of the + Hermitian matrix `A` + + This function is used internally to compute the factorization + for the user-friendly :meth:`block_ldlt` method. Whereas that + function returns three nice matrices, this one returns + + * An array ``p`` of the first `n` natural numbers, permuted + in a way that represents the `n`-by-`n` permutation matrix + `P`, + * A matrix whose lower-triangular portion is ``L``, but whose + (strict) upper-triangular portion is junk, + * A list of the block-diagonal entries of ``D``. + + This is mainly useful to avoid having to "undo" the + construction of the matrix `D` when we don't need it. For + example, it's much easier to compute the inertia of a matrix + from the list of blocks than it is from the block-diagonal + matrix itself; given a block-diagonal matrix, you would + first have to figure out where the blocks are! + + All of the real documentation, examples, and tests for this + method can be found in the user-facing :meth:`block_ldlt` + method. + + EXAMPLES: + + Test that this method can be called directly; the returned ``l`` + is not inspected because its upper-triangular part is undefined:: + + sage: A = matrix(QQ, [[0, 1, 0], + ....: [1, 1, 2], + ....: [0, 2, 0]]) + sage: p,l,d = A._block_ldlt() + sage: p + array('I', [1, 2, 0]) + sage: d + [[1], [-4], [0]] + + """ + cdef Py_ssize_t i, j, k # loop indices + cdef Py_ssize_t r # another row/column index + + # We need to construct 1x1 and 2x2 matrices to stick in d. + from sage.matrix.constructor import matrix + + # We have to make at least one copy of the input matrix so + # that we can change the base ring to its fraction field. Both + # "L" and the intermediate Schur complements will potentially + # have entries in the fraction field. However, we don't need + # to make *two* copies. We can't store the entries of "D" and + # "L" in the same matrix if "D" will contain any 2x2 blocks; + # but we can still store the entries of "L" in the copy of "A" + # that we're going to make. Contrast this with the non-block + # LDL^T factorization where the entries of both "L" and "D" + # overwrite the lower-left half of "A". + # + # This grants us an additional speedup, since we don't have to + # permute the rows/columns of "L" *and* "A" at each iteration. + # + # Beware that the diagonals of "L" are all set to ``1`` only + # at the end of the function, not as its columns are computed. + ring = self.base_ring().fraction_field() + + cdef Matrix A # A copy of the input matrix + if self.base_ring() == ring: + A = self.__copy__() + else: + # Changing the ring of a large matrix can take a loooong + # time, compared with the short (but predictable) time we + # might waste here checking if we need to do it. + A = self.change_ring(ring) + + zero = ring.zero() + one = ring.one() + + # The magic constant (1 + sqrt(17))/8 used by Bunch-Kaufman. + # This is mainly useful for numerical stability, so we use its + # numerical approximation to speed up the comparisons we're + # going to make with it. + cdef double alpha = 0.6403882032022076 + + # Likewise, these two values are only ever used in comparisons + # that determine which row/column swaps we make. It's quite + # pointless to make long, slow comparisons when we happen to be + # working in exact arithmetic where the process is stable anyway. + # So, we define these constants to be C doubles, forcing any + # comparisons to be made quickly. + cdef double omega_1, omega_r = 0 + + # Keep track of the permutations and diagonal blocks in a vector + # rather than in a matrix, for efficiency. + cdef Py_ssize_t n = A._nrows + + # Use a low-level array of unsigned integers for the permutation. + from array import array + p = array('I', range(n)) + + # The list of diagonal blocks. + cdef list d = [] + + # And the parent of those diagonal blocks that are 1x1... + one_by_one_space = A.matrix_space(1,1) + + # The case n == 0 is *almost* handled by skipping the + # forthcoming loop entirely. However, we must stick a trivial + # matrix in "d" to let block_diagonal_matrix() know what its + # base ring should be. + if n == 0: + d.append(A) + + k = 0 + while k < n: + # At each step, we're considering the k-by-k submatrix + # contained in the lower-right corner of "A", because that's + # where we're storing the next iterate. So our indices are + # always "k" greater than those of Higham or B&K. + + A_kk = A.get_unsafe(k,k) + + if k == (n-1): + # Handle this trivial case manually, since otherwise the + # algorithm's references to the e.g. "subdiagonal" are + # meaningless. The corresponding entry of "L" will be + # fixed later (since it's an on-diagonal element, it gets + # set to one eventually). + d.append( one_by_one_space(A_kk) ) + k += 1 + continue + + # Find the largest subdiagonal entry (in magnitude) in the + # kth column. This occurs prior to Step (1) in Higham, + # but is part of Step (1) in Bunch and Kaufman. We adopt + # Higham's "omega" notation instead of B&K's "lambda" + # because "lambda" can lead to some confusion. + # + # Note: omega_1 is defined as a C double, but the abs() + # below would make a complex number approximate anyway. + omega_1 = 0 + for i in range(k+1,n): + a_ik_abs = A.get_unsafe(i,k).abs() + if a_ik_abs > omega_1: + omega_1 = a_ik_abs + # We record the index "r" that corresponds to + # omega_1 for later. This is still part of Step + # (1) in B&K, but occurs later in the "else" + # branch of Higham's Step (1), separate from + # his computation of omega_1. + r = i + + if omega_1 == 0: + # In this case, our matrix looks like + # + # [ a 0 ] + # [ 0 B ] + # + # and we can simply skip to the next step after recording + # the 1x1 pivot "a" in the top-left position. The entry "a" + # will be adjusted to "1" later on to ensure that "L" is + # (block) unit-lower-triangular. + d.append( one_by_one_space(A_kk) ) + k += 1 + continue + + if A_kk.abs() > alpha*omega_1: + # This is the first case in Higham's Step (1), and B&K's + # Step (2). Note that we have skipped the part of B&K's + # Step (1) where we determine "r", since "r" is not yet + # needed and we may waste some time computing it + # otherwise. We are performing a 1x1 pivot, but the + # rows/columns are already where we want them, so nothing + # needs to be permuted. + d.append( one_by_one_space(A_kk) ) + _block_ldlt_pivot1x1(A,k) + k += 1 + continue + + # Continuing the "else" branch of Higham's Step (1), and + # onto B&K's Step (3) where we find the largest + # off-diagonal entry (in magniture) in column "r". Since + # the matrix is Hermitian, we need only look at the + # above-diagonal entries to find the off-diagonal of + # maximal magnitude. + # + # Note: omega_r is defined as a C double, but the abs() + # below would make a complex number approximate anyway. + omega_r = 0 + for j in range(k,r): + a_rj_abs = A.get_unsafe(r,j).abs() + if a_rj_abs > omega_r: + omega_r = a_rj_abs + + if A_kk.abs()*omega_r >= alpha*(omega_1**2): + # Step (2) in Higham or Step (4) in B&K. + d.append( one_by_one_space(A_kk) ) + _block_ldlt_pivot1x1(A,k) + k += 1 + continue + + A_rr = A.get_unsafe(r,r) + if A_rr.abs() > alpha*omega_r: + # This is Step (3) in Higham or Step (5) in B&K. Still + # a 1x1 pivot, but this time we need to swap + # rows/columns k and r. + d.append( one_by_one_space(A_rr) ) + A.swap_columns_c(k,r); A.swap_rows_c(k,r) + p_k = p[k]; p[k] = p[r]; p[r] = p_k + _block_ldlt_pivot1x1(A,k) + k += 1 + continue + + # If we've made it this far, we're at Step (4) in Higham + # or Step (6) in B&K, where we perform a 2x2 pivot. See + # pivot1x1() for an explanation of why it's OK to permute + # the entries of "L" here as well. + A.swap_columns_c(k+1,r); A.swap_rows_c(k+1,r) + p_k = p[k+1]; p[k+1] = p[r]; p[r] = p_k + + # The top-left 2x2 submatrix (starting at position k,k) is + # now our pivot. + E = A[k:k+2,k:k+2] + d.append(E) + + C = A[k+2:n,k:k+2] + B = A[k+2:,k+2:] + + # We don't actually need the inverse of E, what we really need + # is C*E.inverse(), and that can be found by setting + # + # X = C*E.inverse() <====> XE = C. + # + # Then "X" can be found easily by solving a system. Note: I + # do not actually know that sage solves the system more + # intelligently, but this is still The Right Thing To Do. + CE_inverse = E.solve_left(C) + + schur_complement = B - (CE_inverse*C.conjugate_transpose()) + + # Compute the Schur complement that we'll work on during + # the following iteration, and store it back in the lower- + # right-hand corner of "A". + for i in range(n-k-2): + for j in range(i+1): + A.set_unsafe(k+2+i, k+2+j, schur_complement[i,j]) + A.set_unsafe(k+2+j, k+2+i, schur_complement[j,i]) + + # The on- and above-diagonal entries of "L" will be fixed + # later, so we only need to worry about the lower-left entry + # of the 2x2 identity matrix that belongs at the top of the + # new column of "L". + A.set_unsafe(k+1, k, zero) + for i in range(n-k-2): + for j in range(2): + # Store the new (k and (k+1)st) columns of "L" within + # the lower-left-hand corner of "A". + A.set_unsafe(k+i+2, k+j, CE_inverse[i,j]) + + + k += 2 + + for i in range(n): + # We skipped this during the main loop, but it's necessary for + # correctness. + A.set_unsafe(i, i, one) + + return (p,A,d) + + def block_ldlt(self): + r""" + Compute a block-`LDL^{T}` factorization of a Hermitian + matrix. + + The standard `LDL^{T}` factorization of a positive-definite + matrix `A` factors it as `A = LDL^{T}` where `L` is + unit-lower-triangular and `D` is diagonal. If one allows + row/column swaps via a permutation matrix `P`, then this + factorization can be extended to many positive-semidefinite + matrices `A` via the factorization `P^{T}AP = LDL^{T}` that + places the zeros at the bottom of `D` to avoid division by + zero. These factorizations extend easily to complex Hermitian + matrices when one replaces the transpose by the + conjugate-transpose. + + However, we can go one step further. If, in addition, we allow + `D` to potentially contain `2 \times 2` blocks on its + diagonal, then every real or complex Hermitian matrix `A` can + be factored as `A = PLDL^{*}P^{T}`. When the row/column swaps + are made intelligently, this process is numerically stable + over inexact rings like ``RDF``. Bunch and Kaufman describe + such a "pivot" scheme that is suitable for the solution of + Hermitian systems, and that is how we choose our row and + column swaps. + + OUTPUT: + + If the input matrix is Hermitian, we return a triple `(P,L,D)` + such that `A = PLDL^{*}P^{T}` and + + * `P` is a permutation matrix, + * `L` is unit lower-triangular, + * `D` is a block-diagonal matrix whose blocks are of size + one or two. + + If the input matrix is not Hermitian, the output from this + function is undefined. + + ALGORITHM: + + We essentially follow "Algorithm A" in the paper by Bunch and + Kaufman [BK1977]_ that describes the stable pivoting strategy. + The same scheme is described by Higham [Hig2002]_. + + .. SEEALSO:: + + :meth:`indefinite_factorization` + + REFERENCES: + + - [BK1977]_ + - [Hig2002]_ + + EXAMPLES: + + This three-by-three real symmetric matrix has one positive, one + negative, and one zero eigenvalue -- so it is not any flavor of + (semi)definite, yet we can still factor it:: + + sage: A = matrix(QQ, [[0, 1, 0], + ....: [1, 1, 2], + ....: [0, 2, 0]]) + sage: P,L,D = A.block_ldlt() + sage: P + [0 0 1] + [1 0 0] + [0 1 0] + sage: L + [ 1 0 0] + [ 2 1 0] + [ 1 1/2 1] + sage: D + [ 1| 0| 0] + [--+--+--] + [ 0|-4| 0] + [--+--+--] + [ 0| 0| 0] + sage: P.transpose()*A*P == L*D*L.transpose() + True + + This two-by-two matrix has no standard factorization, but it + constitutes its own block-factorization:: + + sage: A = matrix(QQ, [ [0,1], + ....: [1,0] ]) + sage: A.block_ldlt() + ( + [1 0] [1 0] [0 1] + [0 1], [0 1], [1 0] + ) + + The same is true of the following complex Hermitian matrix:: + + sage: A = matrix(QQbar, [ [ 0,I], + ....: [-I,0] ]) + sage: A.block_ldlt() + ( + [1 0] [1 0] [ 0 I] + [0 1], [0 1], [-I 0] + ) + + Complete diagonal pivoting could cause problems for the + following matrix, since the diagonal entries are small + compared to the off-diagonals that must be zeroed; however, + the block algorithm refuses to factor it:: + + sage: A = matrix(RDF, 2, 2, [ [1e-10, 1 ], + ....: [1 , 2e-10] ]) + sage: A.block_ldlt() + ( + [1.0 0.0] [1.0 0.0] [1e-10 1.0] + [0.0 1.0], [0.0 1.0], [ 1.0 2e-10] + ) + + The factorization over an inexact ring is necessarily inexact, + but `P^{T}AP` will ideally be close to `LDL^{*}` in the metric + induced by the norm:: + + sage: A = matrix(CDF, 2, 2, [ [-1.1933, -0.3185 - 1.3553*I], + ....: [-0.3185 + 1.3553*I, 1.5729 ] ]) + sage: P,L,D = A.block_ldlt() + sage: P.T*A*P == L*D*L.H + False + sage: (P.T*A*P - L*D*L.H).norm() < 1e-10 + True + + TESTS: + + All three factors should be the identity when the input matrix is:: + + sage: set_random_seed() + sage: n = ZZ.random_element(6) + sage: I = matrix.identity(QQ,n) + sage: P,L,D = I.block_ldlt() + sage: P == I and L == I and D == I + True + + Ensure that a "random" real symmetric matrix is factored correctly:: + + sage: set_random_seed() + sage: n = ZZ.random_element(6) + sage: A = matrix.random(QQ, n) + sage: A = A + A.transpose() + sage: P,L,D = A.block_ldlt() + sage: A == P*L*D*L.transpose()*P.transpose() + True + + Ensure that a "random" complex Hermitian matrix is factored + correctly:: + + sage: set_random_seed() + sage: n = ZZ.random_element(6) + sage: F = NumberField(x^2 +1, 'I') + sage: A = matrix.random(F, n) + sage: A = A + A.conjugate_transpose() + sage: P,L,D = A.block_ldlt() + sage: A == P*L*D*L.conjugate_transpose()*P.conjugate_transpose() + True + + Ensure that a "random" complex positive-semidefinite matrix is + factored correctly and that the resulting block-diagonal matrix + is in fact diagonal:: + + sage: set_random_seed() + sage: n = ZZ.random_element(6) + sage: F = NumberField(x^2 +1, 'I') + sage: A = matrix.random(F, n) + sage: A = A*A.conjugate_transpose() + sage: P,L,D = A.block_ldlt() + sage: A == P*L*D*L.conjugate_transpose()*P.conjugate_transpose() + True + sage: diagonal_matrix(D.diagonal()) == D + True + + The factorization should be a no-op on diagonal matrices:: + + sage: set_random_seed() + sage: n = ZZ.random_element(6) + sage: A = matrix.diagonal(random_vector(QQ, n)) + sage: I = matrix.identity(QQ,n) + sage: P,L,D = A.block_ldlt() + sage: P == I and L == I and A == D + True + + All three factors have the same base ring, even when they're + trivial:: + + sage: A = matrix(QQ,0,[]) + sage: P,L,D = A.block_ldlt() + sage: P.base_ring() == L.base_ring() + True + sage: L.base_ring() == D.base_ring() + True + + """ + cdef Py_ssize_t n # size of the matrices + cdef Py_ssize_t i, j # loop indices + cdef Matrix P,L,D # output matrices + + p,L,d = self._block_ldlt() + MS = L.matrix_space() + P = MS.matrix(lambda i,j: p[j] == i) + + # Warning: when n == 0, this works, but returns a matrix + # whose (nonexistent) entries are in ZZ rather than in + # the base ring of P and L. Problematic? Who knows. + from sage.matrix.constructor import block_diagonal_matrix + D = block_diagonal_matrix(d) + + # Overwrite the (strict) upper-triangular part of "L", since a + # priori it contains the same entries as "A" did after _block_ldlt(). + n = L._nrows + zero = MS.base_ring().zero() + for i in range(n): + for j in range(i+1,n): + L.set_unsafe(i,j,zero) + + return (P,L,D) + + + def is_positive_semidefinite(self): + r""" + Returns whether or not this matrix is positive-semidefinite. + + By SageMath convention, positive (semi)definite matrices must + be either real symmetric or complex Hermitian. + + ALGORITHM: + + Bunch and Kaufman [BK1977]_ describe a fast, + numerically-stable scheme for computing the "inertia" of a + matrix by way Sylvester's inertia theorem and a + block-`LDL^{T}` factorization. We perform this factorization, + and read off the signs of the eigenvalues from the resulting + diagonal blocks. + + REFERENCES: + + - [BK1977]_ + + .. SEEALSO:: + + :meth:`block_ldlt`, :meth:`is_positive_definite` + + EXAMPLES: + + A positive-definite matrix:: + + sage: A = matrix(QQ, [ [2,1], + ....: [1,2] ] ) + sage: A.eigenvalues() + [3, 1] + sage: A.is_positive_semidefinite() + True + + A positive-semidefinite (but not positive-definite) matrix:: + + sage: A = matrix(QQ, [ [1,1], + ....: [1,1] ] ) + sage: A.eigenvalues() + [2, 0] + sage: A.is_positive_semidefinite() + True + + And finally, an indefinite matrix:: + + sage: A = matrix(QQ, [ [0,1], + ....: [1,0] ] ) + sage: A.eigenvalues() + [1, -1] + sage: A.is_positive_semidefinite() + False + + A non-Hermitian matrix cannot be positive-semidefinite, + regardless of its eigenvalues:: + + sage: A = matrix(QQ, [ [2,1], + ....: [0,0] ]) + sage: A.eigenvalues() + [2, 0] + sage: A.is_positive_semidefinite() + False + + Any of the preceding examples are valid over inexact rings and + with complex numbers as well:: + + sage: A = matrix(CDF, [ [ 2, I], + ....: [-I, 2] ] ) + sage: A.is_positive_semidefinite() + True + + sage: A = matrix(CDF, [ [ 1, I], + ....: [-I, 1] ] ) + sage: A.is_positive_semidefinite() + True + + sage: A = matrix(CDF, [ [0,I], + ....: [I,0] ] ) + sage: A.is_positive_semidefinite() + False + + sage: A = matrix(CDF, [ [2,I], + ....: [0,0] ]) + sage: A.is_positive_semidefinite() + False + + TESTS: + + The trivial matrix is vacuously positive-semidefinite:: + + sage: matrix(QQ, 0).is_positive_semidefinite() + True + sage: matrix(CDF, 0).is_positive_semidefinite() + True + + Check that the naive and fast implementations are the same for + a Hermitian matrix (for a non-Hermitian matrix, both "obviously" + return ``False``):: + + sage: set_random_seed() + sage: F = NumberField(x^2 + 1, 'I') + sage: from sage.misc.prandom import choice + sage: ring = choice([ZZ, QQ, F, RDF, CDF]) + sage: A = matrix.random(ring, 10); A = A + A.conjugate_transpose() + sage: def is_positive_semidefinite_naive(A): + ....: if A.nrows() == 0: + ....: return True + ....: return ( A.is_hermitian() and + ....: all(v >= 0 for v in A.eigenvalues()) ) + sage: expected = is_positive_semidefinite_naive(A) + sage: actual = A.is_positive_semidefinite() + sage: actual == expected + True + """ + if not self.is_hermitian(): + return False + + if self._nrows == 0: + return True # vacuously + + cdef list d + _,_,d = self._block_ldlt() + + # Check each 1x1 block for a negative entry. If we don't + # find any, it's positive-semidefinite. The presence of + # any 2x2 blocks also indicates indefiniteness. + for d_i in d: + if d_i.nrows() == 1: + if d_i < 0: + return False + else: + # There's a 2x2 block + return False + return True + def is_positive_definite(self, certificate=False): r""" Determines if a real or symmetric matrix is positive definite. @@ -17196,3 +17783,40 @@ class NotFullRankError(ValueError): that method raises this error if the system turns out to be singular. """ pass + + +cdef inline void _block_ldlt_pivot1x1(Matrix A, Py_ssize_t k): + r""" + Update the `n`-by-`n` matrix `A` as part of a 1x1 pivot in the + ``k,k`` position (whose value is ``pivot``). Relies on the fact + that `A` is passed in by reference, since for performance reasons + this routine should overwrite its argument. + + There is no return value from this function, as its intended + effect is to update the matrix `A` in-place. + """ + cdef Py_ssize_t i,j # dumy loop indices + cdef Py_ssize_t n = A._nrows + pivot = A.get_unsafe(k,k) + + # Compute the Schur complement that we'll work on during + # the following iteration, and store it back in the lower- + # right-hand corner of "A". + for i in range(n-k-1): + for j in range(i+1): + A.set_unsafe(k+1+i, + k+1+j, + ( A.get_unsafe(k+1+i,k+1+j) - + A.get_unsafe(k+1+i,k)*A.get_unsafe(k,k+1+j)/pivot )) + A.set_unsafe(k+1+j, + k+1+i, + A.get_unsafe(k+1+i,k+1+j).conjugate()) + + for i in range(n-k-1): + # Store the new (kth) column of "L" within the lower- + # left-hand corner of "A". + A.set_unsafe(k+i+1, + k, + A.get_unsafe(k+i+1,k)/ pivot) + + return diff --git a/src/sage/matrix/matrix_cdv.pxd b/src/sage/matrix/matrix_cdv.pxd new file mode 100644 index 00000000000..f7684da49f2 --- /dev/null +++ b/src/sage/matrix/matrix_cdv.pxd @@ -0,0 +1,3 @@ +from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense + +cpdef hessenbergize_cdvf(Matrix_generic_dense) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx new file mode 100644 index 00000000000..2206b48f5d6 --- /dev/null +++ b/src/sage/matrix/matrix_cdv.pyx @@ -0,0 +1,83 @@ +r""" +Special methods for matrices over discrete valuation rings/fields. +""" + +# **************************************************************************** +# Copyright (C) 2020 Xavier Caruso +# Raphaël Pagès +# +# 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.matrix.matrix_generic_dense cimport Matrix_generic_dense +from sage.structure.element cimport RingElement +from sage.rings.infinity import Infinity + + +# We assume that H is square +cpdef hessenbergize_cdvf(Matrix_generic_dense H): + r""" + Replace `H` with an Hessenberg form of it. + + .. NOTE:: + + This function assumes that H is a matrix over + a complete discrete valuation field. + + The pivot on each column is always chosen + with maximal relative precision, which ensures + the numerical stability of the algorithm. + + TESTS:: + + sage: K = Qp(5, print_mode="digits", prec=5) + sage: H = matrix(K, 3, 3, range(9)) + sage: H + [ 0 ...00001 ...00002] + [ ...00003 ...00004 ...000010] + [ ...00011 ...00012 ...00013] + sage: H.hessenbergize() + sage: H + [ 0 ...00010 ...00002] + [ ...00003 ...00024 ...000010] + [ ...00000 ...44440 ...44443] + + :: + + sage: M = random_matrix(K, 6, 6) + sage: M.charpoly()[0] == M.determinant() + True + """ + cdef Py_ssize_t n, m, i, j, k + cdef Matrix_generic_dense c + cdef RingElement pivot, inv, scalar + + n = H.nrows() + for j in range(n-1): + k = j + 1 + maxi = H.get_unsafe(k, j).precision_relative() + i = j + 2 + while maxi is not Infinity and i < n: + entry = H.get_unsafe(i, j) + if entry: + m = entry.precision_relative() + if m > maxi: + maxi = m + k = i + i += 1 + + if k != j + 1: + H.swap_rows_c(j+1, k) + H.swap_columns_c(j+1, k) + pivot = H.get_unsafe(j+1, j) + if pivot: + inv = ~pivot + for i in range(j+2, n): + scalar = inv * H.get_unsafe(i, j) + H.add_multiple_of_row_c(i, j+1, -scalar, j) + H.add_multiple_of_column_c(j+1, i, scalar, 0) diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index 3b835d58fb7..bcd549b7057 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1,5 +1,9 @@ # distutils: language = c++ -# distutils: libraries = ntl +# distutils: libraries = NTL_LIBRARIES +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA """ Matrices over Cyclotomic Fields @@ -1285,7 +1289,8 @@ cdef class Matrix_cyclo_dense(Matrix_dense): if algorithm == 'multimodular': f = self._charpoly_multimodular(var, proof=proof) elif algorithm == 'pari': - f = self._charpoly_over_number_field(var) + paripoly = self.__pari__().charpoly() + f = self.base_ring()[var](paripoly) elif algorithm == 'hessenberg': f = self._charpoly_hessenberg(var) else: diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index fd38eaef8d9..2b7e0b3fefc 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -# distutils: extra_compile_args = M4RI_CFLAGS -# distutils: libraries = iml ntl gmp m CBLAS_LIBRARIES -# distutils: library_dirs = CBLAS_LIBDIR -# distutils: include_dirs = M4RI_INCDIR CBLAS_INCDIR +# distutils: extra_compile_args = NTL_CFLAGS M4RI_CFLAGS +# distutils: libraries = iml NTL_LIBRARIES gmp m CBLAS_LIBRARIES +# distutils: library_dirs = NTL_LIBDIR CBLAS_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA +# distutils: include_dirs = NTL_INCDIR M4RI_INCDIR CBLAS_INCDIR """ Dense matrices over the integer ring diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index c70ea5b9b4e..7348c332d79 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -16,6 +16,8 @@ AUTHORS: - Vincent Neiger (2018-09-29): added functions for computing and for verifying minimal approximant bases +- Vincent Neiger (2020-04-01): added functions for computing and for verifying + minimal kernel bases """ # **************************************************************************** # Copyright (C) 2016 Kwankyu Lee @@ -1612,12 +1614,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): if not self.is_square(): return False # check nonsingular and shifts-(ordered weak) Popov form - if normal_form: - if not self.is_popov(shifts, row_wise, False, False): - return False - else: - if not self.is_weak_popov(shifts, row_wise, True, False): - return False + if normal_form and (not self.is_popov(shifts, row_wise, False, False)): + return False + if (not normal_form) and (not self.is_weak_popov(shifts, row_wise, True, False)): + return False # check that self is a basis of the set of approximants if row_wise: @@ -1672,7 +1672,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): return True - def minimal_approximant_basis(self, order, shifts=None, @@ -1714,7 +1713,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ``None`` is interpreted as ``shifts=[0,...,0]``. - ``row_wise`` -- (optional, default: ``True``) boolean, if ``True`` - then the output basis considered row-wise and operates on the left + then the output basis is considered row-wise and operates on the left of ``self``; otherwise it is column-wise and operates on the right of ``self``. @@ -1992,3 +1991,250 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): else: rest_order[j] -= 1 return appbas, rdeg + + def is_minimal_kernel_basis(self, + pmat, + shifts=None, + row_wise=True, + normal_form=False): + r""" + Return ``True`` if and only if this matrix is a left kernel basis in + ``shifts``-ordered weak Popov form for the polynomial matrix ``pmat``. + + If ``normal_form`` is ``True``, then the kernel basis must furthermore + be in ``shifts``-Popov form. An error is raised if the input dimensions + are not sound. + + INPUT: + + - ``pmat`` -- a polynomial matrix. + + - ``shifts`` -- (optional, default: ``None``) list of integers; + ``None`` is interpreted as ``shifts=[0,...,0]``. + + - ``row_wise`` -- (optional, default: ``True``) boolean, if ``True`` + then the basis is considered row-wise and operates on the left of + ``pmat``; otherwise it is column-wise and operates on the right of + ``pmat``. + + - ``normal_form`` -- (optional, default: ``False``) boolean, if + ``True`` then checks for a basis in ``shifts``-Popov form. + + OUTPUT: a boolean. + + ALGORITHM: + + Verification that the matrix has full rank and is in shifted weak + Popov form is done via :meth:`is_weak_popov`; verification that the + matrix is a left kernel basis is done by checking that the rank is + correct, that the product is indeed zero, and that the matrix is + saturated, i.e. it has unimodular column bases (see Lemma 6.10 of + https://arxiv.org/pdf/1807.01272.pdf for details). + + EXAMPLES:: + + sage: pR. = GF(97)[] + sage: pmat = Matrix(pR, [[1],[x],[x**2]]) + + sage: kerbas = Matrix(pR, [[x,-1,0],[0,x,-1]]) + sage: kerbas.is_minimal_kernel_basis(pmat) + True + + A matrix in Popov form which has the right rank, all rows in the + kernel, but does not generate the kernel:: + + sage: kerbas = Matrix(pR, [[x**2,0,-1],[0,x,-1]]) + sage: kerbas.is_minimal_kernel_basis(pmat) + False + + Shifts and right kernel bases are supported (with ``row_wise``), and one can test whether the kernel basis is normalized in shifted-Popov form (with ``normal_form``):: + + sage: kerbas = Matrix(pR, [[-x,-x**2],[1,0],[0,1]]) + sage: kerbas.is_minimal_kernel_basis(pmat.transpose(),row_wise=False,normal_form=True,shifts=[0,1,2]) + True + """ + m = pmat.nrows() + n = pmat.ncols() + + # set default shifts / check shifts dimension + 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') + elif (not row_wise) and len(shifts) != n: + 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") + elif (not row_wise) and self.nrows() != n: + 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)): + return False + if (not normal_form) and (not self.is_weak_popov(shifts, row_wise, True, False)): + return False + + # check that self consists of kernel vectors + if row_wise and self * pmat != 0: + return False + if (not row_wise) and pmat * self != 0: + return False + + # check self.rank() is right (the above weak Popov test ensures self + # has full row rank if row wise, and full column rank if column wise) + rk = pmat.rank() + if row_wise and self.nrows()!=m-rk: + return False + if (not row_wise) and self.ncols()!=n-rk: + return False + + # final check: self is row saturated (assuming row wise), + # since self has full rank this is equivalent to the fact that its + # columns generate the identity matrix of size m; in particular the + # column Popov and column Hermite forms of self are the identity + if row_wise: + hnf = self.transpose().hermite_form(include_zero_rows=False) + # TODO replace by popov_form (likely more efficient) once it is written + else: + hnf = self.hermite_form(include_zero_rows=False) + # TODO replace by popov_form (likely more efficient) once it is written + if hnf != 1: + return False + + return True + + def minimal_kernel_basis(self, + shifts=None, + row_wise=True, + normal_form=False): + r""" + Return a left kernel basis in ``shifts``-ordered weak Popov form for + this polynomial matrix. + + Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix, + then a left kernel basis for `F` is a polynomial matrix whose rows form + a basis of the left kernel of `F`, which is the module of polynomial + vectors `p` of size `m` such that `p F` is zero. + + If ``normal_form`` is ``True``, then the output basis `P` is + furthermore in ``shifts``-Popov form. By default, `P` is considered + row-wise, that is, its rows are left kernel vectors for ``self``; if + ``row_wise`` is ``False`` then its columns are right kernel vectors for + ``self``. + + An error is raised if the input dimensions are not sound: if working + row-wise (resp. column-wise), the length of ``shifts`` must be the + number of rows (resp. columns) of ``self``. + + INPUT: + + - ``shifts`` -- (optional, default: ``None``) list of integers; + ``None`` is interpreted as ``shifts=[0,...,0]``. + + - ``row_wise`` -- (optional, default: ``True``) boolean, if ``True`` + then the output basis considered row-wise and operates on the left + of ``self``; otherwise it is column-wise and operates on the right + of ``self``. + + - ``normal_form`` -- (optional, default: ``False``) boolean, if + ``True`` then the output basis is in ``shifts``-Popov form. + + OUTPUT: a polynomial matrix. + + ALGORITHM: uses minimal approximant basis computation at a + sufficiently large order so that the approximant basis contains + a kernel basis as a submatrix. + + EXAMPLES:: + + sage: pR. = GF(7)[] + sage: pmat = Matrix([[(x+1)*(x+3)],[(x+1)*(x+3)+1]]) + sage: pmat.minimal_kernel_basis() + [6*x^2 + 3*x + 3 x^2 + 4*x + 3] + + sage: pmat = Matrix([[(x+1)*(x+3)],[(x+1)*(x+4)]]) + sage: pmat.minimal_kernel_basis() + [6*x + 3 x + 3] + + sage: pmat.minimal_kernel_basis(row_wise=False) + [] + + sage: pmat = Matrix(pR, [[1,x,x**2]]) + sage: pmat.minimal_kernel_basis(row_wise=False,normal_form=True) + [x 0] + [6 x] + [0 6] + + sage: pmat.minimal_kernel_basis(row_wise=False,normal_form=True,shifts=[0,1,2]) + [ 6*x 6*x^2] + [ 1 0] + [ 0 1] + """ + m = self.nrows() + n = self.ncols() + d = self.degree() + + # set default shifts / check shifts dimension + 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') + elif (not row_wise) and len(shifts) != n: + raise ValueError('shifts length should be the column dimension') + + # compute kernel basis + if row_wise: + if d is -1: # matrix is zero + from sage.matrix.constructor import Matrix + return Matrix.identity(self.base_ring(), m, m) + + if m <= n and self(0).rank() == m: # early exit: kernel is empty + from sage.matrix.constructor import Matrix + return Matrix(self.base_ring(), 0, m) + + # degree bounds on the kernel basis + degree_bound = min(m,n)*d+max(shifts) + degree_bounds = [degree_bound - shifts[i] for i in range(m)] + + # orders for approximation + orders = self.column_degrees(degree_bounds) + for i in range(n): orders[i] = orders[i]+1 + + # compute approximant basis and retrieve kernel rows + P = self.minimal_approximant_basis(orders,shifts,True,normal_form) + row_indices = [] + for i in range(m): + if P[i,i].degree() + shifts[i] <= degree_bound: + row_indices.append(i) + return P[row_indices,:] + + else: + if d is -1: # matrix is zero + from sage.matrix.constructor import Matrix + return Matrix.identity(self.base_ring(), n, n) + + if n <= m and self(0).rank() == n: # early exit: kernel is empty + from sage.matrix.constructor import Matrix + return Matrix(self.base_ring(), n, 0) + + # degree bounds on the kernel basis + degree_bound = min(m,n)*d+max(shifts) + degree_bounds = [degree_bound - shifts[i] for i in range(n)] + + # orders for approximation + orders = self.row_degrees(degree_bounds) + for i in range(m): orders[i] = orders[i]+1 + + # compute approximant basis and retrieve kernel columns + P = self.minimal_approximant_basis(orders,shifts,False,normal_form) + column_indices = [] + for j in range(n): + if P[j,j].degree() + shifts[j] <= degree_bound: + column_indices.append(j) + return P[:,column_indices] diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index c4793114d03..549c5c46305 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -1,8 +1,9 @@ -# distutils: extra_compile_args = -D_XPG6 M4RI_CFLAGS -# distutils: libraries = iml ntl m CBLAS_LIBRARIES -# distutils: library_dirs = CBLAS_LIBDIR -# distutils: include_dirs = M4RI_INCDIR CBLAS_INCDIR - +# distutils: extra_compile_args = -D_XPG6 NTL_CFLAGS M4RI_CFLAGS +# distutils: extra_link_args = NTL_LIBEXTRA +# distutils: libraries = iml NTL_LIBRARIES m CBLAS_LIBRARIES +# distutils: library_dirs = NTL_LIBDIR CBLAS_LIBDIR +# distutils: include_dirs = NTL_INCDIR M4RI_INCDIR CBLAS_INCDIR +# distutils: language = c++ """ Dense matrices over the rational field diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 35018cc3807..6feda2215a5 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -322,19 +322,19 @@ class OperationTable(SageObject): a| a b b| b a - Here are a couple of improper uses :: + Here are a couple of improper uses:: sage: elts.append(5) sage: OperationTable(H, operator.mul, elements=elts) Traceback (most recent call last): ... TypeError: unable to coerce 5 into Cyclic group of order 4 as a permutation group - sage: elts[2]='(1,3,2,4)' + sage: elts[2] = '(1,3,2,4)' sage: OperationTable(H, operator.mul, elements=elts) Traceback (most recent call last): ... TypeError: unable to coerce (1,3,2,4) into Cyclic group of order 4 as a permutation group - sage: elts[2]='(1,2,3,4)' + sage: elts[2] = '(1,2,3,4)' sage: OperationTable(H, operator.mul, elements=elts) Traceback (most recent call last): ... @@ -353,6 +353,27 @@ class OperationTable(SageObject): ... TypeError: elements () and () of Cyclic group of order 4 as a permutation group are incompatible with operation: + We construct the multiplication table for a finite finitely presented + group, where there is no normalization done when computing the hash:: + + sage: GU. = FreeGroup() + sage: gr0 = GU / (s^(-2)*t*s*t, t^(-2)*s*t*s, s*t*s*t) + sage: gr0.multiplication_table() + * a b c d e f g h i j k l + +------------------------ + a| a b c d e f g h i j k l + b| b e f g a i j k c d l h + c| c g a h l k b d j i f e + d| d k h a i g f c e l b j + e| e a i j b c d l f g h k + f| f j b k h l e g d c i a + g| g l k b c j i f a h e d + h| h f d c j b k a l e g i + i| i d e l k h a j g f c b + j| j h l e f d c i b k a g + 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. @@ -444,8 +465,19 @@ def __init__(self, S, operation, names='letters', elements=None): try: r = get_row(result) - except (KeyError,ValueError): - raise ValueError('%s%s%s=%s, and so the set is not closed' % (g, self._ascii_symbol, h, result)) + except (KeyError, ValueError): + failed = True + # There might be an issue with the hashing, fall back to + # getting the index (which simply uses ==). + if get_row != self._elts.index: + failed = False + get_row = self._elts.index + try: + r = get_row(result) + 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)) row.append(r) self._table.append(row) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index c68a320773d..1f249b4da5f 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -713,7 +713,7 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): sage: A.parent() Full MatrixSpace of 3 by 3 sparse matrices over Real Double Field - sage: j = numpy.complex(0,1) + sage: j = complex(0,1) sage: entries = numpy.array([2.0+j, 8.1, 3.4+2.6*j]); entries array([2. +1.j , 8.1+0.j , 3.4+2.6j]) sage: A = diagonal_matrix(entries); A diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 4fc863d768c..aef8d8555e3 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -79,7 +79,7 @@ from .sage_input import sage_input -lazy_import("sage.misc.cython", ["cython_lambda", "cython_create_local_so"]) +lazy_import("sage.misc.cython", "cython_lambda") lazy_import("sage.misc.cython", "cython_compile", "cython") from .persist import save, load, dumps, loads, db, db_save diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 99bfde6799d..e72c97f5c30 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -31,20 +31,31 @@ from sage.repl.user_globals import get_globals from sage.misc.sage_ostools import restore_cwd, redirection from sage.cpython.string import str_to_bytes +from sage.misc.cachefunc import cached_function -cblas_pc = pkgconfig.parse(get_cblas_pc_module_name()) -cblas_libs = list(cblas_pc['libraries']) -cblas_library_dirs = list(cblas_pc['library_dirs']) -cblas_include_dirs = list(cblas_pc['include_dirs']) - -standard_libs = [ - 'mpfr', 'gmp', 'gmpxx', 'pari', 'm', - 'ec', 'gsl', -] + cblas_libs + [ - 'ntl'] +@cached_function +def _standard_libs_libdirs_incdirs_aliases(): + r""" + Return the list of libraries and library directories. -standard_libdirs = [os.path.join(SAGE_LOCAL, "lib")] + cblas_library_dirs + EXAMPLES:: + sage: from sage.misc.cython import _standard_libs_libdirs_incdirs_aliases + sage: _standard_libs_libdirs_incdirs_aliases() + (['mpfr', 'gmp', 'gmpxx', 'pari', ...], + [...], + [...], + {...}) + """ + aliases = cython_aliases() + standard_libs = [ + 'mpfr', 'gmp', 'gmpxx', 'pari', 'm', + 'ec', 'gsl', + ] + aliases["CBLAS_LIBRARIES"] + [ + 'ntl'] + standard_libdirs = [os.path.join(SAGE_LOCAL, "lib")] + aliases["CBLAS_LIBDIR"] + aliases["NTL_LIBDIR"] + standard_incdirs = sage_include_directories() + aliases["CBLAS_INCDIR"] + aliases["NTL_INCDIR"] + return standard_libs, standard_libdirs, standard_incdirs, aliases ################################################################ # If the user attaches a .spyx file and changes it, we have @@ -266,14 +277,26 @@ def cython(filename, verbose=0, compile_message=False, # Add current working directory to includes. This is needed because # we cythonize from a different directory. See Trac #24764. - includes = [os.getcwd()] + sage_include_directories() + standard_libs, standard_libdirs, standard_includes, aliases = _standard_libs_libdirs_incdirs_aliases() + includes = [os.getcwd()] + standard_includes # Now do the actual build, directly calling Cython and distutils from Cython.Build import cythonize from Cython.Compiler.Errors import CompileError import Cython.Compiler.Options - from distutils.dist import Distribution - from distutils.core import Extension + + try: + # Import setuptools before importing distutils, so that setuptools + # can replace distutils by its own vendored copy. + import setuptools + from setuptools.dist import Distribution + from setuptools.extension import Extension + except ImportError: + # Fall back to distutils (stdlib); note that it is deprecated + # in Python 3.10, 3.11; https://www.python.org/dev/peps/pep-0632/ + from distutils.dist import Distribution + from distutils.core import Extension + from distutils.log import set_verbosity set_verbosity(verbose) @@ -325,7 +348,7 @@ def cython(filename, verbose=0, compile_message=False, with restore_cwd(target_dir): try: ext, = cythonize([ext], - aliases=cython_aliases(), + aliases=aliases, include_path=includes, compiler_directives=directives, quiet=(verbose <= 0), diff --git a/src/sage/misc/element_with_label.py b/src/sage/misc/element_with_label.py index 811f86d250f..c2f513bdab8 100644 --- a/src/sage/misc/element_with_label.py +++ b/src/sage/misc/element_with_label.py @@ -1,171 +1,171 @@ -# -*- coding: utf-8 -*- -r""" -Elements with labels. - -This module implements a simple wrapper class for pairs consisting of -an "element" and a "label". -For representation purposes (``repr``, ``str``, ``latex``), this pair -behaves like its label, while the element is "silent". -However, these pairs compare like usual pairs (i.e., both element and -label have to be equal for two such pairs to be equal). -This is used for visual representations of graphs and posets -with vertex labels. -""" - -from sage.misc.latex import latex - - -class ElementWithLabel(object): - """ - Auxiliary class for showing/viewing :class:`Poset`s with - non-injective labelings. - For hashing and equality testing the resulting object behaves - like a tuple ``(element, label)``. - For any presentation purposes it appears just as ``label`` would. - - EXAMPLES:: - - sage: P = Poset({1: [2,3]}) - sage: labs = {i: P.rank(i) for i in range(1, 4)} - sage: print(labs) - {1: 0, 2: 1, 3: 1} - sage: print(P.plot(element_labels=labs)) - Graphics object consisting of 6 graphics primitives - - sage: from sage.misc.element_with_label import ElementWithLabel - sage: W = WeylGroup("A1") - sage: P = W.bruhat_poset(facade=True) - sage: D = W.domain() - sage: v = D.rho() - D.fundamental_weight(1) - sage: nP = P.relabel(lambda w: ElementWithLabel(w, w.action(v))) - sage: list(nP) - [(0, 0), (0, 0)] - """ - def __init__(self, element, label): - """ - Construct an object that wraps ``element`` but presents itself - as ``label``. - - TESTS:: - - sage: from sage.misc.element_with_label import ElementWithLabel - sage: e = ElementWithLabel(1, 'a') - sage: e - 'a' - sage: e.element - 1 - """ - self.element = element - self.label = label - - def _latex_(self): - """ - Return the latex representation of ``self``, - which is just the latex representation of the label. - - TESTS:: - - sage: var('a_1') - a_1 - sage: from sage.misc.element_with_label import ElementWithLabel - sage: e = ElementWithLabel(1, a_1) - sage: latex(e) - a_{1} - """ - return latex(self.label) - - def __str__(self): - """ - Return the string representation of ``self``, which is just - the string representation of the label. - - TESTS:: - - sage: var('a_1') - a_1 - sage: from sage.misc.element_with_label import ElementWithLabel - sage: e = ElementWithLabel(1, a_1) - sage: str(e) - 'a_1' - """ - return str(self.label) - - def __repr__(self): - """ - Return the representation of ``self``, which is just - the representation of the label. - - TESTS:: - - sage: var('a_1') - a_1 - sage: from sage.misc.element_with_label import ElementWithLabel - sage: e = ElementWithLabel(1, a_1) - sage: repr(e) - 'a_1' - """ - return repr(self.label) - - def __hash__(self): - """ - Return the hash of the labeled element ``self``, - which is just the hash of ``self.element``. - - TESTS:: - - sage: from sage.misc.element_with_label import ElementWithLabel - sage: a = ElementWithLabel(1, 'a') - sage: b = ElementWithLabel(1, 'b') - sage: d = {} - sage: d[a] = 'element 1' - sage: d[b] = 'element 2' - sage: print(d) - {'a': 'element 1', 'b': 'element 2'} - sage: a = ElementWithLabel("a", [2,3]) - sage: hash(a) == hash(a.element) - True - """ - return hash(self.element) - - def __eq__(self, other): - """ - Two labeled elements are equal if and only if both of their - constituents are equal. - - TESTS:: - - sage: from sage.misc.element_with_label import ElementWithLabel - sage: a = ElementWithLabel(1, 'a') - sage: b = ElementWithLabel(1, 'b') - sage: x = ElementWithLabel(1, 'a') - sage: a == b - False - sage: a == x - True - sage: 1 == a - False - sage: b == 1 - False - """ - if not (isinstance(self, ElementWithLabel) and - isinstance(other, ElementWithLabel)): - return False - return self.element == other.element and self.label == other.label - - def __ne__(self, other): - """ - Two labeled elements are not equal if and only if first or second - constituents are not equal. - - TESTS:: - - sage: from sage.misc.element_with_label import ElementWithLabel - sage: a = ElementWithLabel(1, 'a') - sage: b = ElementWithLabel(1, 'b') - sage: x = ElementWithLabel(1, 'a') - sage: a != b - True - sage: a != x - False - """ - return not(self == other) +# -*- coding: utf-8 -*- +r""" +Elements with labels. + +This module implements a simple wrapper class for pairs consisting of +an "element" and a "label". +For representation purposes (``repr``, ``str``, ``latex``), this pair +behaves like its label, while the element is "silent". +However, these pairs compare like usual pairs (i.e., both element and +label have to be equal for two such pairs to be equal). +This is used for visual representations of graphs and posets +with vertex labels. +""" + +from sage.misc.latex import latex + + +class ElementWithLabel(object): + """ + Auxiliary class for showing/viewing :class:`Poset`s with + non-injective labelings. + For hashing and equality testing the resulting object behaves + like a tuple ``(element, label)``. + For any presentation purposes it appears just as ``label`` would. + + EXAMPLES:: + + sage: P = Poset({1: [2,3]}) + sage: labs = {i: P.rank(i) for i in range(1, 4)} + sage: print(labs) + {1: 0, 2: 1, 3: 1} + sage: print(P.plot(element_labels=labs)) + Graphics object consisting of 6 graphics primitives + + sage: from sage.misc.element_with_label import ElementWithLabel + sage: W = WeylGroup("A1") + sage: P = W.bruhat_poset(facade=True) + sage: D = W.domain() + sage: v = D.rho() - D.fundamental_weight(1) + sage: nP = P.relabel(lambda w: ElementWithLabel(w, w.action(v))) + sage: list(nP) + [(0, 0), (0, 0)] + """ + def __init__(self, element, label): + """ + Construct an object that wraps ``element`` but presents itself + as ``label``. + + TESTS:: + + sage: from sage.misc.element_with_label import ElementWithLabel + sage: e = ElementWithLabel(1, 'a') + sage: e + 'a' + sage: e.element + 1 + """ + self.element = element + self.label = label + + def _latex_(self): + """ + Return the latex representation of ``self``, + which is just the latex representation of the label. + + TESTS:: + + sage: var('a_1') + a_1 + sage: from sage.misc.element_with_label import ElementWithLabel + sage: e = ElementWithLabel(1, a_1) + sage: latex(e) + a_{1} + """ + return latex(self.label) + + def __str__(self): + """ + Return the string representation of ``self``, which is just + the string representation of the label. + + TESTS:: + + sage: var('a_1') + a_1 + sage: from sage.misc.element_with_label import ElementWithLabel + sage: e = ElementWithLabel(1, a_1) + sage: str(e) + 'a_1' + """ + return str(self.label) + + def __repr__(self): + """ + Return the representation of ``self``, which is just + the representation of the label. + + TESTS:: + + sage: var('a_1') + a_1 + sage: from sage.misc.element_with_label import ElementWithLabel + sage: e = ElementWithLabel(1, a_1) + sage: repr(e) + 'a_1' + """ + return repr(self.label) + + def __hash__(self): + """ + Return the hash of the labeled element ``self``, + which is just the hash of ``self.element``. + + TESTS:: + + sage: from sage.misc.element_with_label import ElementWithLabel + sage: a = ElementWithLabel(1, 'a') + sage: b = ElementWithLabel(1, 'b') + sage: d = {} + sage: d[a] = 'element 1' + sage: d[b] = 'element 2' + sage: print(d) + {'a': 'element 1', 'b': 'element 2'} + sage: a = ElementWithLabel("a", [2,3]) + sage: hash(a) == hash(a.element) + True + """ + return hash(self.element) + + def __eq__(self, other): + """ + Two labeled elements are equal if and only if both of their + constituents are equal. + + TESTS:: + + sage: from sage.misc.element_with_label import ElementWithLabel + sage: a = ElementWithLabel(1, 'a') + sage: b = ElementWithLabel(1, 'b') + sage: x = ElementWithLabel(1, 'a') + sage: a == b + False + sage: a == x + True + sage: 1 == a + False + sage: b == 1 + False + """ + if not (isinstance(self, ElementWithLabel) and + isinstance(other, ElementWithLabel)): + return False + return self.element == other.element and self.label == other.label + + def __ne__(self, other): + """ + Two labeled elements are not equal if and only if first or second + constituents are not equal. + + TESTS:: + + sage: from sage.misc.element_with_label import ElementWithLabel + sage: a = ElementWithLabel(1, 'a') + sage: b = ElementWithLabel(1, 'b') + sage: x = ElementWithLabel(1, 'a') + sage: a != b + True + sage: a != x + False + """ + return not(self == other) diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 82f011b20cf..4580f836f18 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -914,14 +914,43 @@ def random_sublist(X, s): EXAMPLES:: + sage: from sage.misc.misc import is_sublist sage: S = [1,7,3,4,18] - sage: random_sublist(S, 0.5) + sage: sublist = random_sublist(S, 0.5); sublist # random [1, 3, 4] - sage: random_sublist(S, 0.5) + sage: is_sublist(sublist, S) + True + sage: sublist = random_sublist(S, 0.5); sublist # random [1, 3] + sage: is_sublist(sublist, S) + True """ return [a for a in X if random.random() <= s] +def is_sublist(X, Y): + """ + Test whether ``X`` is a sublist of ``Y``. + + EXAMPLES:: + + sage: from sage.misc.misc import is_sublist + sage: S = [1, 7, 3, 4, 18] + sage: is_sublist([1, 7], S) + True + sage: is_sublist([1, 3, 4], S) + True + sage: is_sublist([1, 4, 3], S) + False + sage: is_sublist(S, S) + True + """ + X_i = 0 + for Y_i, y in enumerate(Y): + if X_i == len(X): + return True + if y == X[X_i]: + X_i += 1 + return X_i == len(X) def some_tuples(elements, repeat, bound, max_samples=None): r""" @@ -986,10 +1015,18 @@ def _some_tuples_sampling(elements, repeat, max_samples, n): TESTS:: sage: from sage.misc.misc import _some_tuples_sampling - sage: list(_some_tuples_sampling(range(3), 3, 2, 3)) - [(0, 1, 0), (1, 1, 1)] - sage: list(_some_tuples_sampling(range(20), None, 4, 20)) - [0, 6, 9, 3] + sage: l = list(_some_tuples_sampling(range(3), 3, 2, 3)) + sage: len(l) + 2 + sage: all(len(tup) == 3 for tup in l) + True + sage: all(el in range(3) for tup in l for el in tup) + True + sage: l = list(_some_tuples_sampling(range(20), None, 4, 20)) + sage: len(l) + 4 + sage: all(el in range(20) for el in l) + True """ from sage.rings.integer import Integer N = n if repeat is None else n**repeat diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index 3f1a78ff829..d0dfa6dffc1 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -40,7 +40,6 @@ from textwrap import dedent import zlib; comp = zlib import bz2; comp_other = bz2 -from .misc import SAGE_DB from .sage_unittest import TestSuite @@ -1124,6 +1123,7 @@ def db(name): The database directory is ``$HOME/.sage/db``. """ + from .misc import SAGE_DB return load('%s/%s'%(SAGE_DB,name)) @@ -1136,4 +1136,5 @@ def db_save(x, name=None): try: x.db(name) except AttributeError: + from .misc import SAGE_DB save(x, '%s/%s'%(SAGE_DB,name)) diff --git a/src/sage/misc/prandom.py b/src/sage/misc/prandom.py index 437baf8fa42..4ec77446cf1 100644 --- a/src/sage/misc/prandom.py +++ b/src/sage/misc/prandom.py @@ -66,6 +66,7 @@ def _pyrand(): EXAMPLES:: + sage: set_random_seed(0) sage: from sage.misc.prandom import _pyrand sage: _pyrand() <...random.Random object at 0x...> @@ -80,12 +81,12 @@ def getrandbits(k): EXAMPLES:: - sage: getrandbits(10) - 114L - sage: getrandbits(200) - 1251230322675596703523231194384285105081402591058406420468435L - sage: getrandbits(10) - 533L + sage: getrandbits(10) in range(2^10) + True + sage: getrandbits(200) in range(2^200) + True + sage: getrandbits(4) in range(2^4) + True """ return _pyrand().getrandbits(k) @@ -98,16 +99,25 @@ def randrange(start, stop=None, step=1): EXAMPLES:: - sage: randrange(0, 100, 11) - 11 - sage: randrange(5000, 5100) - 5051 - sage: [randrange(0, 2) for i in range(15)] - [0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1] - sage: randrange(0, 1000000, 1000) - 486000 - sage: randrange(-100, 10) - -56 + sage: s = randrange(0, 100, 11) + sage: 0 <= s < 100 + True + sage: s % 11 + 0 + + sage: 5000 <= randrange(5000, 5100) < 5100 + True + sage: s = [randrange(0, 2) for i in range(15)] + sage: all(t in [0, 1] for t in s) + True + + sage: s = randrange(0, 1000000, 1000) + sage: 0 <= s < 1000000 + True + sage: s % 1000 + 0 + sage: -100 <= randrange(-100, 10) < 10 + True """ return _pyrand().randrange(start, stop, step) @@ -117,10 +127,12 @@ def randint(a, b): EXAMPLES:: - sage: [randint(0, 2) for i in range(15)] + sage: s = [randint(0, 2) for i in range(15)]; s # random [0, 1, 0, 0, 1, 0, 2, 0, 2, 1, 2, 2, 0, 2, 2] - sage: randint(-100, 10) - -46 + sage: all(t in [0, 1, 2] for t in s) + True + sage: -100 <= randint(-100, 10) <= 10 + True """ return _pyrand().randint(a, b) @@ -130,8 +142,10 @@ def choice(seq): EXAMPLES:: - sage: [choice(list(primes(10, 100))) for i in range(5)] + sage: s = [choice(list(primes(10, 100))) for i in range(5)]; s # random [17, 47, 11, 31, 47] + sage: all(t in primes(10, 100) for t in s) + True """ return _pyrand().choice(seq) @@ -169,10 +183,21 @@ def sample(population, k): EXAMPLES:: - sage: sample(["Here", "I", "come", "to", "save", "the", "day"], 3) + sage: from sage.misc.misc import is_sublist + sage: l = ["Here", "I", "come", "to", "save", "the", "day"] + sage: s = sample(l, 3); s # random ['Here', 'to', 'day'] - sage: sample(range(2^30), 7) + sage: is_sublist(sorted(s), sorted(l)) + True + sage: len(s) + 3 + + sage: s = sample(range(2^30), 7); s # random [357009070, 558990255, 196187132, 752551188, 85926697, 954621491, 624802848] + sage: len(s) + 7 + sage: all(t in range(2^30) for t in s) + True """ return _pyrand().sample(population, k) @@ -182,8 +207,10 @@ def random(): EXAMPLES:: - sage: [random() for i in [1 .. 4]] + sage: sample = [random() for i in [1 .. 4]]; sample # random [0.111439293741037, 0.5143475134191677, 0.04468968524815642, 0.332490606442413] + sage: all(0.0 <= s <= 1.0 for s in sample) + True """ return _pyrand().random() @@ -195,12 +222,15 @@ def uniform(a, b): EXAMPLES:: - sage: uniform(0, 1) + sage: s = uniform(0, 1); s # random 0.111439293741037 - sage: uniform(e, pi) + sage: 0.0 <= s <= 1.0 + True + + sage: s = uniform(e, pi); s # random 0.5143475134191677*pi + 0.48565248658083227*e - sage: RR(_) - 2.93601069876846 + sage: bool(e <= s <= pi) + True """ return _pyrand().uniform(a, b) @@ -213,10 +243,15 @@ def betavariate(alpha, beta): EXAMPLES:: - sage: betavariate(0.1, 0.9) + sage: s = betavariate(0.1, 0.9); s # random 9.75087916621299e-9 - sage: betavariate(0.9, 0.1) + sage: 0.0 <= s <= 1.0 + True + + sage: s = betavariate(0.9, 0.1); s # random 0.941890400939253 + sage: 0.0 <= s <= 1.0 + True """ return _pyrand().betavariate(alpha, beta) @@ -230,12 +265,20 @@ def expovariate(lambd): EXAMPLES:: - sage: [expovariate(0.001) for i in range(3)] + sage: sample = [expovariate(0.001) for i in range(3)]; sample # random [118.152309288166, 722.261959038118, 45.7190543690470] - sage: [expovariate(1.0) for i in range(3)] + sage: all(s >= 0.0 for s in sample) + True + + sage: sample = [expovariate(1.0) for i in range(3)]; sample # random [0.404201816061304, 0.735220464997051, 0.201765578600627] - sage: [expovariate(1000) for i in range(3)] + sage: all(s >= 0.0 for s in sample) + True + + sage: sample = [expovariate(1000) for i in range(3)]; sample # random [0.0012068700332283973, 8.340929747302108e-05, 0.00219877067980605] + sage: all(s >= 0.0 for s in sample) + True """ return _pyrand().expovariate(lambd) @@ -247,10 +290,14 @@ def gammavariate(alpha, beta): EXAMPLES:: - sage: gammavariate(1.0, 3.0) + sage: sample = gammavariate(1.0, 3.0); sample # random 6.58282586130638 - sage: gammavariate(3.0, 1.0) + sage: sample > 0 + True + sage: sample = gammavariate(3.0, 1.0); sample # random 3.07801512341612 + sage: sample > 0 + True """ return _pyrand().gammavariate(alpha, beta) @@ -264,11 +311,11 @@ def gauss(mu, sigma): EXAMPLES:: - sage: [gauss(0, 1) for i in range(3)] + sage: [gauss(0, 1) for i in range(3)] # random [0.9191011757657915, 0.7744526756246484, 0.8638996866800877] - sage: [gauss(0, 100) for i in range(3)] + sage: [gauss(0, 100) for i in range(3)] # random [24.916051749154448, -62.99272061579273, -8.1993122536718...] - sage: [gauss(1000, 10) for i in range(3)] + sage: [gauss(1000, 10) for i in range(3)] # random [998.7590700045661, 996.1087338511692, 1010.1256817458031] """ return _pyrand().gauss(mu, sigma) @@ -283,7 +330,7 @@ def lognormvariate(mu, sigma): EXAMPLES:: - sage: [lognormvariate(100, 10) for i in range(3)] + sage: [lognormvariate(100, 10) for i in range(3)] # random [2.9410355688290246e+37, 2.2257548162070125e+38, 4.142299451717446e+43] """ return _pyrand().lognormvariate(mu, sigma) @@ -296,11 +343,11 @@ def normalvariate(mu, sigma): EXAMPLES:: - sage: [normalvariate(0, 1) for i in range(3)] + sage: [normalvariate(0, 1) for i in range(3)] # random [-1.372558980559407, -1.1701670364898928, 0.04324100555110143] - sage: [normalvariate(0, 100) for i in range(3)] + sage: [normalvariate(0, 100) for i in range(3)] # random [37.45695875041769, 159.6347743233298, 124.1029321124009] - sage: [normalvariate(1000, 10) for i in range(3)] + sage: [normalvariate(1000, 10) for i in range(3)] # random [1008.5303090383741, 989.8624892644895, 985.7728921150242] """ return _pyrand().normalvariate(mu, sigma) @@ -316,8 +363,10 @@ def vonmisesvariate(mu, kappa): EXAMPLES:: - sage: [vonmisesvariate(1.0r, 3.0r) for i in range(1, 5)] # abs tol 1e-12 + sage: sample = [vonmisesvariate(1.0r, 3.0r) for i in range(1, 5)]; sample # random [0.898328639355427, 0.6718030007041281, 2.0308777524813393, 1.714325253725145] + sage: all(s >= 0.0 for s in sample) + True """ return _pyrand().vonmisesvariate(mu, kappa) @@ -327,8 +376,10 @@ def paretovariate(alpha): EXAMPLES:: - sage: [paretovariate(3) for i in range(1, 5)] + sage: sample = [paretovariate(3) for i in range(1, 5)]; sample # random [1.0401699394233033, 1.2722080162636495, 1.0153564009379579, 1.1442323078983077] + sage: all(s >= 1.0 for s in sample) + True """ return _pyrand().paretovariate(alpha) @@ -340,7 +391,9 @@ def weibullvariate(alpha, beta): EXAMPLES:: - sage: [weibullvariate(1, 3) for i in range(1, 5)] + sage: sample = [weibullvariate(1, 3) for i in range(1, 5)]; sample # random [0.49069775546342537, 0.8972185564611213, 0.357573846531942, 0.739377255516847] + sage: all(s >= 0.0 for s in sample) + True """ return _pyrand().weibullvariate(alpha, beta) diff --git a/src/sage/misc/randstate.pyx b/src/sage/misc/randstate.pyx index 3a845fbabbf..8b422fe6d9c 100644 --- a/src/sage/misc/randstate.pyx +++ b/src/sage/misc/randstate.pyx @@ -449,6 +449,7 @@ cpdef randstate current_randstate(): EXAMPLES:: + sage: set_random_seed(0) sage: current_randstate() sage: current_randstate().python_random().random() @@ -551,6 +552,7 @@ cdef class randstate: EXAMPLES:: + sage: set_random_seed(0) sage: from sage.misc.randstate import randstate sage: r = randstate(314159) sage: r.seed() diff --git a/src/sage/misc/sage_unittest.py b/src/sage/misc/sage_unittest.py index ebe604cb944..18827dfd118 100644 --- a/src/sage/misc/sage_unittest.py +++ b/src/sage/misc/sage_unittest.py @@ -548,10 +548,10 @@ def some_elements(self, S=None, repeat=None): You can use ``max_samples`` to sample at random, instead of in order:: sage: tester = InstanceTester(ZZ, elements = srange(8), max_samples = 4) - sage: list(tester.some_elements()) - [0, 3, 7, 1] - sage: list(tester.some_elements(repeat=2)) - [(1, 4), (3, 1), (4, 5), (5, 0)] + sage: all(t in srange(8) for t in tester.some_elements()) + True + sage: all(s in srange(8) and t in srange(8) for s,t in tester.some_elements(repeat=2)) + True Test for :trac:`15919`, :trac:`16244`:: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index a37edbbffc3..ee4f3dafcda 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2242,7 +2242,7 @@ def sage_getsourcelines(obj): sage: cachedfib = cached_function(fibonacci) sage: sage_getsourcelines(cachedfib)[0][0] - 'def fibonacci(n, algorithm="pari") -> Integral:\n' + 'def fibonacci(n, algorithm="pari") -> Integer:\n' sage: sage_getsourcelines(type(cachedfib))[0][0] 'cdef class CachedFunction(object):\n' diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 01749328099..d1206fd3539 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -1961,7 +1961,7 @@ def atkin_lehner_eigenvalue(self, d=None, normalization='analytic', embedding=No sage: K. = CyclotomicField(4) sage: f = Newforms(DirichletGroup(30, QQ).1, 2, K)[0] - sage: f.atkin_lehner_eigenvalue(embedding=K.embeddings(QQbar)[0]) + sage: f.atkin_lehner_eigenvalue(embedding=K.embeddings(QQbar)[1]) -0.8944271909999159? - 0.4472135954999580?*I Check that :trac:`24086` is fixed:: diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index 00be21d1781..3bb80562667 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -195,7 +195,7 @@ def __init__(self, group, weight, sign, base_ring, def new_submodule(self, p=None): r""" - Returns the new or `p`-new submodule of this modular symbols ambient space. + Return the new or `p`-new submodule of this modular symbols ambient space. INPUT: @@ -510,7 +510,7 @@ def change_ring(self, R): def _action_on_modular_symbols(self, g): r""" - Returns the matrix of the action of a 2x2 matrix on this space. + Return the matrix of the action of a 2x2 matrix on this space. INPUT: @@ -675,7 +675,7 @@ def _modular_symbol_0_to_alpha(self, alpha, i=0): if (self.weight()-2-i == 0): p2 = R(one) poly = (p1**i) * (p2**(self.weight()-2-i)) - for s in range(0,self.weight()-1): ## k-2+1 = k-1 + for s in range(0,self.weight()-1): # k-2+1 = k-1 a += poly[s] * self.manin_symbol((s,z,w), check=False) else: for k in range(1,len(c)): @@ -974,12 +974,12 @@ def _compute_hecke_matrix_prime(self, p, rows=None): if isinstance(rows, list): rows = tuple(rows) try: - return self._hecke_matrices[(p,rows)] + return self._hecke_matrices[(p, rows)] except AttributeError: self._hecke_matrices = {} except KeyError: pass - tm = verbose("Computing Hecke operator T_%s"%p) + tm = verbose("Computing Hecke operator T_%s" % p) if is_prime(p): H = heilbronn.HeilbronnCremona(p) @@ -987,12 +987,12 @@ def _compute_hecke_matrix_prime(self, p, rows=None): H = heilbronn.HeilbronnMerel(p) B = self.manin_basis() - if not rows is None: + if rows is not None: B = [B[i] for i in rows] mod2term = self._mod2term R = self.manin_gens_to_basis() K = self.base_ring() - W = R.new_matrix(nrows=len(B), ncols = R.nrows()) + W = R.new_matrix(nrows=len(B), ncols=R.nrows()) syms = self.manin_symbols() j = 0 for i in B: @@ -1150,7 +1150,7 @@ def _latex_(self): def _matrix_of_operator_on_modular_symbols(self, codomain, R): r""" - Returns the matrix of a modular symbols operator. + Return the matrix of a modular symbols operator. .. note:: @@ -1411,7 +1411,7 @@ def cuspidal_submodule(self): S._is_simple = True if self.base_ring().characteristic() == 0: d = self._cuspidal_submodule_dimension_formula() - if not d is None: + if d is not None: assert d == S.dimension(), "According to dimension formulas the cuspidal subspace of \"%s\" has dimension %s; however, computing it using modular symbols we obtained %s, so there is a bug (please report!)."%(self, d, S.dimension()) self.__cuspidal_submodule = S return self.__cuspidal_submodule @@ -1523,7 +1523,7 @@ def _degeneracy_lowering_matrix(self, M, t): def rank(self): """ - Returns the rank of this modular symbols ambient space. + Return the rank of this modular symbols ambient space. OUTPUT: @@ -1645,7 +1645,7 @@ def dual_star_involution_matrix(self): def factorization(self): r""" - Returns a list of pairs `(S,e)` where `S` is spaces + Return a list of pairs `(S,e)` where `S` is spaces of modular symbols and self is isomorphic to the direct sum of the `S^e` as a module over the *anemic* Hecke algebra adjoin the star involution. The cuspidal `S` are all simple, but @@ -1775,7 +1775,8 @@ def factorization(self): # Now actually run through the divisor levels, taking only the ones with that are # a multiple of the conductor. for d in reversed(divisors(self.level())): - if d%cond != 0: continue + if d % cond: + continue n = number_of_divisors(self.level() // d) M = self.modular_symbols_of_level(d) N = M.new_submodule().cuspidal_submodule().decomposition() @@ -1808,7 +1809,7 @@ def factorization(self): def is_cuspidal(self): r""" - Returns True if this space is cuspidal, else False. + Return True if this space is cuspidal, else False. EXAMPLES:: @@ -1831,7 +1832,7 @@ def is_cuspidal(self): def is_eisenstein(self): r""" - Returns True if this space is Eisenstein, else False. + Return True if this space is Eisenstein, else False. EXAMPLES:: @@ -1929,7 +1930,7 @@ def modular_symbols_of_level(self, G): def modular_symbols_of_sign(self, sign): r""" - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, level, etc.) as this space except with given sign. @@ -1961,7 +1962,7 @@ def modular_symbols_of_sign(self, sign): def modular_symbols_of_weight(self, k): r""" - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, sign, etc.) as this space except with weight `k`. @@ -2387,27 +2388,31 @@ class of cuspidal newforms in this ambient space. # i is bad. bad = True continue - if bad: continue + if bad: + continue # It turns out that i is not bad. nz = i break if nz is not None: R = self.hecke_images(nz, v) - return [(R*m, D[i].dual_eigenvector(names=names[i], lift=False, nz=nz)) for i, m in enumerate(B)] - else: - # No single i works, so we do something less uniform. - ans = [] - cache = {} - for i in range(len(D)): - nz = D[i]._eigen_nonzero() - if nz in cache: - R = cache[nz] - else: - R = self.hecke_images(nz, v) - cache[nz] = R - ans.append((R*B[i], D[i].dual_eigenvector(names=names[i], lift=False, nz=nz))) - return ans + return [(R * m, D[i].dual_eigenvector(names=names[i], + lift=False, nz=nz)) + for i, m in enumerate(B)] + + # No single i works, so we do something less uniform. + ans = [] + cache = {} + for i in range(len(D)): + nz = D[i]._eigen_nonzero() + if nz in cache: + R = cache[nz] + else: + R = self.hecke_images(nz, v) + cache[nz] = R + ans.append((R * B[i], D[i].dual_eigenvector(names=names[i], + lift=False, nz=nz))) + return ans class ModularSymbolsAmbient_wtk_g0(ModularSymbolsAmbient): @@ -2478,7 +2483,7 @@ def __init__(self, N, k, sign, F, custom_init=None, category=None): N = int(N) k = int(k) sign = int(sign) - if not sign in [-1,0,1]: + if sign not in [-1, 0, 1]: raise TypeError("sign must be an int in [-1,0,1]") ModularSymbolsAmbient.__init__(self, weight=k, group=arithgroup.Gamma0(N), @@ -2500,8 +2505,9 @@ def _dimension_formula(self): """ if self.base_ring().characteristic() == 0: k, sign = self.weight(), self.sign() - if sign != 0: return None - if k%2 == 1: + if sign != 0: + return None + if k % 2: return 0 elif k > 2: return 2*self.group().dimension_cusp_forms(k) + self.group().ncusps() @@ -2775,7 +2781,8 @@ def _dimension_formula(self): 32 """ if self.base_ring().characteristic() == 0: - if self.sign() != 0: return None + if self.sign() != 0: + return None return 2*self.group().dimension_cusp_forms(2) + self.group().ncusps() - 1 else: raise NotImplementedError @@ -2837,17 +2844,17 @@ def _compute_hecke_matrix_prime(self, p, rows=None): if isinstance(rows, list): rows = tuple(rows) try: - return self._hecke_matrices[(p,rows)] + return self._hecke_matrices[(p, rows)] except AttributeError: self._hecke_matrices = {} except KeyError: pass - tm = verbose("Computing Hecke operator T_%s"%p) + tm = verbose("Computing Hecke operator T_%s" % p) H = heilbronn.HeilbronnCremona(p) - ##H = heilbronn.HeilbronnMerel(p) + # H = heilbronn.HeilbronnMerel(p) B = self.manin_basis() - if not rows is None: + if rows is not None: B = [B[i] for i in rows] N = self.level() @@ -3044,7 +3051,6 @@ def __init__(self, level, weight, sign, F, custom_init=None, category=None): custom_init=custom_init, category=category) - def _dimension_formula(self): r""" Return the dimension of this space using the formula. @@ -3060,9 +3066,10 @@ def _dimension_formula(self): if self.base_ring().characteristic() != 0: raise NotImplementedError level, weight, sign = self.level(), self.weight(), self.sign() - if sign != 0: return None + if sign != 0: + return None d = 2*self.group().dimension_cusp_forms(weight) + self.group().ncusps() - if level == 1 and weight%2 == 1: + if level == 1 and weight % 2: return 0 if weight == 2: return d - 1 @@ -3658,7 +3665,7 @@ def manin_symbols(self): def modular_symbols_of_level(self, N): r""" - Returns a space of modular symbols with the same parameters as + Return a space of modular symbols with the same parameters as this space except with level `N`. INPUT: @@ -3689,7 +3696,7 @@ def modular_symbols_of_level(self, N): def modular_symbols_of_sign(self, sign): r""" - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, level, etc.) as this space except with given sign. @@ -3720,7 +3727,7 @@ def modular_symbols_of_sign(self, sign): def modular_symbols_of_weight(self, k): r""" - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, sign, etc.) as this space except with weight `k`. diff --git a/src/sage/modular/modsym/boundary.py b/src/sage/modular/modsym/boundary.py index c29091cf4a4..cda88865867 100644 --- a/src/sage/modular/modsym/boundary.py +++ b/src/sage/modular/modsym/boundary.py @@ -644,15 +644,15 @@ def __init__(self, level, weight, sign, F): level = int(level) sign = int(sign) weight = int(weight) - if not sign in [-1, 0, 1]: + if sign not in [-1, 0, 1]: raise ArithmeticError("sign must be an int in [-1,0,1]") if level <= 0: raise ArithmeticError("level must be positive") BoundarySpace.__init__(self, - weight = weight, - group = arithgroup.Gamma0(level), - sign = sign, - base_ring = F) + weight=weight, + group=arithgroup.Gamma0(level), + sign=sign, + base_ring=F) def _repr_(self): """ @@ -776,7 +776,6 @@ def __init__(self, level, weight, sign, F): - ``F`` - base ring - EXAMPLES:: sage: from sage.modular.modsym.boundary import BoundarySpace_wtk_g1 @@ -787,16 +786,16 @@ def __init__(self, level, weight, sign, F): """ level = int(level) sign = int(sign) - if not sign in [-1,0,1]: + if sign not in [-1, 0, 1]: raise ArithmeticError("sign must be an int in [-1,0,1]") if level <= 0: raise ArithmeticError("level must be positive") BoundarySpace.__init__(self, - weight = weight, - group = arithgroup.Gamma1(level), - sign = sign, - base_ring = F) + weight=weight, + group=arithgroup.Gamma1(level), + sign=sign, + base_ring=F) def _repr_(self): """ @@ -828,7 +827,7 @@ def _is_equiv(self, c1, c2): def _cusp_index(self, cusp): """ - Returns a pair (i, t), where i is the index of the first cusp in + Return a pair (i, t), where i is the index of the first cusp in self._known_cusps() which is equivalent to cusp, and t is 1 or -1 as cusp is Gamma1-equivalent to plus or minus self._known_cusps()[i]. If cusp is not equivalent to any known @@ -999,14 +998,14 @@ def __init__(self, group, weight, sign, F): Codomain: Boundary Modular Symbols space for Congruence Subgroup Gamma_H(8) ... """ sign = int(sign) - if not sign in [-1, 0, 1]: + if sign not in [-1, 0, 1]: raise ArithmeticError("sign must be an int in [-1,0,1]") BoundarySpace.__init__(self, - weight = weight, - group = group, - sign = sign, - base_ring = F) + weight=weight, + group=group, + sign=sign, + base_ring=F) def _repr_(self): """ @@ -1151,14 +1150,18 @@ def _coerce_cusp(self, c): sign = self.sign() i, eps = self._cusp_index(c) if i != -1: - if i == -2: return self(0) - else: return BoundarySpaceElement(self, {i : eps**k}) + if i == -2: + return self(0) + else: + return BoundarySpaceElement(self, {i : eps**k}) if sign != 0: i2, eps = self._cusp_index(-c) if i2 != -1: - if i2 == -2: return self(0) - else: return BoundarySpaceElement(self, {i2:sign*(eps**k)}) + if i2 == -2: + return self(0) + else: + return BoundarySpaceElement(self, {i2:sign*(eps**k)}) # found a new cusp class g = self._known_gens @@ -1240,16 +1243,16 @@ def __init__(self, eps, weight, sign=0): level = eps.modulus() sign = int(sign) self.__eps = eps - if not sign in [-1,0,1]: + if sign not in [-1, 0, 1]: raise ArithmeticError("sign must be an int in [-1,0,1]") if level <= 0: raise ArithmeticError("level must be positive") BoundarySpace.__init__(self, - weight = weight, - group = arithgroup.Gamma1(level), - sign = sign, - base_ring = eps.base_ring(), - character = eps) + weight=weight, + group=arithgroup.Gamma1(level), + sign=sign, + base_ring=eps.base_ring(), + character=eps) def _repr_(self): """ @@ -1371,13 +1374,15 @@ def _coerce_cusp(self, c): if i == -2: return self(0) else: - return BoundarySpaceElement(self, {i : eps}) + return BoundarySpaceElement(self, {i: eps}) if sign != 0: i2, eps = self._cusp_index(-c) if i2 != -1: - if i2 == -2: return self(0) - else: return BoundarySpaceElement(self, {i2:sign*eps}) + if i2 == -2: + return self(0) + else: + return BoundarySpaceElement(self, {i2: sign * eps}) # found a new cusp class g = self._known_gens diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index ecd7f085ff7..7f69229e4b0 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -271,7 +271,7 @@ def list(self): def manin_symbol_rep(self): """ - Returns a representation of self as a formal sum of Manin symbols. + Return a representation of self as a formal sum of Manin symbols. EXAMPLES:: @@ -298,8 +298,7 @@ def manin_symbol_rep(self): def modular_symbol_rep(self): """ - Returns a representation of self as a formal sum of modular - symbols. + Return a representation of ``self`` as a formal sum of modular symbols. EXAMPLES:: diff --git a/src/sage/modular/modsym/g1list.py b/src/sage/modular/modsym/g1list.py index 3fe8e9d3f9e..8d97f3fab11 100644 --- a/src/sage/modular/modsym/g1list.py +++ b/src/sage/modular/modsym/g1list.py @@ -127,7 +127,7 @@ def normalize(self, u, v): sage: L = sage.modular.modsym.g1list.G1list(4); L.normalize(6, 2) # nonsense! (2, 2) """ - return u % self.__N, v % self.__N + return u % self.__N, v % self.__N class _G1list_old_pickle(G1list): diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index d63d0559802..7afbb1be6ae 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -345,8 +345,9 @@ def ModularSymbols(group = 1, key = canonical_parameters(group, weight, sign, base_ring) if use_cache and key in _cache: - M = _cache[key]() - if not (M is None): return M + M = _cache[key]() + if M is not None: + return M (group, weight, sign, base_ring) = key diff --git a/src/sage/modular/modsym/modular_symbols.py b/src/sage/modular/modsym/modular_symbols.py index 85a346d50fa..ae88f74c911 100644 --- a/src/sage/modular/modsym/modular_symbols.py +++ b/src/sage/modular/modsym/modular_symbols.py @@ -363,8 +363,9 @@ def __manin_symbol_rep(self, alpha): def manin_symbol_rep(self): """ - Returns a representation of self as a formal sum of Manin symbols. - (The result is not cached.) + Return a representation of ``self`` as a formal sum of Manin symbols. + + The result is not cached. EXAMPLES:: diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 38ffb2c481c..e467d89bd17 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -166,7 +166,7 @@ def p1_normalize_int(N, u, v): def p1list_int(int N): r""" - Returns a list of the normalized elements of + Return a list of the normalized elements of `\mathbb{P}^1(\ZZ/N\ZZ)`. INPUT: @@ -421,7 +421,7 @@ def p1_normalize_llong(N, u, v): def p1list_llong(int N): r""" - Returns a list of the normalized elements of + Return a list of the normalized elements of `\mathbb{P}^1(\ZZ/N\ZZ)`, as a plain list of 2-tuples. @@ -482,7 +482,7 @@ def p1list_llong(int N): def p1list(N): """ - Returns the elements of the projective line modulo `N`, + Return the elements of the projective line modulo `N`, `\mathbb{P}^1(\ZZ/N\ZZ)`, as a plain list of 2-tuples. INPUT: @@ -969,7 +969,7 @@ cdef class P1List(object): cpdef index(self, int u, int v): r""" - Returns the index of the class of `(u,v)` in the fixed list + Return the index of the class of `(u,v)` in the fixed list of representatives of `\mathbb{P}^1(\ZZ/N\ZZ)`. @@ -1056,7 +1056,7 @@ cdef class P1List(object): def index_of_normalized_pair(self, int u, int v): r""" - Returns the index of the class of `(u,v)` in the fixed list + Return the index of the class of `(u,v)` in the fixed list of representatives of `\mathbb{P}^1(\ZZ/N\ZZ)`. @@ -1102,7 +1102,7 @@ cdef class P1List(object): def normalize(self, int u, int v): r""" - Returns a normalised element of `\mathbb{P}^1(\ZZ/N\ZZ)`. + Return a normalised element of `\mathbb{P}^1(\ZZ/N\ZZ)`. INPUT: @@ -1134,7 +1134,7 @@ cdef class P1List(object): def normalize_with_scalar(self, int u, int v): r""" - Returns a normalised element of `\mathbb{P}^1(\ZZ/N\ZZ)`, together with + Return a normalised element of `\mathbb{P}^1(\ZZ/N\ZZ)`, together with the normalizing scalar. INPUT: @@ -1163,11 +1163,11 @@ cdef class P1List(object): """ cdef int uu, vv, ss self.__normalize(self.__N, u, v, &uu, &vv, &ss, 1) - return (uu,vv,ss) + return (uu, vv, ss) def N(self): """ - Returns the level or modulus of this P1List. + Return the level or modulus of this P1List. EXAMPLES:: diff --git a/src/sage/modular/modsym/p1list_nf.py b/src/sage/modular/modsym/p1list_nf.py index 7a91d353e3c..709a91035a7 100644 --- a/src/sage/modular/modsym/p1list_nf.py +++ b/src/sage/modular/modsym/p1list_nf.py @@ -211,7 +211,7 @@ def __init__(self, N, c, d=None, check=True): def __repr__(self): """ - Returns the string representation of this MSymbol. + Return the string representation of this MSymbol. EXAMPLES:: @@ -261,7 +261,7 @@ def __richcmp__(self, other, op): def N(self): """ - Returns the level or modulus of this MSymbol. + Return the level or modulus of this MSymbol. EXAMPLES:: @@ -275,7 +275,7 @@ def N(self): def tuple(self): """ - Returns the MSymbol as a list (c, d). + Return the MSymbol as a list (c, d). EXAMPLES:: @@ -312,7 +312,7 @@ def __getitem__(self, n): def __get_c(self): """ - Returns the first coefficient of the M-symbol. + Return the first coefficient of the M-symbol. EXAMPLES:: @@ -327,7 +327,7 @@ def __get_c(self): def __get_d(self): """ - Returns the second coefficient of the M-symbol. + Return the second coefficient of the M-symbol. EXAMPLES:: @@ -546,7 +546,7 @@ def __getitem__(self, n): def __len__(self): """ - Returns the length of this P1NFList. + Return the length of this P1NFList. EXAMPLES:: @@ -560,7 +560,7 @@ def __len__(self): def __repr__(self): """ - Returns the string representation of this P1NFList. + Return the string representation of this P1NFList. EXAMPLES:: @@ -574,7 +574,7 @@ def __repr__(self): def list(self): """ - Returns the underlying list of this P1NFList object. + Return the underlying list of this P1NFList object. EXAMPLES:: @@ -645,7 +645,7 @@ def normalize(self, c, d=None, with_scalar=False): def N(self): """ - Returns the level or modulus of this P1NFList. + Return the level or modulus of this P1NFList. EXAMPLES:: @@ -771,13 +771,14 @@ def index_of_normalized_pair(self, c, d=None): """ if d is None: try: - c = MSymbol(self.__N, c) # check that c is an MSymbol - except ValueError: # catch special case of wrong level + c = MSymbol(self.__N, c) # check that c is an MSymbol + except ValueError: # catch special case of wrong level raise ValueError("The MSymbol is of a different level") t, i = search(self.__list, c) else: t, i = search(self.__list, MSymbol(self.__N, c, d)) - if t: return i + if t: + return i return False def lift_to_sl2_Ok(self, i): @@ -975,7 +976,7 @@ def apply_J_epsilon(self, i, e1, e2=1): def p1NFlist(N): """ - Returns a list of the normalized elements of `\\mathbb{P}^1(R/N)`, where + Return a list of the normalized elements of `\\mathbb{P}^1(R/N)`, where `N` is an integral ideal. INPUT: @@ -1124,9 +1125,10 @@ def lift_to_sl2_Ok(N, c, d): a = (1-B)/d return [a, b, c, d] + def make_coprime(N, c, d): """ - Returns (c, d') so d' is congruent to d modulo N, and such that c and d' are + Return (c, d') so d' is congruent to d modulo N, and such that c and d' are coprime ( + = R). INPUT: diff --git a/src/sage/modular/modsym/subspace.py b/src/sage/modular/modsym/subspace.py index 500067875ce..e27467a3816 100644 --- a/src/sage/modular/modsym/subspace.py +++ b/src/sage/modular/modsym/subspace.py @@ -221,7 +221,7 @@ def eisenstein_subspace(self): def factorization(self): """ - Returns a list of pairs `(S,e)` where `S` is simple + Return a list of pairs `(S,e)` where `S` is simple spaces of modular symbols and self is isomorphic to the direct sum of the `S^e` as a module over the *anemic* Hecke algebra adjoin the star involution. diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 5ccadc90da9..4714ae6c514 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -359,7 +359,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): Vector space of dimension 10 over Complex Double Field sage: parent(vector(RR, x)) Vector space of dimension 10 over Real Field with 53 bits of precision - sage: v = numpy.random.randn(10) * numpy.complex(0,1) + sage: v = numpy.random.randn(10) * complex(0,1) sage: w = vector(v) sage: parent(w) Vector space of dimension 10 over Complex Double Field @@ -454,11 +454,11 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): sage: v.is_immutable() True sage: import numpy as np - sage: w = np.array([1, 2, pi], np.float) + sage: w = np.array([1, 2, pi], float) sage: v = vector(w, immutable=True) sage: v.is_immutable() True - sage: w = np.array([i, 2, 3], np.complex) + sage: w = np.array([i, 2, 3], complex) sage: v = vector(w, immutable=True) sage: v.is_immutable() True diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 5609de574e4..5639c8886ec 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -416,9 +416,12 @@ cdef class GenericBackend: EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver - sage: p.add_constraint(p[0] + p[1], max = 10) # optional - Nonexistent_LP_solver - sage: p.remove_constraints([0]) # optional - Nonexistent_LP_solver + sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: p.add_variables(2) # optional - Nonexistent_LP_solver + 1 + sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6) # optional - Nonexistent_LP_solver + sage: p.add_linear_constraint([(0, 3), (1, 2)], None, 6) # optional - Nonexistent_LP_solver + sage: p.remove_constraints([0, 1]) # optional - Nonexistent_LP_solver """ if type(constraints) == int: self.remove_constraint(constraints) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index dda596ce0eb..fba5a931d2e 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -93,8 +93,7 @@ def nodes(degree,prec): else: nodes = [] n = degree - upto = n//2+1 - for j in xrange(1,upto): + for j in xrange(1, n // 2 + 1): r = R(math.cos(math.pi*(j-0.25)/(n+0.5))) while True: t1,t2=ONE,ZERO diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index fb5a5c77fcb..b0f17fd26a5 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -46,10 +46,10 @@ sage: c = (10, 5) sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P - LP problem (use typeset mode to see details) + LP problem (use 'view(...)' or '%display typeset' for details) It is recommended to copy-paste such examples into your own worksheet, so that -you can run these commands with typeset mode on and get +you can run these commands with typeset mode on (``%display typeset``) and get .. MATH:: @@ -72,7 +72,7 @@ `, which can be created either directly :: sage: InteractiveLPProblemStandardForm(A, b, c, ["C", "B"]) - LP problem (use typeset mode to see details) + LP problem (use ...) or from an already constructed problem of "general type":: @@ -95,7 +95,7 @@ sage: D = P.initial_dictionary() sage: D - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) Using typeset mode as recommended, you'll see @@ -804,9 +804,9 @@ def _repr_(self): sage: c = (10, 5) sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: print(P._repr_()) - LP problem (use typeset mode to see details) + LP problem (use ...) """ - return "LP problem (use typeset mode to see details)" + return "LP problem (use 'view(...)' or '%display typeset' for details)" def _solution(self, x): r""" @@ -2779,12 +2779,12 @@ def _repr_(self): sage: P = InteractiveLPProblemStandardForm(A, b, c) sage: D = P.initial_dictionary() sage: print(D._repr_()) - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) sage: D = P.revised_dictionary() sage: print(D._repr_()) - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) """ - return "LP problem dictionary (use typeset mode to see details)" + return "LP problem dictionary (use 'view(...)' or '%display typeset' for details)" @abstract_method def add_row(self, nonbasic_coefficients, constant, basic_variable=None): @@ -3825,7 +3825,7 @@ class LPDictionary(LPAbstractDictionary): sage: P = InteractiveLPProblemStandardForm(A, b, c) sage: D = P.initial_dictionary() sage: D - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) But if you want you can create a dictionary without starting with an LP problem, here is construction of the same dictionary as above:: @@ -3959,10 +3959,11 @@ def _latex_(self): # Highlight the entering variable column e = 2 * tuple(N).index(self._entering) + 4 for i, lin in enumerate(lines): - lin = lin.split("&") + lin = lin[:-2].split("&") + # Trac #30809: The MathJaX version of \color takes an argument if len(lin) > 1: - lin[e] = r"\color{green}" + lin[e] - lines[i] = "&".join(lin) + lin[e] = r"\color{green}{%s}" % (lin[e],) + lines[i] = "&".join(lin) + r"\\" if self._leaving is not None: # Highlight the leaving variable row l = tuple(B).index(self._leaving) @@ -3970,11 +3971,11 @@ def _latex_(self): l += 3 if style() == "Vanderbei": l += 4 - lin = lines[l].split("&") + lin = lines[l][:-2].split("&") for i, term in enumerate(lin): - lin[i] = r"\color{red}" + term - lin = "&".join(lin) - lin = lin.replace(r"\color{red}\color{green}", r"\color{blue}") + lin[i] = r"\color{red}{%s}" % (term,) + lin = "&".join(lin) + r"\\" + lin = lin.replace(r"\color{red}{\color{green}{", r"\color{blue}{{") lines[l] = lin return "\n".join(lines) @@ -4311,7 +4312,7 @@ def random_dictionary(m, n, bound=5, special_probability=0.2): sage: from sage.numerical.interactive_simplex_method \ ....: import random_dictionary sage: random_dictionary(3, 4) - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) """ A = random_matrix(ZZ, m, n, x=-bound, y=bound).change_ring(QQ) if special_probability < random(): @@ -4411,7 +4412,7 @@ class LPRevisedDictionary(LPAbstractDictionary): sage: D.basic_variables() (x1, x2) sage: D - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) The same dictionary can be constructed through the problem:: @@ -4531,18 +4532,18 @@ def _latex_(self): \begin{array}{l|r|rr||r||r|r|r} x_B & c_B & & \mspace{-16mu} B^{-1} & y & B^{-1} b & B^{-1} A_{x_{1}} & \hbox{Ratio} \\ \hline - \color{red} x_{3} & \color{red} 0 & \color{red} 1 & \color{red} 0 & 0 & \color{red} 1000 & \color{red} 1 & \color{red} 1000 \\ + \color{red}{ x_{3} } & \color{red}{ 0 } & \color{red}{ 1 } & \color{red}{ 0 } & 0 & \color{red}{ 1000 } & \color{red}{ 1 } & \color{red}{ 1000 } \\ x_{4} & 0 & 0 & 1 & 0 & 1500 & 3 & 500 \\ \end{array}\\ \\ \begin{array}{r|rr} - x_N & \color{green} x_{1} & x_{2} \\ + x_N & \color{green}{ x_{1} } & x_{2} \\ \hline - c_N^T & \color{green} 10 & 5 \\ + c_N^T & \color{green}{ 10 } & 5 \\ \hline - y^T A_N & \color{green} 0 & 0 \\ + y^T A_N & \color{green}{ 0 } & 0 \\ \hline - c_N^T - y^T A_N & \color{green} 10 & 5 \\ + c_N^T - y^T A_N & \color{green}{ 10 } & 5 \\ \end{array} \end{array} """ @@ -4593,7 +4594,8 @@ def _latex_(self): for j, t in enumerate(terms): if j == m + 2: continue - terms[j] = r"\color{red} " + t + # Trac #30809: The MathJaX version of \color takes an argument + terms[j] = r"\color{red}{" + t + "}" lines.append(" & ".join(terms) + r" \\") lines.append(r"\end{array}") top = "\n".join(lines) @@ -4601,7 +4603,7 @@ def _latex_(self): def make_line(header, terms): terms = [latex(_) for _ in terms] if entering is not None: - terms[k] = r"\color{green} " + terms[k] + terms[k] = r"\color{green}{" + terms[k] + "}" lines.append(" & ".join([header] + terms) + r" \\") lines = [] @@ -5120,7 +5122,7 @@ def dictionary(self): sage: P = InteractiveLPProblemStandardForm(A, b, c) sage: D = P.revised_dictionary() sage: D.dictionary() - LP problem dictionary (use typeset mode to see details) + LP problem dictionary (use ...) """ D = LPDictionary(self.B_inverse() * self.A_N(), self.constant_terms(), diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index 82fb733f610..6d0c0604c37 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -138,6 +138,7 @@ cpdef is_LinearFunction(x): """ return isinstance(x, LinearFunction) + def is_LinearConstraint(x): """ Test whether ``x`` is a linear constraint @@ -648,7 +649,7 @@ cdef class LinearFunctionsParent_class(Parent): sage: LF.gen(23) x_23 """ - return LinearFunction(self, {i:1}) + return LinearFunction(self, {i: 1}) def _repr_(self): """ @@ -659,11 +660,11 @@ cdef class LinearFunctionsParent_class(Parent): sage: MixedIntegerLinearProgram().linear_functions_parent() Linear functions over Real Double Field """ - return 'Linear functions over '+str(self.base_ring()) + return 'Linear functions over ' + str(self.base_ring()) cpdef _element_constructor_(self, x): """ - Construt a :class:`LinearFunction` from ``x``. + Construct a :class:`LinearFunction` from ``x``. EXAMPLES:: @@ -932,11 +933,11 @@ cdef class LinearFunction(LinearFunctionOrConstraint): -1*x_0 + 8*x_3 """ P = self.parent() - return P(dict([(id,-coeff) for (id, coeff) in self._f.iteritems()])) + return P({id: -coeff for id, coeff in self._f.iteritems()}) cpdef _sub_(self, b): r""" - Defining the - operator (substraction). + Defining the - operator (subtraction). EXAMPLES:: @@ -948,8 +949,8 @@ cdef class LinearFunction(LinearFunctionOrConstraint): -16 + x_0 - 5*x_2 - 10*x_3 """ e = dict(self._f) - for (id,coeff) in b.dict().iteritems(): - e[id] = self._f.get(id,0) - coeff + for id, coeff in b.dict().iteritems(): + e[id] = self._f.get(id, 0) - coeff P = self.parent() return P(e) @@ -1263,11 +1264,11 @@ cdef class LinearConstraintsParent_class(Parent): sage: MixedIntegerLinearProgram().linear_constraints_parent() Linear constraints over Real Double Field """ - return 'Linear constraints over '+str(self.linear_functions_parent().base_ring()) + return 'Linear constraints over ' + str(self.linear_functions_parent().base_ring()) cpdef _element_constructor_(self, left, right=None, equality=False): """ - Construt a :class:`LinearConstraint`. + Construct a :class:`LinearConstraint`. INPUT: @@ -1633,10 +1634,10 @@ cdef class LinearConstraint(LinearFunctionOrConstraint): sage: LC(b[3]) trivial constraint starting with x_0 """ - comparator = ( ' == ' if self.equality else ' <= ' ) + comparator = (' == ' if self.equality else ' <= ') result = comparator.join(map(str, self)) if self.is_trivial(): - return 'trivial constraint starting with '+result + return 'trivial constraint starting with ' + result return result def __nonzero__(self): diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 6753df83e77..7a4a312711b 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -1647,7 +1647,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.solve() # rel tol 1e-15 6.666666666666666 - The two constraints can alse be combined into a single + The two constraints can also be combined into a single vector-valued constraint:: sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK') diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 62852636bdc..e7c67a34a13 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -1137,15 +1137,15 @@ cdef class SemidefiniteProgram(SageObject): """ d = {} for v in L: - for id, coeff in v.iteritems(): + for id, coeff in v.iteritems(): d[id] = coeff + d.get(id, 0) return self.linear_functions_parent()(d) def get_backend(self): r""" - Returns the backend instance used. + Return the backend instance used. - This might be useful when acces to additional functions provided by + This might be useful when access to additional functions provided by the backend is needed. EXAMPLES: diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 8eb7a6d08b9..0247872298d 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -44,7 +44,7 @@ def __init__(self, path, options): vertices += curve codes += (len(curve))*[len(curve)+1] self.codes = codes - self.vertices = np.array(vertices, np.float) + self.vertices = np.array(vertices, float) GraphicPrimitive.__init__(self, options) def get_minmax_data(self): diff --git a/src/sage/plot/bezier_path.py b/src/sage/plot/bezier_path.py index 56da1dcbe45..79a9390c354 100644 --- a/src/sage/plot/bezier_path.py +++ b/src/sage/plot/bezier_path.py @@ -66,7 +66,7 @@ def __init__(self, path, options): vertices += curve codes += (len(curve)) * [len(curve)+1] self.codes = codes - self.vertices = np.array(vertices, np.float) + self.vertices = np.array(vertices, float) GraphicPrimitive_xydata.__init__(self, options) def _allowed_options(self): diff --git a/src/sage/plot/complex_plot.pyx b/src/sage/plot/complex_plot.pyx index e4a8c7fcae3..d688b6e4996 100644 --- a/src/sage/plot/complex_plot.pyx +++ b/src/sage/plot/complex_plot.pyx @@ -100,7 +100,7 @@ def complex_to_rgb(z_values): imax = len(z_values) jmax = len(z_values[0]) - cdef cnumpy.ndarray[cnumpy.float_t, ndim=3, mode='c'] rgb = numpy.empty(dtype=numpy.float, shape=(imax, jmax, 3)) + cdef cnumpy.ndarray[cnumpy.float_t, ndim=3, mode='c'] rgb = numpy.empty(dtype=float, shape=(imax, jmax, 3)) sig_on() for i from 0 <= i < imax: diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 12a2b1f6701..cec0663092f 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -823,6 +823,12 @@ def f(x,y): return cos(x) + sin(y) sage: contour_plot(x - y^2, (x,-5,5), (y,-3,3), ....: contours=[-4,-2,0], fill=False) Graphics object consisting of 1 graphics primitive + + Check that :trac:`18074` is fixed:: + + sage: contour_plot(0, (0,1), (0,1)) + ... UserWarning: No contour levels were found within the data range. + Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid diff --git a/src/sage/plot/density_plot.py b/src/sage/plot/density_plot.py index 9161691c314..b5eb5d8f8c7 100644 --- a/src/sage/plot/density_plot.py +++ b/src/sage/plot/density_plot.py @@ -294,14 +294,21 @@ def f(x,y): return x**2 * cos(x*y) sage: density_plot((x*y)^(1/2), (x,0,3), (y,0,500)) Graphics object consisting of 1 graphics primitive + Check that :trac:`17684` is fixed, i.e., symbolic values can be plotted:: + + sage: def f(x,y): + ....: return SR(x) + sage: density_plot(f, (0,1), (0,1)) + Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid + from sage.rings.real_double import RDF g, ranges = setup_for_eval_on_grid([f], [xrange, yrange], options['plot_points']) g = g[0] xrange, yrange = [r[:2] for r in ranges] - xy_data_array = [[g(x,y) for x in xsrange(*ranges[0], include_endpoint=True)] + xy_data_array = [[RDF(g(x,y)) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] g = Graphics() diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index ee708d98d76..b2dce79d675 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -398,6 +398,31 @@ cdef class Graphics3d(SageObject): sage: (css in str) or (html in str) False + "Fat" line scripts are only included when at least one line + (or one surface with ``mesh=True``) exists with ``thickness > 1``:: + + sage: fat = '// fat_lines.js' + sage: L = line3d([(0, 0, 0), (1, 1, 1)], thickness=1) + sage: str = L._rich_repr_threejs(online=True).html.get_str() + sage: fat in str + False + sage: L = line3d([(0, 0, 0), (1, 1, 1)], thickness=10) + sage: str = L._rich_repr_threejs(online=True).html.get_str() + sage: fat in str + True + sage: d = dodecahedron(mesh=False, thickness=10) + sage: str = d._rich_repr_threejs(online=True).html.get_str() + sage: fat in str + False + sage: d = dodecahedron(mesh=True, thickness=1) + sage: str = d._rich_repr_threejs(online=True).html.get_str() + sage: fat in str + False + sage: d = dodecahedron(mesh=True, thickness=10) + sage: str = d._rich_repr_threejs(online=True).html.get_str() + sage: fat in str + True + If a page title is provided, it is stripped and HTML-escaped:: sage: d = dodecahedron(page_title='\t"Page" & \n') @@ -490,10 +515,15 @@ cdef class Graphics3d(SageObject): reprs = {'point': [], 'line': [], 'text': [], 'surface': []} frame_count = 0 + fat_lines = False for kind, desc in self.threejs_repr(self.default_render_params()): reprs[kind].append(desc) keyframe = int(desc.get('keyframe', -1)) frame_count = max(frame_count, keyframe + 1) + if kind == 'line' or (kind == 'surface' and desc.get('showMeshGrid')): + linewidth = float(desc.get('linewidth', 1)) + if linewidth > 1: + fat_lines = True reprs = {kind: json.dumps(descs) for kind, descs in reprs.items()} from sage.env import SAGE_EXTCODE @@ -501,6 +531,10 @@ cdef class Graphics3d(SageObject): SAGE_EXTCODE, 'threejs', 'threejs_template.html')) as f: html = f.read() + if fat_lines: + with open(os.path.join(SAGE_EXTCODE, 'threejs', 'fat_lines.js')) as f: + scripts += '<script>' + f.read() + '</script>' + js_options['animate'] = js_options['animate'] and frame_count > 1 if js_options['animate']: if js_options['animationControls']: diff --git a/src/sage/plot/plot3d/index_face_set.pyx b/src/sage/plot/plot3d/index_face_set.pyx index f3879b97761..49b781c4c22 100644 --- a/src/sage/plot/plot3d/index_face_set.pyx +++ b/src/sage/plot/plot3d/index_face_set.pyx @@ -1305,11 +1305,12 @@ cdef class IndexFaceSet(PrimitiveObject): sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)], color='red', opacity=0.5, ....: render_order=2, threejs_flat_shading=True, - ....: single_side=True, mesh=True) + ....: single_side=True, mesh=True, thickness=10) sage: G.threejs_repr(G.default_render_params()) [('surface', {'color': '#ff0000', 'faces': [[0, 1, 2]], + 'linewidth': 10.0, 'opacity': 0.5, 'renderOrder': 2.0, 'showMeshGrid': True, @@ -1399,6 +1400,9 @@ cdef class IndexFaceSet(PrimitiveObject): if self._extra_kwds.get('mesh'): surface['showMeshGrid'] = True + if self._extra_kwds.get('thickness'): + surface['linewidth'] = float(self._extra_kwds['thickness']) + return [('surface', surface)] def obj_repr(self, render_params): diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index 83c1034762b..875165ee972 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -452,7 +452,7 @@ def list_plot3d_tuples(v, interpolation_type, **kwds): if interpolation_type == 'linear': T = tri.Triangulation(x, y) f = tri.LinearTriInterpolator(T, z) - j = numpy.complex(0, 1) + j = complex(0, 1) from .parametric_surface import ParametricSurface def g(x, y): @@ -467,7 +467,7 @@ def g(x, y): if interpolation_type == 'clough' or interpolation_type == 'default': points = [[x[i], y[i]] for i in range(len(x))] - j = numpy.complex(0, 1) + j = complex(0, 1) f = interpolate.CloughTocher2DInterpolator(points, z) from .parametric_surface import ParametricSurface diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 3bc6bc5f080..11b02bed916 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -903,6 +903,14 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): var('x y') sphinx_plot(plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True)) + The same with thicker mesh lines (not supported in all viewers):: + + sage: var('x,y') + (x, y) + sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True, + ....: thickness=2, viewer='threejs') + Graphics3d Object + Two wobby translucent planes:: sage: x,y = var('x,y') diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index 37d9f76df94..3bd894851de 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -80,7 +80,7 @@ ZeroDivisionError...Traceback (most recent call last) <ipython-input-...> in <module>... ----> 1 Integer(1)/Integer(0) - .../sage/rings/integer.pyx in sage.rings.integer.Integer...div... (.../cythonized/sage/rings/integer.c:...)() + .../sage/rings/integer.pyx in sage.rings.integer.Integer...div... ... -> ... raise ZeroDivisionError("rational division by zero") ....: x = <Rational> Rational.__new__(Rational) diff --git a/src/sage/rings/bernmm.pyx b/src/sage/rings/bernmm.pyx index 803401131ee..74c2aa19738 100644 --- a/src/sage/rings/bernmm.pyx +++ b/src/sage/rings/bernmm.pyx @@ -1,5 +1,9 @@ # distutils: sources = sage/rings/bernmm/bern_modp.cpp sage/rings/bernmm/bern_modp_util.cpp sage/rings/bernmm/bern_rat.cpp -# distutils: libraries = ntl pthread gmp +# distutils: libraries = NTL_LIBRARIES pthread gmp +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: include_dirs = NTL_INCDIR +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA # distutils: depends = sage/rings/bernmm/bern_modp.h sage/rings/bernmm/bern_modp_util.h sage/rings/bernmm/bern_rat.h # distutils: language = c++ # distutils: define_macros = USE_THREADS=1 THREAD_STACK_SIZE=4096 diff --git a/src/sage/rings/bernoulli_mod_p.pyx b/src/sage/rings/bernoulli_mod_p.pyx index 575435f5eba..514d32d6ec9 100644 --- a/src/sage/rings/bernoulli_mod_p.pyx +++ b/src/sage/rings/bernoulli_mod_p.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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""" Bernoulli numbers modulo p diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index a714b8f14fb..1112255a6d9 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -179,6 +179,7 @@ from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_complex_arb cimport Polynomial_complex_arb from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi from sage.rings.real_arb import RealBallField +from sage.rings.real_mpfi cimport RealIntervalField_class from sage.rings.real_mpfr cimport RealField_class, RealField, RealNumber from sage.rings.ring import Field from sage.structure.element cimport Element, ModuleElement @@ -187,7 +188,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.arith.long cimport is_small_python_int from sage.rings.complex_mpfr import ComplexField -from sage.rings.complex_interval_field import ComplexIntervalField +from sage.rings.complex_interval_field import ComplexIntervalField, ComplexIntervalField_class from sage.rings.integer_ring import ZZ cdef void ComplexIntervalFieldElement_to_acb( @@ -829,9 +830,9 @@ class ComplexBallField(UniqueRepresentation, Field): cdef bint real = False if ring is None: ring = self - elif isinstance(ring, ComplexBallField): + elif isinstance(ring, (ComplexBallField, ComplexIntervalField_class)): pass - elif isinstance(ring, RealBallField): + elif isinstance(ring, (RealBallField, RealIntervalField_class)): real = True elif ring.has_coerce_map_from(self): pass diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index b3030f0cec1..e732148a9a0 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -96,7 +96,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): EXAMPLES:: sage: I = CIF.gen() - sage: b = 1.5 + 2.5*I + sage: b = 3/2 + 5/2*I sage: TestSuite(b).run() """ def __cinit__(self, parent, *args): @@ -1677,7 +1677,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 1.570796326794897? sage: (-i).argument() -1.570796326794897? - sage: (RR('-0.001') - i).argument() + sage: (-1/1000 - i).argument() -1.571796326461564? sage: CIF(2).argument() 0 diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index eb515c0f837..98882f57329 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -477,12 +477,13 @@ def _coerce_map_from_(self, S): - anything that canonically coerces to the real interval field with this precision + - some exact or lazy parents representing subsets of the complex + numbers, such as ``QQbar`` and ``CLF``. + EXAMPLES:: sage: CIF((2,1)) + 2 + I # indirect doctest 4 + 2*I - sage: CIF((2,1)) + RR.pi() - 5.1415926535897932? + 1*I sage: CIF((2,1)) + CC.pi() Traceback (most recent call last): ... @@ -512,21 +513,29 @@ def _coerce_map_from_(self, S): Conversion via _complex_mpfi_ method map: From: Universal Cyclotomic Field To: Complex Interval Field with 53 bits of precision + + TESTS:: + + sage: CIF.has_coerce_map_from(RR) + False + sage: CIF.has_coerce_map_from(RDF) + False + sage: CIF.has_coerce_map_from(float) + False """ # Direct and efficient conversions - if S is ZZ or S is QQ or S is float: - return True - if S is int: + if S is ZZ or S is QQ or S is int: return True if isinstance(S, (ComplexIntervalField_class, RealIntervalField_class)): return S.precision() >= self._prec - # Assume that a _complex_mpfi_ method always defines a - # coercion (as opposed to only a conversion). - f = self._convert_method_map(S) - if f is not None: - return f + # If coercion to CC is possible and there is a _complex_mpfi_ + # method, assume that it defines a coercion to CIF + if self.middle_field().has_coerce_map_from(S): + f = self._convert_method_map(S) + if f is not None: + return f return self._coerce_map_via( (self.real_field(),), S) diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 4513f7c5568..05616f65644 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -85,6 +85,7 @@ def late_import(): global AlgebraicNumber_base global AlgebraicNumber global AlgebraicReal + global UniversalCyclotomicField global AA, QQbar, SR global CLF, RLF, CDF if NumberFieldElement_quadratic is None: @@ -96,6 +97,7 @@ def late_import(): AlgebraicNumber_base = sage.rings.qqbar.AlgebraicNumber_base AlgebraicNumber = sage.rings.qqbar.AlgebraicNumber AlgebraicReal = sage.rings.qqbar.AlgebraicReal + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField AA = sage.rings.qqbar.AA QQbar = sage.rings.qqbar.QQbar import sage.symbolic.ring @@ -558,6 +560,13 @@ class ComplexField_class(ring.Field): return None if S in [AA, QQbar, CLF, RLF]: return self._generic_coerce_map(S) + # Needed to discover the correct coerce map. Without this, the maps + # (direct or via QQbar, with slightly different behavior wrt imaginary + # parts of real elements) that get picked for conversion from UCF both + # to CC and to other types of complex fields depend in which order the + # coercions are discovered. + if isinstance(S, UniversalCyclotomicField): + return self._generic_coerce_map(S) return self._coerce_map_via([CLF], S) def _repr_(self): diff --git a/src/sage/rings/finite_rings/element_givaro.pxd b/src/sage/rings/finite_rings/element_givaro.pxd index d1703ecb450..c4d16de21f5 100644 --- a/src/sage/rings/finite_rings/element_givaro.pxd +++ b/src/sage/rings/finite_rings/element_givaro.pxd @@ -1,4 +1,4 @@ -# distutils: extra_compile_args = GIVARO_CFLAGS +# distutils: extra_compile_args = GIVARO_CFLAGS -std=c++11 # distutils: include_dirs = GIVARO_INCDIR from libcpp.vector cimport vector diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 42a8a08490c..e6f65c84cd4 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl +# distutils: libraries = 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""" Finite Fields of characteristic 2. diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index a4b396621ff..36b46ecb073 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -405,6 +405,51 @@ 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): + 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``. + + INPUT: + + - `n` -- integer. Must not be negative, and must be less than the + cardinality of ``self``. + + 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) + 100*a^3 + 37*a^2 + 12*a + 6 + sage: F.fetch_int(n) in F + True + + TESTS:: + + sage: F = GF(19^5) + sage: F.fetch_int(0) + 0 + sage: _.parent() + Finite Field in ... of size 19^5 + sage: F.fetch_int(-5) + Traceback (most recent call last): + ... + TypeError: n must be between 0 and self.order() + sage: F.fetch_int(F.cardinality()) + Traceback (most recent call last): + ... + TypeError: 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()) + g = self.gen() + return self.sum(self(d) * g**i for i, d in enumerate(digs) if d) + def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ Return ``True`` if the map from self to codomain sending 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 53209be6a9f..28c8b3935c0 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = givaro ntl gmp m +# distutils: libraries = givaro NTL_LIBRARIES gmp m +# 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++ """ Finite field morphisms using Givaro diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index cad995f91af..7ad2a7359ab 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = gmp ntl zn_poly +# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# 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++ "Univariate rational functions over prime fields" diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index cf78a076a6c..d0ad9e582a2 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -3444,14 +3444,9 @@ def number_of_rational_places(self, r=1): R = IntegerRing()[[L.parent().gen()]] # power series ring - old_prec = R.default_prec() - R.set_default_prec(r) - - f = R(Lp / L) + f = R(Lp / L, prec=r) n = f[r-1] + q**r + 1 - R.set_default_prec(old_prec) - return n diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 77a3a18913f..9ca8ab3153d 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -628,9 +628,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: 12 == numpy.int8('12') True - sage: numpy.float('15') == 15 + sage: float('15') == 15 True - sage: 15 == numpy.float('15') + sage: 15 == float('15') True Test underscores as digit separators (PEP 515, diff --git a/src/sage/rings/invariants/__init__.py b/src/sage/rings/invariants/__init__.py index d3f5a12faa9..e69de29bb2d 100644 --- a/src/sage/rings/invariants/__init__.py +++ b/src/sage/rings/invariants/__init__.py @@ -1 +0,0 @@ - diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 6598924813f..f0010cb2e27 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -155,7 +155,7 @@ cdef class LaurentSeries(AlgebraElement): # self is that t^n * u: if not f: - if n == infinity: + if n is infinity: self.__n = 0 self.__u = parent._power_series_ring.zero() else: @@ -163,7 +163,7 @@ cdef class LaurentSeries(AlgebraElement): self.__u = f else: val = f.valuation() - if val == infinity: + if val is infinity: self.__n = 0 self.__u = f elif val == 0: @@ -328,7 +328,7 @@ cdef class LaurentSeries(AlgebraElement): '2 + 2/3*t^3' """ if self.is_zero(): - if self.prec() == infinity: + if self.prec() is infinity: return "0" else: return "O(%s^%s)"%(self._parent.variable_name(),self.prec()) @@ -451,7 +451,7 @@ cdef class LaurentSeries(AlgebraElement): \left(a + b\right)x """ if self.is_zero(): - if self.prec() == infinity: + if self.prec() is infinity: return "0" else: return "0 + \\cdots" @@ -835,7 +835,7 @@ cdef class LaurentSeries(AlgebraElement): sage: (t^(-2)).add_bigoh(-3) O(t^-3) """ - if prec == infinity or prec >= self.prec(): + if prec is infinity or prec >= self.prec(): return self P = self._parent if not self or prec < self.__n: @@ -1080,8 +1080,8 @@ cdef class LaurentSeries(AlgebraElement): raise ZeroDivisionError try: return type(self)(self._parent, - self.__u / right.__u, - self.__n - right.__n) + self.__u / right.__u, + self.__n - right.__n) except TypeError as msg: # todo: this could also make something in the formal fraction field. raise ArithmeticError("division not defined") @@ -1691,7 +1691,7 @@ cdef class LaurentSeries(AlgebraElement): """ if prec is None: prec = self.prec() - if prec == infinity: + if prec is infinity: prec = self.parent().default_prec() else: prec = min(self.prec(), prec) @@ -1708,6 +1708,11 @@ cdef class LaurentSeries(AlgebraElement): def power_series(self): """ + Convert this Laurent series to a power series. + + An error is raised if the Laurent series has a term (or an error + term `O(x^k)`) whose exponent is negative. + EXAMPLES:: sage: R.<t> = LaurentSeriesRing(ZZ) @@ -1730,16 +1735,31 @@ cdef class LaurentSeries(AlgebraElement): sage: L.<t> = LaurentSeriesRing(GF(2)) sage: R.<x,y> = PolynomialRing(L) - sage: O = L._power_series_ring - sage: S.<x,y> = PolynomialRing(O) + sage: S.<x,y> = PolynomialRing(L._power_series_ring) sage: t**(-1)*x*y in S False + + There used to be an issue with non-canonical representations of zero, + see :trac:`31383`:: + + sage: S.<x> = PowerSeriesRing(QQ) + sage: L = Frac(S) + sage: s = L(O(x^2)) + sage: (s*x^(-1)).power_series() + O(x^1) + sage: (s*x^(-2)).power_series() + O(x^0) + sage: (s*x^(-3)).power_series() + Traceback (most recent call last): + ... + TypeError: self is not a power series """ if self.__n < 0: - raise TypeError("self is not a power series") - u = self.__u - t = u.parent().gen() - return t**(self.__n) * u + if self.__u.is_zero() and self.__u.prec() >= - self.__n: + return self.__u >> (- self.__n) + else: + raise TypeError("self is not a power series") + return self.__u << self.__n def inverse(self): """ diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index e462f3d7417..1fc8e3d2411 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -215,12 +215,11 @@ Note that Sage verifies that the morphism is valid:: Endomorphism of power series ring:: - sage: R.<t> = PowerSeriesRing(QQ); R + sage: R.<t> = PowerSeriesRing(QQ, default_prec=10); R Power Series Ring in t over Rational Field sage: f = R.hom([t^2]); f Ring endomorphism of Power Series Ring in t over Rational Field Defn: t |--> t^2 - sage: R.set_default_prec(10) sage: s = 1/(1 + t); s 1 - t + t^2 - t^3 + t^4 - t^5 + t^6 - t^7 + t^8 - t^9 + O(t^10) sage: f(s) @@ -1010,6 +1009,24 @@ cdef class RingHomomorphism(RingMap): of Multivariate Polynomial Ring in x, y over Algebraic Field sage: f(J) <= I True + + TESTS: + + Check that :trac:`31367` is fixed:: + + sage: A.<t> = QQ[] + sage: B.<x,y> = QQ['x,y'].quotient('y') + sage: f = A.hom([x], B) + sage: f.kernel() + Principal ideal (0) of Univariate Polynomial Ring in t over Rational Field + + :: + + sage: A.<t,u> = QQ[] + sage: B.<x,y,z> = QQ['x,y,z'].quotient('z') + sage: f = A.hom([x, y], B) + sage: f.kernel() + Ideal (0) of Multivariate Polynomial Ring in t, u over Rational Field """ from .polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing from .quotient_ring import is_QuotientRing @@ -1033,8 +1050,9 @@ cdef class RingHomomorphism(RingMap): if is_QuotientRing(Q): # elimination_ideal does not work with quotient rings, so # switch to the cover ring - preimage = (Q.cover()._inverse_image_ideal(graph_I) - .elimination_ideal([y.lift() for y in gens_B])) + gens_B_lifted = Q.cover_ring().gens()[:B.ngens()] + graph_I_lifted = Q.cover()._inverse_image_ideal(graph_I) + preimage = graph_I_lifted.elimination_ideal(gens_B_lifted) _, ambient_to_A = to_A return ambient_to_A(preimage) else: diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 28b1f9059f6..b4b41003891 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -10,25 +10,25 @@ Standard test of pickleability:: - sage: G = NumberField(x^3 + 2, 'alpha').galois_group(type="pari"); G - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field in alpha with defining polynomial x^3 + 2 - sage: G == loads(dumps(G)) - True - sage: G = NumberField(x^3 + 2, 'alpha').galois_group(names='beta'); G - Galois group of Galois closure in beta of Number Field in alpha with defining polynomial x^3 + 2 + Galois group 3T2 (S3) with order 6 of x^3 + 2 sage: G == loads(dumps(G)) True """ from sage.structure.sage_object import SageObject -from sage.groups.perm_gps.permgroup import PermutationGroup_generic +from sage.groups.galois_group import _alg_key, GaloisGroup as GaloisGroup_base +from sage.groups.perm_gps.permgroup import PermutationGroup_generic, standardize_generator + from sage.groups.perm_gps.permgroup_element import PermutationGroupElement +from sage.misc.superseded import deprecation from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.libs.pari.all import pari from sage.rings.infinity import infinity from sage.rings.number_field.number_field import refine_embedding from sage.rings.number_field.morphism import NumberFieldHomomorphism_im_gens +from sage.rings.integer_ring import ZZ class GaloisGroup_v1(SageObject): @@ -42,8 +42,11 @@ class GaloisGroup_v1(SageObject): EXAMPLES:: + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 sage: K = QQ[2^(1/3)] - sage: G = K.galois_group(type="pari"); G + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K); G + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field in a with defining polynomial x^3 - 2 with a = 1.259921049894873? sage: G.order() 6 @@ -59,9 +62,14 @@ def __init__(self, group, number_field): EXAMPLES:: - sage: NumberField([x^2 + 1, x^2 + 2],'a').galois_group(type="pari") + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField([x^2 + 1, x^2 + 2],'a') + sage: GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K) + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. Galois group PARI group [4, 1, 2, "E(4) = 2[x]2"] of degree 4 of the Number Field in a0 with defining polynomial x^2 + 1 over its base field """ + deprecation(28782, "GaloisGroup_v1 is deprecated; please use GaloisGroup_v2") self.__group = group self.__number_field = number_field @@ -76,8 +84,13 @@ def __eq__(self, other): EXAMPLES:: - sage: G = NumberField(x^3 + 2, 'alpha').galois_group(type="pari") - sage: H = QQ[sqrt(2)].galois_group(type="pari") + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^3 + 2, 'alpha') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K) + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. + sage: L = QQ[sqrt(2)] + sage: H = GaloisGroup_v1(L.absolute_polynomial().galois_group(pari_group=True), L) sage: H == G False sage: H == H @@ -99,8 +112,13 @@ def __ne__(self, other): EXAMPLES:: - sage: G = NumberField(x^3 + 2, 'alpha').galois_group(type="pari") - sage: H = QQ[sqrt(2)].galois_group(type="pari") + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^3 + 2, 'alpha') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K) + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. + sage: L = QQ[sqrt(2)] + sage: H = GaloisGroup_v1(L.absolute_polynomial().galois_group(pari_group=True), L) sage: H != G True sage: H != H @@ -116,7 +134,11 @@ def __repr__(self): EXAMPLES:: - sage: G = NumberField(x^4 + 2*x + 2, 'a').galois_group(type="pari") + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^4 + 2*x + 2, 'a') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K) + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. sage: G.__repr__() 'Galois group PARI group [24, -1, 5, "S4"] of degree 4 of the Number Field in a with defining polynomial x^4 + 2*x + 2' """ @@ -129,7 +151,11 @@ def group(self): EXAMPLES:: - sage: G = NumberField(x^3 + 2*x + 2, 'theta').galois_group(type="pari") + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^3 + 2*x + 2, 'theta') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K) + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. sage: H = G.group(); H PARI group [6, -1, 2, "S3"] of degree 3 sage: P = H.permutation_group(); P @@ -145,7 +171,11 @@ def order(self): EXAMPLES:: - sage: G = NumberField(x^5 + 2, 'theta_1').galois_group(type="pari"); G + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^5 + 2, 'theta_1') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K); G + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. Galois group PARI group [20, -1, 3, "F(5) = 5:4"] of degree 5 of the Number Field in theta_1 with defining polynomial x^5 + 2 sage: G.order() 20 @@ -158,7 +188,11 @@ def number_field(self): EXAMPLES:: - sage: G = NumberField(x^6 + 2, 't').galois_group(type="pari"); G + sage: from sage.rings.number_field.galois_group import GaloisGroup_v1 + sage: K = NumberField(x^6 + 2, 't') + sage: G = GaloisGroup_v1(K.absolute_polynomial().galois_group(pari_group=True), K); G + ...DeprecationWarning: GaloisGroup_v1 is deprecated; please use GaloisGroup_v2 + See https://trac.sagemath.org/28782 for details. Galois group PARI group [12, -1, 3, "D(6) = S(3)[x]2"] of degree 6 of the Number Field in t with defining polynomial x^6 + 2 sage: G.number_field() Number Field in t with defining polynomial x^6 + 2 @@ -166,11 +200,11 @@ def number_field(self): return self.__number_field -class GaloisGroup_v2(PermutationGroup_generic): +class GaloisGroup_v2(GaloisGroup_base): r""" The Galois group of an (absolute) number field. - .. note:: + .. NOTE:: We define the Galois group of a non-normal field K to be the Galois group of its Galois closure L, and elements are stored as @@ -183,20 +217,27 @@ class GaloisGroup_v2(PermutationGroup_generic): Artin symbols etc) are only available for Galois fields. """ - def __init__(self, number_field, names=None): + def __init__(self, number_field, algorithm='pari', names=None, gc_numbering=None, _type=None): r""" Create a Galois group. EXAMPLES:: sage: QuadraticField(-23,'a').galois_group() - Galois group of Number Field in a with defining polynomial x^2 + 23 with a = 4.795831523312720?*I - sage: NumberField(x^3 - 2, 'b').galois_group() - Traceback (most recent call last): - ... - TypeError: You must specify the name of the generator. - sage: NumberField(x^3 - 2, 'b').galois_group(names="c") - Galois group of Galois closure in c of Number Field in b with defining polynomial x^3 - 2 + Galois group 2T1 (S2) with order 2 of x^2 + 23 + + You can specify the variable name for the Galois closure:: + + sage: G = NumberField(x^3 - 2, 'b').galois_group(names="c"); G + Galois group 3T2 (S3) with order 6 of x^3 - 2 + sage: G._galois_closure + Number Field in c with defining polynomial x^6 + 108 + + Or have one chosen automatically (``c`` is appended to the variable name):: + + sage: G = NumberField(x^3 - 2, 'b').galois_group() + sage: G._galois_closure + Number Field in bc with defining polynomial x^6 + 108 TESTS:: @@ -212,25 +253,321 @@ def __init__(self, number_field, names=None): sage: phi(z) # random z^3 """ - self._number_field = number_field + if not number_field.is_absolute(): + # We eventually want to support relative Galois groups, which currently just create the Galois group of the absolute field + deprecation(28782, "Use .absolute_field().galois_group() if you want the Galois group of the absolute field") + if gc_numbering is None: + gc_numbering = False if algorithm == 'magma' else True + # For the deprecated group() method of GaloisGroup_v1 + self._type = _type + super(GaloisGroup_v2, self).__init__(number_field, algorithm, names, gc_numbering) + + @cached_method(key=GaloisGroup_base._get_algorithm) + def _pol_galgp(self, algorithm=None): + """ + Return the Galois group object associated to the defining polynomial of this field extension. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: G._pol_galgp() + PARI group [6, -1, 2, "S3"] of degree 3 + sage: G._pol_galgp(algorithm="gap") # optional - gap_packages + Transitive group number 2 of degree 3 + """ + algorithm = self._get_algorithm(algorithm) + f = self._field.absolute_polynomial() + pari_group = (self._type != "gap") # while GaloisGroup_v1 is deprecated + return f.galois_group(pari_group=pari_group, algorithm=algorithm) - if not number_field.is_galois(): - self._galois_closure, self._gc_map = number_field.galois_closure(names=names, map=True) + def group(self): + """ + While GaloisGroup_v1 is being deprecated, this provides public access to the Pari/GAP group + in order to keep all aspects of that API. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group(type="pari") + ...DeprecationWarning: the different Galois types have been merged into one class + See https://trac.sagemath.org/28782 for details. + sage: G.group() + ...DeprecationWarning: the group method is deprecated; you can use _pol_galgp if you really need it + See https://trac.sagemath.org/28782 for details. + PARI group [6, -1, 2, "S3"] of degree 3 + """ + deprecation(28782, "the group method is deprecated; you can use _pol_galgp if you really need it") + return self._pol_galgp() + + @cached_method(key=_alg_key) + def order(self, algorithm=None, recompute=False): + """ + Return the order of this Galois group + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: G.order() + 6 + """ + algorithm = self._get_algorithm(algorithm) + K = self._field + if K.absolute_degree() < 12 or algorithm != "pari": + return self._pol_galgp(algorithm=algorithm).order() else: - self._galois_closure, self._gc_map = (number_field, number_field.hom(number_field.gen(), number_field)) + return self._galois_closure.absolute_degree() - self._pari_gc = self._galois_closure.__pari__() + def easy_order(self, algorithm=None): + """ + Return the order of this Galois group if it's quick to compute. - g = self._pari_gc.galoisinit() - self._pari_data = g + EXAMPLES:: - # Sort the vector of permutations using .list() as key to avoid errors - # from using comparison operators on non-scalar PARI objects. - PermutationGroup_generic.__init__(self, - sorted(g[6], key=lambda x: x.list())) + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: G.easy_order() + 6 + sage: L.<b> = NumberField(x^72 + 2*x + 2) + sage: H = L.galois_group() + sage: H.easy_order() + """ + algorithm = self._get_algorithm(algorithm) + if self.order.cache: + return next(iter(self.order.cache.values())) + K = self._field + if K.absolute_degree() < 12 or algorithm != "pari": + size = self._pol_galgp(algorithm=algorithm).order() + self.order.cache[None] = size + return size + + @cached_method(key=_alg_key) + def transitive_number(self, algorithm=None, recompute=False): + """ + Regardless of the value of ``gc_numbering``, this gives the transitive number + for the action on the roots of the defining polynomial of the original number field, + not the Galois closure. + + INPUT: + + - ``algorithm`` -- string, specify the algorithm to be used + - ``recompute`` -- boolean, whether to recompute the result even if known by another algorithm + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 + 2*x + 2) + sage: G = K.galois_group() + sage: G.transitive_number() + 2 + sage: L.<b> = NumberField(x^13 + 2*x + 2) + sage: H = L.galois_group(algorithm="gap") + sage: H.transitive_number() # optional - gap_packages + 9 + """ + algorithm = self._get_algorithm(algorithm) + K = self._field + if K.absolute_degree() < 12 or algorithm != "pari": + return self._pol_galgp(algorithm=algorithm).transitive_number() + else: + if self._gc_numbering: + G = self._field.galois_group(algorithm=self._default_algorithm, names=self._gc_names, gc_numbering=False) + else: + G = self + return ZZ(G.gap().TransitiveIdentification()) - # PARI computes all the elements of self anyway, so we might as well store them - self._elts = sorted([self(x, check=False) for x in g[5]]) + def pari_label(self): + """ + Return the label assigned by Pari for this Galois group, an attempt at giving a human readable description of the group. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^8 - x^5 + x^4 - x^3 + 1) + sage: G = K.galois_group() + sage: G.transitive_label() + '8T44' + sage: G.pari_label() + '[2^4]S(4)' + """ + return self._pol_galgp().label() + + @cached_method + def signature(self): + """ + Return 1 if contained in the alternating group, -1 otherwise. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 - 2) + sage: K.galois_group().signature() + -1 + sage: K.<a> = NumberField(x^3 - 3*x - 1) + sage: K.galois_group().signature() + 1 + """ + if self._field.absolute_degree() < 12: + return self._pol_galgp().signature() + elif self._field.absolute_polynomial().discriminant().is_square(): + return ZZ(1) + else: + return ZZ(-1) + + # We compute various attributes lazily so that we can support quick lookup + # of some that are more easily computed. This allows us to emulate + # having initialized as a permutation group. + @lazy_attribute + def _gcdata(self): + """ + Return the galois closure, together with the embedding of the top field into it + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 - 2) + sage: G = K.galois_group() + sage: G._gcdata + (Number Field in ac with defining polynomial x^6 + 108, + Ring morphism: + From: Number Field in a with defining polynomial x^3 - 2 + To: Number Field in ac with defining polynomial x^6 + 108 + Defn: a |--> -1/36*ac^4 - 1/2*ac) + + TESTS: + + Check that it works for relative number fields. This behavior will change in the future:: + + sage: K.<a> = NumberField(x^3 - 2) + sage: L.<b> = NumberField(x^2 - x + 17*a) + sage: G = L.galois_group() + ...DeprecationWarning: Use .absolute_field().galois_group() if you want the Galois group of the absolute field + See https://trac.sagemath.org/28782 for details. + sage: M, emb = G._gcdata + sage: emb.domain() is L + True + sage: emb.codomain() is M + True + sage: G + Galois group 6T11 (2 wr S(3)) with order 48 of x^2 - x + 17*a + sage: M.degree() + 48 + """ + K = self._field + if self.is_galois(): + return K, K.hom(K.gen(), K) + else: + if K.is_relative(): + # Switch to the absolute field + K = K.absolute_field(K.variable_name() + 'a') + from_abs, to_abs = K.structure() + else: + to_abs = None + L, emb = K.galois_closure(names=self._gc_names, map=True) + if to_abs is not None: + emb = emb * to_abs + return L, emb + + @lazy_attribute + def _pari_data(self): + """ + Return the corresponding Pari Galois group structure. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 - 2) + sage: G = K.galois_group() + sage: G._pari_data + [y^6 + 108, ...] + """ + return self._galois_closure.__pari__().galoisinit() + + @lazy_attribute + def _elts(self): + """ + Return the list of all elements of this group. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^3 - 2) + sage: G = K.galois_group() + sage: G._elts + [(), + (1,2,3)(4,5,6), + (1,3,2)(4,6,5), + (1,4)(2,6)(3,5), + (1,5)(2,4)(3,6), + (1,6)(2,5)(3,4)] + sage: G = K.galois_group(gc_numbering=False) + sage: G._elts + [(), (2,3), (1,2), (1,2,3), (1,3,2), (1,3)] + """ + if self._gc_numbering: + # PARI computes all the elements of self anyway, so we might as well store them + return sorted([self(x, check=False) for x in self._pari_data[5]]) + else: + return sorted(list(self.iteration())) + + @lazy_attribute + def _gens(self): + """ + Computes the generators as permutations. + + EXAMPLES:: + + sage: R.<x> = ZZ[] + sage: K.<a> = NumberField(x^5-2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._gens + [(1,2,3,5), (1,4,3,2,5)] + sage: G = K.galois_group(gc_numbering=True) + sage: G._gens + [(1,2,15,3)(4,19,11,8)(5,20,13,7)(6,9,10,16)(12,17,18,14), + (1,7,17,11,6)(2,8,5,9,18)(3,12,16,13,19)(4,14,20,15,10)] + """ + if self._gc_numbering: + gens = [standardize_generator(x, as_cycles=True) for x in self._pari_data[6]] + if not gens: + gens = [()] + gens = [self.element_class(x, self, check=False) for x in gens] + return sorted(set(gens)) + else: + G = self._field.galois_group(algorithm=self._default_algorithm, names=self._gc_names, gc_numbering=True) + self._galois_closure = L = G._galois_closure + gens = [g.as_hom() for g in G._gens] + if gens: + # We add None so that we're 1-indexed + roots = [None] + self._field.absolute_polynomial().roots(L, multiplicities=False) + new_gens = [] + for g in gens: + seen = set() + cycles = [] + for start in range(1, len(roots)): + if start in seen: + continue + cycle = [start] + r = roots[start] + while True: + r = g(r) + i = roots.index(r) + seen.add(i) + if i == start: + break + cycle.append(i) + cycles.append(tuple(cycle)) + new_gens.append(cycles) + else: + new_gens = [()] + # Want order to match G's, so don't sort + return [self.element_class(x, self, check=False) for x in new_gens] def _element_constructor_(self, x, check=True): """ @@ -274,7 +611,7 @@ def _element_constructor_(self, x, check=True): def is_galois(self): r""" - Return True if the underlying number field of self is actually Galois. + Whether the underlying number field is Galois EXAMPLES:: @@ -283,38 +620,38 @@ def is_galois(self): sage: NumberField(x^2 - x + 1,'a').galois_group().is_galois() True """ - if self._number_field == self._galois_closure: - return True + K = self._field + d = K.absolute_degree() + if d < 12: + return self._pol_galgp().order() == d else: - return False - - def ngens(self): - r""" Number of generators of self. - - EXAMPLES:: - - sage: QuadraticField(-23, 'a').galois_group().ngens() - 1 - """ - return len(self._gens) + return len(K.automorphisms()) == d def _repr_(self): r""" - String representation of self. + String representation of this Galois group EXAMPLES:: sage: G = QuadraticField(-23, 'a').galois_group() sage: G._repr_() - 'Galois group of Number Field in a with defining polynomial x^2 + 23 with a = 4.795831523312720?*I' + 'Galois group 2T1 (S2) with order 2 of x^2 + 23' sage: G = NumberField(x^3 - 2, 'a').galois_group(names='b') sage: G._repr_() - 'Galois group of Galois closure in b of Number Field in a with defining polynomial x^3 - 2' + 'Galois group 3T2 (S3) with order 6 of x^3 - 2' """ - if self.is_galois(): - return "Galois group of %s" % self.number_field() + K = self.number_field() + f = K.defining_polynomial() + d = K.absolute_degree() + if d < 12: + plabel = self.pari_label().split('=')[-1].strip() + tlabel = "%sT%s (%s) with order %s " % (d, self.transitive_number(), plabel, self.order()) else: - return "Galois group of Galois closure in %s of %s" % (self.splitting_field().gen(), self.number_field()) + tlabel = "" + if d < 12 or self.is_galois(): + return "Galois group %sof %s" % (tlabel, f) + else: + return "Galois group %sof (non-Galois) %s" % (tlabel, f) def number_field(self): r""" @@ -326,21 +663,7 @@ def number_field(self): sage: K.galois_group(names='b').number_field() is K True """ - return self._number_field - - def splitting_field(self): - r""" - The Galois closure of the ambient number field. - - EXAMPLES:: - - sage: K = NumberField(x^3 - x + 1, 'a') - sage: K.galois_group(names='b').splitting_field() - Number Field in b with defining polynomial x^6 - 6*x^4 + 9*x^2 + 23 - sage: L = QuadraticField(-23, 'c'); L.galois_group().splitting_field() is L - True - """ - return self._galois_closure + return self._field def list(self): r""" @@ -397,7 +720,7 @@ def subgroup(self, elts): sage: G = NumberField(x^3 - x - 1, 'a').galois_closure('b').galois_group() sage: G.subgroup([ G(1), G([(1,2,3),(4,5,6)]), G([(1,3,2),(4,6,5)]) ]) - Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group of Number Field in b with defining polynomial x^6 - 6*x^4 + 9*x^2 + 23 + Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group 6T2 ([3]2) with order 6 of x^6 - 6*x^4 + 9*x^2 + 23 Subgroups can be specified using generators (:trac:`26816`):: @@ -473,7 +796,7 @@ def decomposition_group(self, P): sage: P = K.ideal([17, a^2]) sage: G = K.galois_group() sage: G.decomposition_group(P) - Subgroup [(), (1,8)(2,7)(3,6)(4,5)] of Galois group of Number Field in a with defining polynomial x^8 - 20*x^6 + 104*x^4 - 40*x^2 + 1156 + Subgroup [(), (1,8)(2,7)(3,6)(4,5)] of Galois group 8T4 ([4]2) with order 8 of x^8 - 20*x^6 + 104*x^4 - 40*x^2 + 1156 sage: G.decomposition_group(P^2) Traceback (most recent call last): ... @@ -556,9 +879,9 @@ def ramification_group(self, P, v): sage: G=K.galois_group() sage: P = K.primes_above(3)[0] sage: G.ramification_group(P, 3) - Subgroup [(), (1,2,4)(3,5,6), (1,4,2)(3,6,5)] of Galois group of Number Field in b with defining polynomial x^6 + 243 + Subgroup [(), (1,2,4)(3,5,6), (1,4,2)(3,6,5)] of Galois group 6T2 ([3]2) with order 6 of x^6 + 243 sage: G.ramification_group(P, 5) - Subgroup [()] of Galois group of Number Field in b with defining polynomial x^6 + 243 + Subgroup [()] of Galois group 6T2 ([3]2) with order 6 of x^6 + 243 """ if not self.is_galois(): raise TypeError("Ramification groups only defined for Galois extensions") @@ -580,9 +903,9 @@ def inertia_group(self, P): sage: K.<b> = NumberField(x^2 - 3,'a') sage: G = K.galois_group() sage: G.inertia_group(K.primes_above(2)[0]) - Subgroup [(), (1,2)] of Galois group of Number Field in b with defining polynomial x^2 - 3 + Subgroup [(), (1,2)] of Galois group 2T1 (S2) with order 2 of x^2 - 3 sage: G.inertia_group(K.primes_above(5)[0]) - Subgroup [()] of Galois group of Number Field in b with defining polynomial x^2 - 3 + Subgroup [()] of Galois group 2T1 (S2) with order 2 of x^2 - 3 """ if not self.is_galois(): raise TypeError("Inertia groups only defined for Galois extensions") @@ -654,7 +977,6 @@ def artin_symbol(self, P): raise ValueError("%s is ramified" % P) return t[0] - class GaloisGroup_subgroup(GaloisGroup_v2): r""" A subgroup of a Galois group, as returned by functions such as ``decomposition_group``. @@ -673,7 +995,7 @@ def __init__(self, ambient, elts): sage: from sage.rings.number_field.galois_group import GaloisGroup_subgroup sage: G = NumberField(x^3 - x - 1, 'a').galois_closure('b').galois_group() sage: GaloisGroup_subgroup( G, [ G(1), G([(1,2,3),(4,5,6)]), G([(1,3,2),(4,6,5)])]) - Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group of Number Field in b with defining polynomial x^6 - 6*x^4 + 9*x^2 + 23 + Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group 6T2 ([3]2) with order 6 of x^6 - 6*x^4 + 9*x^2 + 23 TESTS: @@ -692,13 +1014,30 @@ def __init__(self, ambient, elts): PermutationGroup_generic.__init__(self, elts, canonicalize=True, domain=ambient.domain()) self._ambient = ambient - self._number_field = ambient.number_field() + self._field = ambient.number_field() self._galois_closure = ambient._galois_closure self._pari_data = ambient._pari_data - self._pari_gc = ambient._pari_gc self._gc_map = ambient._gc_map + self._default_algorithm = ambient._default_algorithm + self._type = None # Backward compatibility self._elts = sorted(self.iteration()) + def order(self): + """ + Return the order of this subgroup. + + EXAMPLES:: + + sage: K.<a> = NumberField(x^6-3*x^2-1) + sage: L.<b> = K.galois_closure() + sage: G = L.galois_group() + sage: P = L.primes_above(3)[0] + sage: H = G.decomposition_group(P) + sage: H.order() + 3 + """ + return ZZ(len(self._elts)) + def fixed_field(self): r""" Return the fixed field of this subgroup (as a subfield of the Galois @@ -741,7 +1080,7 @@ def _repr_(self): sage: G = NumberField(x^3 - x - 1, 'a').galois_closure('b').galois_group() sage: H = G.subgroup([ G(1), G([(1,2,3),(4,5,6)]), G([(1,3,2),(4,6,5)])]) sage: H._repr_() - 'Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group of Number Field in b with defining polynomial x^6 - 6*x^4 + 9*x^2 + 23' + 'Subgroup [(), (1,2,3)(4,5,6), (1,3,2)(4,6,5)] of Galois group 6T2 ([3]2) with order 6 of x^6 - 6*x^4 + 9*x^2 + 23' """ return "Subgroup %s of %s" % (self._elts, self._ambient) diff --git a/src/sage/rings/number_field/morphism.py b/src/sage/rings/number_field/morphism.py index cb968189019..c6e2b0bd425 100644 --- a/src/sage/rings/number_field/morphism.py +++ b/src/sage/rings/number_field/morphism.py @@ -55,7 +55,7 @@ def __invert__(self): Defn: z |--> z^2, Ring endomorphism of Cyclotomic Field of order 5 and degree 4 Defn: z |--> z^3) - sage: (tau4, ~tau4) + sage: (tau3, ~tau3) (Ring endomorphism of Cyclotomic Field of order 5 and degree 4 Defn: z |--> z^3, Ring endomorphism of Cyclotomic Field of order 5 and degree 4 diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index ebf83f478aa..9ac18eb2f13 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -100,6 +100,7 @@ from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecation import sage.libs.ntl.all as ntl import sage.interfaces.gap @@ -125,7 +126,6 @@ import sage.rings.ring from sage.misc.latex import latex_variable_name from sage.misc.misc import union -from sage.misc.superseded import deprecation from .unit_group import UnitGroup from .class_group import ClassGroup @@ -751,8 +751,8 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name The Galois group is a product of 3 groups of order 2:: - sage: k.galois_group(type="pari") - Galois group PARI group [8, 1, 3, "E(8)=2[x]2[x]2"] of degree 8 of the Number Field in a with defining polynomial x^2 + 1 over its base field + sage: k.absolute_field(names='c').galois_group() + Galois group 8T3 (2[x]2[x]2) with order 8 of x^8 + 36*x^6 + 302*x^4 + 564*x^2 + 121 Repeatedly calling base_field allows us to descend the internally constructed tower of fields:: @@ -3475,12 +3475,82 @@ def ideal(self, *gens, **kwds): Fractional ideal (i + 2) sage: K.ideal(0) Ideal (0) of Number Field in i with defining polynomial x^2 + 1 + + TESTS: + + Check that :trac:`25934` is fixed:: + + sage: x = polygen(QQ) + 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): + r""" + Return a solution of the Chinese Remainder Theorem problem + for ideals in a number field. + + This is a wrapper around the pari function :pari:`idealchinese`. + + INPUT: + + - ``ideals`` - a list of ideals of the number field. + + - ``residues`` - a list of elements of the number field. + + OUTPUT: + + Return an element `b` of the number field such that + `b \equiv x_i \bmod I_i` for all residues `x_i` and + respective ideals `I_i`. + + .. SEEALSO:: + + - :func:`crt` + + EXAMPLES: + + This is the example from the pari page on ``idealchinese``:: + + sage: K.<sqrt2> = NumberField(sqrt(2).minpoly()) + sage: ideals = [K.ideal(4),K.ideal(3)] + sage: residues = [sqrt2,1] + sage: r = K.idealchinese(ideals,residues); r + -3*sqrt2 + 4 + sage: all((r - a) in I for I,a in zip(ideals,residues)) + True + + The result may be non-integral if the results are non-integral:: + + sage: K.<sqrt2> = NumberField(sqrt(2).minpoly()) + sage: ideals = [K.ideal(4),K.ideal(21)] + sage: residues = [1/sqrt2,1] + sage: r = K.idealchinese(ideals,residues); r + -63/2*sqrt2 - 20 + sage: all( + ....: (r-a).valuation(P) >= k + ....: for I,a in zip(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] + x = pari.Mat([ + pari.Col([p.pari_prime(),k]) + for f in factorizations + for p,k in f + ]).mattranspose() + r = self.pari_nf().idealchinese(x,y) + return self(r) + def fractional_ideal(self, *gens, **kwds): r""" Return the ideal in `\mathcal{O}_K` generated by gens. @@ -5767,11 +5837,7 @@ def is_galois(self): sage: NumberField(x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 10, 'a').is_galois() False """ - #return self.galois_group(type="pari").order() == self.degree() - if self.degree() < 12: - return self.galois_group(type='pari').order() == self.degree() - else: - return len(self.automorphisms()) == self.degree() + return self.galois_group().is_galois() @cached_method def is_abelian(self): @@ -5808,43 +5874,42 @@ def is_abelian(self): return pari_pol.galoisinit().galoisisabelian(1)==1 @cached_method - def galois_group(self, type=None, algorithm='pari', names=None): + def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=None): r""" Return the Galois group of the Galois closure of this number field. INPUT: - - ``type`` - ``none``, ``gap``, or ``pari``. If None (the default), - return an explicit group of automorphisms of self as a - ``GaloisGroup_v2`` object. Otherwise, return a ``GaloisGroup_v1`` - wrapper object based on a PARI or Gap transitive group object, which - is quicker to compute, but rather less useful (in particular, it - can't be made to act on self). + - ``type`` - Deprecated; the different versions of Galois groups have been + merged in :trac:`28782`. + + - ``algorithm`` - 'pari', 'gap', 'kash', 'magma'. (default: 'pari'; + for degrees between 12 and 15 default is 'gap', and + when the degree is >= 16 it is 'kash'.) - - ``algorithm`` - 'pari', 'kash', 'magma'. (default: 'pari', except - when the degree is >= 12 when 'kash' is tried.) + - ``names`` - a string giving a name for the generator of the Galois + closure of self, when this field is not Galois. - - ``name`` - a string giving a name for the generator of the Galois - closure of self, when self is not Galois. This is ignored if type is - not None. + - ``gc_numbering`` -- if ``True``, permutations will be written + in terms of the action on the roots of a defining polynomial + for the Galois closure, rather than the defining polynomial for + the original number field. This is significantly faster; + but not the standard way of presenting Galois groups. + The default currently depends on the algorithm (``True`` for ``'pari'``, + ``False`` for ``'magma'``) and may change in the future. - Note that computing Galois groups as abstract groups is often much - faster than computing them as explicit automorphism groups (but of - course you get less information out!) For more (important!) - documentation, so the documentation for Galois groups of polynomials + The resulting group will only compute with automorphisms when necessary, + so certain functions (such as :meth:`sage.rings.number_field.galois_group.GaloisGroup_v2.order`) + will still be fast. For more (important!) + documentation, see the documentation for Galois groups of polynomials over `\QQ`, e.g., by typing ``K.polynomial().galois_group?``, where `K` is a number field. - To obtain actual field homomorphisms from the number field to its - splitting field, use type=None. - - EXAMPLES: - - With type ``None``:: + EXAMPLES:: sage: k.<b> = NumberField(x^2 - 14) # a Galois extension sage: G = k.galois_group(); G - Galois group of Number Field in b with defining polynomial x^2 - 14 + Galois group 2T1 (S2) with order 2 of x^2 - 14 sage: G.gen(0) (1,2) sage: G.gen(0)(b) @@ -5854,29 +5919,10 @@ def galois_group(self, type=None, algorithm='pari', names=None): sage: k.<b> = NumberField(x^3 - x + 1) # not Galois sage: G = k.galois_group(names='c'); G - Galois group of Galois closure in c of Number Field in b with defining polynomial x^3 - x + 1 + Galois group 3T2 (S3) with order 6 of x^3 - x + 1 sage: G.gen(0) (1,2,3)(4,5,6) - With type ``'pari'``:: - - sage: NumberField(x^3-2, 'a').galois_group(type="pari") - Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field in a with defining polynomial x^3 - 2 - - :: - - sage: NumberField(x-1, 'a').galois_group(type="gap") - Galois group Transitive group number 1 of degree 1 of the Number Field in a with defining polynomial x - 1 - sage: NumberField(x^2+2, 'a').galois_group(type="gap") - Galois group Transitive group number 1 of degree 2 of the Number Field in a with defining polynomial x^2 + 2 - sage: NumberField(x^3-2, 'a').galois_group(type="gap") - Galois group Transitive group number 2 of degree 3 of the Number Field in a with defining polynomial x^3 - 2 - - :: - - sage: x = polygen(QQ) - sage: NumberField(x^3 + 2*x + 1, 'a').galois_group(type='gap') - Galois group Transitive group number 2 of degree 3 of the Number Field in a with defining polynomial x^3 + 2*x + 1 sage: NumberField(x^3 + 2*x + 1, 'a').galois_group(algorithm='magma') # optional - magma Galois group Transitive group number 2 of degree 3 of the Number Field in a with defining polynomial x^3 + 2*x + 1 @@ -5900,18 +5946,42 @@ def galois_group(self, type=None, algorithm='pari', names=None): ] sage: G[2](b1) 1/12*b1^4 + 1/2*b1 - """ - from .galois_group import GaloisGroup_v1, GaloisGroup_v2 - if type is None: - return GaloisGroup_v2(self, names) + many examples for higher degrees may be found in the online databases + http://galoisdb.math.upb.de/ by Jürgen Klüners and Gunter Malle and + https://www.lmfdb.org/NumberField/ by the LMFDB collaboration, + although these might need a lot of computing time. - elif type=="pari": - return GaloisGroup_v1(self.absolute_polynomial().galois_group(pari_group=True, algorithm=algorithm), self) - elif type=="gap": - return GaloisGroup_v1(self.absolute_polynomial().galois_group(pari_group=False, algorithm=algorithm), self) - else: - raise ValueError("Galois group type must be None, 'pari', or 'gap'.") + If `L/K` is a relative number field, this method will currently return `Gal(L/\QQ)`. This behavior will + change in the future, so it's better to explicitly call :meth:`absolute_field` if that is + the desired behavior:: + + sage: x = polygen(QQ) + sage: K.<a> = NumberField(x^2 + 1) + sage: R.<t> = PolynomialRing(K) + sage: L = K.extension(t^5-t+a, 'b') + sage: L.galois_group() + ...DeprecationWarning: Use .absolute_field().galois_group() if you want the Galois group of the absolute field + See https://trac.sagemath.org/28782 for details. + Galois group 10T22 (S(5)[x]2) with order 240 of t^5 - t + a + + TESTS: + + We check that the changes in :trac:`28782` won't break code that used v1 Galois groups:: + + sage: G = NumberField(x^3-2, 'a').galois_group(type="pari") + ...DeprecationWarning: the different Galois types have been merged into one class + See https://trac.sagemath.org/28782 for details. + sage: G.group() + ...DeprecationWarning: the group method is deprecated; you can use _pol_galgp if you really need it + See https://trac.sagemath.org/28782 for details. + PARI group [6, -1, 2, "S3"] of degree 3 + """ + if type is not None: + deprecation(28782, "the different Galois types have been merged into one class") + + from .galois_group import GaloisGroup_v2 + return GaloisGroup_v2(self, algorithm=algorithm, names=names, gc_numbering=gc_numbering, _type=type) def _normalize_prime_list(self, v): """ @@ -6276,87 +6346,6 @@ def _positive_integral_elements_with_trace(self, C): S.append(self(theta)) return S - def zeta_function(self, prec=53, - max_imaginary_part=0, - max_asymp_coeffs=40, algorithm=None): - r""" - Return the Dedekind zeta function of this number field. - - Actually, this returns an interface for computing with the - Dedekind zeta function `\zeta_F(s)` of the number field `F`. - - INPUT: - - - ``prec`` -- optional integer (default 53) bits precision - - - ``max_imaginary_part`` -- optional real number (default 0) - - - ``max_asymp_coeffs`` -- optional integer (default 40) - - - ``algorithm`` -- optional (default "gp") either "gp" or "pari" - - OUTPUT: The zeta function of this number field. - - If algorithm is "gp", this returns an interface to Tim - Dokchitser's gp script for computing with L-functions. - - If algorithm is "pari", this returns instead an interface to Pari's - own general implementation of L-functions. - - EXAMPLES:: - - sage: K.<a> = NumberField(ZZ['x'].0^2+ZZ['x'].0-1) - sage: Z = K.zeta_function(); Z - PARI zeta function associated to Number Field in a with defining polynomial x^2 + x - 1 - sage: Z(-1) - 0.0333333333333333 - sage: L.<a, b, c> = NumberField([x^2 - 5, x^2 + 3, x^2 + 1]) - sage: Z = L.zeta_function() - sage: Z(5) - 1.00199015670185 - - Using the algorithm "pari":: - - sage: K.<a> = NumberField(ZZ['x'].0^2+ZZ['x'].0-1) - sage: Z = K.zeta_function(algorithm="pari") - sage: Z(-1) - 0.0333333333333333 - sage: L.<a, b, c> = NumberField([x^2 - 5, x^2 + 3, x^2 + 1]) - sage: Z = L.zeta_function(algorithm="pari") - sage: Z(5) - 1.00199015670185 - """ - if algorithm is None: - algorithm = 'pari' - - if algorithm == 'gp': - from sage.lfunctions.all import Dokchitser - r1, r2 = self.signature() - zero = [0] - one = [1] - Z = Dokchitser(conductor=abs(self.absolute_discriminant()), - gammaV=(r1 + r2) * zero + r2 * one, - weight=1, - eps=1, - poles=[1], - prec=prec) - s = 'nf = nfinit(%s);' % self.absolute_polynomial() - s += 'dzk = dirzetak(nf,cflength());' - Z.init_coeffs('dzk[k]', pari_precode=s, - max_imaginary_part=max_imaginary_part, - max_asymp_coeffs=max_asymp_coeffs) - Z.check_functional_equation() - Z.rename('Dokchitser Zeta function associated to %s' % self) - return Z - - if algorithm == 'pari': - from sage.lfunctions.pari import lfun_number_field, LFunction - Z = LFunction(lfun_number_field(self), prec=prec) - Z.rename('PARI zeta function associated to %s' % self) - return Z - - raise ValueError('algorithm must be "gp" or "pari"') - @cached_method def narrow_class_group(self, proof=None): r""" @@ -8631,7 +8620,7 @@ def _galois_closure_and_embedding(self, names=None): this computation is feasible:: sage: K.<a> = NumberField(x^6 + 4*x^2 + 2) - sage: K.galois_group(type='pari').order() + sage: K.galois_group().order() 48 sage: L, phi = K._galois_closure_and_embedding('c') sage: phi.domain() is K, phi.codomain() is L @@ -8655,10 +8644,7 @@ def _galois_closure_and_embedding(self, names=None): pass # Compute degree of Galois closure if possible - try: - deg = self.galois_group(type='pari').order() - except NotImplementedError: - deg = None + deg = self.galois_group().easy_order() L, self_into_L = self.defining_polynomial().change_ring(self).splitting_field(names, map=True, degree_multiple=deg) self.__galois_closure = L @@ -8780,37 +8766,21 @@ def automorphisms(self): sage: prod(x - sigma(a) for sigma in A) == f.monic() True """ - try: - # this should be concordant with embeddings - return self.__embeddings[self] - except AttributeError: - self.__embeddings = {} - except KeyError: - pass - f = self.pari_polynomial('y') - # Compute the conjugates of Mod(x, f). - conj = self.pari_nf().nfgaloisconj() - # Convert these to conjugates of self.gen(). - P = self._pari_absolute_structure()[1].lift() - conj = sorted([self(P(g.Mod(f))) for g in conj]) - v = [self.hom([e]) for e in conj] # check=False here? - put_natural_embedding_first(v) - self.__embeddings[self] = Sequence(v, cr=(v != []), immutable=True, - check=False, universe=self.Hom(self)) - return self.__embeddings[self] + return self.embeddings(self) + @cached_method def embeddings(self, K): """ - Compute all field embeddings of self into the field K (which need + Compute all field embeddings of this field into the field K (which need 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 self into K is put first + If possible, the most natural embedding of this field into K is put first in the list. INPUT: - - ``K`` - a number field + - ``K`` - a field EXAMPLES:: @@ -8866,16 +8836,18 @@ def embeddings(self, K): sage: K.embeddings(GF(3)) [] """ - try: - # this should be concordant with automorphisms - return self.__embeddings[K] - except AttributeError: - self.__embeddings = {} - except KeyError: - pass if K is self: - return self.automorphisms() - if K.characteristic() != 0: + f = self.pari_polynomial('y') + # Compute the conjugates of Mod(x, f). + conj = self.pari_nf().nfgaloisconj() + # Convert these to conjugates of self.gen(). + P = self._pari_absolute_structure()[1].lift() + conj = sorted([self(P(g.Mod(f))) for g in conj]) + v = [self.hom([e]) for e in conj] # check=False here? + put_natural_embedding_first(v) + return Sequence(v, cr=(v != []), immutable=True, + check=False, universe=self.Hom(self)) + elif K.characteristic() != 0: return Sequence([], immutable=True, check=False, universe=self.Hom(K)) f = self.defining_polynomial() @@ -8884,10 +8856,8 @@ 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) - - self.__embeddings[K] = Sequence(v, cr=v!=[], immutable=True, - check=False, universe=self.Hom(K)) - return self.__embeddings[K] + return Sequence(v, cr=v!=[], immutable=True, + check=False, universe=self.Hom(K)) def minkowski_embedding(self, B=None, prec=None): r""" @@ -11060,6 +11030,46 @@ def complex_embedding(self, prec=53): CC = sage.rings.complex_mpfr.ComplexField(prec) return self.hom([CC.zeta(self._n())], check=False) + @cached_method + def embeddings(self, K): + r""" + Compute all field embeddings of this field into the field ``K``. + + INPUT: + + - ``K`` -- a field + + EXAMPLES:: + + sage: CyclotomicField(5).embeddings(ComplexField(53))[1] + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: Complex Field with 53 bits of precision + Defn: zeta5 |--> -0.809016994374947 + 0.587785252292473*I + sage: CyclotomicField(5).embeddings(Qp(11, 4, print_mode='digits'))[1] + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: 11-adic Field with capped relative precision 4 + Defn: zeta5 |--> ...1525 + """ + n = self._n() + if K.characteristic() == 0: + try: + z = K.zeta(n) + except ValueError: + # No nth root of unity + v = [] + except AttributeError: + # zeta not defined + return super(NumberField_cyclotomic, self).embeddings(K) + else: + 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 = [] + return Sequence(v, cr=True, immutable=True, + check=False, universe=self.Hom(K)) + def complex_embeddings(self, prec=53): r""" Return all embeddings of this cyclotomic field into the approximate @@ -11091,19 +11101,7 @@ def complex_embeddings(self, prec=53): ] """ CC = sage.rings.complex_mpfr.ComplexField(prec) - try: - return self.__embeddings[CC] - except AttributeError: - self.__embeddings = {} - except KeyError: - pass - n = self._n() - z = CC.zeta(n) - X = [m for m in range(n) if arith.gcd(m,n) == 1] - v = [self.hom([z**i], check=False) for i in X] - self.__embeddings[CC] = Sequence(v, cr=True, immutable=True, - check=False, universe=self.Hom(CC)) - return self.__embeddings[CC] + return self.embeddings(CC) def real_embeddings(self, prec=53): r""" @@ -11114,8 +11112,8 @@ def real_embeddings(self, prec=53): EXAMPLES:: - sage: CyclotomicField(4).real_embeddings() - [] + sage: len(CyclotomicField(4).real_embeddings()) + 0 sage: CyclotomicField(2).real_embeddings() [ Ring morphism: @@ -11125,12 +11123,7 @@ def real_embeddings(self, prec=53): ] """ K = sage.rings.real_mpfr.RealField(prec) - n = self._n() - if n > 2: - return Sequence([], cr=False, immutable=True, - check=False, universe=self.Hom(K)) - else: - return self.embeddings(K) + return self.embeddings(K) def signature(self): """ diff --git a/src/sage/rings/number_field/number_field_base.pyx b/src/sage/rings/number_field/number_field_base.pyx index 406bcfa7c3c..fd16dabe2e7 100644 --- a/src/sage/rings/number_field/number_field_base.pyx +++ b/src/sage/rings/number_field/number_field_base.pyx @@ -424,3 +424,46 @@ cdef class NumberField(Field): return self._gen_approx[i] else: raise ValueError("No embedding set. You need to specify a a real embedding.") + + def _matrix_charpoly(self, M, var): + r""" + Use PARI to compute the characteristic polynomial of self as a + polynomial over the base ring. + + EXAMPLES:: + + sage: x = QQ['x'].gen() + sage: K.<a> = NumberField(x^2 - 2) + sage: m = matrix(K, [[a-1, 2], [a, a+1]]) + sage: m.charpoly('Z') # indirect doctest + Z^2 - 2*a*Z - 2*a + 1 + sage: m.charpoly('a')(m) == 0 # indirect doctest + True + sage: m = matrix(K, [[0, a, 0], [-a, 0, 0], [0, 0, 0]]) + sage: m.charpoly('Z') # indirect doctest + Z^3 + 2*Z + + :: + + sage: L.<b> = K.extension(x^3 - a) + sage: m = matrix(L, [[b+a, 1], [a, b^2-2]]) + sage: m.charpoly('Z') # indirect doctest + Z^2 + (-b^2 - b - a + 2)*Z + a*b^2 - 2*b - 2*a + sage: m.charpoly('a') # indirect doctest + a^2 + (-b^2 - b - a + 2)*a + a*b^2 - 2*b - 2*a + sage: m.charpoly('a')(m) == 0 + True + + :: + + sage: M.<c> = L.extension(x^2 - a*x + b) + sage: m = matrix(M, [[a+b+c, 0, b], [0, c, 1], [a-1, b^2+1, 2]]) + sage: f = m.charpoly('Z'); f # indirect doctest + Z^3 + (-2*c - b - a - 2)*Z^2 + ((b + 2*a + 4)*c - b^2 + (-a + 2)*b + 2*a - 1)*Z + (b^2 + (a - 3)*b - 4*a + 1)*c + a*b^2 + 3*b + 2*a + sage: f(m) == 0 + True + sage: f.base_ring() is M + True + """ + paripoly = M.__pari__().charpoly() + return self[var](paripoly) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 562a76fa845..34bdd77c753 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl +# distutils: libraries = 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++ """ Number Field Elements 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 b261d26d6c9..e35315f1462 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl +# distutils: libraries = 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++ """ Optimized Quadratic Number Field Elements diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 2e6a368843e..01e5d479024 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -1417,7 +1417,7 @@ def decomposition_group(self): EXAMPLES:: sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() - Subgroup [(), (1,2)] of Galois group of Number Field in w with defining polynomial x^2 + 23 with w = 4.795831523312720?*I + Subgroup [(), (1,2)] of Galois group 2T1 (S2) with order 2 of x^2 + 23 """ return self.number_field().galois_group().decomposition_group(self) @@ -1433,9 +1433,9 @@ def ramification_group(self, v): EXAMPLES:: sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) - Subgroup [(), (1,2)] of Galois group of Number Field in w with defining polynomial x^2 + 23 with w = 4.795831523312720?*I + Subgroup [(), (1,2)] of Galois group 2T1 (S2) with order 2 of x^2 + 23 sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) - Subgroup [()] of Galois group of Number Field in w with defining polynomial x^2 + 23 with w = 4.795831523312720?*I + Subgroup [()] of Galois group 2T1 (S2) with order 2 of x^2 + 23 """ return self.number_field().galois_group().ramification_group(self, v) @@ -1451,7 +1451,7 @@ def inertia_group(self): EXAMPLES:: sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() - Subgroup [(), (1,2)] of Galois group of Number Field in w with defining polynomial x^2 + 23 with w = 4.795831523312720?*I + Subgroup [(), (1,2)] of Galois group 2T1 (S2) with order 2 of x^2 + 23 """ return self.ramification_group(0) diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 312cbdf874a..e103bd8e59a 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -2345,40 +2345,6 @@ def order(self, *gens, **kwds): gens = [self(x) for x in gens] return relative_order_from_ring_generators(gens, **kwds) - def galois_group(self, type='pari', algorithm='pari', names=None): - r""" - Return the Galois group of the Galois closure of this number - field as an abstract group. Note that even though this is an - extension `L/K`, the group will be computed as if it were `L/\QQ`. - - INPUT: - - - ``type`` - ``'pari'`` or ``'gap'``: type of object to return -- a - wrapper around a Pari or Gap transitive group object. - - - - algorithm -- 'pari', 'kash', 'magma' (default: 'pari', except when - the degree is >= 12 when 'kash' is tried) - - At present much less functionality is available for Galois groups of - relative extensions than absolute ones, so try the galois_group method - of the corresponding absolute field. - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: K.<a> = NumberField(x^2 + 1) - sage: R.<t> = PolynomialRing(K) - sage: L = K.extension(t^5-t+a, 'b') - sage: L.galois_group(type="pari") - Galois group PARI group [240, -1, 22, "S(5)[x]2"] of degree 10 of the Number Field in b with defining polynomial t^5 - t + a over its base field - """ - - if type is None: - raise NotImplementedError("Galois groups of relative extensions not implemented (use the corresponding absolute field)") - else: - # silly bug in cached_method - return NumberField_generic.galois_group.f(self, type, algorithm, names) - def is_free(self, proof=None): r""" Determine whether or not `L/K` is free (i.e. if `\mathcal{O}_L` is 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 acef5bcf43a..16a9e0d4ff7 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ `p`-Adic ``ZZ_pX`` CA Element 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 e4100d7e6f9..87d70edaf51 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ `p`-Adic ``ZZ_pX`` CR Element 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 2c8463bac71..c3cf8f96770 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ `p`-Adic ``ZZ_pX`` FM Element diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index 2f3da7cad62..420bf72703f 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ `p`-Adic ``ZZ_pX Element`` diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index c00d00a5fca..f1f663cc6da 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ p-Adic Extension Element diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index 0b1c346bf55..f0fea52b8d2 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = gmp ntl m +# distutils: libraries = NTL_LIBRARIES ntl m +# 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++ """ p-Adic Printing diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index 3092a2a9ff0..ec4ff3009dc 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ PowComputer diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 6f8f356af52..f4e60ee393b 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ """ PowComputer_ext diff --git a/src/sage/rings/padics/pow_computer_flint.pyx b/src/sage/rings/padics/pow_computer_flint.pyx index 42bebf3cc2a..3868c11a6f7 100644 --- a/src/sage/rings/padics/pow_computer_flint.pyx +++ b/src/sage/rings/padics/pow_computer_flint.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = gmp ntl +# 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++ from cysignals.memory cimport sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off diff --git a/src/sage/rings/padics/pow_computer_relative.pyx b/src/sage/rings/padics/pow_computer_relative.pyx index 9bd8a28634f..d80419863bb 100644 --- a/src/sage/rings/padics/pow_computer_relative.pyx +++ b/src/sage/rings/padics/pow_computer_relative.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp m +# distutils: libraries = NTL_LIBRARIES gmp m +# 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++ # -*- coding: utf-8 -*- r""" diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py index a96f5a30854..e7e8e4a8359 100644 --- a/src/sage/rings/polynomial/binary_form_reduce.py +++ b/src/sage/rings/polynomial/binary_form_reduce.py @@ -231,13 +231,13 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): w = z v0 = v0 - NJinv*G.subs({u: v0[0], t: v0[1]}) z = v0[1].constant_coefficient() + v0[0].constant_coefficient()*CF.gen(0) - err = z.diameter() # precision - zz = (w - z).abs() # difference in w and z + err = z.diameter() # precision + zz = (w - z).abs().lower() # difference in w and z else: # despite there is no break, this happens if err > error_limit or err.is_NaN(): raise ValueError("accuracy of Newton's root not within tolerance(%s > %s), increase precision" % (err, error_limit)) - if z.imag() <= z.diameter(): + if z.imag().upper() <= z.diameter(): raise ArithmeticError("Newton's method converged to z not in the upper half plane") z = z.center() diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index 51e5e4112a9..bcb4bbfdcf3 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -181,9 +181,9 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): [(-14.61803398874990?..., 1), (-12.3819660112501...? + 0.?e-27*I, 1)] sage: sorted((v[0][0].real(),v[1][0].real())) [-14.61803398874989?, -12.3819660112501...?] - sage: v[0][0].imag() < 1e25 + sage: v[0][0].imag().upper() < 1e25 True - sage: v[1][0].imag() < 1e25 + sage: v[1][0].imag().upper() < 1e25 True sage: K.<im> = QuadraticField(-1) diff --git a/src/sage/rings/polynomial/evaluation_ntl.pyx b/src/sage/rings/polynomial/evaluation_ntl.pyx index 3f256df8059..28b3917d3a1 100644 --- a/src/sage/rings/polynomial/evaluation_ntl.pyx +++ b/src/sage/rings/polynomial/evaluation_ntl.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl +# distutils: libraries = 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""" Fast evaluation of polynomials (Horner's rule) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index dfec6b2555d..611a355ab11 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -875,7 +875,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: R.<x,y> = K[] sage: f = x^2 + z*y sage: f.change_ring(K.embeddings(CC)[1]) - x^2 + (-0.500000000000000 + 0.866025403784439*I)*y + x^2 + (-0.500000000000000 - 0.866025403784438*I)*y TESTS: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 38bf2b01d17..130d8317a7a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2097,9 +2097,8 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): You can use Giac to compute the elimination ideal:: - sage: I.elimination_ideal([t, s], algorithm="giac") == J - ... - Running a probabilistic check for the reconstructed Groebner basis... + sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J + possible output... True The list of available Giac options is provided at @@ -2123,8 +2122,8 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): sage: J = I.elimination_ideal([t,s]); J Ideal (y^2 - x*z, x*y - z, x^2 - y) of Multivariate Polynomial Ring in x, y, t, s, z over Algebraic Field - sage: I.elimination_ideal([t, s], algorithm="giac") == J - Running a probabilistic check for the reconstructed Groebner basis... + sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J + possible output... True """ if not isinstance(variables, (list, tuple)): @@ -4018,8 +4017,8 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal sage: A9=PolynomialRing(QQ,9,'x') sage: I9=sage.rings.ideal.Katsura(A9) - sage: I9.groebner_basis("giac",proba_epsilon=1e-7) # long time (3s) - ...Running a probabilistic check for the reconstructed Groebner basis... + sage: print("possible output from giac", flush=True); I9.groebner_basis("giac",proba_epsilon=1e-7) # long time (3s) + possible output... Polynomial Sequence with 143 Polynomials in 9 Variables The list of available Giac options is provided at :func:`sage.libs.giac.groebner_basis`. diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 836afdcdcd7..b3bb733d7aa 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -663,7 +663,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): sage: P(B.gen(0)) x - If everything else fails, we try to coerce to the base ring:: + If everything else fails, we try to convert to the base ring:: sage: R.<x,y,z> = GF(3)[] sage: R(1/2) @@ -777,9 +777,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): cdef poly *mon cdef poly *El_poly cdef ring *_ring = self._ring - cdef number *_n cdef ring *El_ring - cdef long mpos cdef MPolynomial_libsingular Element cdef MPolynomialRing_libsingular El_parent cdef int i, j @@ -787,27 +785,61 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): cdef list ind_map = [] cdef sBucket *bucket - if _ring!=currRing: rChangeCurrRing(_ring) + if _ring != currRing: rChangeCurrRing(_ring) - base_ring = self.base_ring() + base_ring = self._base if isinstance(element, MPolynomial_libsingular): - n = (<MPolynomial_libsingular>element)._parent.ngens() - if element.parent() is self: + Element = <MPolynomial_libsingular>element + El_parent = Element._parent + if El_parent is self: return element - elif(base_ring is element.base_ring() and - self.ngens() >= n and - self.variable_names()[:n] == (<MPolynomial_libsingular>element)._parent.variable_names()): - if self.term_order() == (<MPolynomial_libsingular>element)._parent.term_order(): - _p = prCopyR_NoSort((<MPolynomial_libsingular>element)._poly, - (<MPolynomial_libsingular>element)._parent_ring, - _ring) + El_poly = Element._poly + El_ring = Element._parent_ring + El_base = El_parent._base + El_n = El_parent.ngens() + if (base_ring is El_base and self.ngens() >= El_n + and self.variable_names()[:El_n] == El_parent.variable_names()): + if self.term_order() == El_parent.term_order(): + _p = prCopyR_NoSort(El_poly, El_ring, _ring) else: - _p = prCopyR((<MPolynomial_libsingular>element)._poly, - (<MPolynomial_libsingular>element)._parent_ring, _ring) + _p = prCopyR(El_poly, El_ring, _ring) return new_MP(self, _p) - elif base_ring.has_coerce_map_from(element.parent()._mpoly_base_ring(self.variable_names())): - return self(element._mpoly_dict_recursive(self.variable_names(), base_ring)) + variable_names_t = self.variable_names() + if base_ring.has_coerce_map_from(El_parent._mpoly_base_ring(variable_names_t)): + return self(element._mpoly_dict_recursive(variable_names_t, base_ring)) + else: + variable_names_s = El_parent.variable_names() + if set(variable_names_s).issubset(variable_names_t): + for v in variable_names_s: + ind_map.append(variable_names_t.index(v)+1) + else: + ind_map = [i+1 for i in range(_ring.N)] + + if El_n <= self.ngens(): + # Map the variables by indices + _p = p_ISet(0, _ring) + + #this loop needs improvement + while El_poly: + c = si2sa(p_GetCoeff(El_poly, El_ring), El_ring, El_base) + if check: + try: + c = base_ring(c) + except TypeError: + p_Delete(&_p, _ring) + raise + if c: + mon = p_Init(_ring) + p_SetCoeff(mon, sa2si(c, _ring), _ring) + for j from 1 <= j <= El_ring.N: + e = p_GetExp(El_poly, j, El_ring) + if e: + p_SetExp(mon, ind_map[j-1], e, _ring) + p_Setm(mon, _ring) + _p = p_Add_q(_p, mon, _ring) + El_poly = pNext(El_poly) + return new_MP(self, _p) elif isinstance(element, MPolynomial_polydict): if element.parent() == self: @@ -838,77 +870,62 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): return new_MP(self, _p) elif base_ring.has_coerce_map_from(element.parent()._mpoly_base_ring(self.variable_names())): return self(element._mpoly_dict_recursive(self.variable_names(), base_ring)) + else: + variable_names_s = element.parent().variable_names() + variable_names_t = self.variable_names() + + if set(variable_names_s).issubset(variable_names_t): + for v in variable_names_s: + ind_map.append(variable_names_t.index(v)+1) + else: + ind_map = [i+1 for i in range(_ring.N)] + + if element.parent().ngens() <= self.ngens(): + bucket = sBucketCreate(_ring) + try: + for (m,c) in element.element().dict().iteritems(): + if check: + c = base_ring(c) + if not c: + continue + mon = p_Init(_ring) + p_SetCoeff(mon, sa2si(c , _ring), _ring) + for pos in m.nonzero_positions(): + overflow_check(m[pos], _ring) + p_SetExp(mon, ind_map[pos], m[pos], _ring) + p_Setm(mon, _ring) + sBucket_Merge_m(bucket, mon) + e=0 + sBucketClearMerge(bucket, &_p, &e) + sBucketDestroy(&bucket) + except TypeError: + sBucketDeleteAndDestroy(&bucket) + raise + return new_MP(self, _p) elif isinstance(element, polynomial_element.Polynomial): if base_ring.has_coerce_map_from(element.parent()._mpoly_base_ring(self.variable_names())): return self(element._mpoly_dict_recursive(self.variable_names(), base_ring)) - if isinstance(element, (SingularElement, cypari2.gen.Gen)): - element = str(element) - - if isinstance(element, MPolynomial_libsingular) and element.parent() is not self and element.parent() != self: - variable_names_s = element.parent().variable_names() - variable_names_t = self.variable_names() - - if set(variable_names_s).issubset(variable_names_t): - for v in variable_names_s: - ind_map.append(variable_names_t.index(v)+1) - else: - ind_map = [i+1 for i in range(_ring.N)] - - if element.parent().ngens() <= self.ngens(): - # Map the variables by indices + elif isinstance(element, dict): + if not element: _p = p_ISet(0, _ring) - Element = <MPolynomial_libsingular>element - El_poly = Element._poly - El_parent = Element._parent - El_ring = Element._parent_ring - El_base = El_parent._base - - #this loop needs improvement - while El_poly: - c = si2sa(p_GetCoeff(El_poly, El_ring), El_ring, El_base) - if check: - try: - c = base_ring(c) - except TypeError: - p_Delete(&_p, _ring) - raise - if c: - mon = p_Init(_ring) - p_SetCoeff(mon, sa2si(c, _ring), _ring) - for j from 1 <= j <= El_ring.N: - e = p_GetExp(El_poly, j, El_ring) - if e: - p_SetExp(mon, ind_map[j-1], e, _ring) - p_Setm(mon, _ring) - _p = p_Add_q(_p, mon, _ring) - El_poly = pNext(El_poly) - return new_MP(self, _p) - - if isinstance(element, MPolynomial_polydict): - variable_names_s = element.parent().variable_names() - variable_names_t = self.variable_names() - - if set(variable_names_s).issubset(variable_names_t): - for v in variable_names_s: - ind_map.append(variable_names_t.index(v)+1) else: - ind_map = [i+1 for i in range(_ring.N)] - - if element.parent().ngens() <= self.ngens(): bucket = sBucketCreate(_ring) try: - for (m,c) in element.element().dict().iteritems(): + for (m,c) in element.iteritems(): if check: c = base_ring(c) if not c: continue mon = p_Init(_ring) p_SetCoeff(mon, sa2si(c , _ring), _ring) - for pos in m.nonzero_positions(): - overflow_check(m[pos], _ring) - p_SetExp(mon, ind_map[pos], m[pos], _ring) + if len(m) != self.ngens(): + raise TypeError("tuple key must have same length as ngens") + for pos from 0 <= pos < len(m): + if m[pos]: + overflow_check(m[pos], _ring) + p_SetExp(mon, pos+1, m[pos], _ring) p_Setm(mon, _ring) sBucket_Merge_m(bucket, mon) e=0 @@ -917,7 +934,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): except TypeError: sBucketDeleteAndDestroy(&bucket) raise - return new_MP(self, _p) + return new_MP(self, _p) from sage.rings.polynomial.pbori.pbori import BooleanPolynomial if isinstance(element, BooleanPolynomial): @@ -938,6 +955,11 @@ 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)): + element = str(element) + elif is_Macaulay2Element(element): + element = element.external_string() + if isinstance(element, str): # let python do the parsing d = self.gens_dict() @@ -956,43 +978,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): # element in self. return self._coerce_c(element) - if isinstance(element, dict): - if len(element)==0: - _p = p_ISet(0, _ring) - else: - bucket = sBucketCreate(_ring) - try: - for (m,c) in element.iteritems(): - if check: - c = base_ring(c) - if not c: - continue - mon = p_Init(_ring) - p_SetCoeff(mon, sa2si(c , _ring), _ring) - if len(m) != self.ngens(): - raise TypeError("tuple key must have same length as ngens") - for pos from 0 <= pos < len(m): - if m[pos]: - overflow_check(m[pos], _ring) - p_SetExp(mon, pos+1, m[pos], _ring) - p_Setm(mon, _ring) - sBucket_Merge_m(bucket, mon) - e=0 - sBucketClearMerge(bucket, &_p, &e) - sBucketDestroy(&bucket) - except TypeError: - sBucketDeleteAndDestroy(&bucket) - raise - return new_MP(self, _p) - - try: #if hasattr(element,'_polynomial_'): - # SymbolicVariable + if hasattr(element,'_polynomial_'): # symbolic.expression.Expression return element._polynomial_(self) - except AttributeError: - pass - if is_Macaulay2Element(element): - return self(element.external_string()) try: return self(str(element)) except TypeError: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e1fc8972917..37dc468e80a 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3237,8 +3237,8 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: K.<z> = CyclotomicField(3) sage: R.<x> = K[] sage: f = x^2 + z - sage: f.change_ring(K.embeddings(CC)[0]) - x^2 - 0.500000000000000 - 0.866025403784439*I + sage: f.change_ring(K.embeddings(CC)[1]) + x^2 - 0.500000000000000 - 0.866025403784438*I :: diff --git a/src/sage/rings/polynomial/polynomial_gf2x.pyx b/src/sage/rings/polynomial/polynomial_gf2x.pyx index 726bbb43e90..8149a3ba6e5 100644 --- a/src/sage/rings/polynomial/polynomial_gf2x.pyx +++ b/src/sage/rings/polynomial/polynomial_gf2x.pyx @@ -1,6 +1,8 @@ -# distutils: libraries = gmp ntl -# distutils: extra_compile_args = M4RI_CFLAGS -# distutils: include_dirs = M4RI_INCDIR +# distutils: libraries = gmp NTL_LIBRARIES +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA +# distutils: extra_compile_args = NTL_CFLAGS M4RI_CFLAGS +# distutils: include_dirs = NTL_INCDIR M4RI_INCDIR # distutils: language = c++ """ Univariate Polynomials over GF(2) via NTL's GF2X diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 0337b39d55d..0e2223b128e 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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++ """ Dense univariate polynomials over `\ZZ`, implemented using FLINT diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index b6a9c514992..7693cd29c84 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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`, implemented using NTL. diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 9cc44743fb0..77f64bf3b99 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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++ """ Dense univariate polynomials over `\ZZ/n\ZZ`, implemented using NTL diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 441f901c1af..2dfe570bd12 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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""" Univariate polynomials over `\QQ` implemented via FLINT @@ -56,7 +60,7 @@ from sage.misc.cachefunc import cached_method cdef inline bint _do_sig(fmpq_poly_t op): """ - Returns 1 when signal handling should be carried out for an operation + Return 1 when signal handling should be carried out for an operation on this polynomial and 0 otherwise. Strictly speaking, whether or not signal handling should be carried @@ -315,7 +319,7 @@ cdef class Polynomial_rational_flint(Polynomial): def __copy__(self): """ - Returns a copy of self. + Return a copy of self. TESTS:: @@ -330,7 +334,7 @@ cdef class Polynomial_rational_flint(Polynomial): def _singular_(self, singular=singular_default, have_ring=False): """ - Returns a Singular representation of self. + Return a Singular representation of self. INPUT: @@ -551,7 +555,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef Polynomial truncate(self, long n): """ - Returns self truncated modulo `t^n`. + Return self truncated modulo `t^n`. INPUT: @@ -719,7 +723,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef bint is_zero(self) except -1: """ - Returns whether or not self is the zero polynomial. + Return whether or not self is the zero polynomial. EXAMPLES:: @@ -734,7 +738,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef bint is_one(self) except -1: r""" - Returns whether or not this polynomial is one. + Return whether or not this polynomial is one. EXAMPLES:: @@ -754,7 +758,7 @@ cdef class Polynomial_rational_flint(Polynomial): def __nonzero__(self): """ - Returns whether or not self is non-zero. + Return whether or not self is non-zero. EXAMPLES:: @@ -838,7 +842,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _add_(self, right): """ - Returns the sum of two rational polynomials. + Return the sum of two rational polynomials. EXAMPLES:: @@ -866,7 +870,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _sub_(self, right): """ - Returns the difference of two rational polynomials. + Return the difference of two rational polynomials. EXAMPLES:: @@ -894,7 +898,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _neg_(self): """ - Returns the difference of two rational polynomials. + Return the difference of two rational polynomials. EXAMPLES:: @@ -921,7 +925,7 @@ cdef class Polynomial_rational_flint(Polynomial): @coerce_binop def quo_rem(self, right): """ - Returns the quotient and remainder of the Euclidean division of + Return the quotient and remainder of the Euclidean division of self and right. Raises a ZerodivisionError if right is zero. @@ -952,7 +956,7 @@ cdef class Polynomial_rational_flint(Polynomial): @coerce_binop def gcd(self, right): """ - Returns the (monic) greatest common divisor of self and right. + Return the (monic) greatest common divisor of self and right. Corner cases: if self and right are both zero, returns zero. If only one of them is zero, returns the other polynomial, up to @@ -981,7 +985,7 @@ cdef class Polynomial_rational_flint(Polynomial): @coerce_binop def lcm(self, right): """ - Returns the monic (or zero) least common multiple of self and right. + Return the monic (or zero) least common multiple of self and right. Corner cases: if either of self and right are zero, returns zero. This behaviour is ensures that the relation lcm(a,b) gcd(a,b) == a b @@ -1008,7 +1012,7 @@ cdef class Polynomial_rational_flint(Polynomial): @coerce_binop def xgcd(self, right): """ - Returns polynomials d, s, and t such that d == s * self + t * right, + Return polynomials d, s, and t such that d == s * self + t * right, where d is the (monic) greatest common divisor of self and right. The choice of s and t is not specified any further. @@ -1048,7 +1052,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _mul_(self, right): """ - Returns the product of self and right. + Return the product of self and right. EXAMPLES:: @@ -1114,7 +1118,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _rmul_(self, Element left): r""" - Returns left * self, where left is a rational number. + Return left * self, where left is a rational number. EXAMPLES:: @@ -1134,7 +1138,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _lmul_(self, Element right): r""" - Returns self * right, where right is a rational number. + Return self * right, where right is a rational number. EXAMPLES:: @@ -1154,7 +1158,7 @@ cdef class Polynomial_rational_flint(Polynomial): def __pow__(Polynomial_rational_flint self, exp, ignored): """ - Returns self raised to the power of exp. + Return self raised to the power of exp. The corner case of ``exp == 0`` is handled by returning the constant polynomial 1. Note that this includes the case ``0^0 == 1``. @@ -1297,7 +1301,7 @@ cdef class Polynomial_rational_flint(Polynomial): def __floordiv__(Polynomial_rational_flint self, right): """ - Returns the quotient of self and right obtain by Euclidean division. + Return the quotient of self and right obtain by Euclidean division. EXAMPLES:: @@ -1394,7 +1398,7 @@ cdef class Polynomial_rational_flint(Polynomial): cpdef _mod_(self, right): """ - Returns the remainder of self and right obtain by Euclidean division. + Return the remainder of self and right obtain by Euclidean division. EXAMPLES:: @@ -1433,7 +1437,7 @@ cdef class Polynomial_rational_flint(Polynomial): def numerator(self): """ - Returns the numerator of self. + Return the numerator of self. Representing self as the quotient of an integer polynomial and a positive integer denominator (coprime to the content of the @@ -1458,7 +1462,7 @@ cdef class Polynomial_rational_flint(Polynomial): def denominator(self): """ - Returns the denominator of self. + Return the denominator of self. EXAMPLES:: @@ -1525,7 +1529,7 @@ cdef class Polynomial_rational_flint(Polynomial): def real_root_intervals(self): """ - Returns isolating intervals for the real roots of self. + Return isolating intervals for the real roots of self. EXAMPLES: @@ -1543,7 +1547,7 @@ cdef class Polynomial_rational_flint(Polynomial): @coerce_binop def resultant(Polynomial_rational_flint self, right): r""" - Returns the resultant of self and right. + Return the resultant of self and right. Enumerating the roots over `\QQ` as `r_1, \cdots, r_m` and `s_1, \cdots, s_n` and letting `x` and `y` denote the leading @@ -2054,9 +2058,9 @@ cdef class Polynomial_rational_flint(Polynomial): # Methods using PARI # ########################################################################### - def galois_group(self, pari_group = False, algorithm = 'pari'): + def galois_group(self, pari_group=False, algorithm='pari'): """ - Returns the Galois group of self as a permutation group. + Return the Galois group of this polynomial as a permutation group. INPUT: @@ -2068,9 +2072,10 @@ cdef class Polynomial_rational_flint(Polynomial): up a group in Gap. To get a permutation group from a PARI group ``P``, type ``PermutationGroup(P)``. - - ``algorithm`` - ``'pari'``, ``'kash'``, ``'magma'`` (default: - ``'pari'``, except when the degree is at least 12 in which case - ``'kash'`` is tried). + - ``algorithm`` - ``'pari'``, ``'gap'``, ``'kash'``, ``'magma'`` (default: + ``'pari'``, for degrees is at most 11; + ``'gap'``, for degrees from 12 to 15; + ``'kash'``, for degrees from 16 or more). OUTPUT: @@ -2079,7 +2084,7 @@ cdef class Polynomial_rational_flint(Polynomial): ALGORITHM: The Galois group is computed using PARI in C library mode, or possibly - KASH or MAGMA. + GAP, KASH, or MAGMA. .. NOTE:: @@ -2089,6 +2094,9 @@ cdef class Polynomial_rational_flint(Polynomial): rare cases, a wrong result may be returned if the initial precision was not sufficient. + GAP needs an optional transitive group library installed, + from database_gap spkg. + MAGMA does not return a provably correct result. Please see the MAGMA documentation for how to obtain a provably correct result. @@ -2116,10 +2124,10 @@ cdef class Polynomial_rational_flint(Polynomial): sage: PermutationGroup(G) Transitive group number 5 of degree 4 - You can use KASH to compute Galois groups as well. The advantage is - that KASH can compute Galois groups of fields up to degree 21, whereas - PARI only goes to degree 11. (In my not-so-thorough experiments PARI - is faster than KASH.) + You can use KASH or GAP to compute Galois groups as well. The advantage is + that KASH (resp. GAP) can compute Galois groups of fields up to + degree 23 (resp. 15), whereas PARI only goes to degree 11. + (In my not-so-thorough experiments PARI is faster than KASH.) :: @@ -2128,6 +2136,14 @@ cdef class Polynomial_rational_flint(Polynomial): Transitive group number 5 of degree 4 sage: f = x^4 - 17*x^3 - 2*x + 1 + sage: f.galois_group(algorithm='gap') # optional - database_gap + Transitive group number 5 of degree 4 + sage: f = x^13 - 17*x^3 - 2*x + 1 + sage: f.galois_group(algorithm='gap') # optional - database_gap + Transitive group number 9 of degree 13 + sage: f = x^12 - 2*x^8 - x^7 + 2*x^6 + 4*x^4 - 2*x^3 - x^2 - x + 1 + sage: f.galois_group(algorithm='gap') # optional - database_gap + Transitive group number 183 of degree 12 sage: f.galois_group(algorithm='magma') # optional - magma Transitive group number 5 of degree 4 @@ -2156,7 +2172,10 @@ cdef class Polynomial_rational_flint(Polynomial): raise ValueError("The polynomial must be irreducible") if self.degree() > 11 and algorithm == 'pari': - algorithm = 'kash' + if self.degree() < 16: + algorithm = 'gap' + else: + algorithm = 'kash' if self.degree() > 21 and algorithm == 'kash': raise NotImplementedError("Galois group computation is " @@ -2189,6 +2208,15 @@ cdef class Polynomial_rational_flint(Polynomial): "supports degrees up to 21, or use algorithm='magma' if " + "you have magma.") + elif algorithm == 'gap': + if self.degree() > 15: + raise NotImplementedError("Galois group computation is " + + "supported for degrees up to 15 using GAP. Try " + + "algorithm='kash'.") + from sage.libs.gap.libgap import libgap + fgap = libgap(self) + return TransitiveGroup(self.degree(), fgap.GaloisType()) + elif algorithm == 'magma': from sage.interfaces.all import magma X = magma(self).GaloisGroup() @@ -2207,7 +2235,7 @@ cdef class Polynomial_rational_flint(Polynomial): def factor_mod(self, p): """ - Returns the factorization of self modulo the prime ``p``. + Return the factorization of self modulo the prime ``p``. Assumes that the degree of this polynomial is at least one, and raises a ``ValueError`` otherwise. @@ -2331,7 +2359,7 @@ cdef class Polynomial_rational_flint(Polynomial): monic factors, computes the Hensel lifts of these factors modulo `p^e`. We assume that ``self`` has integer coefficients. - Returns an empty list if this polynomial has degree less than one. + Return an empty list if this polynomial has degree less than one. INPUT: @@ -2406,7 +2434,7 @@ cdef class Polynomial_rational_flint(Polynomial): def discriminant(self): r""" - Returns the discriminant of this polynomial. + Return the discriminant of this polynomial. The discriminant `R_n` is defined as diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index e454d28768b..fc7898dde64 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = gmp ntl zn_poly +# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# 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++ """ Dense univariate polynomials over `\ZZ/n\ZZ`, implemented using FLINT diff --git a/src/sage/rings/polynomial/polynomial_zz_pex.pyx b/src/sage/rings/polynomial/polynomial_zz_pex.pyx index e8117befcd3..c9f2a309365 100644 --- a/src/sage/rings/polynomial/polynomial_zz_pex.pyx +++ b/src/sage/rings/polynomial/polynomial_zz_pex.pyx @@ -1,4 +1,8 @@ -# distutils: libraries = ntl gmp +# distutils: libraries = NTL_LIBRARIES gmp +# 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++ """ Univariate Polynomials over GF(p^e) via NTL's ZZ_pEX diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index 67bba231a14..b2be674256d 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -610,8 +610,9 @@ cdef class PowerSeries_poly(PowerSeries): that `XY = 1`). The first nonzero coefficient must be a unit in - the coefficient ring. If the valuation of the series is positive, - this function will return a :doc:`laurent_series_ring_element`. + the coefficient ring. If the valuation of the series is positive or + `X` is not a unit, this function will return a + :class:`sage.rings.laurent_series_ring_element.LaurentSeries`. EXAMPLES:: @@ -622,13 +623,12 @@ cdef class PowerSeries_poly(PowerSeries): 1 - q + q^2 - q^3 + q^4 - q^5 + q^6 - q^7 + q^8 - q^9 + q^10 - q^11 + q^12 - q^13 + q^14 - q^15 + q^16 - q^17 + q^18 - q^19 + O(q^20) sage: prec = R.default_prec(); prec 20 - sage: R.set_default_prec(5) - sage: 1/(1+q) + sage: 1/(1+q) + O(q^5) 1 - q + q^2 - q^3 + q^4 + O(q^5) :: - sage: 1/(q + q^2) + sage: 1/(q + q^2) + O(q^4) q^-1 - 1 + q - q^2 + q^3 + O(q^4) sage: g = 1/(q + q^2 + O(q^5)) sage: g; g.parent() @@ -644,13 +644,12 @@ cdef class PowerSeries_poly(PowerSeries): :: - sage: 1/(2 + q) + sage: 1/(2 + q) + O(q^5) 1/2 - 1/4*q + 1/8*q^2 - 1/16*q^3 + 1/32*q^4 + O(q^5) :: - sage: R.<q> = QQ[['q']] - sage: R.set_default_prec(5) + sage: R.<q> = PowerSeriesRing(QQ, name='q', default_prec=5) sage: f = 1 + q + q^2 + O(q^50) sage: f/10 1/10 + 1/10*q + 1/10*q^2 + O(q^50) @@ -666,15 +665,32 @@ cdef class PowerSeries_poly(PowerSeries): sage: u*v 1 + O(t^12) - We try a non-zero, non-unit leading coefficient:: + If we try a non-zero, non-unit constant term, we end up in + the fraction field, i.e. the Laurent series ring:: sage: R.<t> = PowerSeriesRing(ZZ) sage: ~R(2) - Traceback (most recent call last): - ... - ValueError: constant term is not a unit + 1/2 + sage: parent(~R(2)) + Laurent Series Ring in t over Rational Field + + As for units, we stay in the power series ring:: + sage: ~R(-1) -1 + sage: parent(~R(-1)) + Power Series Ring in t over Integer Ring + + However, inversion of non-unit elements must fail when the underlying + ring is not an integral domain:: + + sage: R = IntegerModRing(8) + sage: P.<s> = R[[]] + sage: ~P(2) + Traceback (most recent call last): + ... + ValueError: must be an integral domain + """ if self.is_one(): return self @@ -686,7 +702,12 @@ cdef class PowerSeries_poly(PowerSeries): # constant series a = self[0] if not a.is_unit(): - raise ValueError("constant term is not a unit") + from sage.categories.integral_domains import IntegralDomains + if self._parent in IntegralDomains(): + R = self._parent.fraction_field() + return 1 / R(a) + else: + raise ValueError('must be an integral domain') try: a = a.inverse_unit() except (AttributeError, NotImplementedError): diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 0c41ab3bec3..1bca8fa5f3b 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -35,6 +35,17 @@ sage: S = R([1, 3, 5, 7]); S 1 + 3*t + 5*t^2 + 7*t^3 +The default precision of a power series ring stays fixed and cannot be +changed. To work with different default precision, create a new power series +ring:: + + sage: R.<x> = PowerSeriesRing(QQ, default_prec=10) + sage: sin(x) + x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 + O(x^10) + sage: R.<x> = PowerSeriesRing(QQ, default_prec=15) + sage: sin(x) + x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 - 1/39916800*x^11 + 1/6227020800*x^13 + O(x^15) + An iterated example:: sage: R.<t> = PowerSeriesRing(ZZ) @@ -730,7 +741,8 @@ def _element_constructor_(self, f, prec=infinity, check=True): sage: R(1/x, 5) Traceback (most recent call last): ... - TypeError: no canonical coercion from Laurent Series Ring in t over Integer Ring to Power Series Ring in t over Integer Ring + TypeError: no canonical coercion from Laurent Series Ring in t over + Rational Field to Power Series Ring in t over Integer Ring sage: PowerSeriesRing(PowerSeriesRing(QQ,'x'),'y')(x) x @@ -860,7 +872,8 @@ def _coerce_impl(self, x): sage: R._coerce_(1/t) Traceback (most recent call last): ... - TypeError: no canonical coercion from Laurent Series Ring in t over Integer Ring to Power Series Ring in t over Integer Ring + TypeError: no canonical coercion from Laurent Series Ring in t over + Rational Field to Power Series Ring in t over Integer Ring sage: R._coerce_(5) 5 sage: tt = PolynomialRing(ZZ,'t').gen() @@ -1253,14 +1266,18 @@ def laurent_series_ring(self): self.base_ring(), self.variable_name(), default_prec=self.default_prec(), sparse=self.is_sparse()) return self.__laurent_series_ring + class PowerSeriesRing_domain(PowerSeriesRing_generic, ring.IntegralDomain): def fraction_field(self): """ - Return the fraction field of this power series ring, which is - defined since this is over a domain. + Return the Laurent series ring over the fraction field of the base + ring. - This fraction field is just the Laurent series ring over the - fraction field of the base ring. + This is actually *not* the fraction field of this ring, but its + completion with respect to the topology defined by the valuation. + When we are working at finite precision, these two fields are + indistinguishable; that is the reason why we allow ourselves to + make this confusion here. EXAMPLES:: @@ -1295,6 +1312,7 @@ def fraction_field(self): """ return self.laurent_series_ring() + def unpickle_power_series_ring_v0(base_ring, name, default_prec, sparse): """ Unpickle (deserialize) a univariate power series ring according to diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 2da0686b411..a021390d012 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1064,6 +1064,24 @@ cdef class PowerSeries(AlgebraElement): t + O(t^21) sage: (t^5/(t^2 - 2)) * (t^2 -2 ) t^5 + O(t^25) + + TESTS: + + The following tests against bugs that were fixed in :trac:`8972`:: + + sage: P.<t> = ZZ[] + sage: R.<x> = P[[]] + sage: 1/(t*x) + 1/t*x^-1 + sage: R.<x> = ZZ[[]] + sage: (1/x).parent() + Laurent Series Ring in x over Rational Field + sage: F = FractionField(R) + sage: 1/x in F + True + sage: (1/(2*x)).parent() + Laurent Series Ring in x over Rational Field + """ denom = <PowerSeries>denom_r if denom.is_zero(): @@ -1073,8 +1091,11 @@ cdef class PowerSeries(AlgebraElement): v = denom.valuation() if v > self.valuation(): - R = self._parent.laurent_series_ring() - return R(self)/R(denom) + try: + R = self._parent.fraction_field() + except (TypeError, NotImplementedError): # no integral domain + R = self._parent.laurent_series_ring() + return R(self) / R(denom) # Algorithm: Cancel common factors of q from top and bottom, # then invert the denominator. We do the cancellation first diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 5aee59a518e..f3d3f0e6a83 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -6869,9 +6869,9 @@ def _real_refine_interval(self, interval, prec): newton_lower = not newton_lower if newton_lower: - interval = interval.intersection(l - pl/slope) + interval = interval.intersection(field(l) - pl/slope) else: - interval = interval.intersection(u - pu/slope) + interval = interval.intersection(field(u) - pu/slope) new_diam = interval.diameter() if new_diam == 0: diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index d977e6a72fb..72396d24f3e 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -1,4 +1,9 @@ -# distutils: libraries = ntl +# distutils: libraries = 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""" Rational Numbers diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 720833f9908..b2152df48df 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1099,6 +1099,24 @@ def order(self): from sage.rings.infinity import Infinity return Infinity + def polynomial(self): + r"""Return a defining polynomial of `\QQ`, as for other number fields. + + This is is also aliased to :meth:`self.defining_polynomial()` + and :meth:`self.absolute_polynomial()`. + + EXAMPLES:: + + sage: QQ.polynomial() + x + + """ + from sage.rings.polynomial.polynomial_ring import polygen + return polygen(self) + + defining_polynomial = polynomial + absolute_polynomial = polynomial + def _an_element_(self): r""" Return an element of `\QQ`. diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 24c652f9958..a87d9903dda 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -115,7 +115,7 @@ satisfying, but we have chosen the latter. sage: a = R(1.25) sage: a.str(style='brackets') '[1.2 .. 1.3]' - sage: a == 1.25 + sage: a == 5/4 True sage: a == 2 False @@ -228,21 +228,17 @@ specified if given a non-interval and an interval:: sage: val.overlaps(ref) # known bug False -TESTS: - -Comparisons with numpy types are right (see :trac:`17758` and :trac:`18076`):: +TESTS:: sage: import numpy - sage: RIF(0,1) < numpy.float('2') - True - sage: RIF(0,1) <= numpy.float('1') - True - sage: RIF(0,1) <= numpy.float('0.5') - False sage: RIF(2) == numpy.int8('2') True sage: numpy.int8('2') == RIF(2) True + sage: RIF(0,1) < float('2') + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for <: ... """ # **************************************************************************** @@ -269,6 +265,7 @@ from sage.arith.constants cimport LOG_TEN_TWO_PLUS_EPSILON cimport sage.structure.element from sage.structure.element cimport RingElement, Element, ModuleElement +from sage.structure.element cimport have_same_parent from sage.structure.parent cimport Parent from sage.structure.richcmp cimport richcmp @@ -289,6 +286,7 @@ import operator from sage.cpython.string cimport char_to_str, bytes_to_str +from sage.misc.superseded import deprecation import sage.rings.infinity #***************************************************************************** @@ -759,14 +757,11 @@ cdef class RealIntervalField_class(Field): - this mpfi field itself - - any mpfr real field with precision that is as large as this - one - - any other mpfi real field with precision that is as large as this one - - anything that canonically coerces to the mpfr real field - with same precision as ``self``. + - some exact or lazy parents representing subsets of the real + numbers, such as ``ZZ``, ``QQ``, ``AA``, and ``RLF``. Values which can be exactly represented as a floating-point number are coerced to a precise interval, with upper and lower bounds @@ -781,12 +776,13 @@ cdef class RealIntervalField_class(Field): To: Real Interval Field with 53 bits of precision sage: phi(3^100) 5.153775207320114?e47 - sage: phi = RIF.coerce_map_from(float); phi - Coercion map: - From: Set of Python objects of class 'float' + + :: + + sage: phi = RIF.coerce_map_from(AA); phi + Conversion via _real_mpfi_ method map: + From: Algebraic Real Field To: Real Interval Field with 53 bits of precision - sage: phi(math.pi) - 3.1415926535897932? Coercion can decrease precision, but not increase it:: @@ -794,35 +790,29 @@ cdef class RealIntervalField_class(Field): Coercion map: From: Real Interval Field with 100 bits of precision To: Real Interval Field with 53 bits of precision - sage: phi = RIF.coerce_map_from(RealField(100)); phi - Coercion map: - From: Real Field with 100 bits of precision - To: Real Interval Field with 53 bits of precision sage: print(RIF.coerce_map_from(RealIntervalField(20))) None - sage: print(RIF.coerce_map_from(RealField(20))) - None - :: + There are no coercions from plain floating-point numbers to intervals + (otherwise the rounding errors resulting from floating-point operations + could easily lead to incorrect interval results):: - sage: phi = RIF.coerce_map_from(AA); phi - Conversion via _real_mpfi_ method map: - From: Algebraic Real Field - To: Real Interval Field with 53 bits of precision + sage: RIF.has_coerce_map_from(RR) + False + sage: RIF.has_coerce_map_from(RDF) + False + sage: RIF.has_coerce_map_from(float) + False """ prec = self.__prec # Direct and efficient conversions if S is ZZ or S is QQ: return True - if S is float or S is int or S is long: + if S is int or S is long: return True if isinstance(S, RealIntervalField_class): return (<RealIntervalField_class>S).__prec >= prec - if isinstance(S, RealField_class): - return (<RealField_class>S).__prec >= prec - if S is RDF: - return 53 >= prec from .number_field.number_field import NumberField_quadratic if isinstance(S, NumberField_quadratic): return S.discriminant() > 0 @@ -2550,7 +2540,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: I = RIF(e, pi) sage: a, b = I.bisection() - sage: a.intersection(b) == I.center() + sage: a.intersection(b) == RIF(I.center()) True sage: a.union(b).endpoints() == I.endpoints() True @@ -2580,6 +2570,107 @@ cdef class RealIntervalFieldElement(RingElement): # Basic Arithmetic ######################## + def __add__(left, right): + r""" + TESTS:: + + sage: RIF(1) + RR(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 2 + sage: import warnings; warnings.resetwarnings() + """ + cdef RealIntervalFieldElement _left = (<RealIntervalFieldElement> left) + if have_same_parent(left, right): + return _left._add_(right) + if (type(right) is RealNumber + and _left._parent.prec() <= right.parent().prec()): + deprecation(15114, "automatic conversions from floating-point " + "numbers to intervals are deprecated") + return left + _left._parent(right) + elif isinstance(left, RealIntervalFieldElement): + return Element.__add__(left, right) + else: + return Element.__radd__(right, left) + + def __sub__(left, right): + r""" + TESTS:: + + sage: RIF(2) - RR(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 1 + sage: import warnings; warnings.resetwarnings() + """ + cdef RealIntervalFieldElement _left = (<RealIntervalFieldElement> left) + if have_same_parent(left, right): + return _left._sub_(right) + if (type(right) is RealNumber + and _left._parent.prec() <= right.parent().prec()): + deprecation(15114, "automatic conversions from floating-point " + "numbers to intervals are deprecated") + return left - _left._parent(right) + elif isinstance(left, RealIntervalFieldElement): + return Element.__sub__(left, right) + else: + return Element.__rsub__(right, left) + + def __mul__(left, right): + r""" + TESTS:: + + sage: RIF(1) * RR(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 1 + sage: import warnings; warnings.resetwarnings() + """ + cdef RealIntervalFieldElement _left = (<RealIntervalFieldElement> left) + if have_same_parent(left, right): + return _left._mul_(right) + if (type(right) is RealNumber + and _left._parent.prec() <= right.parent().prec()): + deprecation(15114, "automatic conversions from floating-point " + "numbers to intervals are deprecated") + return left * _left._parent(right) + elif isinstance(left, RealIntervalFieldElement): + return Element.__mul__(left, right) + else: + return Element.__rmul__(right, left) + + def __truediv__(left, right): + r""" + TESTS:: + + sage: RIF(1) / RR(1/2) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 2 + sage: import warnings; warnings.resetwarnings() + """ + cdef RealIntervalFieldElement _left = (<RealIntervalFieldElement> left) + if have_same_parent(left, right): + return _left._div_(right) + if (type(right) is RealNumber + and _left._parent.prec() <= right.parent().prec()): + deprecation(15114, "automatic conversions from floating-point " + "numbers to intervals are deprecated") + return left / _left._parent(right) + elif (type(left) is RealNumber + and left.parent().prec() >= right.parent().prec()): + deprecation(15114, "automatic conversions from floating-point " + "numbers to intervals are deprecated") + return right.parent()(left)/right + elif isinstance(left, RealIntervalFieldElement): + return Element.__truediv__(left, right) + else: + return Element.__rtruediv__(right, left) + cpdef _add_(self, other): """ Add two real intervals with the same parent. @@ -4385,7 +4476,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: r = RIF(16.0); r.log10() 1.204119982655925? - sage: r.log() / log(10.0) + sage: r.log() / RIF(10).log() 1.204119982655925? :: @@ -4976,7 +5067,8 @@ cdef class RealIntervalFieldElement(RingElement): -3.54490770181104? sage: gamma(-1/2).n(100) in RIF(-1/2).gamma() True - sage: 0 in (RealField(2000)(-19/3).gamma() - RealIntervalField(1000)(-19/3).gamma()) + sage: RIF1000 = RealIntervalField(1000) + sage: 0 in (RIF1000(RealField(2000)(-19/3).gamma()) - RIF1000(-19/3).gamma()) True sage: gamma(RIF(100)) 9.33262154439442?e155 @@ -5005,7 +5097,7 @@ cdef class RealIntervalFieldElement(RingElement): [-infinity .. +infinity] """ x = self._new() - if self > 1.462: + if self.lower() > 1.462: # increasing mpfr_gamma(&x.value.left, &self.value.left, MPFR_RNDD) mpfr_gamma(&x.value.right, &self.value.right, MPFR_RNDU) @@ -5017,7 +5109,7 @@ cdef class RealIntervalFieldElement(RingElement): elif self.contains_zero(): # [-infinity, infinity] return ~self - elif self < 1.461: + elif self.upper() < 1.461: # 0 < self as well, so decreasing mpfr_gamma(&x.value.left, &self.value.right, MPFR_RNDD) mpfr_gamma(&x.value.right, &self.value.left, MPFR_RNDU) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 7f5b64a4f41..c8e60361c3c 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -133,6 +133,7 @@ from sage.cpython.string cimport char_to_str, str_to_bytes from sage.misc.superseded import deprecation from sage.structure.element cimport RingElement, Element, ModuleElement +from sage.structure.element cimport have_same_parent from sage.structure.richcmp cimport rich_to_bool_sgn, rich_to_bool cdef bin_op from sage.structure.element import bin_op @@ -2377,6 +2378,90 @@ cdef class RealNumber(sage.structure.element.RingElement): # Basic Arithmetic ######################## + def __add__(left, right): + r""" + TESTS:: + + sage: RR(1) + RIF(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 2 + sage: import warnings; warnings.resetwarnings() + """ + if have_same_parent(left, right): + return (<RealNumber> left)._add_(right) + from .real_mpfi import RealIntervalFieldElement + if type(right) is RealIntervalFieldElement: + return right.__add__(left) + elif isinstance(left, RealNumber): + return Element.__add__(left, right) + else: + return Element.__add__(right, left) + + def __sub__(left, right): + r""" + TESTS:: + + sage: RR(2) - RIF(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 1 + sage: import warnings; warnings.resetwarnings() + """ + if have_same_parent(left, right): + return (<RealNumber> left)._sub_(right) + from .real_mpfi import RealIntervalFieldElement + if type(right) is RealIntervalFieldElement: + return (-right).__add__(left) + elif isinstance(left, RealNumber): + return Element.__sub__(left, right) + else: + return Element.__rsub__(right, left) + + def __mul__(left, right): + r""" + TESTS:: + + sage: RR(1) * RIF(1) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 1 + sage: import warnings; warnings.resetwarnings() + """ + if have_same_parent(left, right): + return (<RealNumber> left)._mul_(right) + from .real_mpfi import RealIntervalFieldElement + if type(right) is RealIntervalFieldElement: + return right.__mul__(left) + elif isinstance(left, RealNumber): + return Element.__mul__(left, right) + else: + return Element.__rmul__(right, left) + + def __truediv__(left, right): + r""" + TESTS:: + + sage: RR(1) / RIF(1/2) + doctest:...: + DeprecationWarning: automatic conversions from floating-point numbers to intervals are deprecated + See http://trac.sagemath.org/15114 for details. + 2 + sage: import warnings; warnings.resetwarnings() + """ + if have_same_parent(left, right): + return (<RealNumber> left)._div_(right) + from .real_mpfi import RealIntervalFieldElement + if type(right) is RealIntervalFieldElement: + return right.__rtruediv__(left) + elif isinstance(left, RealNumber): + return Element.__truediv__(left, right) + else: + return Element.__rtruediv__(right, left) + cpdef _add_(self, other): """ Add two real numbers with the same parent. diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 2c7e09e0ed2..9c7ed719378 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -256,7 +256,7 @@ class UCFtoQQbar(Morphism): -0.500000000000000? + 0.866025403784439?*I sage: CC(UCF.gen(7,2) + UCF.gen(7,6)) - 0.400968867902419 + 0.193096429713794*I + 0.400968867902419 + 0.193096429713793*I sage: complex(E(7)+E(7,2)) (0.40096886790241915+1.7567593946498534j) diff --git a/src/sage/sandpiles/all.py b/src/sage/sandpiles/all.py index 7f453e8941d..78266048fed 100644 --- a/src/sage/sandpiles/all.py +++ b/src/sage/sandpiles/all.py @@ -1,12 +1,12 @@ from sage.misc.lazy_import import lazy_import -from .sandpile import (Sandpile, - SandpileDivisor, - SandpileConfig, - random_DAG, - firing_graph, - parallel_firing_graph, - wilmes_algorithm, - triangle_sandpile) +lazy_import('sage.sandpiles.sandpile', 'Sandpile') +lazy_import('sage.sandpiles.sandpile', 'SandpileDivisor') +lazy_import('sage.sandpiles.sandpile', 'SandpileConfig') +lazy_import('sage.sandpiles.sandpile', 'random_DAG') +lazy_import('sage.sandpiles.sandpile', 'firing_graph') +lazy_import('sage.sandpiles.sandpile', 'parallel_firing_graph') +lazy_import('sage.sandpiles.sandpile', 'wilmes_algorithm') +lazy_import('sage.sandpiles.sandpile', 'triangle_sandpile') lazy_import('sage.sandpiles.examples', 'sandpiles') diff --git a/src/sage/schemes/affine/affine_space.py b/src/sage/schemes/affine/affine_space.py index d39124d870c..8deae258f1f 100644 --- a/src/sage/schemes/affine/affine_space.py +++ b/src/sage/schemes/affine/affine_space.py @@ -20,7 +20,8 @@ from sage.categories.fields import Fields _Fields = Fields() from sage.categories.number_fields import NumberFields -from sage.misc.all import latex +from sage.misc.all import (latex, + cartesian_product_iterator) from sage.structure.category_object import normalize_names from sage.schemes.generic.scheme import AffineScheme from sage.schemes.generic.ambient_space import AmbientSpace @@ -208,7 +209,7 @@ def __iter__(self): [(0), (1), (2)] sage: AA.<z,w> = AffineSpace(FF, 2) sage: [ x for x in AA ] - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] + [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] AUTHOR: @@ -216,22 +217,12 @@ def __iter__(self): """ n = self.dimension_relative() R = self.base_ring() - zero = R(0) - P = [ zero for _ in range(n) ] - yield self(P) - iters = [ iter(R) for _ in range(n) ] - for x in iters: next(x) # put at zero - i = 0 - while i < n: - try: - P[i] = next(iters[i]) - yield self(P) - i = 0 - except StopIteration: - iters[i] = iter(R) # reset - next(iters[i]) # put at zero - P[i] = zero - i += 1 + AHom = self.point_homset() + C = AHom.codomain() + + for v in cartesian_product_iterator([R for _ in range(n)]): + yield C._point(AHom, v, check=False) + def ngens(self): """ @@ -261,7 +252,7 @@ def rational_points(self, F=None): [(0), (b), (b + 1), (2*b + 1), (2), (2*b), (2*b + 2), (b + 2), (1)] sage: AffineSpace(2, ZZ).rational_points(GF(2)) - [(0, 0), (1, 0), (0, 1), (1, 1)] + [(0, 0), (0, 1), (1, 0), (1, 1)] TESTS:: diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 434a4c51d77..b380710fc49 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -534,14 +534,11 @@ def plane_projection(self, PP=None): sage: C = P.curve([x^2 - 6*y^2, w*z*u - y^3 + 4*y^2*z, u^2 - x^2]) sage: C.plane_projection() (Scheme morphism: - From: Projective Curve over Finite Field of size 7 defined by x^2 + - y^2, -y^3 - 3*y^2*z + z*w*u, -x^2 + u^2 + From: Projective Curve over Finite Field of size 7 defined by x^2 + y^2, -y^3 - 3*y^2*z + z*w*u, -x^2 + u^2 To: Projective Space of dimension 2 over Finite Field of size 7 Defn: Defined on coordinates by sending (x : y : z : w : u) to - (y : z : -x + w), - Projective Plane Curve over Finite Field of size 7 defined by x0^10 - - 2*x0^9*x1 + 3*x0^8*x1^2 - 2*x0^7*x1^3 + x0^6*x1^4 + 2*x0^6*x1^2*x2^2 - - 2*x0^5*x1^3*x2^2 - x0^4*x1^4*x2^2 + x0^2*x1^4*x2^4) + (x : z : -y + w), + Projective Plane Curve over Finite Field of size 7 defined by x0^10 + 2*x0^8*x1^2 + 2*x0^6*x1^4 - 3*x0^6*x1^3*x2 + 2*x0^6*x1^2*x2^2 - 2*x0^4*x1^4*x2^2 + x0^2*x1^4*x2^4) :: @@ -2723,14 +2720,9 @@ def number_of_rational_points(self, r=1): L = R(L) Lp = R(Lp) - previous_prec = R.default_prec() - R.set_default_prec(r) - - f = Lp / L + f = R(Lp / L, prec=r) n = f[r-1] + q**r + 1 - R.set_default_prec(previous_prec) - return n diff --git a/src/sage/schemes/elliptic_curves/Qcurves.py b/src/sage/schemes/elliptic_curves/Qcurves.py new file mode 100644 index 00000000000..e3e579ee99b --- /dev/null +++ b/src/sage/schemes/elliptic_curves/Qcurves.py @@ -0,0 +1,573 @@ +# -*- coding: utf-8 -*- +r""" +Testing whether elliptic curves over number fields are `\QQ`-curves + +AUTHORS: + +- John Cremona (February 2021) + +The code here implements the algorithm of Cremona and Najman presented +in [CrNa2020]_. +""" + +############################################################################## +# Copyright (C) 2020-2021 John Cremona <john.cremona@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/ +############################################################################## + +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. + + INPUT: + + - ``E`` (elliptic curve) -- an elliptic curve over a number field. + + - ``maxp`` (int, default 100): bound on primes used for checking + necessary local conditions. The result will not depend on this, + but using a larger value may return ``False`` faster. + + - ``certificate`` (bool, default ``False``): if ``True`` then a + second value is returned giving a certificate for the + `\QQ`-curve property. + + OUTPUT: + + If ``certificate`` is ``False``: either ``True`` (if `E` is a + `\QQ`-curve), or ``False``. + + If ``certificate`` is ``True``: a tuple consisting of a boolean + flag as before and a certificate, defined as follows: + + - when the flag is ``True``, so `E` is a `\QQ`-curve: + + - either {'CM':`D`} where `D` is a negative discriminant, when + `E` has potential CM with discriminant `D`; + + - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, 'r': + `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, where the + core polynomial `f` is an irreducible monic polynomial over + `QQ` of degree `2^\rho`, all of whose roots are + `j`-invariants of curves isogenous to `E`, the core level + `N` is a square-free integer with `r` prime factors which is + the LCM of the degrees of the isogenies between these + conjugates. For example, if there exists a curve `E'` + isogenous to `E` with `j(E')=j\in\QQ`, then the certificate + is {'CM':0, 'r':0, 'rho':0, 'core_poly': x-j, 'N':1}. + + - when the flag is ``False``, so `E` is not a `\QQ`-curve, the + certificate is a prime `p` such that the reductions of `E` at + the primes dividing `p` are inconsistent with the property of + being a `\QQ`-curve. See the ALGORITHM section for details. + + ALGORITHM: + + See [CrNa2020]_ for details. + + 1. If `E` has rational `j`-invariant, or has CM, then return + ``True``. + + 2. Replace `E` by a curve defined over `K=\QQ(j(E))`. Let `N` be + the conductor norm. + + 3. For all primes `p\mid N` check that the valuations of `j` at + all `P\mid p` are either all negative or all non-negative; if not, + return ``False``. + + 4. For `p\le maxp`, `p\not\mid N`, check that either `E` is + ordinary mod `P` for all `P\mid p`, or `E` is supersingular mod + `P` for all `P\mid p`; if neither, return ``False``. If all are + ordinary, check that the integers `a_P(E)^2-4N(P)` have the same + square-free part; if not, return ``False``. + + 5. Compute the `K`-isogeny class of `E` using the "heuristic" + option (which is faster, but not guaranteed to be complete). + Check whether the set of `j`-invariants of curves in the class of + `2`-power degree contains a complete Galois orbit. If so, return + ``True``. + + 6. Otherwise repeat step 4 for more primes, and if still + undecided, repeat Step 5 without the "heuristic" option, to get + the complete `K`-isogeny class (which will probably be no bigger + than before). Now return ``True`` if the set of `j`-invariants of + curves in the class contains a complete Galois orbit, otherwise + return ``False``. + + EXAMPLES: + + A non-CM curve over `\QQ` and a CM curve over `\QQ` are both + trivially `\QQ`-curves:: + + sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve + sage: E = EllipticCurve([1,2,3,4,5]) + sage: flag, cert = is_Q_curve(E, certificate=True) + sage: flag + True + sage: cert + {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0} + + sage: E = EllipticCurve(j=8000) + sage: flag, cert = is_Q_curve(E, certificate=True) + sage: flag + True + sage: cert + {'CM': -8} + + A non-`\QQ`-curve over a quartic field. The local data at bad + primes above `3` is inconsistent:: + + sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([3, 0, -5, 0, 1])) + sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) + sage: is_Q_curve(E, certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve + No: inconsistency at the 2 primes dividing 3 + - potentially multiplicative: [True, False] + (False, 3) + + A non-`\QQ`-curve over a quadratic field. The local data at bad + primes is consistent, but the local test at good primes above `13` + is not:: + + sage: K.<a> = NumberField(R([-10, 0, 1])) + sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])]) + sage: is_Q_curve(E, certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve + Applying local tests at good primes above p<=100 + No: inconsistency at the 2 ordinary primes dividing 13 + - Frobenius discriminants mod squares: [-1, -3] + No: local test at p=13 failed + (False, 13) + + A quadratic `\QQ`-curve with CM discriminant `-15` (`j`-invariant not in `\QQ`):: + + sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([-1, -1, 1])) + sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])]) + sage: is_Q_curve(E, certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve + Yes: E is CM (discriminant -15) + (True, {'CM': -15}) + + An example over `\QQ(\sqrt{2},\sqrt{3})`. The `j`-invariant is in + `\QQ(\sqrt{6})`, so computations will be done over that field, and + in fact there is an isogenous curve with rational `j`, so we have + a so-called rational `\QQ`-curve:: + + sage: K.<a> = NumberField(R([1, 0, -4, 0, 1])) + sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])]) + sage: flag, cert = is_Q_curve(E, certificate=True) + sage: flag + True + sage: cert + {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0} + + Over the same field, a so-called strict `\QQ`-curve which is not + isogenous to one with rational `j`, but whose core field is + quadratic. In fact the isogeny class over `K` consists of `6` + curves, four with conjugate quartic `j`-invariants and `2` with + quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` (but which + are not base-changes from the quadratic subfield):: + + sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])]) + sage: flag, cert = is_Q_curve(E, certificate=True) + sage: flag + True + sage: cert + {'CM': 0, + 'N': 2, + 'core_degs': [1, 2], + 'core_poly': x^2 - 840064*x + 1593413632, + 'r': 1, + 'rho': 1} + + """ + from sage.rings.number_field.number_field_base import is_NumberField + + if verbose: + print("Checking whether {} is a Q-curve".format(E)) + + 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()") + + from sage.rings.integer_ring import ZZ + from sage.arith.functions import lcm + from sage.libs.pari import pari + from sage.rings.number_field.number_field import NumberField + from sage.schemes.elliptic_curves.constructor import EllipticCurve + from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant + + # Step 1 + + # all curves with rational j-invariant are Q-curves: + jE = E.j_invariant() + if jE in QQ: + if verbose: + print("Yes: j(E) is in QQ") + if certificate: + # test for CM + for d, f, j in cm_j_invariants_and_orders(QQ): + if jE == j: + 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: + return True + + # CM curves are Q-curves: + flag, df = is_cm_j_invariant(jE) + if flag: + d, f = df + D = d*f**2 + if verbose: + print("Yes: E is CM (discriminant {})".format(D)) + if certificate: + return True, {'CM': D} + else: + return True + + # Step 2: replace E by a curve defined over Q(j(E)): + + K = E.base_field() + jpoly = jE.minpoly() + 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()}) + 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 + E = EllipticCurve(j=jE) + K = K2 + if verbose: + print("New test curve is {}".format(E)) + + # Step 3: check primes of bad reduction + + NN = E.conductor().norm() + for p in NN.support(): + Plist = K.primes_above(p) + 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(" - potentially multiplicative: {}".format(pot_mult)) + if certificate: + return False, p + else: + return False + + # Step 4 check: primes P of good reduction above p<=B: + + if verbose: + print("Applying local tests at good primes above p<={}".format(maxp)) + + res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose) + if not res4: + if verbose: + print("No: local test at p={} failed".format(p)) + if certificate: + return False, p + else: + return False + + if verbose: + print("...all local tests pass for p<={}".format(maxp)) + + # Step 5: compute the (partial) K-isogeny class of E and test the + # set of j-invariants in the class: + + C = E.isogeny_class(algorithm='heuristic', minimal_models=False) + jC = [E2.j_invariant() for E2 in C] + centrejpols = conjugacy_test(jC, verbose=verbose) + if centrejpols: + if verbose: + print("Yes: the isogeny class contains a complete conjugacy class of j-invariants") + if certificate: + for f in centrejpols: + rho = f.degree().valuation(2) + 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} + return True, cert + print("No central curve found") + else: + return True + + # Now we are undecided. This can happen if either (1) E is not a + # Q-curve but we did not use enough primes in Step 4 to detect + # this, or (2) E is a Q-curve but in Step 5 we did not compute the + # complete isogeny class. Case (2) is most unlikely since the + # heuristic bound used in computing isogeny classes means that we + # have all isogenous curves linked to E by an isogeny of degree + # supported on primes<1000. + + # We first rerun Step 4 with a larger bound. + + xmaxp = 10 * maxp + if verbose: + print("Undecided after first round, so we apply more local tests, up to {}".format(xmaxp)) + + res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose) + if not res4: + if verbose: + print("No: local test at p={} failed".format(p)) + if certificate: + return False, p + else: + return False + + # Now we rerun Step 5 using a rigorous computation of the complete + # isogeny class. This will probably contain no more curves than + # before, in which case -- since we already tested that the set of + # j-invariants does not contain a complete Galois conjugacy class + # -- we can deduce that E is not a Q-curve. + + if verbose: + print("...all local tests pass for p<={}".format(xmaxp)) + print("We now compute the complete isogeny class...") + + Cfull = E.isogeny_class(minimal_models=False) + jCfull = [E2.j_invariant() for E2 in Cfull] + + if len(jC) == len(jCfull): + if verbose: + print("...and find that we already had the complete class:so No") + if certificate: + return False, 0 + else: + return False + if verbose: + print("...and find that the class contains {} curves, not just the {} we computed originally".format(len(jCfull), len(jC))) + centrejpols = conjugacy_test(jCfull, verbose=verbose) + if cert: + if verbose: + print("Yes: the isogeny class contains a complete conjugacy class of j-invariants") + if certificate: + return True, centrejpols + else: + return True + if verbose: + print("No: the isogeny class does *not* contain a complete conjugacy class of j-invariants") + if certificate: + return False, 0 + 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. + + INPUT: + + - `E` (elliptic curve): an elliptic curve defined over a number field + + - `B` (integer): upper bound on primes to test + + - `oldB` (integer, default 0): lower bound on primes to test + + - `verbose` (boolean, default ``False``): verbosity flag + + OUTPUT: + + Either (``False``, `p`), if the local test at `p` proves that `E` + is not a `\QQ`-curve, or (``True``, `0`) if all local tests at + primes between ``oldB`` and ``B`` fail to prove that `E` is not a + `\QQ`-curve. + + ALGORITHM (see [CrNa2020]_ for details): + + This local test at `p` only applies if `E` has good reduction at + all of the primes lying above `p` in the base field `K` of `E`. It + tests whether (1) `E` is either ordinary at all `P\mid p`, or + supersingular at all; (2) if ordinary at all, it tests that the + squarefree part of `a_P^2-4N(P)` is the same for all `P\mid p`. + + EXAMPLES: + + A non-`\QQ`-curve over a quartic field (with LMFDB label + '4.4.8112.1-12.1-a1') fails this test at `p=13`:: + + sage: from sage.schemes.elliptic_curves.Qcurves import Step4Test + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([3, 0, -5, 0, 1])) + sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) + sage: Step4Test(E, 100, verbose=True) + No: inconsistency at the 2 ordinary primes dividing 13 + - Frobenius discriminants mod squares: [-3, -1] + (False, 13) + + A `\QQ`-curve over a sextic field (with LMFDB label + '6.6.1259712.1-64.1-a6') passes this test for all `p<100`:: + + sage: from sage.schemes.elliptic_curves.Qcurves import Step4Test + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([-3, 0, 9, 0, -6, 0, 1])) + sage: E = EllipticCurve([K([1,-3,0,1,0,0]),K([5,-3,-6,1,1,0]),K([1,-3,0,1,0,0]),K([-139,-129,331,277,-76,-63]),K([2466,1898,-5916,-4582,1361,1055])]) + sage: Step4Test(E, 100, verbose=True) + (True, 0) + + """ + from sage.arith.misc import primes + K = E.base_field() + NN = E.conductor().norm() + for p in primes(B): + if p <= oldB or p.divides(NN): + continue + Plist = K.primes_above(p) + if len(Plist) < 2: + continue + + EmodP = [E.reduction(P) for P in Plist] + + # (a) Check all are ordinary or all supersingular: + ordinary = [Ei.is_ordinary() for Ei in EmodP] + consistent = all(ordinary) or not any(ordinary) + if not consistent: + if verbose: + print("No: inconsistency at the {} primes dividing {} ".format(len(Plist),p)) + print(" - ordinary: {}".format(ordinary)) + return False, p + + # (b) Skip if all are supersingular: + if not ordinary[0]: + continue + + # else compare a_P^2-4*N(P) which should have the same squarefree part: + + discs = [(Ei.trace_of_frobenius()**2 - 4 * P.norm()).squarefree_part() for P, Ei in zip(Plist, EmodP)] + if any(d != discs[0] for d in discs[1:]): + if verbose: + print("No: inconsistency at the {} ordinary primes dividing {} ".format(len(Plist), p)) + print(" - Frobenius discriminants mod squares: {}".format(discs)) + return False, p + # 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 + conjugacy class of 2-power degree. + + INPUT: + + - `jlist` (list): a list of algebraic numbers in the same field + + - `verbose` (boolean, default ``False``): verbosity flag + + OUTPUT: + + A possibly empty list of irreducible polynomials over `\QQ` of + 2-power degree all of whose roots are in the list. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.Qcurves import conjugacy_test + sage: conjugacy_test([3]) + [x - 3] + sage: K.<a> = QuadraticField(2) + sage: conjugacy_test([K(3), a]) + [x - 3] + sage: conjugacy_test([K(3), 3+a]) + [x - 3] + sage: conjugacy_test([3+a]) + [] + sage: conjugacy_test([3+a, 3-a]) + [x^2 - 6*x + 7] + sage: x = polygen(QQ) + sage: f = x^3-3 + sage: K.<a> = f.splitting_field() + sage: js = f.roots(K, multiplicities=False) + sage: conjugacy_test(js) + [] + sage: f = x^4-3 + sage: K.<a> = NumberField(f) + sage: js = f.roots(K, multiplicities=False) + sage: conjugacy_test(js) + [] + sage: K.<a> = f.splitting_field() + sage: js = f.roots(K, multiplicities=False) + sage: conjugacy_test(js) + [x^4 - 3] + + """ + from sage.sets.set import Set + + # First test to see if the list contains a rational + + jQ = next((j for j in jlist if j in QQ), None) + if jQ: + if verbose: + print("Yes: an isogenous curve has rational j-invariant {}".format(jQ)) + x = polygen(QQ) + 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. + + K = jlist[0].parent() + if K.degree() % 2: + if verbose: + print("Odd-degree case: no rational j-invariant in the class {}".format(jlist)) + return [] + + # 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 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] + + # pick out those of 2-power degree + pols = [f for f in pols if f.degree().prime_to_m_part(2) == 1] + + # If none, there is nothing to do + if not pols: + return [] + + # see if there's a poly of degree d appearing d times. NB There + # may be more than one of these, possibly including some conjugacy + # 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]) + 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: + if verbose: + print("Yes: the isogeny class contains all j-invariants with min poly {}".format(centrepols)) + return centrepols + if verbose: + print("No complete conjugacy class of 2-power size found in {}".format(jlist)) + return [] diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 23e68850970..e9320cc174b 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -327,7 +327,7 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, EXAMPLES:: sage: EllipticCurve.create_key_and_extra_args(j=8000) - ((Rational Field, (0, -1, 0, -3, -1)), {}) + ((Rational Field, (0, 1, 0, -3, 1)), {}) When constructing a curve over `\\QQ` from a Cremona or LMFDB label, the invariants from the database are returned as @@ -597,8 +597,13 @@ def EllipticCurve_from_j(j, minimal_twist=True): - ``j`` -- an element of some field. - - ``minimal_twist`` (boolean, default True) -- If True and ``j`` is in `\QQ`, the curve returned is a - minimal twist, i.e. has minimal conductor. If `j` is not in `\QQ` this parameter is ignored. + - ``minimal_twist`` (boolean, default True) -- If True and ``j`` + is in `\QQ`, the curve returned is a minimal twist, i.e. has + minimal conductor; when there is more than one curve with + minimal conductor, the curve returned is the one whose label + comes first if the curves are in the CremonaDatabase, otherwise + the one whose minimal a-invarinats are first lexicographically. + If `j` is not in `\QQ` this parameter is ignored. OUTPUT: @@ -721,7 +726,15 @@ def coefficients_from_j(j, minimal_twist=True): tw = [-1,2,-2,3,-3,6,-6] E1 = EllipticCurve([0,0,0,a4,a6]) Elist = [E1] + [E1.quadratic_twist(t) for t in tw] - Elist.sort(key=lambda E: E.conductor()) + min_cond = min(E.conductor() for E in Elist) + Elist = [E for E in Elist if E.conductor() == min_cond] + if len(Elist) > 1: + from sage.databases.cremona import CremonaDatabase, parse_cremona_label + if min_cond <= CremonaDatabase().largest_conductor(): + sorter = lambda E: parse_cremona_label(E.label(), numerical_class_code=True) + else: + sorter = lambda E: E.ainvs() + Elist.sort(key=sorter) return Sequence(Elist[0].ainvs()) # defaults for all other fields: diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 87fbec3b69b..2aca9491afb 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1420,16 +1420,91 @@ def set_order(self, value, num_checks=8): raise ValueError('Value %s illegal (multiple of random point not the identity)' % value) self._order = value +# dict to hold precomputed coefficient vectors of supersingular j values (excluding 0, 1728): -def supersingular_j_polynomial(p): +supersingular_j_polynomials = {} + +def fill_ss_j_dict(): + r""" + Fill the global cache of supersingular j-_polynomials. + + This function does nothing except the first time it is called, + when it fills ``supersingular_j_polynomials`` with precomputed + values for `p<300`. Setting the values this way avoids start-up + costs. + + """ + global supersingular_j_polynomials + if not supersingular_j_polynomials: + supersingular_j_polynomials[13] = [8, 1] + supersingular_j_polynomials[17] = [9, 1] + supersingular_j_polynomials[19] = [12, 1] + supersingular_j_polynomials[23] = [4, 1] + supersingular_j_polynomials[29] = [21, 2, 1] + supersingular_j_polynomials[31] = [8, 25, 1] + supersingular_j_polynomials[37] = [11, 5, 23, 1] + supersingular_j_polynomials[41] = [18, 10, 19, 1] + supersingular_j_polynomials[43] = [32, 11, 21, 1] + supersingular_j_polynomials[47] = [35, 33, 31, 1] + supersingular_j_polynomials[53] = [24, 9, 30, 7, 1] + supersingular_j_polynomials[59] = [39, 31, 35, 39, 1] + supersingular_j_polynomials[61] = [60, 21, 27, 8, 60, 1] + supersingular_j_polynomials[67] = [8, 36, 47, 4, 53, 1] + supersingular_j_polynomials[71] = [18, 54, 28, 33, 1, 1] + supersingular_j_polynomials[73] = [7, 39, 38, 9, 68, 60, 1] + supersingular_j_polynomials[79] = [10, 25, 1, 63, 57, 55, 1] + supersingular_j_polynomials[83] = [43, 72, 81, 81, 62, 11, 1] + supersingular_j_polynomials[89] = [42, 79, 23, 22, 37, 86, 60, 1] + supersingular_j_polynomials[97] = [19, 28, 3, 72, 2, 96, 10, 60, 1] + supersingular_j_polynomials[101] = [9, 76, 45, 79, 1, 68, 87, 60, 1] + supersingular_j_polynomials[103] = [64, 15, 24, 58, 70, 83, 84, 100, 1] + supersingular_j_polynomials[107] = [6, 18, 72, 59, 43, 19, 17, 68, 1] + supersingular_j_polynomials[109] = [107, 22, 39, 83, 30, 34, 108, 104, 60, 1] + supersingular_j_polynomials[113] = [86, 71, 75, 6, 47, 97, 100, 4, 60, 1] + supersingular_j_polynomials[127] = [32, 31, 5, 50, 115, 122, 114, 67, 38, 35, 1] + supersingular_j_polynomials[131] = [65, 64, 10, 34, 129, 35, 94, 127, 7, 7, 1] + supersingular_j_polynomials[137] = [104, 83, 3, 82, 112, 23, 77, 135, 18, 50, 60, 1] + supersingular_j_polynomials[139] = [87, 79, 109, 21, 138, 9, 104, 130, 61, 118, 90, 1] + supersingular_j_polynomials[149] = [135, 55, 80, 86, 87, 74, 32, 60, 130, 80, 146, 60, 1] + supersingular_j_polynomials[151] = [94, 125, 8, 6, 93, 21, 114, 80, 107, 58, 42, 18, 1] + supersingular_j_polynomials[157] = [14, 95, 22, 58, 110, 23, 71, 51, 47, 5, 147, 59, 60, 1] + supersingular_j_polynomials[163] = [102, 26, 74, 95, 112, 151, 98, 107, 27, 37, 25, 111, 109, 1] + supersingular_j_polynomials[167] = [14, 9, 27, 109, 97, 55, 51, 74, 145, 125, 36, 113, 89, 1] + supersingular_j_polynomials[173] = [152, 73, 56, 12, 18, 96, 98, 49, 30, 43, 52, 79, 163, 60, 1] + supersingular_j_polynomials[179] = [110, 51, 3, 94, 123, 90, 156, 90, 88, 119, 158, 27, 71, 29, 1] + supersingular_j_polynomials[181] = [7, 65, 77, 29, 139, 34, 65, 84, 164, 73, 51, 136, 7, 141, 60, 1] + supersingular_j_polynomials[191] = [173, 140, 144, 3, 135, 80, 182, 84, 93, 75, 83, 17, 22, 42, 160, 1] + supersingular_j_polynomials[193] = [23, 48, 26, 15, 108, 141, 124, 44, 132, 49, 72, 173, 126, 101, 22, 60, 1] + supersingular_j_polynomials[197] = [14, 111, 64, 170, 193, 32, 124, 91, 112, 163, 14, 112, 167, 191, 183, 60, 1] + supersingular_j_polynomials[199] = [125, 72, 65, 30, 63, 45, 10, 177, 91, 102, 28, 27, 5, 150, 51, 128, 1] + supersingular_j_polynomials[211] = [27, 137, 128, 90, 102, 141, 5, 77, 131, 144, 83, 108, 23, 105, 98, 13, 80, 1] + supersingular_j_polynomials[223] = [56, 183, 46, 133, 191, 94, 20, 8, 92, 100, 57, 200, 166, 67, 59, 218, 28, 32, 1] + supersingular_j_polynomials[227] = [79, 192, 142, 66, 11, 114, 100, 208, 57, 147, 32, 5, 144, 93, 185, 147, 92, 16, 1] + supersingular_j_polynomials[229] = [22, 55, 182, 130, 228, 172, 63, 25, 108, 99, 100, 101, 220, 111, 205, 199, 91, 163, 60, 1] + supersingular_j_polynomials[233] = [101, 148, 85, 113, 226, 68, 71, 103, 61, 44, 173, 175, 5, 225, 227, 99, 146, 170, 60, 1] + supersingular_j_polynomials[239] = [225, 81, 47, 26, 133, 182, 238, 2, 144, 154, 234, 178, 165, 130, 35, 61, 144, 112, 207, 1] + supersingular_j_polynomials[241] = [224, 51, 227, 139, 134, 186, 187, 152, 161, 175, 213, 59, 105, 88, 87, 124, 202, 40, 15, 60, 1] + supersingular_j_polynomials[251] = [30, 183, 80, 127, 40, 56, 230, 168, 192, 48, 226, 61, 214, 54, 165, 147, 105, 88, 38, 171, 1] + supersingular_j_polynomials[257] = [148, 201, 140, 146, 169, 147, 220, 4, 205, 224, 35, 42, 198, 97, 127, 7, 110, 229, 118, 202, 60, 1] + supersingular_j_polynomials[263] = [245, 126, 72, 213, 14, 64, 152, 83, 169, 114, 9, 128, 138, 231, 103, 85, 114, 211, 173, 249, 135, 1] + supersingular_j_polynomials[269] = [159, 32, 69, 95, 201, 266, 190, 176, 76, 151, 212, 21, 106, 49, 263, 105, 136, 194, 215, 181, 237, 60, 1] + supersingular_j_polynomials[271] = [169, 87, 179, 109, 133, 101, 31, 167, 208, 99, 127, 120, 83, 62, 36, 23, 61, 50, 69, 263, 265, 111, 1] + supersingular_j_polynomials[277] = [251, 254, 171, 72, 190, 237, 12, 231, 123, 217, 263, 151, 270, 183, 29, 228, 85, 4, 67, 101, 29, 169, 60, 1] + supersingular_j_polynomials[281] = [230, 15, 146, 69, 41, 23, 142, 232, 18, 80, 58, 134, 270, 62, 272, 70, 247, 189, 118, 255, 274, 159, 60, 1] + supersingular_j_polynomials[283] = [212, 4, 42, 155, 38, 1, 270, 175, 172, 256, 264, 232, 50, 82, 244, 127, 148, 46, 249, 72, 59, 124, 75, 1] + supersingular_j_polynomials[293] = [264, 66, 165, 144, 243, 25, 163, 210, 18, 107, 160, 153, 70, 255, 91, 211, 22, 7, 256, 50, 150, 94, 225, 60, 1] + +def supersingular_j_polynomial(p, use_cache=True): r""" - Return a polynomial whose roots are the supersingular `j`-invariants - in characteristic `p`, other than 0, 1728. + Return a polynomial whose roots are the supersingular + `j`-invariants in characteristic `p`, other than 0, 1728. INPUT: - `p` (integer) -- a prime number. + - `use_cache` (boolean, default ``True``) -- use cached coefficients if they exist + ALGORITHM: First compute H(X) whose roots are the Legendre @@ -1440,6 +1515,11 @@ def supersingular_j_polynomial(p): `j`-invariants. Factors of `j` and `j-1728` are removed if present. + .. note:: + + The only point of the use_cache parameter is to allow checking + the precomputed coefficients. + EXAMPLES:: sage: from sage.schemes.elliptic_curves.ell_finite_field import supersingular_j_polynomial @@ -1455,10 +1535,17 @@ def supersingular_j_polynomial(p): TESTS:: + sage: from sage.schemes.elliptic_curves.ell_finite_field import supersingular_j_polynomial sage: supersingular_j_polynomial(6) Traceback (most recent call last): ... ValueError: p (=6) should be a prime number + + Check the cached values are correct:: + + sage: from sage.schemes.elliptic_curves.ell_finite_field import supersingular_j_polynomial as ssjpol + sage: assert all(ssjpol(p,True) == ssjpol(p,False) for p in primes(300)) + """ try: p = ZZ(p) @@ -1470,6 +1557,11 @@ def supersingular_j_polynomial(p): J = polygen(GF(p),'j') if p<13: return J.parent().one() + if use_cache: + fill_ss_j_dict() + if p in supersingular_j_polynomials: + return J.parent()(supersingular_j_polynomials[p]) + from sage.misc.all import prod m=(p-1)//2 X,T = PolynomialRing(GF(p),2,names=['X','T']).gens() @@ -1481,73 +1573,9 @@ def supersingular_j_polynomial(p): R = R // J if R(1728) == 0: R = R // (J - 1728) + supersingular_j_polynomials[p] = R.coefficients(sparse=False) return R -# For p in [13..300] we have precomputed these polynomials and store -# them (as lists of their coefficients in ZZ) in a dict: - - -supersingular_j_polynomials = {} - -supersingular_j_polynomials[13] = [8, 1] -supersingular_j_polynomials[17] = [9, 1] -supersingular_j_polynomials[19] = [12, 1] -supersingular_j_polynomials[23] = [4, 1] -supersingular_j_polynomials[29] = [21, 2, 1] -supersingular_j_polynomials[31] = [8, 25, 1] -supersingular_j_polynomials[37] = [11, 5, 23, 1] -supersingular_j_polynomials[41] = [18, 10, 19, 1] -supersingular_j_polynomials[43] = [32, 11, 21, 1] -supersingular_j_polynomials[47] = [35, 33, 31, 1] -supersingular_j_polynomials[53] = [24, 9, 30, 7, 1] -supersingular_j_polynomials[59] = [39, 31, 35, 39, 1] -supersingular_j_polynomials[61] = [60, 21, 27, 8, 60, 1] -supersingular_j_polynomials[67] = [8, 36, 47, 4, 53, 1] -supersingular_j_polynomials[71] = [18, 54, 28, 33, 1, 1] -supersingular_j_polynomials[73] = [7, 39, 38, 9, 68, 60, 1] -supersingular_j_polynomials[79] = [10, 25, 1, 63, 57, 55, 1] -supersingular_j_polynomials[83] = [43, 72, 81, 81, 62, 11, 1] -supersingular_j_polynomials[89] = [42, 79, 23, 22, 37, 86, 60, 1] -supersingular_j_polynomials[97] = [19, 28, 3, 72, 2, 96, 10, 60, 1] -supersingular_j_polynomials[101] = [9, 76, 45, 79, 1, 68, 87, 60, 1] -supersingular_j_polynomials[103] = [64, 15, 24, 58, 70, 83, 84, 100, 1] -supersingular_j_polynomials[107] = [6, 18, 72, 59, 43, 19, 17, 68, 1] -supersingular_j_polynomials[109] = [107, 22, 39, 83, 30, 34, 108, 104, 60, 1] -supersingular_j_polynomials[113] = [86, 71, 75, 6, 47, 97, 100, 4, 60, 1] -supersingular_j_polynomials[127] = [32, 31, 5, 50, 115, 122, 114, 67, 38, 35, 1] -supersingular_j_polynomials[131] = [65, 64, 10, 34, 129, 35, 94, 127, 7, 7, 1] -supersingular_j_polynomials[137] = [104, 83, 3, 82, 112, 23, 77, 135, 18, 50, 60, 1] -supersingular_j_polynomials[139] = [87, 79, 109, 21, 138, 9, 104, 130, 61, 118, 90, 1] -supersingular_j_polynomials[149] = [135, 55, 80, 86, 87, 74, 32, 60, 130, 80, 146, 60, 1] -supersingular_j_polynomials[151] = [94, 125, 8, 6, 93, 21, 114, 80, 107, 58, 42, 18, 1] -supersingular_j_polynomials[157] = [14, 95, 22, 58, 110, 23, 71, 51, 47, 5, 147, 59, 60, 1] -supersingular_j_polynomials[163] = [102, 26, 74, 95, 112, 151, 98, 107, 27, 37, 25, 111, 109, 1] -supersingular_j_polynomials[167] = [14, 9, 27, 109, 97, 55, 51, 74, 145, 125, 36, 113, 89, 1] -supersingular_j_polynomials[173] = [152, 73, 56, 12, 18, 96, 98, 49, 30, 43, 52, 79, 163, 60, 1] -supersingular_j_polynomials[179] = [110, 51, 3, 94, 123, 90, 156, 90, 88, 119, 158, 27, 71, 29, 1] -supersingular_j_polynomials[181] = [7, 65, 77, 29, 139, 34, 65, 84, 164, 73, 51, 136, 7, 141, 60, 1] -supersingular_j_polynomials[191] = [173, 140, 144, 3, 135, 80, 182, 84, 93, 75, 83, 17, 22, 42, 160, 1] -supersingular_j_polynomials[193] = [23, 48, 26, 15, 108, 141, 124, 44, 132, 49, 72, 173, 126, 101, 22, 60, 1] -supersingular_j_polynomials[197] = [14, 111, 64, 170, 193, 32, 124, 91, 112, 163, 14, 112, 167, 191, 183, 60, 1] -supersingular_j_polynomials[199] = [125, 72, 65, 30, 63, 45, 10, 177, 91, 102, 28, 27, 5, 150, 51, 128, 1] -supersingular_j_polynomials[211] = [27, 137, 128, 90, 102, 141, 5, 77, 131, 144, 83, 108, 23, 105, 98, 13, 80, 1] -supersingular_j_polynomials[223] = [56, 183, 46, 133, 191, 94, 20, 8, 92, 100, 57, 200, 166, 67, 59, 218, 28, 32, 1] -supersingular_j_polynomials[227] = [79, 192, 142, 66, 11, 114, 100, 208, 57, 147, 32, 5, 144, 93, 185, 147, 92, 16, 1] -supersingular_j_polynomials[229] = [22, 55, 182, 130, 228, 172, 63, 25, 108, 99, 100, 101, 220, 111, 205, 199, 91, 163, 60, 1] -supersingular_j_polynomials[233] = [101, 148, 85, 113, 226, 68, 71, 103, 61, 44, 173, 175, 5, 225, 227, 99, 146, 170, 60, 1] -supersingular_j_polynomials[239] = [225, 81, 47, 26, 133, 182, 238, 2, 144, 154, 234, 178, 165, 130, 35, 61, 144, 112, 207, 1] -supersingular_j_polynomials[241] = [224, 51, 227, 139, 134, 186, 187, 152, 161, 175, 213, 59, 105, 88, 87, 124, 202, 40, 15, 60, 1] -supersingular_j_polynomials[251] = [30, 183, 80, 127, 40, 56, 230, 168, 192, 48, 226, 61, 214, 54, 165, 147, 105, 88, 38, 171, 1] -supersingular_j_polynomials[257] = [148, 201, 140, 146, 169, 147, 220, 4, 205, 224, 35, 42, 198, 97, 127, 7, 110, 229, 118, 202, 60, 1] -supersingular_j_polynomials[263] = [245, 126, 72, 213, 14, 64, 152, 83, 169, 114, 9, 128, 138, 231, 103, 85, 114, 211, 173, 249, 135, 1] -supersingular_j_polynomials[269] = [159, 32, 69, 95, 201, 266, 190, 176, 76, 151, 212, 21, 106, 49, 263, 105, 136, 194, 215, 181, 237, 60, 1] -supersingular_j_polynomials[271] = [169, 87, 179, 109, 133, 101, 31, 167, 208, 99, 127, 120, 83, 62, 36, 23, 61, 50, 69, 263, 265, 111, 1] -supersingular_j_polynomials[277] = [251, 254, 171, 72, 190, 237, 12, 231, 123, 217, 263, 151, 270, 183, 29, 228, 85, 4, 67, 101, 29, 169, 60, 1] -supersingular_j_polynomials[281] = [230, 15, 146, 69, 41, 23, 142, 232, 18, 80, 58, 134, 270, 62, 272, 70, 247, 189, 118, 255, 274, 159, 60, 1] -supersingular_j_polynomials[283] = [212, 4, 42, 155, 38, 1, 270, 175, 172, 256, 264, 232, 50, 82, 244, 127, 148, 46, 249, 72, 59, 124, 75, 1] -supersingular_j_polynomials[293] = [264, 66, 165, 144, 243, 25, 163, 210, 18, 107, 160, 153, 70, 255, 91, 211, 22, 7, 256, 50, 150, 94, 225, 60, 1] - - def is_j_supersingular(j, proof=True): r""" Return True if `j` is a supersingular `j`-invariant. @@ -1625,11 +1653,9 @@ def is_j_supersingular(j, proof=True): # if p occurs in the precomputed list, use that: - try: - coeffs = supersingular_j_polynomials[p] - return PolynomialRing(F,'x')(coeffs)(j).is_zero() - except KeyError: - pass + fill_ss_j_dict() + if p in supersingular_j_polynomials: + return supersingular_j_polynomial(p)(j).is_zero() # Over GF(p), supersingular elliptic curves have cardinality # exactly p+1, so we check some random points in order to detect diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a4df3e24f5b..a32f64ea47b 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -218,9 +218,8 @@ def _repr_(self): self._sign, self._base_ring, self._E) class ModularSymbolECLIB(ModularSymbol): - def __init__(self, E, sign): - r""" - Modular symbols attached to `E` using ``eclib``. + def __init__(self, E, sign, nap=1000): + r"""Modular symbols attached to `E` using ``eclib``. Note that the normalization used within ``eclib`` differs from the normalization chosen here by a factor of 2 in the case of elliptic @@ -233,8 +232,12 @@ def __init__(self, E, sign): INPUT: - ``E`` - an elliptic curve + - ``sign`` - an integer, -1 or 1 + - ``nap`` - (int, default 1000): the number of ap of E to use + in determining the normalisation of the modular symbols. + EXAMPLES:: sage: import sage.schemes.elliptic_curves.ell_modular_symbols @@ -278,7 +281,6 @@ def __init__(self, E, sign): sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] - The scaling factor relative to eclib's normalization is 1/2 for curves of negative discriminant:: sage: [E.discriminant() for E in cremona_curves([14])] @@ -295,6 +297,21 @@ def __init__(self, E, sign): 7/10 sage: m(0) 1/5 + + If ``nap`` is too small, the normalization in eclib may be incorrect. See :trac:`31317`:: + + sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB + sage: E = EllipticCurve('1590g1') + sage: m = ModularSymbolECLIB(E, sign=+1, nap=300) + sage: [m(a/5) for a in [1..4]] + [1001/153, -1001/153, -1001/153, 1001/153] + + Those values are incorrect. The correct values are:: + + sage: m = ModularSymbolECLIB(E, sign=+1, nap=400) + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + """ from sage.libs.eclib.newforms import ECModularSymbol @@ -306,7 +323,7 @@ def __init__(self, E, sign): self._implementation="eclib" self._base_ring = QQ # The ECModularSymbol class must be initialized with sign=0 to compute minus symbols - self._modsym = ECModularSymbol(E, int(sign==1)) + self._modsym = ECModularSymbol(E, int(sign==1), nap) self.cache = {True: {}, False: {}} def _call_with_caching(self, r, base_at_infinity=True): diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index dded480ba8a..29316bebeb8 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -2598,6 +2598,61 @@ def period_lattice(self, embedding): from sage.schemes.elliptic_curves.period_lattice import PeriodLattice_ell return PeriodLattice_ell(self,embedding) + + def real_components(self, embedding): + """ + Return the number of real components with respect to a real embedding of the base field. + + EXAMPLES:: + + sage: K.<a> = QuadraticField(5) + sage: embs = K.real_embeddings() + sage: E = EllipticCurve([0,1,1,a,a]) + sage: [e(E.discriminant()) > 0 for e in embs] + [True, False] + sage: [E.real_components(e) for e in embs] + [2, 1] + + + TESTS:: + + sage: K.<a> = QuadraticField(-1) + sage: e = K.complex_embeddings()[0] + sage: E = EllipticCurve([0,1,1,a,a]) + sage: E.real_components(e) + Traceback (most recent call last): + ... + ValueError: invalid embedding specified: should be real + + sage: E.real_components('banana') + Traceback (most recent call last): + ... + ValueError: invalid embedding + + sage: K.<a> = QuadraticField(-1) + sage: E = EllipticCurve([0,1,1,a,a]) + sage: K1.<a> = QuadraticField(5) + sage: e = K1.real_embeddings()[0] + sage: E.real_components(e) + Traceback (most recent call last): + ... + ValueError: invalid embedding specified: should have domain ... + """ + from sage.rings.real_mpfr import is_RealField + try: + if not embedding.domain() is self.base_field(): + raise ValueError("invalid embedding specified: should have domain {}".format(self.base_field())) + if not is_RealField(embedding.codomain()): + raise ValueError("invalid embedding specified: should be real") + except AttributeError: + raise ValueError("invalid embedding") + + from sage.rings.number_field.number_field import refine_embedding + from sage.rings.infinity import Infinity + e = refine_embedding(embedding,Infinity) + + return 2 if e(self.discriminant()) > 0 else 1 + def height_function(self): """ Return the canonical height function attached to self. @@ -3726,6 +3781,152 @@ def has_rational_cm(self, field=None): raise ValueError("Error in has_rational_cm: %s is not an extension field of %s" % (field,self.base_field())) + @cached_method + def is_Q_curve(self, maxp=100, certificate=False, verbose=False): + r""" + Return ``True`` if this is a `\QQ`-curve, with optional certificate. + + INPUT: + + - ``maxp`` (int, default 100): bound on primes used for + checking necessary local conditions. The result will not + depend on this, but using a larger value may return + ``False`` faster. + + - ``certificate`` (bool, default ``False``): if ``True`` then + a second value is returned giving a certificate for the + `\QQ`-curve property. + + OUTPUT: + + If ``certificate`` is ``False``: either ``True`` (if `E` is a + `\QQ`-curve), or ``False``. + + If ``certificate`` is ``True``: a tuple consisting of a boolean + flag as before and a certificate, defined as follows: + + - when the flag is ``True``, so `E` is a `\QQ`-curve: + + - either {'CM':`D`} where `D` is a negative discriminant, when + `E` has potential CM with discriminant `D`; + + - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, + 'r': `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, + where the core polynomial `f` is an irreducible monic + polynomial over `QQ` of degree `2^\rho`, all of whose + roots are `j`-invariants of curves isogenous to `E`, the + core level `N` is a square-free integer with `r` prime + factors which is the LCM of the degrees of the isogenies + between these conjugates. For example, if there exists + a curve `E'` isogenous to `E` with `j(E')=j\in\QQ`, then + the certificate is {'CM':0, 'r':0, 'rho':0, 'core_poly': + x-j, 'N':1}. + + - when the flag is ``False``, so `E` is not a `\QQ`-curve, the + certificate is a prime `p` such that the reductions of `E` + at the primes dividing `p` are inconsistent with the + property of being a `\QQ`-curve. See the documentation for + :meth:`sage.src.schemes.elliptic_curves.Qcurves.is_Q_curve` + for details. + + ALGORITHM: + + See the documentation for + :meth:`sage.src.schemes.elliptic_curves.Qcurves.is_Q_curve`, and + [CrNa2020]_ for details. + + EXAMPLES: + + A non-CM curve over `\QQ` and a CM curve over `\QQ` are both + trivially `\QQ`-curves:: + + sage: E = EllipticCurve([1,2,3,4,5]) + sage: flag, cert = E.is_Q_curve(certificate=True) + sage: flag + True + sage: cert + {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0} + + sage: E = EllipticCurve(j=8000) + sage: flag, cert = E.is_Q_curve(certificate=True) + sage: flag + True + sage: cert + {'CM': -8} + + A non-`\QQ`-curve over a quartic field. The local data at bad + primes above `3` is inconsistent:: + + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([3, 0, -5, 0, 1])) + sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) + sage: E.is_Q_curve(certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve + No: inconsistency at the 2 primes dividing 3 + - potentially multiplicative: [True, False] + (False, 3) + + A non-`\QQ`-curve over a quadratic field. The local data at bad + primes is consistent, but the local test at good primes above `13` + is not:: + + sage: K.<a> = NumberField(R([-10, 0, 1])) + sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])]) + sage: E.is_Q_curve(certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve + Applying local tests at good primes above p<=100 + No: inconsistency at the 2 ordinary primes dividing 13 + - Frobenius discriminants mod squares: [-1, -3] + No: local test at p=13 failed + (False, 13) + + A quadratic `\QQ`-curve with CM discriminant `-15` + (so the `j`-invariant is not in `\QQ`):: + + sage: R.<x> = PolynomialRing(QQ) + sage: K.<a> = NumberField(R([-1, -1, 1])) + sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])]) + sage: E.is_Q_curve(certificate=True, verbose=True) + Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve + Yes: E is CM (discriminant -15) + (True, {'CM': -15}) + + An example over `\QQ(\sqrt{2},\sqrt{3})`. The `j`-invariant + is in `\QQ(\sqrt{6})`, so computations will be done over that + field, and in fact there is an isogenous curve with rational + `j`, so we have a so-called rational `\QQ`-curve:: + + sage: K.<a> = NumberField(R([1, 0, -4, 0, 1])) + sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])]) + sage: flag, cert = E.is_Q_curve(certificate=True) + sage: flag + True + sage: cert + {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0} + + Over the same field, a so-called strict `\QQ`-curve which is + not isogenous to one with rational `j`, but whose core field + is quadratic. In fact the isogeny class over `K` consists of + `6` curves, four with conjugate quartic `j`-invariants and `2` + with quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` + (but which are not base-changes from the quadratic subfield):: + + sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])]) + sage: flag, cert = E.is_Q_curve(certificate=True) + sage: flag + True + sage: cert + {'CM': 0, + 'N': 2, + 'core_degs': [1, 2], + 'core_poly': x^2 - 840064*x + 1593413632, + 'r': 1, + 'rho': 1} + + """ + from sage.schemes.elliptic_curves.Qcurves import is_Q_curve as isQ + return isQ(self, maxp, certificate, verbose) + def saturation(self, points, verbose=False, max_prime=0, one_prime=0, odd_primes_only=False, lower_ht_bound=None, reg=None, debug=False): diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 2953e80cac1..a792afce8dc 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1093,7 +1093,7 @@ def abelian_variety(self): """ return self.modular_symbol_space(sign=0).abelian_variety() - def _modular_symbol_normalize(self, sign, normalize, implementation): + def _modular_symbol_normalize(self, sign, normalize, implementation, nap): r""" Normalize parameters for :meth:`modular_symbol`. @@ -1106,110 +1106,94 @@ def _modular_symbol_normalize(self, sign, normalize, implementation): if sign not in [1,-1]: raise ValueError("The sign of a modular symbol must be 1 or -1") sign = ZZ(sign) + if implementation == 'eclib' and nap == 0: + nap = min(100*self.conductor().isqrt(), 10000) if normalize is None: normalize = "L_ratio" if normalize not in ["L_ratio", "period", "none"]: raise ValueError("normalize should be one of 'L_ratio', 'period' or 'none'") if implementation not in ["sage", "eclib", "num"]: raise ValueError("Implementation should be one of 'sage', 'num' or 'eclib'") - return (sign, normalize, implementation) + return (sign, normalize, implementation, nap) @cached_method(key = _modular_symbol_normalize) - def modular_symbol(self, sign=+1, normalize=None, implementation='eclib'): - r""" - Return the modular symbol associated to this elliptic curve - with given sign. This is a map that sends any rational number`r` - to a rational number `[r]_E`, defined to be a certain - multiple of the integral of `2 \pi i f(z) dz` - from `\infty` to `r` where `f` is the newform attached to `E`. - - More precisely: If the sign is +1, then the value returned is the - quotient of the real part of this integral by the least positive - period `\Omega_E^{+}` of `E`. In particular for `r=0`, the value - is equal to `L(E,1)/\Omega_E^{+}` (unlike in ``L_ratio`` of - ``lseries()``, where the value is also divided by the number of - connected components of `E(\RR)`). In particular the modular - symbol depends on `E` and not only the isogeny class of `E`. - For sign `-1`, it is the quotient of the imaginary part of the - integral divided by the purely imaginary period of `E` with - smallest positive imaginary part. Note however there is an - issue about these normalizations, hence the optional argument - ``normalize`` explained below + def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0): + r"""Return the modular symbol map associated to this elliptic curve + with given sign. INPUT: - ``sign`` - +1 (default) or -1. - - ``normalize`` - (default: None); either 'L_ratio', 'period', - or 'none' when ``implementation`` is 'sage'; ignored if - ``implementation`` is ``eclib`` or ``num``. + - ``normalize`` - (default: None); either 'L_ratio', 'period', + or 'none'; ignored unless ``implementation`` is 'sage'. For 'L_ratio', the modular symbol tries to normalize - correctly as explained below by comparing it to - ``L_ratio`` for the curve and some small twists. - The normalization 'period' uses the - ``integral_period_map`` for modular symbols which is known - to be equal to the desired normalization, up to the sign - and a possible power of 2. With normalization 'none', the - modular symbol is almost certainly not correctly - normalized, i.e. all values will be a fixed scalar multiple - of what they should be. However, the initial computation - of the modular symbol is much faster when implementation - ``sage`` is chosen, though evaluation of it after computing - it is no faster. - - - - ``implementation`` - either 'eclib' (default), 'sage' or - 'num'. Here 'eclib' uses John Cremona's implementation in - his eclib library. Instead 'sage' uses an implementation - within Sage which is often quite a bit slower. - Finally 'num' uses an implementation of numerical - modular symbols. + correctly as explained below by comparing it to ``L_ratio`` + for the curve and some small twists. The normalization + 'period' uses the ``integral_period_map`` for modular + symbols which is known to be equal to the desired + normalization, up to the sign and a possible power of 2. + With normalization 'none', the modular symbol is almost + certainly not correctly normalized, i.e. all values will be + a fixed scalar multiple of what they should be. + + - ``implementation`` - either 'eclib' (default), 'sage' or + 'num'. Here, 'eclib' uses Cremona's ``C++`` implementation + in the ``eclib`` library, 'sage' uses an implementation + within Sage which is often quite a bit slower, and 'num' + uses Wuthrich's implementation of numerical modular + symbols. + + - ``nap`` - (int, default 0); ignored unless implementation is + 'eclib'. The number of ap of E to use in determining the + normalisation of the modular symbols. If 0 (the default), + then the value of 100*E.conductor().isqrt() is used. Using + too small a value can lead to incorrect normalisation. + + DEFINITION: + + The modular symbol map sends any rational number `r` to the + rational number whichis the ratio of the real or imaginary + part (depending on the sign) of the integral of `2 \pi i + f(z) dz` from `\infty` to `r`, where `f` is the newform + attached to `E`, to the real or imaginary period of `E`. + + More precisely: If the sign is +1, then the value returned + is the quotient of the real part of this integral by the + least positive period `\Omega_E^{+}` of `E`. In particular + for `r=0`, the value is equal to `L(E,1)/\Omega_E^{+}` + (unlike in ``L_ratio`` of ``lseries()``, where the value is + also divided by the number of connected components of + `E(\RR)`). In particular the modular symbol depends on `E` + and not only the isogeny class of `E`. For sign `-1`, it + is the quotient of the imaginary part of the integral + divided by the purely imaginary period of `E` with smallest + positive imaginary part. Note however there is an issue + about these normalizations, hence the optional argument + ``normalize`` explained below ALGORITHM: For the implementations 'sage' and 'eclib', the used algorithm starts by finding the space of modular symbols within the full space of all modular symbols of that - level. This initial step will take a very long time if - the conductor is large (e.g. minutes for five digit - conductors). Once the space is determined, each - evaluation is very fast (logarithmic in the - denominator of `r`). - - The implementation 'num' uses a different algorithm. - It uses numerical integration along paths in the upper - half plane. The bounds are rigorously proved so that - the outcome is known to be correct. The initial step - costs no time, instead each evaluation will take more - time than in the above. More information in the - documentation of the class ``ModularSymbolNumerical``. + level. This initial step will take a very long time if the + conductor is large (e.g. minutes for five digit + conductors). Once the space is determined, each evaluation + is very fast (logarithmic in the denominator of `r`). + + The implementation 'num' uses a different algorithm. It + uses numerical integration along paths in the upper half + plane. The bounds are rigorously proved so that the outcome + is known to be correct. The initial step costs no time, + instead each evaluation will take more time than in the + above. More information in the documentation of the class + ``ModularSymbolNumerical``. .. SEEALSO:: :meth:`modular_symbol_numerical` - .. note:: - - The value at a rational number `r` is proportional to the - real or imaginary part of the integral of `2 \pi i f(z) dz` - from `\infty` to `r`, where `f` is the newform attached to - `E`, suitably normalized so that all values of this map - take values in `\QQ`. - - The normalization is such that for sign +1, the value at - the cusp `r` is equal to the quotient of the real part of - `\int_{\infty}^{r}2\pi i f(z)dz` by the least positive - period of `E`, where `f` is the newform attached to the - isogeny class of `E`. This is in contrast to the method - ``L_ratio`` of ``lseries()``, where the value is also - divided by the number of connected components of - `E(\RR)`). In particular the modular symbol depends on `E` - and not only the isogeny class of `E`. For negative - modular symbols, the value is the quotient of the imaginary - part of the above integral by the imaginary part of the - smallest positive imaginary period. - - EXAMPLES:: sage: E = EllipticCurve('37a1') @@ -1298,10 +1282,31 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib'): Modular symbol with sign -1 over Rational Field attached to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] + + With the default 'eclib' implementation, if ``nap`` is too + small, the normalization may be computed incorrectly. See + :trac:`31317`:: + + sage: E = EllipticCurve('1590g1') + sage: m = E.modular_symbol(nap=300) + sage: [m(a/5) for a in [1..4]] + [1001/153, -1001/153, -1001/153, 1001/153] + + Those values are incorrect. The correct values may be + obtained by increasing ``nap``, as verified by the numerical + implementation:: + + sage: m = E.modular_symbol(nap=400) + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + sage: m = E.modular_symbol(implementation='num') + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + """ - sign, normalize, implementation = self._modular_symbol_normalize(sign, normalize, implementation) + sign, normalize, implementation, nap = self._modular_symbol_normalize(sign, normalize, implementation, nap) if implementation == 'eclib': - M = ell_modular_symbols.ModularSymbolECLIB(self, sign) + M = ell_modular_symbols.ModularSymbolECLIB(self, sign, nap) elif implementation == 'sage': M = ell_modular_symbols.ModularSymbolSage(self, sign, normalize=normalize) else: # implementation == "num": @@ -3202,7 +3207,7 @@ def tamagawa_product(self): def real_components(self): """ - Return 1 if there is 1 real component and 2 if there are 2. + Return the number of real components. EXAMPLES:: @@ -3216,13 +3221,7 @@ def real_components(self): sage: E.real_components () 1 """ - invs = self.short_weierstrass_model().ainvs() - x = rings.polygen(self.base_ring()) - f = x**3 + invs[3]*x + invs[4] - if f.discriminant() > 0: - return 2 - else: - return 1 + return 2 if self.discriminant() > 0 else 1 def has_good_reduction_outside_S(self, S=[]): r""" diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 859227c23c2..e0f6f9c6b91 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -6765,7 +6765,7 @@ def _adjust_heegner_index(self, a): EXAMPLES:: sage: E = EllipticCurve('11a1') - sage: a = RIF(sqrt(2))-1.4142135623730951 + sage: a = RIF(sqrt(2))-RIF(1.4142135623730951) sage: E._adjust_heegner_index(a) 1.?e-8 """ diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index d02885cb935..858946c5bb7 100644 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -1709,7 +1709,7 @@ def complex_intersection_is_empty(self, Bk, v, verbose=False, use_half=True): break else: z = CIF(intersection.innermost_point()) - if all(wp((k+1)*z) < B for k, B in enumerate(bounds)): + if all(wp((k+1)*z).upper() < B for k, B in enumerate(bounds)): return False # Now try to prove a positive result. @@ -1719,6 +1719,7 @@ def complex_intersection_is_empty(self, Bk, v, verbose=False, use_half=True): for B, n in sorted(zip(bounds, ZZ.range(1, k+1))): T = PeriodicRegion(CDF(1), CDF(tau), vals < B, full=not use_half).expand().refine() + B = RIF(B) leaning_right = tau.real() / tau.imag() >= 0 def check_line(z): wpz = wp(z) diff --git a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx index f58258de2b3..f308727efdb 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx +++ b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx @@ -168,7 +168,7 @@ cdef class PeriodicRegion: sage: S = PeriodicRegion(CDF(1), CDF(I), data) sage: S.border() [(1, 1, 0), (2, 1, 0), (1, 1, 1), (1, 2, 1)] - sage: condition = lambda z: z.real().abs()<0.5 + sage: condition = lambda z: z.real().abs()<1/2 sage: S.verify(condition) False sage: condition = lambda z: z.real().abs()<1 diff --git a/src/sage/schemes/elliptic_curves/sha_tate.py b/src/sage/schemes/elliptic_curves/sha_tate.py index 2bc2f07e189..54e59169987 100644 --- a/src/sage/schemes/elliptic_curves/sha_tate.py +++ b/src/sage/schemes/elliptic_curves/sha_tate.py @@ -1023,7 +1023,7 @@ def bound_kolyvagin(self, D=0, regulator=None, hZ = regulator/2 else: hZ = F.regulator(use_database=True)/2 - I = RIF(alpha) * RIF(LE1-err_E, LE1+err_E) * RIF(LF1-err_F, LF1+err_F) / hZ + I = RIF(alpha) * RIF(LE1-err_E, LE1+err_E) * RIF(LF1-err_F, LF1+err_F) / RIF(hZ) else: # E has odd rank @@ -1037,7 +1037,7 @@ def bound_kolyvagin(self, D=0, regulator=None, err_E = max(err_E, MIN_ERR) # I = alpha * LE1 * LF1 / hZ - I = RIF(alpha) * RIF(LE1-err_E, LE1+err_E) * RIF(LF1-err_F, LF1+err_F) / hZ + I = RIF(alpha) * RIF(LE1-err_E, LE1+err_E) * RIF(LF1-err_F, LF1+err_F) / RIF(hZ) verbose('interval = %s' % I) t, n = I.is_int() diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 77366969fc4..4416daf1f70 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -1920,7 +1920,7 @@ def change_ring(self, R): Closed subscheme of Projective Space of dimension 1 over Complex Field with 53 bits of precision defined by: x^2 + (0.623489801858734 + 0.781831482468030*I)*y^2 - sage: X.change_ring(K).change_ring(K.embeddings(QQbar)[0]) + sage: X.change_ring(K).change_ring(K.embeddings(QQbar)[3]) Closed subscheme of Projective Space of dimension 1 over Algebraic Field defined by: x^2 + (-0.9009688679024191? - 0.4338837391175581?*I)*y^2 diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index 911f7c44e49..b97d2d61e5e 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -38,6 +38,7 @@ from sage.categories.homset import HomsetWithBase from sage.structure.factory import UniqueFactory +from sage.structure.parent import Set_generic from sage.rings.all import ZZ from sage.rings.ring import CommutativeRing @@ -270,7 +271,6 @@ def __call__(self, *args, **kwds): (4, 5) """ # Homset (base of HomsetWithBase) overrides __call__ @#$ - from sage.structure.parent import Set_generic return Set_generic.__call__(self, *args, **kwds) def _repr_(self): diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index dcf78e82471..04264a6ce68 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -85,6 +85,7 @@ from sage.rings.fraction_field import is_FractionField from sage.categories.map import FormalCompositeMap, Map from sage.misc.constant_function import ConstantFunction +from sage.misc.lazy_attribute import lazy_attribute from sage.categories.morphism import SetMorphism from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme @@ -167,9 +168,37 @@ def __init__(self, parent, codomain=None): if not isinstance(parent, Homset): raise TypeError("parent (=%s) must be a Homspace"%parent) Element.__init__(self, parent) - self.domain = ConstantFunction(parent.domain()) self._codomain = parent.codomain() - self.codomain = ConstantFunction(self._codomain) + + @lazy_attribute + def domain(self): + r""" + The constant function from the domain. + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(QQ['x,y']) + sage: H = A.Hom(A) + sage: f = H([y,x^2+y]) + sage: f.domain() is A + True + """ + return ConstantFunction(self.parent().domain()) + + @lazy_attribute + def codomain(self): + r""" + The constant function from the codomain. + + EXAMPLES:: + + sage: A.<x,y> = AffineSpace(QQ['x,y']) + sage: H = A.Hom(A) + sage: f = H([y,x^2+y]) + sage: f.codomain() is A + True + """ + return ConstantFunction(self._codomain) # We copy methods of sage.categories.map.Map, to make # a future transition of SchemeMorphism to a sub-class of Morphism diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx index f60b85e3bd9..785b7812835 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx @@ -1,8 +1,11 @@ # 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: include_dirs = sage/libs/ntl/ sage/schemes/hyperelliptic_curves/hypellfrob/ -# distutils: libraries = gmp ntl zn_poly +# distutils: include_dirs = sage/libs/ntl/ sage/schemes/hyperelliptic_curves/hypellfrob/ NTL_INCDIR +# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: extra_compile_args = NTL_CFLAGS +# distutils: library_dirs = NTL_LIBDIR +# distutils: extra_link_args = NTL_LIBEXTRA r""" Frobenius on Monsky-Washnitzer cohomology of a hyperelliptic curve over a diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 510d9531632..f76c6e06bac 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -451,9 +451,8 @@ def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'): if d == 0: raise TypeError("P = %s is a Weierstrass point. Use local_coordinates_at_weierstrass instead!"%P) pol = self.hyperelliptic_polynomials()[0] - L = PowerSeriesRing(self.base_ring(), name) + L = PowerSeriesRing(self.base_ring(), name, default_prec=prec) t = L.gen() - L.set_default_prec(prec) K = PowerSeriesRing(L, 'x') pol = K(pol) b = P[0] diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index 152a4e3f109..8e0ac579eab 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -1255,17 +1255,13 @@ def __iter__(self): sage: P = ProductProjectiveSpaces([2, 1], GF(3)) sage: [x for x in P] - [(0 : 0 : 1 , 0 : 1), (1 : 0 : 1 , 0 : 1), (2 : 0 : 1 , 0 : 1), (0 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 0 : 1), - (2 : 1 : 1 , 0 : 1), (0 : 2 : 1 , 0 : 1), (1 : 2 : 1 , 0 : 1), (2 : 2 : 1 , 0 : 1), (0 : 1 : 0 , 0 : 1), - (1 : 1 : 0 , 0 : 1), (2 : 1 : 0 , 0 : 1), (1 : 0 : 0 , 0 : 1), (0 : 0 : 1 , 1 : 1), (1 : 0 : 1 , 1 : 1), - (2 : 0 : 1 , 1 : 1), (0 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 1 : 1), (2 : 1 : 1 , 1 : 1), (0 : 2 : 1 , 1 : 1), - (1 : 2 : 1 , 1 : 1), (2 : 2 : 1 , 1 : 1), (0 : 1 : 0 , 1 : 1), (1 : 1 : 0 , 1 : 1), (2 : 1 : 0 , 1 : 1), - (1 : 0 : 0 , 1 : 1), (0 : 0 : 1 , 2 : 1), (1 : 0 : 1 , 2 : 1), (2 : 0 : 1 , 2 : 1), (0 : 1 : 1 , 2 : 1), - (1 : 1 : 1 , 2 : 1), (2 : 1 : 1 , 2 : 1), (0 : 2 : 1 , 2 : 1), (1 : 2 : 1 , 2 : 1), (2 : 2 : 1 , 2 : 1), - (0 : 1 : 0 , 2 : 1), (1 : 1 : 0 , 2 : 1), (2 : 1 : 0 , 2 : 1), (1 : 0 : 0 , 2 : 1), (0 : 0 : 1 , 1 : 0), - (1 : 0 : 1 , 1 : 0), (2 : 0 : 1 , 1 : 0), (0 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 0), (2 : 1 : 1 , 1 : 0), - (0 : 2 : 1 , 1 : 0), (1 : 2 : 1 , 1 : 0), (2 : 2 : 1 , 1 : 0), (0 : 1 : 0 , 1 : 0), (1 : 1 : 0 , 1 : 0), - (2 : 1 : 0 , 1 : 0), (1 : 0 : 0 , 1 : 0)] + [(0 : 0 : 1 , 0 : 1), + (0 : 1 : 1 , 0 : 1), + (0 : 2 : 1 , 0 : 1), + ... + (1 : 1 : 0 , 1 : 0), + (2 : 1 : 0 , 1 : 0), + (1 : 0 : 0 , 1 : 0)] """ iters = [iter(T) for T in self._components] L=[] diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 1cbe52d0e5f..5327aebe5b7 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -71,13 +71,13 @@ - Rebecca Lauren Miller (March 2016) : added point_transformation_matrix """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.arith.all import gcd, binomial, srange from sage.rings.all import (PolynomialRing, @@ -91,15 +91,13 @@ from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields -_Fields = Fields() - -from sage.categories.homset import Hom from sage.categories.number_fields import NumberFields +from sage.categories.homset import Hom from sage.categories.map import Map - from sage.misc.all import (latex, prod) from sage.misc.all import cartesian_product_iterator +from sage.misc.persist import register_unpickle_override from sage.structure.category_object import normalize_names from sage.structure.unique_representation import UniqueRepresentation @@ -120,6 +118,11 @@ SchemeMorphism_polynomial_projective_space_field, SchemeMorphism_polynomial_projective_space_finite_field) + +# for better efficiency +_Fields = Fields() + + def is_ProjectiveSpace(x): r""" Return True if ``x`` is a projective space. @@ -139,6 +142,7 @@ def is_ProjectiveSpace(x): """ return isinstance(x, ProjectiveSpace_ring) + def ProjectiveSpace(n, R=None, names=None): r""" Return projective space of dimension ``n`` over the ring ``R``. @@ -237,7 +241,8 @@ def ProjectiveSpace(n, R=None, names=None): if n.variable_names() != names: # The provided name doesn't match the name of R's variables raise NameError("variable names passed to ProjectiveSpace conflict with names in ring") - A = ProjectiveSpace(n.ngens()-1, n.base_ring(), names=n.variable_names()) + A = ProjectiveSpace(n.ngens() - 1, n.base_ring(), + names=n.variable_names()) A._coordinate_ring = n return A if names is None: @@ -256,7 +261,7 @@ def ProjectiveSpace(n, R=None, names=None): elif isinstance(R, CommutativeRing): 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): @@ -318,7 +323,7 @@ def __classcall__(cls, n, R=ZZ, names=None): sage: ProjectiveSpace(QQ, 2, names='XYZ') is ProjectiveSpace(QQ, 2, names='XYZ') True """ - normalized_names = normalize_names(n+1, names) + normalized_names = normalize_names(n + 1, names) return super(ProjectiveSpace_ring, cls).__classcall__(cls, n, R, normalized_names) def __init__(self, n, R=ZZ, names=None): @@ -390,15 +395,15 @@ def _check_satisfies_equations(self, v): TypeError: the components of v=[1/2, 0, 1] must be elements of Integer Ring """ if not isinstance(v, (list, tuple)): - raise TypeError('the argument v=%s must be a list or tuple'%v) + raise TypeError('the argument v=%s must be a list or tuple' % v) n = self.ngens() if not len(v) == n: - raise TypeError('the list v=%s must have %s components'%(v, n)) + raise TypeError('the list v=%s must have %s components' % (v, n)) R = self.base_ring() for coord in v: - if not coord in R: - raise TypeError('the components of v=%s must be elements of %s'%(v, R)) - zero = [R(0)]*n + if coord not in R: + raise TypeError('the components of v=%s must be elements of %s' % (v, R)) + zero = [R(0)] * n if v == zero: raise TypeError('the zero vector is not a point in projective space') return True @@ -426,7 +431,8 @@ def coordinate_ring(self): return self._coordinate_ring except AttributeError: self._coordinate_ring = PolynomialRing(self.base_ring(), - self.variable_names(), self.dimension_relative()+1) + self.variable_names(), + self.dimension_relative() + 1) return self._coordinate_ring def _validate(self, polynomials): @@ -468,7 +474,7 @@ def _validate(self, polynomials): TypeError: the argument polynomials=x*y - z must be a list or tuple """ if not isinstance(polynomials, (list, tuple)): - raise TypeError('the argument polynomials=%s must be a list or tuple'%polynomials) + raise TypeError('the argument polynomials=%s must be a list or tuple' % polynomials) for f in polynomials: if not f.is_homogeneous(): raise TypeError("%s is not a homogeneous polynomial" % f) @@ -545,7 +551,7 @@ def __mul__(self, right): projective space, product of projective spaces, or subscheme """ if self.base_ring() != right.base_ring(): - raise ValueError ('Must have the same base ring') + raise ValueError('Must have the same base ring') from sage.schemes.product_projective.space import ProductProjectiveSpaces_ring from sage.schemes.product_projective.space import ProductProjectiveSpaces @@ -558,7 +564,7 @@ def __mul__(self, right): return self.__pow__(2) return ProductProjectiveSpaces([self, right]) elif isinstance(right, AlgebraicScheme_subscheme): - AS = self*right.ambient_space() + AS = self * right.ambient_space() CR = AS.coordinate_ring() n = self.ambient_space().coordinate_ring().ngens() @@ -566,7 +572,7 @@ def __mul__(self, right): psi = right.ambient_space().coordinate_ring().hom(list(CR.gens()[n:]), CR) return AS.subscheme([phi(t) for t in self.defining_polynomials()] + [psi(t) for t in right.defining_polynomials()]) else: - raise TypeError('%s must be a projective space, product of projective spaces, or subscheme'%right) + raise TypeError('%s must be a projective space, product of projective spaces, or subscheme' % right) def _latex_(self): r""" @@ -582,7 +588,8 @@ def _latex_(self): sage: ProjectiveSpace(3, Zp(5), 'y')._latex_() '{\\mathbf P}_{\\Bold{Z}_{5}}^3' """ - return "{\\mathbf P}_{%s}^%s"%(latex(self.base_ring()), self.dimension_relative()) + return "{\\mathbf P}_{%s}^%s" % (latex(self.base_ring()), + self.dimension_relative()) def _linear_system_as_kernel(self, d, pt, m): """ @@ -675,44 +682,45 @@ def _linear_system_as_kernel(self, d, pt, m): """ if not isinstance(d, (int, Integer)): - raise TypeError('the argument d=%s must be an integer'%d) + raise TypeError('the argument d=%s must be an integer' % d) if d < 0: - raise ValueError('the integer d=%s must be nonnegative'%d) - if not isinstance(pt, (list, tuple, \ + raise ValueError('the integer d=%s must be nonnegative' % d) + if not isinstance(pt, (list, tuple, SchemeMorphism_point_projective_ring)): raise TypeError('the argument pt=%s must be a list, tuple, or ' - 'point on a projective space'%pt) + 'point on a projective space' % pt) pt, R = prepare(pt, None) n = self.dimension_relative() - if not len(pt) == n+1: + if not len(pt) == n + 1: raise TypeError('the sequence pt=%s must have %s ' - 'components'%(pt, n + 1)) + 'components' % (pt, n + 1)) if not R.has_coerce_map_from(self.base_ring()): raise TypeError('unable to find a common ring for all elements') try: i = pt.index(1) except Exception: raise TypeError('at least one component of pt=%s must be equal ' - 'to 1'%pt) - pt = pt[:i] + pt[i+1:] + 'to 1' % pt) + pt = pt[:i] + pt[i + 1:] if not isinstance(m, (int, Integer)): - raise TypeError('the argument m=%s must be an integer'%m) + raise TypeError('the argument m=%s must be an integer' % m) if m < 0: - raise ValueError('the integer m=%s must be nonnegative'%m) + raise ValueError('the integer m=%s must be nonnegative' % m) # the components of partials correspond to partial derivatives # of order at most m-1 with respect to n variables - partials = IntegerVectors(m-1, n+1).list() + partials = IntegerVectors(m - 1, n + 1).list() # the components of monoms correspond to monomials of degree # at most d in n variables - monoms = IntegerVectors(d, n+1).list() - M = matrix(R,len(partials),len(monoms)) + monoms = IntegerVectors(d, n + 1).list() + M = matrix(R, len(partials), len(monoms)) for row in range(M.nrows()): - e = partials[row][:i] + partials[row][i+1:] + e = partials[row][:i] + partials[row][i + 1:] for col in range(M.ncols()): - f = monoms[col][:i] + monoms[col][i+1:] - if min([f[j]-e[j] for j in range(n)]) >= 0: - M[row,col] = prod([ binomial(f[j],e[j]) * pt[j]**(f[j]-e[j]) - for j in (k for k in range(n) if f[k] > e[k]) ]) + f = monoms[col][:i] + monoms[col][i + 1:] + if all(f[j] >= e[j] for j in range(n)): + M[row, col] = prod(binomial(fj, ej) * ptj**(fj - ej) + for ptj, fj, ej in zip(pt, f, e) + if fj > ej) return M def _morphism(self, *args, **kwds): @@ -787,10 +795,10 @@ def point(self, v, check=True): ValueError: [+Infinity] not well defined in dimension > 1 """ from sage.rings.infinity import infinity - if v is infinity or\ - (isinstance(v, (list,tuple)) and len(v) == 1 and v[0] is infinity): + if v is infinity or (isinstance(v, (list, tuple)) and + len(v) == 1 and v[0] is infinity): if self.dimension_relative() > 1: - raise ValueError("%s not well defined in dimension > 1"%v) + raise ValueError("%s not well defined in dimension > 1" % v) v = [1, 0] return self.point_homset()(v, check=check) @@ -824,7 +832,7 @@ def _repr_(self): sage: ProjectiveSpace(3, Zp(5), 'y')._repr_() 'Projective Space of dimension 3 over 5-adic Ring with capped relative precision 20' """ - return "Projective Space of dimension %s over %s"%(self.dimension_relative(), self.base_ring()) + return "Projective Space of dimension %s over %s" % (self.dimension_relative(), self.base_ring()) def _repr_generic_point(self, v=None): """ @@ -844,7 +852,7 @@ def _repr_generic_point(self, v=None): """ if v is None: v = self.gens() - return '(%s)'%(" : ".join([repr(f) for f in v])) + return '(%s)' % (" : ".join([repr(f) for f in v])) def _latex_generic_point(self, v=None): """ @@ -864,7 +872,7 @@ def _latex_generic_point(self, v=None): """ if v is None: v = self.gens() - return '\\left(%s\\right)'%(" : ".join([str(latex(f)) for f in v])) + return '\\left(%s\\right)' % (" : ".join(str(latex(f)) for f in v)) def change_ring(self, R): r""" @@ -1014,27 +1022,27 @@ def affine_patch(self, i, AA=None): i = int(i) # implicit type checking n = self.dimension_relative() if i < 0 or i > n: - raise ValueError("argument i (= %s) must be between 0 and %s"%(i, n)) + raise ValueError("argument i (= %s) must be between 0 and %s" % (i, n)) try: A = self.__affine_patches[i] - #assume that if you've passed in a new affine space you want to override - #the existing patch + # assume that if you've passed in a new affine space you + # want to override the existing patch if AA is None or A == AA: return A except AttributeError: self.__affine_patches = {} except KeyError: pass - #if no ith patch exists, we may still be here with AA==None + # if no ith patch exists, we may still be here with AA==None if AA is None: from sage.schemes.affine.affine_space import AffineSpace g = self.gens() - gens = g[:i] + g[i+1:] + gens = g[:i] + g[i + 1:] AA = AffineSpace(n, self.base_ring(), names=gens, ambient_projective_space=self, default_embedding_index=i) elif AA.dimension_relative() != n: - raise ValueError("affine space must be of the dimension %s"%(n)) + raise ValueError("affine space must be of the dimension %s" % (n)) self.__affine_patches[i] = AA return AA @@ -1097,12 +1105,12 @@ def Lattes_map(self, E, m): if self.base_ring() != E.base_ring(): E = E.change_ring(self.base_ring()) - L = E.multiplication_by_m(m, x_only = True) + L = E.multiplication_by_m(m, x_only=True) F = [L.numerator(), L.denominator()] R = self.coordinate_ring() x, y = R.gens() - phi = F[0].parent().hom([x],R) - F = [phi(F[0]).homogenize(y), phi(F[1]).homogenize(y)*y] + phi = F[0].parent().hom([x], R) + F = [phi(F[0]).homogenize(y), phi(F[1]).homogenize(y) * y] from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective return DynamicalSystem_projective(F, domain=self) @@ -1215,12 +1223,12 @@ def chebyshev_polynomial(self, n, kind='first', monic=False): n = ZZ(n) if (n < 0): raise ValueError("first parameter 'n' must be a non-negative integer") - #use the affine version and then homogenize. + # use the affine version and then homogenize. A = self.affine_patch(1) f = A.chebyshev_polynomial(n, kind) if monic and self.base().characteristic() != 2: f = f.homogenize(1) - return f.conjugate(matrix([[1/ZZ(2), 0],[0, 1]])) + return f.conjugate(matrix([[~ZZ(2), 0], [0, 1]])) return f.homogenize(1) def veronese_embedding(self, d, CS=None, order='lex'): @@ -1276,20 +1284,20 @@ def veronese_embedding(self, d, CS=None, order='lex'): """ d = ZZ(d) if d <= 0: - raise ValueError("(=%s) must be a positive integer"%d) + raise ValueError("(=%s) must be a positive integer" % d) N = self.dimension() # construct codomain space if not given if CS is None: CS = ProjectiveSpace(self.base_ring(), binomial(N + d, d) - 1) else: if not is_ProjectiveSpace(CS): - raise TypeError("(=%s) must be a projective space"%CS) + raise TypeError("(=%s) must be a projective space" % CS) if CS.dimension() != binomial(N + d, d) - 1: - raise TypeError("(=%s) has the wrong dimension to serve as the codomain space"%CS) + raise TypeError("(=%s) has the wrong dimension to serve as the codomain space" % CS) R = self.coordinate_ring().change_ring(order=order) - monomials = sorted([R({tuple(v) : 1}) for v in WeightedIntegerVectors(d, [1] * (N + 1))]) - monomials.reverse() # order the monomials greatest to least via the given monomial order + monomials = sorted([R({tuple(v): 1}) for v in WeightedIntegerVectors(d, [1] * (N + 1))]) + monomials.reverse() # order the monomials greatest to least via the given monomial order return Hom(self, CS)(monomials) @@ -1386,28 +1394,30 @@ def points_of_bounded_height(self, **kwds): sage: len(list(P.points_of_bounded_height(bound=1.5, tolerance=0.1))) 57 """ - 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 + 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 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 + B = bound**(self.base_ring().absolute_degree()) # convert to relative height n = self.dimension_relative() R = self.base_ring() if ftype: - zero = R(0) + zero = R.zero() i = n while not i < 0: - P = [ zero for _ in range(i) ] + [ R(1) ] + [ zero for _ in range(n-i) ] + 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 + 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: @@ -1415,14 +1425,15 @@ def points_of_bounded_height(self, **kwds): 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 + 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)]): + 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) @@ -1497,46 +1508,45 @@ def subscheme_from_Chow_form(self, Ch, dim): raise ValueError("Chow form must be a homogeneous polynomial") n = self.dimension_relative() R = Ch.parent() - if binomial(n+1,n-dim) != R.ngens(): - raise ValueError("for given dimension, there should be %d variables in the Chow form" % binomial(n+1,n-dim)) - #create the brackets associated to variables + if binomial(n + 1, n - dim) != R.ngens(): + raise ValueError("for given dimension, there should be %d variables in the Chow form" % binomial(n + 1, n - dim)) + # create the brackets associated to variables L1 = [] for t in UnorderedTuples(list(range(n + 1)), dim + 1): if all(t[i] < t[i + 1] for i in range(dim)): L1.append(t) - #create the dual brackets + # create the dual brackets L2 = [] signs = [] for l in L1: s = [] - for v in range(n+1): - if not v in l: + for v in range(n + 1): + if v not in l: s.append(v) - t1 = [b+1 for b in l] - t2 = [b+1 for b in s] - perm = Permutation(t1+t2) + t1 = [b + 1 for b in l] + t2 = [b + 1 for b in s] + perm = Permutation(t1 + t2) signs.append(perm.sign()) L2.append(s) - #create the polys associated to dual brackets - if n-dim-1 > 0: - S = PolynomialRing(R.base_ring(),n+1,'z') - T = PolynomialRing(S,(n+1)*(n-dim-1),'s') - M = matrix(T,n-dim,n+1,list(S.gens())+list(T.gens())) + # create the polys associated to dual brackets + if n - dim - 1 > 0: + S = PolynomialRing(R.base_ring(), n + 1, 'z') + T = PolynomialRing(S, (n + 1) * (n - dim - 1), 's') + M = matrix(T, n - dim, n + 1, list(S.gens()) + list(T.gens())) else: - T = PolynomialRing(R.base_ring(),n+1,'z') - M = matrix(T,n-dim,n+1,list(T.gens())) - coords=[] + T = PolynomialRing(R.base_ring(), n + 1, 'z') + M = matrix(T, n - dim, n + 1, list(T.gens())) + coords = [] for i in range(len(L2)): - coords.append(signs[i]*M.matrix_from_columns(L2[i]).det()) - #substitute in dual brackets to chow form - phi = R.hom(coords,T) + coords.append(signs[i] * M.matrix_from_columns(L2[i]).det()) + # substitute in dual brackets to chow form + phi = R.hom(coords, T) ch = phi(Ch) - #coefficients are polys in zs which are the chow equations for the chow form - if n-dim-1 > 0: - X = self.subscheme(ch.coefficients()) + # coefficients are polys in zs which are the chow equations for the chow form + if n - dim - 1 > 0: + return self.subscheme(ch.coefficients()) else: - X = self.subscheme(ch) - return X + return self.subscheme(ch) def point_transformation_matrix(self, points_source, points_target): r""" @@ -1643,16 +1653,16 @@ def point_transformation_matrix(self, points_source, points_target): """ r = self.base_ring() n = self.dimension_relative() - P = ProjectiveSpace(r, n**2+2*n,'p') + P = ProjectiveSpace(r, n * (n + 2), 'p') # makes sure there aren't to few or two many points if len(points_source) != n + 2: - raise ValueError ("incorrect number of points in source, need %d points"%(n+2)) + raise ValueError("incorrect number of points in source, need %d points" % (n + 2)) if len(points_target) != n + 2: - raise ValueError ("incorrect number of points in target, need %d points"%(n+2)) - if any(x.codomain()!=self for x in points_source): - raise ValueError ("source points not in self") - if any(x.codomain()!=self for x in points_target): - raise ValueError ("target points not in self") + raise ValueError("incorrect number of points in target, need %d points" % (n + 2)) + if any(x.codomain() != self for x in points_source): + raise ValueError("source points not in self") + if any(x.codomain() != self for x in points_target): + raise ValueError("target points not in self") # putting points as the rows of the matrix Ms = matrix(r, [list(s) for s in points_source]) if any(m == 0 for m in Ms.minors(n + 1)): @@ -1661,17 +1671,18 @@ def point_transformation_matrix(self, points_source, points_target): if any(l == 0 for l in Mt.minors(n + 1)): raise ValueError("target points not independent") A = matrix(P.coordinate_ring(), n + 1, n + 1, P.gens()) - #transpose to get image points and then get the list of image points with columns - funct = (A*Ms.transpose()).columns() + # transpose to get image points and then get the list of image points with columns + funct = (A * Ms.transpose()).columns() eq = [] - for k in range(n+2):# n+2 num f point and n is size of pts - eq = eq+ [funct[k][i]*points_target[k][j] - funct[k][j]*points_target[k][i]\ - for i in range(0,n+1) for j in range(i+1, n+1)] + for fk, ptk in zip(funct, points_target): + # n+2 num f point and n is size of pts + eq += [fk[i] * ptk[j] - fk[j] * ptk[i] + for i in range(n + 1) for j in range(i + 1, n + 1)] v = P.subscheme(eq) w = v.rational_points() - return matrix(r, n+1, n+1, list(w[0])) + return matrix(r, n + 1, n + 1, list(w[0])) - def curve(self,F): + def curve(self, F): r""" Return a curve defined by ``F`` in this projective space. @@ -1753,14 +1764,13 @@ def _morphism(self, *args, **kwds): """ return SchemeMorphism_polynomial_projective_space_finite_field(*args, **kwds) - def __iter__(self): r""" Return iterator over the elements of this projective space. Note that iteration is over the decomposition - `\mathbb{P}^n = \mathbb{A}A^n \cup \mathbb{P}^n-1`, where - `\mathbb{A}A^n` is the `n`-th affine patch and + `\mathbb{P}^n = \mathbb{A}^n \cup \mathbb{P}^n-1`, where + `\mathbb{A}^n` is the `n`-th affine patch and `\mathbb{P}^n-1` is the hyperplane at infinity `x_n = 0`. @@ -1776,22 +1786,22 @@ def __iter__(self): sage: PP = ProjectiveSpace(2,FF) sage: [ x for x in PP ] [(0 : 0 : 1), - (1 : 0 : 1), - (2 : 0 : 1), - (0 : 1 : 1), - (1 : 1 : 1), - (2 : 1 : 1), - (0 : 2 : 1), - (1 : 2 : 1), - (2 : 2 : 1), - (0 : 1 : 0), - (1 : 1 : 0), - (2 : 1 : 0), - (1 : 0 : 0)] + (0 : 1 : 1), + (0 : 2 : 1), + (1 : 0 : 1), + (1 : 1 : 1), + (1 : 2 : 1), + (2 : 0 : 1), + (2 : 1 : 1), + (2 : 2 : 1), + (0 : 1 : 0), + (1 : 1 : 0), + (2 : 1 : 0), + (1 : 0 : 0)] AUTHORS: - - David Kohel + - David Kohel, John Cremona .. TODO:: @@ -1801,25 +1811,14 @@ def __iter__(self): """ n = self.dimension_relative() R = self.base_ring() - zero = R(0) - i = n - while not i < 0: - P = [ zero for _ in range(i) ] + [ R(1) ] + [ zero for _ in range(n-i) ] - yield self(P) - iters = [ iter(R) 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] = iter(R) # reset - next(iters[j]) # put at zero - P[j] = zero - j += 1 - i -= 1 + zero = (R.zero(), ) + one = (R.one(), ) + PHom = self.point_homset() + C = PHom.codomain() + + for k in range(n + 1): # position of last 1 before the 0's + for v in cartesian_product_iterator([R for _ in range(n - k)]): + yield C._point(PHom, v + one + zero * k, check=False) def rational_points(self, F=None): """ @@ -1835,10 +1834,10 @@ def rational_points(self, F=None): [(0 : 1), (b : 1), (b + 1 : 1), (2*b + 1 : 1), (2 : 1), (2*b : 1), (2*b + 2 : 1), (b + 2 : 1), (1 : 1), (1 : 0)] """ if F is None: - return [ P for P in self ] + return [P for P in self] elif not is_FiniteField(F): - raise TypeError("second argument (= %s) must be a finite field"%F) - return [ P for P in self.base_extend(F) ] + raise TypeError("second argument (= %s) must be a finite field" % F) + return [P for P in self.base_extend(F)] def rational_points_dictionary(self): r""" @@ -1863,26 +1862,28 @@ def rational_points_dictionary(self): """ n = self.dimension_relative() R = self.base_ring() - D={} - zero = R(0) + D = {} + zero = R.zero() i = n - index=0 + index = 0 while not i < 0: - P = [ zero for _ in range(i) ] + [ R(1) ] + [ zero for _ in range(n-i) ] - D.update({self(P):index}) - index+=1 - iters = [ iter(R) for _ in range(i) ] - for x in iters: next(x) # put at zero + P = [zero for _ in range(i)] + [R.one()] + P += [zero for _ in range(n - i)] + D.update({self(P): index}) + index += 1 + iters = [iter(R) for _ in range(i)] + for x in iters: + next(x) # put at zero j = 0 while j < i: try: P[j] = next(iters[j]) - D.update({self(P):index}) - index+=1 + D.update({self(P): index}) + index += 1 j = 0 except StopIteration: iters[j] = iter(R) # reset - next(iters[j]) # put at zero + next(iters[j]) # put at zero P[j] = zero j += 1 i -= 1 @@ -1941,17 +1942,17 @@ def rational_points(self, bound=0): n = self.dimension_relative() - Q = [k-bound for k in range(2*bound+1)] # the affine coordinates - R = [(k+1) for k in range(bound)] # the projective coordinate - S = [Tuples(Q, (k+1)) for k in range(n)] + Q = [k - bound for k in range(2 * bound + 1)] # the affine coordinates + R = [(k + 1) for k in range(bound)] # the projective coordinate + S = [Tuples(Q, (k + 1)) for k in range(n)] pts = [] i = n while i > 0: - P = [ 0 for _ in range(n+1) ] + P = [0 for _ in range(n + 1)] for ai in R: P[i] = ai - for tup in S[i-1]: + for tup in S[i - 1]: if gcd([ai] + tup) == 1: for j in range(i): P[j] = tup[j] @@ -1960,13 +1961,13 @@ def rational_points(self, bound=0): # now do i=0; this is treated as a special case so that # we don't have all points (1:0),(2,0),(3,0),etc. - P = [ 0 for _ in range(n+1) ]; P[0] = 1 + P = [0 for _ in range(n + 1)] + P[0] = 1 pts.append(self(P)) return pts # fix the pickles from moving projective_space.py -from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.schemes.generic.projective_space', 'ProjectiveSpace_field', ProjectiveSpace_field) @@ -1974,4 +1975,3 @@ def rational_points(self, bound=0): register_unpickle_override('sage.schemes.generic.projective_space', 'ProjectiveSpace_rational_field', ProjectiveSpace_rational_field) - diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 4e174df8c91..1f6e2513866 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -132,12 +132,12 @@ cpdef py_scalar_parent(py_type): sage: py_scalar_parent(numpy.uint64) Integer Ring - sage: py_scalar_parent(numpy.float) + sage: py_scalar_parent(float) Real Double Field sage: py_scalar_parent(numpy.double) Real Double Field - sage: py_scalar_parent(numpy.complex) + sage: py_scalar_parent(complex) Complex Double Field sage: import gmpy2 @@ -328,7 +328,7 @@ cpdef bint parent_is_integers(P) except -1: True sage: parent_is_integers(numpy.uint64) True - sage: parent_is_integers(numpy.float) + sage: parent_is_integers(float) False sage: import gmpy2 @@ -420,8 +420,6 @@ cpdef bint is_numpy_type(t): True sage: is_numpy_type(numpy.floating) True - sage: is_numpy_type(numpy.float) # Alias for Python float - False sage: is_numpy_type(numpy.ndarray) True sage: is_numpy_type(numpy.matrix) @@ -512,9 +510,9 @@ cdef class CoercionModel: sage: x * numpy.float32('1.5') 1.50000000000000*x sage: p = x**3 + 2*x - 1 - sage: p(numpy.float('1.2')) + sage: p(float('1.2')) 3.12800000000000 - sage: p(numpy.int('2')) + sage: p(int('2')) 11.0000000000000 This used to fail (see :trac:`18076`):: diff --git a/src/sage/structure/mutability.pyx b/src/sage/structure/mutability.pyx index 2d72ad9ce01..3d54c790263 100644 --- a/src/sage/structure/mutability.pyx +++ b/src/sage/structure/mutability.pyx @@ -177,6 +177,71 @@ cdef class Mutability: """ return not self._is_immutable + 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 + ....: def change(self, val): + ....: self._require_mutable() + ....: self._val = val + ....: def __hash__(self): + ....: self._require_immutable() + ....: return hash(self._val) + sage: a = A(4) + sage: a.__dict__ + {'_val': 4} + sage: a.__getstate__() + {'_is_immutable': False, '_val': 4} + sage: a.__reduce__() # indirect doctest + (<function _reconstructor at ...>, + (<class '__main__.A'>, + <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 + return state + + def __setstate__(self, state): + r""" + Set the state of ``self`` from the dictionary ``state`` including the + mutability status. + + TESTS:: + + sage: class A(SageObject, Mutability): + ....: def __init__(self, val): + ....: self._val = val + ....: def change(self, val): + ....: self._require_mutable() + ....: self._val = val + ....: def __hash__(self): + ....: self._require_immutable() + ....: return hash(self._val) + sage: a = A(4) + sage: a.is_immutable() + False + sage: d = a.__getstate__(); d + {'_is_immutable': False, '_val': 4} + sage: d['_is_immutable'] = True + sage: a.__setstate__(d) + sage: a.is_immutable() + True + sage: a.__getstate__() + {'_is_immutable': True, '_val': 4} + + """ + if hasattr(self, '__dict__'): + self.__dict__ = state + self._is_immutable = state['_is_immutable'] + ########################################################################## ## Method decorators for mutating methods resp. methods that assume immutability diff --git a/src/sage/structure/nonexact.py b/src/sage/structure/nonexact.py index e32b0a72711..913f42c9b19 100644 --- a/src/sage/structure/nonexact.py +++ b/src/sage/structure/nonexact.py @@ -1,24 +1,90 @@ -"Precision management for non-exact objects" +r""" +Precision management for non-exact objects -import sage.rings.integer +Manage the default precision for non-exact objects such as power series rings or +laurent series rings. + +EXAMPLES:: + + sage: R.<x> = PowerSeriesRing(QQ) + sage: R.default_prec() + 20 + sage: cos(x) + 1 - 1/2*x^2 + 1/24*x^4 - 1/720*x^6 + 1/40320*x^8 - 1/3628800*x^10 + + 1/479001600*x^12 - 1/87178291200*x^14 + 1/20922789888000*x^16 - + 1/6402373705728000*x^18 + O(x^20) + +:: + + sage: R.<x> = PowerSeriesRing(QQ, default_prec=10) + sage: R.default_prec() + 10 + sage: cos(x) + 1 - 1/2*x^2 + 1/24*x^4 - 1/720*x^6 + 1/40320*x^8 + O(x^10) + +.. NOTE:: + + Subclasses of :class:`Nonexact` which require to change the default + precision should implement a method `set_default_prec`. + +""" + +from sage.rings.integer import Integer class Nonexact: + r""" + A non-exact object with default precision. + + INPUT: + + - ``prec`` -- a non-negative integer representing the default precision of + ``self`` (default: ``20``) + + """ def __init__(self, prec=20): - self.__default_prec = sage.rings.integer.Integer(prec) + if prec < 0: + raise ValueError(f"prec (= {prec}) must be non-negative") + self._default_prec = Integer(prec) def default_prec(self): r""" - Return the default precision for self. Use - ``set_default_prec`` to set the default precision. + Return the default precision for ``self``. + + EXAMPLES:: + + sage: R = QQ[[x]] + sage: R.default_prec() + 20 + + :: + + sage: R.<x> = PowerSeriesRing(QQ, default_prec=10) + sage: R.default_prec() + 10 + """ try: - return self.__default_prec + return self._default_prec except AttributeError: - self.default_prec = 20 - return self.__default_prec - return self.__default_prec + self._default_prec = 20 + return self._default_prec def set_default_prec(self, prec): - self.__default_prec = sage.rings.integer.Integer(prec) + r""" + Set the default precision for ``self`` + + .. WARNING:: + + This method is outdated. If a subclass of class:`Nonexact` requires + this method, please overload it instead. + + """ + # TODO: remove in Sage 9.4 + from sage.misc.superseded import deprecation + msg = "The method set_default_prec() is deprecated and will be removed " + msg += "in a future version of Sage. The default precision is set " + msg += "during construction." + deprecation(18416, msg) + self._default_prec = Integer(prec) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 860fefec333..2da78492137 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -140,6 +140,13 @@ Test if :trac:`24883` is fixed:: sage: a*b 1/4*((I + 1)*sqrt(2) - 2)*(-(I + 1)*sqrt(2) - 2) +Test that :trac:`20784` is fixed (equations should stay unevaluated):: + + sage: limit(1/x, x=0) == unsigned_infinity + Infinity == Infinity + sage: SR(unsigned_infinity) == unsigned_infinity + Infinity == Infinity + Many tests about comparison. Use :func:`sage.symbolic.comparison.mixed_order`` instead of @@ -1231,6 +1238,13 @@ cdef class Expression(CommutativeRingElement): sage: latex(1+x^(2/3)+x^(-2/3)) x^{\frac{2}{3}} + \frac{1}{x^{\frac{2}{3}}} + 1 + + Check that pynac understands rational powers (:trac:`30446`):: + + sage: QQ((24*sqrt(3))^(100/50))==1728 + True + sage: float((24*sqrt(3))^(100/51)) + 1493.0092154... """ return self._parent._latex_element_(self) @@ -4140,6 +4154,35 @@ cdef class Expression(CommutativeRingElement): sage: elem = SR(2)^n sage: (elem, elem.parent()) (2^n, Asymptotic Ring <SR^n * n^SR> over Symbolic Ring) + + Check that pynac understands rational powers (:trac:`30446`, + :trac:`28620`, :trac:`30304`, and :trac:`30786`):: + + sage: QQ((24*sqrt(3))^(100/50))==1728 + True + sage: float((24*sqrt(3))^(100/51)) + 1493.0092154... + sage: t=((1/10)*I/pi)^(3/2) + sage: t^2 + -1/1000*I/pi^3 + sage: (2*pi)^QQ(2) + 4*pi^2 + sage: exp(-3*ln(-9*x)/3) + -1/9/x + + Check that :trac:`31137` is also fixed:: + + sage: _ = var('A, L, G, R, f, k, n, q, u, beta, gamma', domain="positive") + sage: a = I*R^2*f^3*k*q*A*u + sage: b = 2*pi*L*R^2*G*f^4*k^2*q - 2*pi*L*R^2*G*f^4*q - 2*pi*L*R^2*beta^2*G*q + sage: c = (2*I*pi*L*R^2*beta*gamma*q + 2*I*pi*L*R*(beta + q))*G*f^3 + sage: d = 2*(pi*(beta^2 + 1)*L*R^2*q + pi*L*R*beta*gamma*q + pi*L*beta)*G*f^2 + sage: e = (-2*I*pi*L*R^2*beta*gamma*q - 2*I*pi*(beta^2*q + beta)*L*R)*G*f + sage: expr = a / ((b + c + d + e)*n) + sage: R = ((sqrt(expr.real()^2 + expr.imag()^2).factor())^2).factor() + sage: Rs = R.subs(f = 2*beta) + sage: len(str(Rs)) + 520 """ cdef Expression nexp = <Expression>other cdef GEx x @@ -4823,6 +4866,15 @@ cdef class Expression(CommutativeRingElement): 1/(x^2 + 2*x + 1) sage: (((x-1)/(x+1))^2).expand() x^2/(x^2 + 2*x + 1) - 2*x/(x^2 + 2*x + 1) + 1/(x^2 + 2*x + 1) + + Check that :trac:`30688` is fixed:: + + sage: assume(x < 0) + sage: sqrt(-x).expand() + sqrt(-x) + sage: ((-x)^(3/4)).expand() + (-x)^(3/4) + sage: forget() """ if side is not None: if not is_a_relational(self._gobj): @@ -5307,6 +5359,21 @@ cdef class Expression(CommutativeRingElement): x^4 + y y + .. WARNING:: + + Unexpected results may occur if the left-hand side of some substitution + is not just a single variable (or is a "wildcard" variable). For example, + the result of ``cos(cos(cos(x))).subs({cos(x) : x})`` is ``x``, because + the substitution is applied repeatedly. Such repeated substitutions (and + pattern-matching code that may be somewhat unpredictable) are disabled + only in the basic case where the left-hand side of every substitution is + a variable. In particular, although the result of + ``(x^2).subs({x : sqrt(x)})`` is ``x``, the result of + ``(x^2).subs({x : sqrt(x), y^2 : y})`` is ``sqrt(x)``, because repeated + substitution is enabled by the presence of the expression ``y^2`` in the + left-hand side of one of the substitutions, even though that particular + substitution does not get applied. + TESTS: No arguments return the same expression:: @@ -5410,6 +5477,18 @@ cdef class Expression(CommutativeRingElement): x^2 + 1/x sage: (sqrt(x) + 1/sqrt(x)).subs({x: 1/x}) sqrt(x) + 1/sqrt(x) + + Check that :trac:`30378` is fixed:: + + sage: (x^2).subs({x: sqrt(x)}) + x + sage: f(x) = x^2 + sage: f(sqrt(x)) + x + sage: a = var("a") + sage: f = function("f") + sage: integrate(f(x), x, 0, a).subs(a=cos(a)) + integrate(f(x), x, 0, cos(a)) """ cdef dict sdict = {} cdef GEx res @@ -5430,6 +5509,28 @@ cdef class Expression(CommutativeRingElement): # Check for duplicate _dict_update_check_duplicate(sdict, varkwds) + # To work around the pynac bug in :trac:`30378`, we use two steps to do a + # substitution that only involves plugging expressions into variables, but + # where some of the expressions include variables that are in self. + if all(self.parent(k).is_symbol() for k in sdict.keys()): + dict_vars = tuple(v for k in sdict.keys() + for v in self.parent(sdict[k]).variables()) + if not set(self.variables()).isdisjoint(dict_vars): + # Step 1: replace each variable with a new temporary variable + temp_vars = {v : self.parent().symbol() for v in self.variables()} + with hold: + first_step = self.substitute(temp_vars) + # Step 2: make the original substitutions into the new variables + result = first_step.substitute({ + temp_vars[v] : + sdict[v] if v in sdict.keys() else v + for v in self.variables()}) + if not set(result.variables()).issubset(self.variables() + dict_vars): + raise RuntimeError("substitution failed") + return result + + # We are not in the basic case of only substituting expressions into + # variables, so we ask Ginac to do the work. cdef GExMap smap for k, v in sdict.iteritems(): smap.insert(make_pair((<Expression>self.coerce_in(k))._gobj, @@ -12513,6 +12614,11 @@ cdef class Expression(CommutativeRingElement): (x, y) |--> 2*x + 2*y sage: integral(f, z) (x, y) |--> (x + y)*z + + We check that :trac:`13097` is resolved:: + + sage: integrate(ln(1+4/5*sin(x)), x, -3.1415, 3.1415) # tol 10e-6 + -1.40205228301000 """ from sage.symbolic.integration.integral import \ integral, _normalize_integral_input diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index bae58348f9d..9cf695317fc 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -1073,6 +1073,21 @@ def arithmetic(self, ex, operator): Traceback (most recent call last): ... TypeError: unable to convert pi^6 to Algebraic Field + + Test that :trac:`14602` is fixed:: + + sage: K = QuadraticField(3) + sage: K(sqrt(3)).parent() is K + True + sage: sqrt(K(3)).parent() is K + True + sage: (K(3)^(1/2)).parent() + Symbolic Ring + sage: bool(K.gen() == K(3)^(1/2) == sqrt(K(3)) == K(sqrt(3)) == sqrt(3)) + True + sage: L = QuadraticField(3, embedding=-AA(3).sqrt()) + sage: bool(L.gen() == -sqrt(3)) + True """ # We try to avoid simplifying, because maxima's simplify command # can change the value of a radical expression (by changing which diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 21bdb10ad3f..ff3c51c7680 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -540,7 +540,7 @@ cdef class Function(SageObject): 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=numpy.complex) + sage: a = numpy.array([1+2*I, -2-3*I], dtype=complex) sage: real_part(a) array([ 1., -2.]) sage: imag_part(a) diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 13dac36b516..46b1a11a82a 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -972,6 +972,15 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): sage: f = (I*a*tan(d*x + c) + a)^3*tan(d*x + c) sage: integrate(f, x, algorithm="fricas") # optional - fricas -2/3*(24*a^3*e^(4*I*d*x + 4*I*c) + 33*a^3*e^(2*I*d*x + 2*I*c) + 13*a^3 + 6*(a^3*e^(6*I*d*x + 6*I*c) + 3*a^3*e^(4*I*d*x + 4*I*c) + 3*a^3*e^(2*I*d*x + 2*I*c) + a^3)*log(e^(2*I*d*x + 2*I*c) + 1))/(d*e^(6*I*d*x + 6*I*c) + 3*d*e^(4*I*d*x + 4*I*c) + 3*d*e^(2*I*d*x + 2*I*c) + d) + + The fundamental theorem of calculus holds for elliptic integrals + of the second kind, :trac:`26563`:: + + sage: x,m = SR.var('x,m', domain='real') # long time + sage: integrate(elliptic_e(x,m).diff(x), x) # long time + elliptic_e(x, m) + + """ expression, v, a, b = _normalize_integral_input(expression, v, a, b) if algorithm is not None: diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 238414f2b37..692266db00c 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -1194,9 +1194,9 @@ cdef class NumpyToSRMorphism(Morphism): This behavior also applies to standard functions:: - sage: cos(numpy.int('2')) + sage: cos(int('2')) cos(2) - sage: numpy.cos(numpy.int('2')) + sage: numpy.cos(int('2')) -0.4161468365471424 """ cdef _intermediate_ring diff --git a/src/sage/symbolic/symengine.py b/src/sage/symbolic/symengine.py new file mode 100644 index 00000000000..123eb51b824 --- /dev/null +++ b/src/sage/symbolic/symengine.py @@ -0,0 +1,16 @@ +r""" +EXAMPLES:: + + sage: import symengine # optional - symengine_py + sage: x, y = symengine.var("x y") # optional - symengine_py + sage: expr = (x + symengine.GoldenRatio * symengine.exp(y))**2 # optional - symengine_py + sage: SR(expr) # optional - symengine_py + (golden_ratio*e^y + x)^2 + + sage: f = symengine.Lambdify([x, y], expr) # optional - symengine_py + sage: f(3, 5) # optional - symengine_py + array(59115.86131768) + sage: g = fast_callable(SR(expr), vars=[SR(x),SR(y)], domain=RDF) # optional - symengine_py + sage: g(3, 5) # optional - symengine_py + 59115.86131767523 +""" diff --git a/src/sage/symbolic/todo.txt b/src/sage/symbolic/todo.txt deleted file mode 100644 index be2829aa65e..00000000000 --- a/src/sage/symbolic/todo.txt +++ /dev/null @@ -1,174 +0,0 @@ -done * get it to work with a union -done * make type and value private -done * move all definition code into cpp file -done * add an int type -done * create a coercion model -done * make everything actually work with these two types -done * get rid of ginsh and other crap from distro. -done * change verbose logging stuff to be macros -done * figure out how to properly check for and raise a Python exception -done * implement stub functions: -done * use gmp to implement binomial on ui's -@deftypefunx void mpz_bin_uiui (mpz_t @var{rop}, unsigned long int @var{n}, @w{unsigned long int @var{k}}) -done * fix the binomial expand memory leak -done * Need to print with parenthesis, e.g.: -sage: K.<alpha> = NumberField(x^5 + x^2 + x -1) -sage: var('x,y,z,w',ns=1); P=x.parent() -sage: x^(alpha+1) -x^alpha + 1 -done * fix symbol order (done by changing hashing and compare_same_type in symbol.cpp) -mostly done/changed -- above fix made all this change. replaced by new todo * Fix printing order. -sage: x^2 + x^4 + x^3 -x^4 + x^2 + x^3 -sage: var('a,x,y') -(a, x, y) -sage: a+x+y -y + x + a -sage: a+y+x -y + x + a -sage: y+x+a -y + x + a - -* print degree order: -Now, -sage: x^2 + x^4 + x^3 -x^2 + x^3 + x^4 -sage: a^3*x^10 + x^12 - a^15 -x^12 + a^3*x^10 - a^15 - -So it is printing from lowest to highest degree, like mathematica (or power series), -but unlike the standard sage convention (or maple, singular, MATH, etc.): -sage: R.<a,x> = QQ[] -sage: a^3*x^10 + x^12 - a^15 --a^15 + a^3*x^10 + x^12 -sage: singular(a^3*x^10 + x^12 - a^15) --a^15+a^3*x^10+x^12 -done * switch everything to use except+. -done * doctest all existing functions -done * add copyright headers -done * wrap differentiation -done * integrate in burcin's new code -done * put sig_on()/ sig_off() back so control-c works. -done (?) * printing sums is still out of wack and non-deterministic -done * implement isqrt, etc. and other stubs in pynac.pyx -done * BUG: -get gcd to work -sage: from sage.all import *; x,y= var('x,y',ns=1); (x**2-y**2).gcd(x-y) -boom! - -done * Bug... -sage: var('x',ns=1); k.<a> = GF(9); k(2)*x -x -with 9 replaced by 25 it works fine... - -done * wrap basic pattern matching - -done * wrap basic polynomial functions - -done * wrap substituting expressions - -done * {{{id=27| -var('x,y',ns=1) -S = x.parent() -pi = sage.symbolic.ring.pi -def hermite(n,y): - if n == 1: - return 2*y - if n == 0: - return 1 - return 2*y*hermite(n-1,y) - 2*(n-1)*hermite(n-2,y) - -def phi(n,y): - return 1/(sqrt(S(2^n*factorial(n)))*pi^(1/4))*exp(-y^2/2)*hermite(n,y) - -time a = phi(25,4) -/// -** Hit STUB**: irem -- need to compute r!!! -PYTHON ERROR! error calling function -}}} - -DONE * Control-c --> SIGBUS: - -sage: R.<u,v> = QQ[] -sage: var('a,b,c', ns=1) -(a, b, c) -sage: expand((u + v + a + b + c)^2) -u^2 + 2*u*v + v^2 + a^2 + 2*b*a + (2*u + 2*v)*b + b^2 + 2*b*c + (2*u + 2*v)*a + c^2 + (2*u + 2*v)*c + 2*a*c -sage: time z = expand((u + v + a + b + c)^10) -CPU times: user 0.01 s, sys: 0.00 s, total: 0.01 s -Wall time: 0.05 s -sage: time z = expand((u + v + a + b + c)^100) -^C^C^C ------------------------------------------------------------- -Unhandled SIGBUS: A bus error occurred in Sage. - -done * pretty serious todo -- all predefined constants in utils.cpp get made wrong: - // TODO: BECAUSE of Floor division, this gives the wrong answer!! - // However, it gets called at GiNaC startup, so I don't yet - // know how to change it to make a Sage Rational. Argh. - value = Number_T(numer) / Number_T(denom); - -done * wrap taylor series -done * power series print wrong -- no spaces around operators - -done * figure out how to check for true or false-ness of inequalities in GiNaC manual. - sage: var('x,y,z,w',ns=1); P=x.parent() - sage: bool(P(2)== P(2)) - -done-ish * implement conversion to RealField, and more general coercions... - -done * BUG: -sage: S(0.5).arccosh() -1.12762596520638 # very wrong! -Notes -(1) sage: (0.5).arccosh() -NaN -(2) but however it seems the code in pynac.pyx isn't even called. - -done * make it so "make install" actually works without manual intervention, especially on osx - ------------------------------------------------------------------- - - -****************************************************************************** -For PHASE II: LATER (Hopefully Burcin) -****************************************************************************** - -* genuine coercions to real field, etc. - -* optimize is_even in numeric.cpp - -* Support pickle via the "archive" print mode. - -done * CRASH: -sage: f = (Mod(2,7)*x^2 + Mod(2,7))^7 ------------------------------------------------------------- -Unhandled SIGBUS: A bus error occurred in Sage. - -It works fine with leading coefficient Mod(1,7) - -done * Weird wrong answers: -sage: k = GF(7) -sage: f = expand((k(1)*x^5 + k(1)*x^2 + k(2))^7); f -2 + 35*x^26 + 21*x^29 + 7*x^17 + 21*x^20 + 35*x^23 + x^14 + 7*x^32 + x^35 -sage: f * k(-1) * k(-1) -2 - -* need to be able to do this (from ginsh): -> collect_common_factors(x/(x^2 + x)); -(1+x)^(-1) - - -* Maybe change Sage's GiNaC to make a call to a Cython gcd function, then use -Singular, since Singular's gcd over QQ is much better than GiNaC's, I think, -and GiNaC *only* does GCD over QQ. Actually, just make everything in normal.cpp -be implemented via Singular, probably... - -done * Make it so these work the same way, i.e., in second case below the Mod(1,7) -should also reduce. Probably in some cases GiNaC just doesn't bother to do -the multiply because Mod(1,7) == 1 is true. - -sage: a.expand()*Mod(2,7) -5 + (5)*x + (2)*x^3 + (4)*x^2 -sage: a.expand()*Mod(1,7) -1000 + 300*x + x^3 + 30*x^2 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 ca61ded9717..867d4e5c48f 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 @@ -149,7 +149,7 @@ sage: mpmath.quad(sin(sin(x)), [0, 1]) Traceback (most recent call last): ... - TypeError: no canonical coercion from <type 'sage.libs.mpmath.ext_main.mpf'> to Symbolic Ring + TypeError: unable to convert mpf('0.5') to a symbolic expression Sage example in ./integration.tex, line 866:: diff --git a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py index 6c25aa5dc5e..ac15dad7a9d 100644 --- a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py @@ -66,8 +66,7 @@ ~~~~~~~~~~~~~~~~~~~~~~ :: sage: G = L.galois_group(); G - Galois group of Number Field in b with - defining polynomial x^8 + 28*x^4 + 2500 + Galois group 8T4 ([4]2) with order 8 of x^8 + 28*x^4 + 2500 ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/version.py b/src/sage/version.py index 1bc38a69a1f..b553bef2a4e 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.3.beta7' -date = '2021-02-07' -banner = 'SageMath version 9.3.beta7, Release Date: 2021-02-07' +version = '9.3.beta9' +date = '2021-03-14' +banner = 'SageMath version 9.3.beta9, Release Date: 2021-03-14' diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_docbuild/__init__.py similarity index 96% rename from src/sage_setup/docbuild/__init__.py rename to src/sage_docbuild/__init__.py index b07e9c100cf..79005b903ab 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -90,16 +90,16 @@ def builder_helper(type): Check that :trac:`25161` has been resolved:: - sage: from sage_setup.docbuild import DocBuilder, setup_parser + sage: from sage_docbuild import DocBuilder, setup_parser sage: DocBuilder._options = setup_parser().parse_args([])[0] # builder_helper needs _options to be set - sage: import sage_setup.docbuild.sphinxbuild + sage: import sage_docbuild.sphinxbuild sage: def raiseBaseException(): ....: raise BaseException("abort pool operation") - sage: original_runsphinx, sage_setup.docbuild.sphinxbuild.runsphinx = sage_setup.docbuild.sphinxbuild.runsphinx, raiseBaseException + sage: original_runsphinx, sage_docbuild.sphinxbuild.runsphinx = sage_docbuild.sphinxbuild.runsphinx, raiseBaseException - sage: from sage_setup.docbuild import builder_helper, build_ref_doc - sage: from sage_setup.docbuild import _build_many as build_many + sage: from sage_docbuild import builder_helper, build_ref_doc + sage: from sage_docbuild import _build_many as build_many sage: helper = builder_helper("html") sage: try: ....: build_many(build_ref_doc, [("docname", "en", "html", {})]) @@ -180,7 +180,7 @@ def _output_dir(self, type): EXAMPLES:: - sage: from sage_setup.docbuild import DocBuilder + sage: from sage_docbuild import DocBuilder sage: b = DocBuilder('tutorial') sage: b._output_dir('html') '.../html/en/tutorial' @@ -197,7 +197,7 @@ def _doctrees_dir(self): EXAMPLES:: - sage: from sage_setup.docbuild import DocBuilder + sage: from sage_docbuild import DocBuilder sage: b = DocBuilder('tutorial') sage: b._doctrees_dir() '.../doctrees/en/tutorial' @@ -212,7 +212,7 @@ def _output_formats(self): EXAMPLES:: - sage: from sage_setup.docbuild import DocBuilder + sage: from sage_docbuild import DocBuilder sage: b = DocBuilder('tutorial') sage: b._output_formats() ['changes', 'html', 'htmlhelp', 'inventory', 'json', 'latex', 'linkcheck', 'pickle', 'web'] @@ -236,7 +236,7 @@ def pdf(self): EXAMPLES:: - sage: from sage_setup.docbuild import DocBuilder + sage: from sage_docbuild import DocBuilder sage: b = DocBuilder('tutorial') sage: b.pdf() #not tested """ @@ -286,13 +286,15 @@ def clean(self, *args): from .utils import build_many as _build_many -def build_many(target, args): +def build_many(target, args, processes=None): """ - Thin wrapper around `sage_setup.docbuild.utils.build_many` which uses the + Thin wrapper around `sage_docbuild.utils.build_many` which uses the docbuild settings ``NUM_THREADS`` and ``ABORT_ON_ERROR``. """ + if processes is None: + processes = NUM_THREADS try: - _build_many(target, args, processes=NUM_THREADS) + _build_many(target, args, processes=processes) except BaseException as exc: if ABORT_ON_ERROR: raise @@ -349,7 +351,13 @@ def _wrapper(self, name, *args, **kwds): # build the other documents in parallel L = [(doc, name, kwds) + args for doc in others] - build_many(build_other_doc, L) + + # Trac #31344: Work around crashes from multiprocessing + if sys.platform == 'darwin': + for target in L: + build_other_doc(target) + else: + build_many(build_other_doc, L) logger.warning("Elapsed time: %.1f seconds."%(time.time()-start)) logger.warning("Done building the documentation!") @@ -361,7 +369,7 @@ def get_all_documents(self): EXAMPLES:: - sage: from sage_setup.docbuild import AllBuilder + sage: from sage_docbuild import AllBuilder sage: documents = AllBuilder().get_all_documents() sage: 'en/tutorial' in documents True @@ -517,7 +525,7 @@ def _output_dir(self, type, lang='en'): EXAMPLES:: - sage: from sage_setup.docbuild import ReferenceBuilder + sage: from sage_docbuild import ReferenceBuilder sage: b = ReferenceBuilder('reference') sage: b._output_dir('html') '.../html/en/reference' @@ -680,7 +688,7 @@ def get_all_documents(self, refdir): EXAMPLES:: - sage: from sage_setup.docbuild import ReferenceBuilder + sage: from sage_docbuild import ReferenceBuilder sage: b = ReferenceBuilder('reference') sage: refdir = os.path.join(os.environ['SAGE_DOC_SRC'], 'en', b.name) sage: sorted(b.get_all_documents(refdir)) @@ -1038,7 +1046,7 @@ def auto_rest_filename(self, module_name): EXAMPLES:: - sage: from sage_setup.docbuild import ReferenceSubBuilder + sage: from sage_docbuild import ReferenceSubBuilder sage: ReferenceSubBuilder("reference").auto_rest_filename("sage.combinat.partition") '.../doc/en/reference/sage/combinat/partition.rst' """ @@ -1458,6 +1466,24 @@ def help_wrapper(option, opt_str, value, parser): print(help_documents(), end="") if option.dest == 'formats': print(help_formats(), end="") + if option.dest == 'all_documents': + if value == 'en/reference' or value == 'reference': + b = ReferenceBuilder('reference') + refdir = os.path.join(os.environ['SAGE_DOC_SRC'], 'en', b.name) + s = b.get_all_documents(refdir) + # Put the bibliography first, because it needs to be built first: + s.remove('reference/references') + s.insert(0, 'reference/references') + elif value == 'all': + s = get_documents() + # Put the reference manual first, because it needs to be built first: + s.remove('reference') + s.insert(0, 'reference') + else: + raise ValueError("argument for --all-documents must be either 'all'" + " or 'reference'") + for d in s: + print(d) setattr(parser.values, 'printed_list', 1) @@ -1573,6 +1599,12 @@ def setup_parser(): advanced.add_option("-k", "--keep-going", dest="keep_going", default=False, action="store_true", help="Do not abort on errors but continue as much as possible after an error") + advanced.add_option("--all-documents", dest="all_documents", + type="str", metavar="ARG", + action="callback", callback=help_wrapper, + help="if ARG is 'reference', list all subdocuments" + " of en/reference. If ARG is 'all', list all main" + " documents") parser.add_option_group(advanced) return parser @@ -1584,7 +1616,7 @@ def setup_logger(verbose=1, color=True): EXAMPLES:: - sage: from sage_setup.docbuild import setup_logger, logger + sage: from sage_docbuild import setup_logger, logger sage: setup_logger() sage: type(logger) <class 'logging.Logger'> diff --git a/src/sage_setup/docbuild/__main__.py b/src/sage_docbuild/__main__.py similarity index 100% rename from src/sage_setup/docbuild/__main__.py rename to src/sage_docbuild/__main__.py diff --git a/src/sage_setup/docbuild/build_options.py b/src/sage_docbuild/build_options.py similarity index 100% rename from src/sage_setup/docbuild/build_options.py rename to src/sage_docbuild/build_options.py diff --git a/src/sage_setup/docbuild/ext/__init__.py b/src/sage_docbuild/ext/__init__.py similarity index 100% rename from src/sage_setup/docbuild/ext/__init__.py rename to src/sage_docbuild/ext/__init__.py diff --git a/src/sage_setup/docbuild/ext/inventory_builder.py b/src/sage_docbuild/ext/inventory_builder.py similarity index 98% rename from src/sage_setup/docbuild/ext/inventory_builder.py rename to src/sage_docbuild/ext/inventory_builder.py index c3b58cca354..a4941c21981 100644 --- a/src/sage_setup/docbuild/ext/inventory_builder.py +++ b/src/sage_docbuild/ext/inventory_builder.py @@ -99,3 +99,4 @@ def cleanup(self): def setup(app): app.add_builder(InventoryBuilder) + return {'parallel_read_safe': True} diff --git a/src/sage_setup/docbuild/ext/multidocs.py b/src/sage_docbuild/ext/multidocs.py similarity index 99% rename from src/sage_setup/docbuild/ext/multidocs.py rename to src/sage_docbuild/ext/multidocs.py index 1e2b2905945..9a8c4fef19e 100644 --- a/src/sage_setup/docbuild/ext/multidocs.py +++ b/src/sage_docbuild/ext/multidocs.py @@ -321,3 +321,4 @@ def setup(app: Sphinx): app.add_config_value('multidocs_subdoc_list', [], True) app.add_config_value('multidoc_first_pass', 0, False) # 1 = deactivate the loading of the inventory app.connect('builder-inited', init_subdoc) + return {'parallel_read_safe': True} diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py similarity index 100% rename from src/sage_setup/docbuild/ext/sage_autodoc.py rename to src/sage_docbuild/ext/sage_autodoc.py diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py similarity index 98% rename from src/sage_setup/docbuild/sphinxbuild.py rename to src/sage_docbuild/sphinxbuild.py index b08ea9b7421..f58f6c61d76 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -172,7 +172,7 @@ def _check_errors(self, line): EXAMPLES:: sage: from sys import stdout - sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: from sage_docbuild.sphinxbuild import SageSphinxLogger sage: logger = SageSphinxLogger(stdout, "doctesting") sage: logger._log_line("Segmentation fault!\n") # indirect doctest [doctestin] Segmentation fault! @@ -200,7 +200,7 @@ def _log_line(self, line): EXAMPLES:: sage: from sys import stdout - sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: from sage_docbuild.sphinxbuild import SageSphinxLogger sage: logger = SageSphinxLogger(stdout, "doctesting") sage: logger._log_line("building documentation…\n") [doctestin] building documentation… @@ -243,7 +243,7 @@ def raise_errors(self): EXAMPLES:: sage: from sys import stdout - sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: from sage_docbuild.sphinxbuild import SageSphinxLogger sage: logger = SageSphinxLogger(stdout, "doctesting") sage: logger._log_line("This is a SEVERE error\n") [doctestin] This is a SEVERE error diff --git a/src/sage_setup/docbuild/utils.py b/src/sage_docbuild/utils.py similarity index 99% rename from src/sage_setup/docbuild/utils.py rename to src/sage_docbuild/utils.py index 056392ac39e..526d92b9574 100644 --- a/src/sage_setup/docbuild/utils.py +++ b/src/sage_docbuild/utils.py @@ -42,7 +42,7 @@ def build_many(target, args, processes=None): EXAMPLES:: - sage: from sage_setup.docbuild.utils import build_many + sage: from sage_docbuild.utils import build_many sage: def target(N): ....: import time ....: time.sleep(float(0.1)) diff --git a/src/sage_setup/autogen/__init__.py b/src/sage_setup/autogen/__init__.py index 0fc5d2a5f1f..6a0a6fdc5da 100644 --- a/src/sage_setup/autogen/__init__.py +++ b/src/sage_setup/autogen/__init__.py @@ -1,5 +1,6 @@ import os - +from . import interpreters +from sage.env import SAGE_SRC def autogen_all(): """ @@ -8,8 +9,6 @@ def autogen_all(): Return a list of sub-packages that should be appended to the list of packages built/installed by setup.py. """ - - from . import interpreters - interpreters.rebuild(os.path.join("sage", "ext", "interpreters")) + interpreters.rebuild(os.path.join(SAGE_SRC, "sage", "ext", "interpreters")) return ['sage.ext.interpreters'] diff --git a/src/sage_setup/clean.py b/src/sage_setup/clean.py index a742d085e4b..08b583f88af 100644 --- a/src/sage_setup/clean.py +++ b/src/sage_setup/clean.py @@ -78,7 +78,8 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module after installation, there are no stale files:: sage: from sage.env import SAGE_SRC, SAGE_LIB, SAGE_ROOT - sage: cythonized_dir = os.path.join(SAGE_ROOT, "build", "pkgs", "sagelib", "src", "build", "cythonized") + sage: from sage_setup.find import _cythonized_dir + sage: cythonized_dir = _cythonized_dir(SAGE_SRC) 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']) diff --git a/src/sage_setup/command/sage_build.py b/src/sage_setup/command/sage_build.py index 963d4b207d2..229af4292fb 100644 --- a/src/sage_setup/command/sage_build.py +++ b/src/sage_setup/command/sage_build.py @@ -1,3 +1,7 @@ +# Import setuptools before importing distutils, so that setuptools +# can replace distutils by its own vendored copy. +import setuptools + from distutils import log from distutils.command.build import build diff --git a/src/sage_setup/command/sage_build_cython.py b/src/sage_setup/command/sage_build_cython.py index f62b44e6da4..678662345b6 100644 --- a/src/sage_setup/command/sage_build_cython.py +++ b/src/sage_setup/command/sage_build_cython.py @@ -8,10 +8,13 @@ import sys import time import json + +# Import setuptools before importing distutils, so that setuptools +# can replace distutils by its own vendored copy. +import setuptools + from distutils import log -from distutils.cmd import Command -from distutils.errors import (DistutilsModuleError, - DistutilsOptionError) +from setuptools import Command from sage_setup.util import stable_uniq from sage_setup.find import find_extra_files @@ -130,12 +133,12 @@ def finalize_options(self): try: self.parallel = int(self.parallel) except ValueError: - raise DistutilsOptionError("parallel should be an integer") + raise ValueError("parallel should be an integer") try: import Cython except ImportError: - raise DistutilsModuleError( + raise ImportError( "Cython must be installed and importable in order to run " "the cythonize command") diff --git a/src/sage_setup/command/sage_build_ext.py b/src/sage_setup/command/sage_build_ext.py index acb1d5fad4b..1a012b804d7 100644 --- a/src/sage_setup/command/sage_build_ext.py +++ b/src/sage_setup/command/sage_build_ext.py @@ -1,7 +1,12 @@ import os import errno + +# Import setuptools before importing distutils, so that setuptools +# can replace distutils by its own vendored copy. +import setuptools + from distutils import log -from distutils.command.build_ext import build_ext +from setuptools.command.build_ext import build_ext from distutils.dep_util import newer_group from distutils.errors import DistutilsSetupError from sage_setup.run_parallel import execute_list_of_commands diff --git a/src/sage_setup/command/sage_build_ext_minimal.py b/src/sage_setup/command/sage_build_ext_minimal.py new file mode 100644 index 00000000000..34ac31f8a87 --- /dev/null +++ b/src/sage_setup/command/sage_build_ext_minimal.py @@ -0,0 +1,35 @@ +import os +import multiprocessing +from setuptools.command.build_ext import build_ext + + +class sage_build_ext_minimal(build_ext): + """ + In contrast to :func:`~sage_setup.sage_build_ext.sage_build_ext`, this build extension is designed + to be used in combination with Cython's cythonize function. + Thus, we only take care of some options and letting Cython do the main work. + """ + + def initialize_options(self): + build_ext.initialize_options(self) + self.parallel = self.get_default_number_build_jobs() + + @staticmethod + def get_default_number_build_jobs() -> int: + """ + Get number of parallel build jobs used by default, i.e. unless explicitly + set by the --parallel command line argument of setup.py. + + First, the environment variable `SAGE_NUM_THREADS` is checked. + If that is unset, return the number of processors on the system, + with a maximum of 10 (to prevent overloading the system if there a lot of CPUs). + + OUTPUT: + number of parallel jobs that should be run + """ + try: + cpu_count = len(os.sched_getaffinity(0)) + except AttributeError: + cpu_count = multiprocessing.cpu_count() + cpu_count = min(cpu_count, 10) + return int(os.environ.get("SAGE_NUM_THREADS", cpu_count)) diff --git a/src/sage_setup/command/sage_install.py b/src/sage_setup/command/sage_install.py index f8d375a0287..41a9391c130 100644 --- a/src/sage_setup/command/sage_install.py +++ b/src/sage_setup/command/sage_install.py @@ -4,6 +4,11 @@ import os import time + +# Import setuptools before importing distutils, so that setuptools +# can replace distutils by its own vendored copy. +import setuptools + from distutils import log from distutils.command.install import install diff --git a/src/sage_setup/extensions.py b/src/sage_setup/extensions.py new file mode 100644 index 00000000000..4e1a17c6ced --- /dev/null +++ b/src/sage_setup/extensions.py @@ -0,0 +1,10 @@ + +def create_extension(template, kwds): + from Cython.Build.Dependencies import default_create_extension + from sage.env import sage_include_directories + + # Add numpy and source folder to the include search path used by the compiler + # This is a workaround for https://github.com/cython/cython/issues/1480 + include_dirs = kwds.get('include_dirs', []) + sage_include_directories(use_sources=True) + kwds['include_dirs'] = include_dirs + return default_create_extension(template, kwds) diff --git a/src/sage_setup/find.py b/src/sage_setup/find.py index 3630ac7b79a..3b96fde74c1 100644 --- a/src/sage_setup/find.py +++ b/src/sage_setup/find.py @@ -1,7 +1,7 @@ """ Recursive Directory Contents """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com> # # This program is free software: you can redistribute it and/or modify @@ -9,8 +9,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** - +# **************************************************************************** import importlib.machinery import importlib.util @@ -19,6 +18,7 @@ from collections import defaultdict + def read_distribution(src_file): """ Parse ``src_file`` for a ``# sage_setup: distribution = PKG`` directive. @@ -58,6 +58,7 @@ def read_distribution(src_file): return value return '' + def find_python_sources(src_dir, modules=['sage'], distributions=None): """ Find all Python packages and Python/Cython modules in the sources. @@ -115,7 +116,7 @@ def find_python_sources(src_dir, modules=['sage'], distributions=None): Filtering by distribution (distutils package):: sage: find_python_sources(SAGE_SRC, distributions=['sage-tdlib']) - ([], [], [<distutils.extension.Extension('sage.graphs.graph_decompositions.tdlib')...>]) + ([], [], [<setuptools.extension.Extension('sage.graphs.graph_decompositions.tdlib')...>]) Benchmarking:: @@ -130,7 +131,7 @@ def find_python_sources(src_dir, modules=['sage'], distributions=None): sage: find_python_sources(SAGE_SRC, modules=['sage_setup']) (['sage_setup', ...], [...'sage_setup.find'...], []) """ - from distutils.extension import Extension + from setuptools import Extension PYMOD_EXT = get_extensions('source')[0] INIT_FILE = '__init__' + PYMOD_EXT @@ -172,6 +173,93 @@ def is_in_distributions(filename): os.chdir(cwd) return python_packages, python_modules, cython_modules +def filter_cython_sources(src_dir, distributions): + """ + Find all Cython modules in the given source directory that belong to the + given distributions. + + INPUT: + + - ``src_dir`` -- root directory for the sources + + - ``distributions`` -- a sequence or set of strings: only find modules whose + ``distribution`` (from a ``# sage_setup: distribution = PACKAGE`` + directive in the module source file) is an element of + ``distributions``. + + OUTPUT: List of absolute paths to Cython files (``*.pyx``). + + EXAMPLES:: + + sage: from sage.env import SAGE_SRC + sage: from sage_setup.find import filter_cython_sources + sage: cython_modules = filter_cython_sources(SAGE_SRC, ["sage-tdlib"]) + + Cython module relying on tdlib:: + + sage: any(f.endswith('sage/graphs/graph_decompositions/tdlib.pyx') for f in cython_modules) + True + + Cython module not relying on tdlib:: + + sage: any(f.endswith('sage/structure/sage_object.pyx') for f in cython_modules) + False + + Benchmarking:: + + sage: timeit('filter_cython_sources(SAGE_SRC, ["sage-tdlib"])', # random output + ....: number=1, repeat=1) + 1 loops, best of 1: 850 ms per loop + """ + files: list[str] = [] + + 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: + files.append(filepath) + + return files + +def _cythonized_dir(src_dir=None, editable_install=None): + """ + Return the path where Cython-generated files are placed by the build system. + + INPUT: + + - ``src_dir`` -- string or path (default: the value of ``SAGE_SRC``). The + root directory for the sources. + + - ``editable_install`` -- boolean (default: determined from the existing + installation). Whether this is an editable install of the Sage library. + + EXAMPLES:: + + sage: from sage_setup.find import _cythonized_dir + sage: from sage.env import SAGE_SRC + sage: _cythonized_dir(SAGE_SRC) # optional - build + PosixPath('...') + sage: _cythonized_dir(SAGE_SRC, editable_install=False) + PosixPath('.../build/cythonized') + + """ + from importlib import import_module + from pathlib import Path + from sage.env import SAGE_ROOT, SAGE_SRC + if editable_install is None: + if src_dir is None: + src_dir = SAGE_SRC + src_dir = Path(src_dir) + sage = import_module('sage') + d = Path(sage.__file__).resolve().parent.parent + editable_install = d == src_dir.resolve() + if editable_install: + # Editable install: Cython generates files in the source tree + return src_dir + else: + return Path(SAGE_ROOT) / "build" / "pkgs" / "sagelib" / "src" / "build" / "cythonized" + def find_extra_files(src_dir, modules, cythonized_dir, special_filenames=[]): """ Find all extra files which should be installed. @@ -206,9 +294,9 @@ def find_extra_files(src_dir, modules, cythonized_dir, special_filenames=[]): EXAMPLES:: - sage: from sage_setup.find import find_extra_files + sage: from sage_setup.find import find_extra_files, _cythonized_dir sage: from sage.env import SAGE_SRC, SAGE_ROOT - sage: cythonized_dir = os.path.join(SAGE_ROOT, "build", "pkgs", "sagelib", "src", "build", "cythonized") + sage: cythonized_dir = _cythonized_dir(SAGE_SRC) sage: extras = find_extra_files(SAGE_SRC, ["sage"], cythonized_dir) sage: extras["sage/libs/mpfr"] [...sage/libs/mpfr/types.pxd...] @@ -267,8 +355,7 @@ def installed_files_by_module(site_packages, modules=('sage',)): EXAMPLES:: - sage: from site import getsitepackages - sage: site_packages = getsitepackages()[0] + sage: site_packages = os.path.dirname(os.path.dirname(sage.__file__)) sage: from sage_setup.find import installed_files_by_module sage: files_by_module = installed_files_by_module(site_packages) sage: from sage.misc.sageinspect import loadable_module_extension diff --git a/src/sage_setup/optional_extension.py b/src/sage_setup/optional_extension.py index 2f7e8f733ac..06586f1d393 100644 --- a/src/sage_setup/optional_extension.py +++ b/src/sage_setup/optional_extension.py @@ -7,18 +7,17 @@ package which must be installed. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Jeroen Demeyer <jdemeyer@cage.ugent.be> # # 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 distutils.extension import Extension +from setuptools.extension import Extension from sage.misc.package import list_packages all_packages = list_packages(local=True) @@ -40,6 +39,7 @@ class CythonizeExtension(Extension): """ skip_build = True + def is_package_installed_and_updated(pkg): from sage.misc.package import is_package_installed try: @@ -51,6 +51,7 @@ def is_package_installed_and_updated(pkg): condition = (pkginfo["installed_version"] == pkginfo["remote_version"]) return condition + def OptionalExtension(*args, **kwds): """ If some condition (see INPUT) is satisfied, return an ``Extension``. diff --git a/src/sage_setup/util.py b/src/sage_setup/util.py index 9d4a448dda1..ad2f95df02d 100644 --- a/src/sage_setup/util.py +++ b/src/sage_setup/util.py @@ -2,20 +2,21 @@ Utility functions for building Sage """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2017 Jeroen Demeyer <J.Demeyer@UGent.be> # # 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/ +# **************************************************************************** + def stable_uniq(L): """ Given an iterable L, remove duplicate items from L by keeping only - the last occurance of any item. + the last occurrence of any item. The items must be hashable. diff --git a/src/setup.py b/src/setup.py index 458a9d7b386..df8242c71a0 100755 --- a/src/setup.py +++ b/src/setup.py @@ -3,91 +3,116 @@ from __future__ import print_function import os +import platform import sys import time +from setuptools import setup, find_namespace_packages from distutils import log -from setuptools import setup +import multiprocessing.pool + +# PEP 517 builds do not have . in sys.path +sys.path.insert(0, os.path.dirname(__file__)) + +import sage.misc.lazy_import_cache + +from sage_setup.optional_extension import is_package_installed_and_updated +from sage_setup.command.sage_build_ext_minimal import sage_build_ext_minimal +from sage_setup.command.sage_install import sage_install +from sage_setup.find import filter_cython_sources +from sage_setup.cython_options import compiler_directives, compile_time_env_variables +from sage_setup.extensions import create_extension +from sage_setup.excepthook import excepthook # Work around a Cython problem in Python 3.8.x on macOS # https://github.com/cython/cython/issues/3262 -if os.uname().sysname == 'Darwin': +if platform.system() == 'Darwin': import multiprocessing multiprocessing.set_start_method('fork', force=True) -######################################################### -### Set source directory -######################################################### +# ######################################################## +# ## Set source directory +# ######################################################## import sage.env sage.env.SAGE_SRC = os.getcwd() from sage.env import * -from sage_setup.excepthook import excepthook sys.excepthook = excepthook -######################################################### -### Configuration -######################################################### +# ######################################################## +# ## Configuration +# ######################################################## if len(sys.argv) > 1 and sys.argv[1] == "sdist": sdist = True else: sdist = False -######################################################### -### Testing related stuff -######################################################### +# ######################################################## +# ## Testing related stuff +# ######################################################## # Remove (potentially invalid) star import caches -import sage.misc.lazy_import_cache if os.path.exists(sage.misc.lazy_import_cache.get_cache_file()): os.unlink(sage.misc.lazy_import_cache.get_cache_file()) -from sage_setup.command.sage_build import sage_build -from sage_setup.command.sage_build_cython import sage_build_cython -from sage_setup.command.sage_build_ext import sage_build_ext - +# ######################################################## +# ## Discovering Sources +# ######################################################## -######################################################### -### Discovering Sources -######################################################### - -# TODO: This should be quiet by default -print("Discovering Python/Cython source code....") +log.info("Discovering Python/Cython source code....") t = time.time() -distributions = [''] - -from sage_setup.optional_extension import is_package_installed_and_updated - -optional_packages_with_extensions = ['mcqd', 'bliss', 'tdlib', 'primecount', - 'coxeter3', 'fes', 'sirocco', 'meataxe'] - -distributions += ['sage-{}'.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', 'sage_setup'], distributions=distributions) - -log.debug('python_packages = {0}'.format(python_packages)) - -print("Discovered Python/Cython sources, time: %.2f seconds." % (time.time() - t)) - - -from sage_setup.command.sage_install import sage_install_and_clean - -######################################################### -### Distutils -######################################################### - +# Exclude a few files if the corresponding distribution is not loaded +optional_packages = ['mcqd', 'bliss', 'tdlib', 'primecount', + 'coxeter3', 'fes', 'sirocco', 'meataxe'] +not_installed_packages = [package for package in optional_packages + if not is_package_installed_and_updated(package)] + +distributions_to_exclude = [f"sage-{pkg}" + for pkg in not_installed_packages] +files_to_exclude = filter_cython_sources(SAGE_SRC, distributions_to_exclude) + +log.debug(f"files_to_exclude = {files_to_exclude}") + +python_packages = find_namespace_packages(where=SAGE_SRC, include=['sage', 'sage_setup']) +log.debug(f"python_packages = {python_packages}") + +log.info(f"Discovered Python/Cython sources, time: {(time.time() - t):.2f} seconds.") + +# from sage_build_cython: +import Cython.Compiler.Options +Cython.Compiler.Options.embed_pos_in_docstring = True +gdb_debug = os.environ.get('SAGE_DEBUG', None) != 'no' + +try: + log.info("Generating auto-generated sources") + from sage_setup.autogen import autogen_all + autogen_all() + + from Cython.Build import cythonize + from sage.env import cython_aliases, sage_include_directories + extensions = cythonize( + ["**/*.pyx"], + exclude=files_to_exclude, + include_path=sage_include_directories(use_sources=True) + ['.'], + compile_time_env=compile_time_env_variables(), + compiler_directives=compiler_directives(False), + aliases=cython_aliases(), + create_extension=create_extension, + gdb_debug=gdb_debug, + nthreads=4) +except Exception as exception: + log.warn(f"Exception while generating and cythonizing source files: {exception}") + extensions = None + +# ######################################################## +# ## Distutils +# ######################################################## code = setup( - packages = python_packages, - package_data = { + packages=python_packages, + package_data={ 'sage.libs.gap': ['sage.gaprc'], 'sage.interfaces': ['sage-maxima.lisp'], 'sage.doctest': ['tests/*'], @@ -116,58 +141,59 @@ 'ext_data/valgrind/*', 'ext_data/threejs/*'] }, - scripts = [## The sage script - 'bin/sage', - ## Other scripts that should be in the path also for OS packaging of sage: - 'bin/sage-eval', - 'bin/sage-runtests', # because it is useful for doctesting user scripts too - 'bin/sage-fixdoctests', # likewise - 'bin/sage-coverage', # because it is useful for coverage-testing user scripts too - 'bin/sage-coverageall', # likewise - 'bin/sage-cython', # deprecated, might be used in user package install scripts - ## Helper scripts invoked by sage script - ## (they would actually belong to something like libexec) - 'bin/sage-cachegrind', - 'bin/sage-callgrind', - 'bin/sage-massif', - 'bin/sage-omega', - 'bin/sage-valgrind', - 'bin/sage-venv-config', - 'bin/sage-version.sh', - 'bin/sage-cleaner', - ## Only makes sense in sage-the-distribution. TODO: Move to another installation script. - 'bin/sage-list-packages', - 'bin/sage-location', - ## Uncategorized scripts in alphabetical order - 'bin/math-readline', - 'bin/sage-env', - # sage-env-config -- installed by sage_conf - # sage-env-config.in -- not to be installed - 'bin/sage-gdb-commands', - 'bin/sage-grep', - 'bin/sage-grepdoc', - 'bin/sage-inline-fortran', - 'bin/sage-ipynb2rst', - 'bin/sage-ipython', - 'bin/sage-native-execute', - 'bin/sage-notebook', - 'bin/sage-num-threads.py', - 'bin/sage-open', - 'bin/sage-preparse', - 'bin/sage-python', - 'bin/sage-rebase.bat', - 'bin/sage-rebase.sh', - 'bin/sage-rebaseall.bat', - 'bin/sage-rebaseall.sh', - 'bin/sage-rst2txt', - 'bin/sage-run', - 'bin/sage-run-cython', - 'bin/sage-startuptime.py', - 'bin/sage-update-src', - 'bin/sage-update-version', - ], - cmdclass = dict(build=sage_build, - build_cython=sage_build_cython, - build_ext=sage_build_ext, - install=sage_install_and_clean), - ext_modules = cython_modules) + scripts=[ + # The sage script + 'bin/sage', + # Other scripts that should be in the path also for OS packaging of sage: + 'bin/sage-eval', + 'bin/sage-runtests', # because it is useful for doctesting user scripts too + 'bin/sage-fixdoctests', # likewise + 'bin/sage-coverage', # because it is useful for coverage-testing user scripts too + 'bin/sage-cython', # deprecated, might be used in user package install scripts + # Helper scripts invoked by sage script + # (they would actually belong to something like libexec) + 'bin/sage-cachegrind', + 'bin/sage-callgrind', + 'bin/sage-massif', + 'bin/sage-omega', + 'bin/sage-valgrind', + 'bin/sage-venv-config', + 'bin/sage-version.sh', + 'bin/sage-cleaner', + # Only makes sense in sage-the-distribution. TODO: Move to another installation script. + 'bin/sage-list-packages', + 'bin/sage-location', + # Uncategorized scripts in alphabetical order + 'bin/math-readline', + 'bin/sage-env', + # sage-env-config -- installed by sage_conf + # sage-env-config.in -- not to be installed + 'bin/sage-gdb-commands', + 'bin/sage-grep', + 'bin/sage-grepdoc', + 'bin/sage-inline-fortran', + 'bin/sage-ipynb2rst', + 'bin/sage-ipython', + 'bin/sage-native-execute', + 'bin/sage-notebook', + 'bin/sage-num-threads.py', + 'bin/sage-open', + 'bin/sage-preparse', + 'bin/sage-python', + 'bin/sage-rebase.bat', + 'bin/sage-rebase.sh', + 'bin/sage-rebaseall.bat', + 'bin/sage-rebaseall.sh', + 'bin/sage-rst2txt', + 'bin/sage-run', + 'bin/sage-run-cython', + 'bin/sage-startuptime.py', + 'bin/sage-update-src', + 'bin/sage-update-version', + ], + cmdclass={ + "build_ext": sage_build_ext_minimal, + "install": sage_install, + }, + ext_modules=extensions +) diff --git a/src/tox.ini b/src/tox.ini index bc55fbf215f..dc773b0973d 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -41,9 +41,8 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} -whitelist_externals = sh commands = - sh -c '{env:SAGE} -t -p 0 {posargs:--all}' + {env:SAGE} -t -p 0 {posargs:--all} [testenv:coverage] description = @@ -53,9 +52,8 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} -whitelist_externals = sh commands = - sh -c 'if [ -z "{posargs}" ]; then {env:SAGE} --coverageall; else {env:SAGE} --coverage {posargs}; fi' + {env:SAGE} --coverage {posargs:--all} [testenv:startuptime] description = @@ -65,9 +63,8 @@ description = passenv = {[sagedirect]passenv} setenv = {[sagedirect]setenv} envdir = {[sagedirect]envdir} -whitelist_externals = sh commands = - sh -c '{env:SAGE} --startuptime {posargs}' + {env:SAGE} --startuptime {posargs} [testenv:pycodestyle] description = @@ -98,8 +95,8 @@ description = # https://github.com/codingjoe/relint # The patterns are in .relint.yml deps = relint -whitelist_externals = sh -commands = sh -c 'relint -c {toxinidir}/.relint.yml $(for a in {posargs:{toxinidir}/sage/}; do find $a -type f; done)' +whitelist_externals = find +commands = find {posargs:{toxinidir}/sage/} -exec relint -c {toxinidir}/.relint.yml \{\} + [testenv:codespell] description = @@ -115,3 +112,6 @@ commands = codespell \ --dictionary={toxinidir}/.codespell-dictionary.txt \ --ignore-words={toxinidir}/.codespell-ignore.txt \ {posargs:{toxinidir}/sage/} + +[pytest] +python_files = *_test.py diff --git a/tox.ini b/tox.ini index 1df66db6370..229ef6dc559 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,4 @@ -# Run a specific environemnt: +# Run a specific environment: # tox -e docker-fedora-31 # Run all in parallel: # tox -p auto @@ -40,7 +40,8 @@ envlist = ### Only one "local" target can be run at a time. ### However, "local" targets install in a separate prefix (SAGE_LOCAL=.tox/TOXENV/local) ### rather than "local/" and also place log files into .tox/TOXENV/log, where TOXENV - ### is the name of the environment. + ### is the name of the environment. (The prefix can be configured by passing the + ### environment variable PREFIX.) # # The "local-homebrew" toxenvs create an "isolated" homebrew installation (not in /usr/local). # (However, many configure scripts still look for stuff in /usr/local.) @@ -110,6 +111,7 @@ passenv = # If set, we use this prefix and push to it docker: DOCKER_PUSH_REPOSITORY local: MAKE + local: PREFIX # Set to 1 to skip preliminary install phases before make is invoked local: SKIP_SYSTEM_PKG_INSTALL local: SKIP_BOOTSTRAP @@ -130,9 +132,13 @@ setenv = # and ignoring errors. We use that to take care of old versions of distributions. IGNORE_MISSING_SYSTEM_PACKAGES=no # What system packages should be installed. Default: All standard packages with spkg-configure. - SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard: - minimal: SAGE_PACKAGE_LIST_ARGS=_prereq - maximal: SAGE_PACKAGE_LIST_ARGS=:standard: :optional: + SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard: + minimal: SAGE_PACKAGE_LIST_ARGS=_prereq + maximal: SAGE_PACKAGE_LIST_ARGS=:standard: :optional: + conda-environment: SAGE_PACKAGE_LIST_ARGS=_prereq + # Whether to add the system packages needed for bootstrapping + EXTRA_SAGE_PACKAGES=_bootstrap + nobootstrap: EXTRA_SAGE_PACKAGES= # local envs need HOME set, also Docker 19.03 needs HOME {local,docker}: HOME={envdir} # for local envs we can guess the package system if it is not provided as a factor @@ -240,10 +246,10 @@ setenv = # https://hub.docker.com/r/continuumio # conda: SYSTEM=conda + conda: CONDARC=/dev/null conda-forge: BASE_IMAGE=continuumio/miniconda3 conda-forge: CONDARC=condarc.yml conda-anaconda3: BASE_IMAGE=continuumio/anaconda3 - conda-anaconda3: CONDARC=/dev/null conda-anaconda3: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/nixos/nix/ @@ -350,6 +356,7 @@ setenv = homebrew: SYSTEM=homebrew local: SHARED_CACHE_DIR={toxworkdir}/Caches local: SETENV=: + local: SETENV_CONFIGURE=: local-nobootstrap: BOOTSTRAP=: local-!direct: PATH=/usr/bin:/bin:/usr/sbin:/sbin local-sudo: __SUDO=--sudo @@ -367,7 +374,13 @@ setenv = local-conda: CONDA_OS=$(uname | sed 's/^Darwin/MacOSX/;') local-conda-forge: CONDA_INSTALLER_URL_BASE=https://github.com/conda-forge/miniforge/releases/latest/download/ local-conda-forge: CONDA_INSTALLER_FILE=Miniforge3-{env:CONDA_OS}-x86_64.sh - local-conda: SETENV=. {env:CONDA_PREFIX}/bin/activate + local-conda-miniconda: CONDA_INSTALLER_URL_BASE=https://repo.anaconda.com/miniconda/ + local-conda-miniconda: CONDA_INSTALLER_FILE=Miniconda3-latest-{env:CONDA_OS}-x86_64.sh + local-conda: SETENV=. {env:CONDA_PREFIX}/bin/activate base + local-conda-environment: CONDA_SAGE_ENVIRONMENT=sage-build + local-conda-environment: CONDA_SAGE_ENVIRONMENT_FILE=environment.yml + local-conda-environment-optional: CONDA_SAGE_ENVIRONMENT_FILE=environment-optional.yml + local-conda-environment: SETENV_CONFIGURE=( {env:CONDA_PREFIX}/bin/conda env create -n {env:CONDA_SAGE_ENVIRONMENT} --file {env:CONDA_SAGE_ENVIRONMENT_FILE} || {env:CONDA_PREFIX}/bin/conda env update -n {env:CONDA_SAGE_ENVIRONMENT} --file {env:CONDA_SAGE_ENVIRONMENT_FILE} ) && . {env:CONDA_PREFIX}/bin/activate {env:CONDA_SAGE_ENVIRONMENT} # # Configuration factors # @@ -378,6 +391,10 @@ setenv = macos-python3_xcode: CONFIG_CONFIGURE_ARGS_1=--with-python=/usr/bin/python3 # Must manually download and install from https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-python=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3 + # Homebrew keg installs + homebrew-python3.7: CONFIG_CONFIGURE_ARGS_1=--with-python={env:HOMEBREW}/opt/python@3.7/bin/python3 + homebrew-python3.8: CONFIG_CONFIGURE_ARGS_1=--with-python={env:HOMEBREW}/opt/python@3.8/bin/python3 + homebrew-python3.9: CONFIG_CONFIGURE_ARGS_1=--with-python={env:HOMEBREW}/opt/python@3.9/bin/python3 # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp38-cp38/bin/python3 manylinux-python3.6: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp36-cp36m/bin/python3 @@ -417,7 +434,7 @@ commands = # # https://docs.brew.sh/Installation homebrew: bash -c 'if [ ! -x {env:HOMEBREW}/bin/brew ]; then mkdir -p {env:HOMEBREW} && cd {env:HOMEBREW} && curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 ; fi' - homebrew: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) PACKAGES=$(build/bin/sage-get-system-packages homebrew $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) _bootstrap); {env:HOMEBREW}/bin/brew install $PACKAGES; {env:HOMEBREW}/bin/brew upgrade $PACKAGES;; esac' + homebrew: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) PACKAGES=$(build/bin/sage-get-system-packages homebrew $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) {env:EXTRA_SAGE_PACKAGES}); {env:HOMEBREW}/bin/brew install $PACKAGES; {env:HOMEBREW}/bin/brew upgrade $PACKAGES;; esac' # # local-conda # @@ -430,20 +447,24 @@ commands = local-conda: bash -c 'cat {env:CONDARC} >> {env:CONDA_PREFIX}/.condarc' local-conda: bash -c 'if [ ! -x {env:CONDA_PREFIX}/bin/conda ]; then curl -L {env:CONDA_INSTALLER_URL_BASE}{env:CONDA_INSTALLER_FILE} -C - -o {env:SHARED_CACHE_DIR}/{env:CONDA_INSTALLER_FILE} && bash {env:SHARED_CACHE_DIR}/{env:CONDA_INSTALLER_FILE} -b -f -p {env:CONDA_PREFIX}; fi' local-conda: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) {env:SETENV} && {env:CONDA_PREFIX}/bin/conda update -n base --yes conda;; esac' - local-conda: bash -c 'PACKAGES=$(build/bin/sage-get-system-packages conda $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) _bootstrap); {env:SETENV} && {env:CONDA_PREFIX}/bin/conda install --yes --quiet $PACKAGES' + local-conda: bash -c 'PACKAGES=$(build/bin/sage-get-system-packages conda $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) {env:EXTRA_SAGE_PACKAGES}); {env:SETENV} && {env:CONDA_PREFIX}/bin/conda install --yes --quiet $PACKAGES' + # + # local-cygwin-choco: Use choco to install cygwin packages + # + local-cygwin-choco: bash -c 'PACKAGES=$(build/bin/sage-get-system-packages {env:SYSTEM} $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) _bootstrap); /cygdrive/c/ProgramData/Chocolatey/bin/choco install $PACKAGES --source cygwin' # # local-root: Assume we are root, run the system package commands # local-sudo: Use sudo to run the system package commands as root # local-{root,sudo}: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) eval $(build/bin/sage-print-system-package-command {env:SYSTEM} {env:__SUDO:} update) ;; esac' - local-{root,sudo}: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) PACKAGES=$(build/bin/sage-get-system-packages {env:SYSTEM} $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) _bootstrap); eval $(build/bin/sage-print-system-package-command {env:SYSTEM} {env:__SUDO:} --yes --no-install-recommends install $PACKAGES) || [ "$IGNORE_MISSING_SYSTEM_PACKAGES" = yes ] && echo "(ignoring errors)" ;; esac' + local-{root,sudo}: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) PACKAGES=$(build/bin/sage-get-system-packages {env:SYSTEM} $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) {env:EXTRA_SAGE_PACKAGES}); eval $(build/bin/sage-print-system-package-command {env:SYSTEM} {env:__SUDO:} --yes --no-install-recommends install $PACKAGES) || [ "$IGNORE_MISSING_SYSTEM_PACKAGES" = yes ] && echo "(ignoring errors)" ;; esac' # # All "local" environments # # Install symbolic links "config.log" and "logs" in SAGE_ROOT so that log files are written into the tox log directory. local: bash -c 'touch {envlogdir}/config.log; ln -sf {envlogdir}/config.log .; if [ ! -d logs -o -L logs ]; then rm -f logs; ln -sf {envlogdir} logs; fi' # Install a symbolic link "prefix" in SAGE_ROOT for convenient inspection; it is not used in the build. - local: bash -c 'if [ ! -d prefix -o -L prefix ]; then rm -f prefix; ln -sf {envdir}/local prefix; fi' + local: bash -c 'if [ ! -d prefix -o -L prefix ]; then rm -f prefix; ln -sf {env:PREFIX:{envdir}/local} prefix; fi' ##commands = docker: bash -c 'build/bin/write-dockerfile.sh {env:SYSTEM} "{env:SAGE_PACKAGE_LIST_ARGS:}" {env:WITH_SYSTEM_SPKG} {env:IGNORE_MISSING_SYSTEM_PACKAGES} > {envdir}/Dockerfile' @@ -472,14 +493,15 @@ commands = # linbox/cysignals testsuites fail. ppl takes very long. local: bash -c 'export PATH={env:PATH} && {env:SETENV} && \ local: case "{env:SKIP_BOOTSTRAP:}" in 1|y*|Y*);; *) {env:BOOTSTRAP} ;; esac && \ - local: case "{env:SKIP_CONFIGURE:}" in 1|y*|Y*);; *) ./configure --prefix={envdir}/local {env:CONFIGURE_ARGS} ;; esac && \ + local: {env:SETENV_CONFIGURE} && \ + local: case "{env:SKIP_CONFIGURE:}" in 1|y*|Y*);; *) ./configure --prefix={env:PREFIX:{envdir}/local} {env:CONFIGURE_ARGS} ;; esac && \ local: case "{posargs:}" in \ local: bash) bash -i; exit ;; \ local: config*) ;; \ local: *) make -k V=0 base-toolchain ;; \ local: esac && \ local: make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!nose,!gap,!cysignals,!linbox,!git,!ppl,!cmake,!networkx,!rpy2,!symengine_py,!sage_sws2rst" {env:TARGETS_PRE:} {posargs:build} && \ - local: ([ -z "{env:TARGETS_OPTIONAL:}" ] || make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!nose,!gap,!cysignals,!linbox,!git,!ppl,!cmake,!networkx,!rpy2,!symengine_py,!sage_sws2rst" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' + local: ( [ -z "{env:TARGETS_OPTIONAL:}" ] || make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!nose,!gap,!cysignals,!linbox,!git,!ppl,!cmake,!networkx,!rpy2,!symengine_py,!sage_sws2rst" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' [testenv:check_configure] ## Test that configure behaves properly