diff --git a/.github/workflows/ci-cygwin.yml b/.github/workflows/ci-cygwin.yml index 10c965eae01..35d18553fbd 100644 --- a/.github/workflows/ci-cygwin.yml +++ b/.github/workflows/ci-cygwin.yml @@ -3,21 +3,339 @@ name: windows-latest on: pull_request: types: [opened, synchronize] + push: + tags: + - '*' + +env: + MAKE: make -j8 + SAGE_NUM_THREADS: 3 + SAGE_CHECK: warn + SAGE_CHECK_PACKAGES: "!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl" + CYGWIN: winsymlinks:native + CONFIGURE_ARGS: --enable-experimental-packages --enable-download-from-upstream-url + SAGE_FAT_BINARY: yes jobs: - cygwin: + cygwin-stage-i-a: + env: + STAGE: i-a + # builds openblas + TARGETS: iml gsl + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + # upload-artifact@v2-preview does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-i-b: + env: + STAGE: i-b + TARGETS: cython setuptools_scm + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} runs-on: windows-latest strategy: fail-fast: false matrix: - pkgs: [minimal, standard] + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + # upload-artifact@v2-preview does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-a: + env: + STAGE: ii-a + PREVIOUS_STAGES: i-* + TARGETS: cvxopt rpy2 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-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; src/bin/sage-rebase.sh 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 + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + # upload-artifact@v2-preview does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-b: + env: + STAGE: ii-b + PREVIOUS_STAGES: i-* + TARGETS: ppl singular maxima gap pari gfan palp flintqs ratpoints arb ecm glpk givaro + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; src/bin/sage-rebase.sh 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 + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iii: env: + STAGE: iii + PREVIOUS_STAGES: ii-* + TARGETS: build + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} - MAKE: make -j8 + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] steps: - run: | git config --global core.autocrlf false @@ -33,23 +351,32 @@ jobs: 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: matrix.pkgs == 'standard' + if: contains(matrix.pkgs, 'standard') shell: bash {0} run: | PACKAGES=$(sed 's/#.*//;' ./build/pkgs/*/distros/cygwin.txt) choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; ; src/bin/sage-rebase.sh local' - name: configure run: | - C:\\tools\\cygwin\\bin\\bash -l -c 'export PATH=/usr/local/bin:/usr/bin && cd $(cygpath -u "$GITHUB_WORKSPACE") && ./configure' + 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 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl" build' + 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' - name: Prepare logs artifact shell: bash run: | - mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + 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" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2-preview with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} @@ -61,3 +388,335 @@ jobs: run: | find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iv-a: + env: + STAGE: iv-a + PREVIOUS_STAGES: iii + TARGETS: ptest + 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] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; ; src/bin/sage-rebase.sh local' + src\\bin\\sage-rebaseall.bat + - 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 $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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iv-b: + env: + STAGE: iv-b + PREVIOUS_STAGES: iii + 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] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; src/bin/sage-rebase.sh 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 + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iv-c: + env: + STAGE: iv-c + PREVIOUS_STAGES: iii + 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] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; src/bin/sage-rebase.sh 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 + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iv-d: + env: + STAGE: iv-d + PREVIOUS_STAGES: iii + 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] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal, standard, standard-python2] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and minimal prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES=$(sed 's/#.*//;' ./build/pkgs/cygwin.txt ./build/pkgs/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) + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2-preview + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + - name: Extract sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We set the installation records to the same mtime so that no rebuilds due to dependencies + # among these packages are triggered. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && ls -l sage-local-*.tar; for a in sage-local-${{ env.PREVIOUS_STAGES }}.tar; do echo Extracting $a; tar xf $a; done; (cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *); ls -l local local/var/lib/sage/installed/; src/bin/sage-rebase.sh 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 + 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' + - 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" + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f local/lib64; tar -cf sage-local-${{ env.STAGE }}.tar local' + if: always() + - uses: actions/upload-artifact@v2-preview + with: + path: sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() diff --git a/.github/workflows/tox-optional.yml b/.github/workflows/tox-optional.yml new file mode 100644 index 00000000000..1b23a1922a8 --- /dev/null +++ b/.github/workflows/tox-optional.yml @@ -0,0 +1,118 @@ +name: Run SAGE_ROOT/tox.ini TARGETS_OPTIONAL + +## This GitHub Actions workflow runs SAGE_ROOT/tox.ini with select environments, +## whenever a GitHub pull request is opened or synchronized in a repository +## where GitHub Actions are enabled. +## +## It builds and checks some sage spkgs as defined in TARGETS. +## +## A job succeeds if there is no error. +## +## The build is run with "make V=0", so the build logs of individual packages are suppressed. +## +## At the end, all package build logs that contain an error are printed out. +## +## After all jobs have finished (or are canceled) and a short delay, +## tar files of all logs are made available as "build artifacts". + +#on: [push, pull_request] + +on: + pull_request: + types: [opened, synchronize] + push: + tags: + - '*' + +env: + TARGETS_PRE: build/make/Makefile + TARGETS: build/make/Makefile + TARGETS_OPTIONAL: 4ti2 pynormaliz qepcad lrslib latte_int topcom barvinok isl qhull sage_numerical_backends_coin primecount plantri polymake jupymake kenzo libsemigroups mcqd meataxe mpfrcx openssl p_group_cohomology rst2ipynb sirocco tdlib tides + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-eoan, ubuntu-focal, debian-jessie, debian-stretch, debian-buster, debian-bullseye, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, centos-7, centos-8, archlinux-latest, slackware-14.2, ubuntu-bionic-i386, ubuntu-eoan-i386, debian-buster-i386, centos-7-i386] + tox_packages_factor: [maximal] + env: + TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} + DOCKER_TARGETS: with-targets-optional + steps: + - uses: actions/checkout@v2 + - name: free disk space + run: | + sudo swapoff -a + sudo rm -f /swapfile + sudo apt clean + docker rmi $(docker image ls -aq) + df -h + - name: Install test prerequisites + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + - run: | + set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=3\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;" + - name: Copy logs from the build container + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME" + cp -r .tox/$TOX_ENV/Dockerfile .tox/$TOX_ENV/log "artifacts/$LOGS_ARTIFACT_NAME" + if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags)); else CONTAINERS=$(docker ps -q -a); fi + for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done + if: always() + - uses: actions/upload-artifact@v1 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # and markup the output with GitHub Actions logging commands + run: | + .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + + local-macos: + + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + tox_system_factor: [homebrew-macos-python2, homebrew-macos, homebrew-macos-python3_xcode, homebrew-macos-python3_xcode-nokegonly, homebrew-macos-python3_pythonorg, conda-forge-macos] + tox_packages_factor: [maximal] + 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 }} + steps: + - uses: actions/checkout@v2 + - name: Install test prerequisites + run: | + brew install tox + - name: Install python3 from python.org + # As of 2020-03-30 (https://github.com/actions/virtual-environments/blob/master/images/macos/macos-10.15-Readme.md), + # Python 3.7.7 is installed on GitHub Actions runners. But we install our own copy from the python.org binary package. + run: | + curl -o python3.pkg https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg + sudo installer -verbose -pkg python3.pkg -target / + if: contains(matrix.tox_system_factor, 'python3_pythonorg') + - name: Build and test with tox + # We use a high parallelization on purpose in order to catch possible parallelization bugs in the build scripts. + # For doctesting, we use a lower parallelization to avoid timeouts. + run: | + MAKE="make -j12" tox -e $TOX_ENV -- SAGE_NUM_THREADS=4 $TARGETS + - name: Prepare logs artifact + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v1 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # and markup the output with GitHub Actions logging commands + run: | + .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME" + if: always() diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 6462b5fbd1c..a2c1d551885 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -20,6 +20,9 @@ name: Run SAGE_ROOT/tox.ini on: pull_request: types: [opened, synchronize] + push: + tags: + - '*' env: TARGETS_PRE: sagelib-build-deps diff --git a/Makefile b/Makefile index 5bff5821304..822d1d316ff 100644 --- a/Makefile +++ b/Makefile @@ -50,14 +50,7 @@ build/make/Makefile: configure build/make/deps $(SPKG_COLLECT_FILES) $(CONFIG_FI ./config.status --recheck && ./config.status; \ else \ ./configure $$PREREQ_OPTIONS; \ - fi || ( \ - if [ "x$$SAGE_PORT" = x ]; then \ - echo "If you would like to try to build Sage anyway (to help porting),"; \ - echo "export the variable 'SAGE_PORT' to something non-empty."; \ - exit 1; \ - else \ - echo "Since 'SAGE_PORT' is set, we will try to build anyway."; \ - fi; ) + fi # This is used to monitor progress towards Python 3 and prevent # regressions. Should be removed after the full switch to python3. diff --git a/VERSION.txt b/VERSION.txt index 9c965858ee6..ec5033757b9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.1.rc1, Release Date: 2020-04-22 +SageMath version 9.1.rc2, Release Date: 2020-04-25 diff --git a/build/bin/sage-print-system-package-command b/build/bin/sage-print-system-package-command index 7802d447df2..fa3f1557494 100755 --- a/build/bin/sage-print-system-package-command +++ b/build/bin/sage-print-system-package-command @@ -1,52 +1,85 @@ #!/usr/bin/env bash # system=$1 -command=$2 -if [ -z "$system" -o -z "$command" ]; then - echo >&2 "usage: $0 {debian|arch|conda|...} {install|remove|...} PACKAGES..." - exit 1 -fi -shift 2 -system_packages="$*" -shopt -s extglob -if [ -n "$system_packages" ]; then - # No command needed to install nothing - case $system:$command in - debian*|ubuntu*) - echo "sudo apt-get $command $system_packages" - ;; - @(fedora*|redhat*|centos*):install) - echo "sudo yum install $system_packages" - ;; - arch*:install) - echo "sudo pacman -S $system_packages" +shift +IF_VERBOSE=: +SUDO= +PROMPT= +while : +do + case "$1" in + --verbose) + IF_VERBOSE= ;; - *conda*:install) - echo "conda install $system_packages" + --sudo) + # Whether to print sudo for package managers that need sudo for non-root users + SUDO="sudo " ;; - homebrew*:install) - echo "brew install $system_packages" - echo "# Afterwards: " + --prompt) + PROMPT=' $ ' ;; - slackware*:install) - echo "sudo slackpkg install $system_packages" - ;; - cygwin*:install) - echo "# first install apt-cyg from https://github.com/transcode-open/apt-cyg" - echo "apt-cyg install $system_packages" + -*) + echo >&2 "$0: unknown option $2" + exit 1 ;; *) - echo "# $command the following packages: $system_packages" - ;; + break esac + shift +done +command=$1 +shift +if [ -z "$system" -o -z "$command" ]; then + echo >&2 "usage: $0 {debian|arch|conda|...} [--verbose] [--sudo] [--prompt] {update|install|setup-build-env|remove|...} PACKAGES..." + exit 1 fi -# Messages that should go out even if not packages need to be installed +system_packages="$*" +shopt -s extglob case $system:$command in - homebrew*:install) - echo "# To automatically take care of homebrew messages regarding " - echo "# keg-only packages for the current shell session:" + homebrew*:setup-build-env) + $IF_VERBOSE echo "# To automatically take care of homebrew messages regarding " + $IF_VERBOSE echo "# keg-only packages for the current shell session:" [ -n "$SAGE_ROOT" ] || SAGE_ROOT=. - echo "# $ source $SAGE_ROOT/.homebrew-build-env" - echo "# Add this to your shell profile if you want it to persist between shell sessions." + echo "${PROMPT}source $SAGE_ROOT/.homebrew-build-env" + $IF_VERBOSE echo "# Add this to your shell profile if you want it to persist between shell sessions." + ;; + *:setup-build-env) + # Nothing needed + ;; + # + # 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" + ;; + @(debian*|ubuntu*):*) + [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}apt-get $command $system_packages" + ;; + @(fedora*|redhat*|centos*):install) + [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}yum install $system_packages" + ;; + arch*:install) + [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}pacman -S $system_packages" + ;; + *conda*:install) + [ -n "$system_packages" ] && echo "${PROMPT}conda install $system_packages" + ;; + homebrew*:install) + [ -n "$system_packages" ] && echo "${PROMPT}brew install $system_packages" + ;; + slackware*:install) + [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}slackpkg install $system_packages" + ;; + cygwin*:update) + echo "# first install apt-cyg from https://github.com/transcode-open/apt-cyg" + ;; + cygwin*:install) + [ -n "$system_packages" ] && echo "${PROMPT}apt-cyg install $system_packages" + ;; + *:update) + # Nothing needed + ;; + *) + echo "# $command the following packages: $system_packages" ;; esac diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 5cdd55db3fd..6129555958a 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -390,7 +390,7 @@ if [ $INFO -ne 0 -a "$USE_LOCAL_SCRIPTS" = yes ]; then ;; esac echo -n " " - sage-print-system-package-command $system install $system_packages + sage-print-system-package-command $system --prompt --sudo install $system_packages fi done if [ -z "$system" ]; then diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index bf76aa2e4eb..e82d1ea8a5a 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -43,6 +43,7 @@ case $SYSTEM in ARG BASE_IMAGE=ubuntu:latest FROM \${BASE_IMAGE} as with-system-packages EOF + EXISTS="2>/dev/null >/dev/null apt-cache show" UPDATE="apt-get update &&" INSTALL="DEBIAN_FRONTEND=noninteractive apt-get install -qqq --no-install-recommends --yes" CLEAN="&& apt-get clean" @@ -52,6 +53,7 @@ EOF ARG BASE_IMAGE=fedora:latest FROM \${BASE_IMAGE} as with-system-packages EOF + EXISTS="2>/dev/null >/dev/null yum install -y --downloadonly" INSTALL="yum install -y" ;; slackware*) @@ -60,6 +62,8 @@ EOF ARG BASE_IMAGE=vbatts/slackware:latest FROM \${BASE_IMAGE} as with-system-packages EOF + # slackpkg install ignores packages that it does not know, so we do not have to filter + EXISTS="true" UPDATE="slackpkg update &&" INSTALL="slackpkg install" ;; @@ -69,7 +73,9 @@ EOF ARG BASE_IMAGE=archlinux:latest FROM \${BASE_IMAGE} as with-system-packages EOF - INSTALL="pacman -Syu --noconfirm" + UPDATE="pacman -Sy &&" + EXISTS="pacman -Si" + INSTALL="pacman -Su --noconfirm" ;; conda*) cat < - #include - USING_NAMESPACE_PBORI - USING_NAMESPACE_PBORIGB - - class MyConstant : public BooleConstant{ - public: void negate() { this->m_value = !this->m_value; } - }; - ],[ - BoolePolyRing r = BoolePolyRing(2, COrderEnums::dlex); - ReductionStrategy rs = ReductionStrategy(r); - rs.llReduceAll(); // uses groebner lib - if (2 != r.nVariables()) { return 1; } - if (r.constant(true) == r.constant(false)) { return 2; } - MyConstant f = MyConstant(); - f.negate(); // ensures v1.1.0+ if m_value isn't const - if (!f.isOne()) { return 3; } - return 0; - ]) - ], - [ - AC_MSG_RESULT([yes]) - sage_spkg_install_brial=no - ], - [ - AC_MSG_RESULT([no]) - sage_spkg_install_brial=yes - ]) - LIBS=$SAVED_LIBS - AC_LANG_POP - ], - [ # If we're installing sage's boost or m4ri, then we have to - # install its BRiAl, too. - sage_spkg_install_brial=yes - ]) -]) diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 17afb76788b..04bc4881a52 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=4e229f01e58451cce917d20718584565fc37a395 -md5=5f227165cf7fd23ed7e0fe890a7c1b1e -cksum=2087518725 +sha1=840e31bcaa0f8c2ff6fcd6aedade6322c9cd9847 +md5=e014eaafe01d115e3cbe9e6ed5db4c94 +cksum=2845446683 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index c88a789afba..9087199c2d5 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -358caa9bccf45c8142c77d0bcf38dc6e44df8dfd +119bf4775960e4184b1831067df9c86ef7f14180 diff --git a/build/pkgs/cygwin.txt b/build/pkgs/cygwin.txt index a0a65123320..51c562cb9b9 100644 --- a/build/pkgs/cygwin.txt +++ b/build/pkgs/cygwin.txt @@ -25,3 +25,5 @@ findutils which # For python3 build libcrypt-devel +# For R build +libiconv-devel diff --git a/build/pkgs/giac/checksums.ini b/build/pkgs/giac/checksums.ini index d5037efc991..da103a0046c 100644 --- a/build/pkgs/giac/checksums.ini +++ b/build/pkgs/giac/checksums.ini @@ -1,4 +1,5 @@ tarball=giac-VERSION.tar.bz2 -sha1=7ea2f353b7f63f484d38fe86c5720c82d770c69c -md5=652634cf95590a5601d17c90c3fc410c -cksum=2772110213 +sha1=eadeb7194b1809298a0d94642d94c8dff80bbe3d +md5=7760e342f5e5b3f5da0f1b9c15546b96 +cksum=1678878999 +upstream_url=https://trac.sagemath.org/raw-attachment/ticket/29521/giac-1.5.0.63-p0.tar.bz2 diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index 6d48b14db2e..64d874f14d8 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.5.0.63.p0 +1.5.0.63-p0 diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index c8ee7cae7c7..4d093016af8 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -15,12 +15,13 @@ set -e VERSION="1.5.0" VERSIONREV="63" +PATCHSUFFIX="-p0" # The upstream tarball name is: giac"$SOURCEORIG".tar.gz SOURCEORIG=_"$VERSION"-"$VERSIONREV" # The name of the output file without tar.gz or tar.bz2 extension -OUTPUTFILEBASENAME="$SAGE_DISTFILES"/giac-"$VERSION"."$VERSIONREV" +OUTPUTFILEBASENAME="$SAGE_DISTFILES"/giac-"$VERSION"."$VERSIONREV""$PATCHSUFFIX" # Testing if the output file already exist in one of gz or bz2 format: if [ -f "$OUTPUTFILEBASENAME".tar.gz -o -f "$OUTPUTFILEBASENAME".tar.bz2 ] ; then @@ -28,8 +29,8 @@ if [ -f "$OUTPUTFILEBASENAME".tar.gz -o -f "$OUTPUTFILEBASENAME".tar.bz2 ] ; the exit 1 fi -# Build a temporary working dir. (subdir of SAGE_DISTFILES) -TARGET=$(mktemp -d -p"$SAGE_DISTFILES") +# Build a temporary working dir. (preferably, a subdir of SAGE_DISTFILES) +TARGET=$(mktemp -d -p"$SAGE_DISTFILES" || mktemp -d) ORIGDIR=`pwd` cd "$TARGET" @@ -44,6 +45,9 @@ tar -xzf giac"$SOURCEORIG".tar.gz # rename top sourcedir mv giac-"$VERSION" src +# remove unnecessary files +rm -rf src/doc/pari/*.html + # removing french html doc, but keep keywords, and working makefiles. # NB: the french html doc is huge and not GPL. # it is freely redistributable only for non commercial purposes. @@ -66,9 +70,7 @@ touch html_vall # building giac source tarball for the spkg cd ../../../ -tar -cj src -f "$OUTPUTFILEBASENAME".tar.bz2 - - +tar -cjf "$OUTPUTFILEBASENAME".tar.bz2 src # cleaning extracted dir. cd .. diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt index 266146b87cb..7cdcdd5508f 100644 --- a/build/pkgs/linbox/package-version.txt +++ b/build/pkgs/linbox/package-version.txt @@ -1 +1 @@ -1.6.3 +1.6.3.p0 diff --git a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch b/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch new file mode 100644 index 00000000000..c93915fb1b0 --- /dev/null +++ b/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch @@ -0,0 +1,23 @@ +Backported from: + +From 426eb97ba762c7663884f57ead0909f2aa3cd6a5 Mon Sep 17 00:00:00 2001 +From: Cyril Bouvier +Date: Thu, 17 Jan 2019 16:32:19 +0100 +Subject: [PATCH] Remove @LINBOXSAGE_LIBS@ from linbox.pc.in + +--- + linbox.pc.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/linbox.pc.in b/linbox.pc.in +index 278f127e4..c6b8091eb 100644 +--- a/linbox.pc.in ++++ b/linbox.pc.in +@@ -9,6 +9,6 @@ Description: Exact Linear Algebra library + URL: http://github.com/linbox-team/linbox + Version: @VERSION@ + Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 +-Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ ++Libs: -L${libdir} -llinbox @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ + Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ + \------------------------------------------------------- diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 9004e57199e..912248c474c 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -47,16 +47,9 @@ SAGE_SPKG_CONFIGURE([openblas], [ ], [ dnl No openblas.pc AS_CASE([$host], - [*-*-cygwin*], [dnl #29398: cygwin uses openblas only to provide a binary-compatible - dnl dll, /usr/bin/cygblas-0.dll, which shadows /usr/lib/lapack/cygblas-0.dll - dnl There is no .pc file, and openblas-specific functions such as - dnl openblas_get_config are not exported. We only accept the system BLAS - dnl if it is provided by OpenBLAS. - AC_MSG_CHECKING([whether cygblas-0.dll is openblas]) - AS_IF([grep -q openblas_get_config $(which cygblas-0.dll)], - [AS_VAR_SET([HAVE_OPENBLAS], [yes])], - [AS_VAR_SET([HAVE_OPENBLAS], [no])]) - AC_MSG_RESULT([$HAVE_OPENBLAS]) + [*-*-cygwin*], [dnl #29538 - workaround failing build of matplotlib etc. + AS_VAR_SET([HAVE_OPENBLAS], [no]) + AC_MSG_RESULT([$HAVE_OPENBLAS, test for OpenBLAS disabled on Cygwin]) ], [dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as dnl part of openblas_get_config. We reject all older versions. diff --git a/build/pkgs/p_group_cohomology/dependencies b/build/pkgs/p_group_cohomology/dependencies index 636dbe064f0..9492937c0f8 100644 --- a/build/pkgs/p_group_cohomology/dependencies +++ b/build/pkgs/p_group_cohomology/dependencies @@ -1 +1 @@ -$(PYTHON) cython cysignals singular meataxe $(SAGE_SRC)/sage/matrix/matrix_gfpn_dense.pxd $(SAGE_SRC)/sage/structure/element.pxd $(SAGE_SRC)/sage/matrix/matrix_gfpn_dense.pxd $(SAGE_SRC)/sage/matrix/matrix0.pxd $(SAGE_SRC)/sage/libs/meataxe.pxd $(SAGE_SRC)/sage/rings/morphism.pxd | $(PYTHON_TOOLCHAIN) matplotlib gap xz $(SAGERUNTIME) +$(PYTHON) cython cysignals singular meataxe $(SAGE_SRC)/sage/matrix/matrix_gfpn_dense.pxd $(SAGE_SRC)/sage/structure/element.pxd $(SAGE_SRC)/sage/matrix/matrix_gfpn_dense.pxd $(SAGE_SRC)/sage/matrix/matrix0.pxd $(SAGE_SRC)/sage/libs/meataxe.pxd $(SAGE_SRC)/sage/rings/morphism.pxd | $(PYTHON_TOOLCHAIN) matplotlib gap xz $(SAGERUNTIME) ipywidgets diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 2ef21964f2a..401663b00a0 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,14 +1,7 @@ SAGE_SPKG_CONFIGURE([pari], [ - dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV - m4_pushdef([SAGE_PARI_MINVER],["133889"]) - AC_REQUIRE([SAGE_SPKG_CONFIGURE_GMP]) - AC_REQUIRE([SAGE_SPKG_CONFIGURE_READLINE]) - AC_MSG_CHECKING([installing gmp/mpir or readline? ]) - if test x$sage_spkg_install_mpir = xyes -o x$sage_spkg_install_gmp = xyes -o x$sage_spkg_install_readline = xyes; then dnl deps test - AC_MSG_RESULT([yes; install pari as well]) - sage_spkg_install_pari=yes - else - AC_MSG_RESULT([no]) + dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV + m4_pushdef([SAGE_PARI_MINVER],["133889"]) + SAGE_SPKG_DEPCHECK([gmp mpir readline], [ AC_PATH_PROG([GP], [gp]) if test x$GP = x; then dnl GP test AC_MSG_NOTICE([gp is not found]) @@ -74,8 +67,8 @@ SAGE_SPKG_CONFIGURE([pari], [ sage_spkg_install_pari=yes fi AC_MSG_CHECKING([whether hyperellcharpoly bug is fixed]) - bug_check=$(echo "hyperellcharpoly([x^10 + x^9 + x^8 + x,0]*Mod(1,3))" | $GP -qf 2>> config.log) - expected="%1 = x^8 + 731*x^7 + 6*x^6 - 720*x^5 + 18*x^4 - 2160*x^3 + 54*x^2 + 19737*x + 81" + bug_check=`echo "hyperellcharpoly(Mod(1,3)*(x^10 + x^9 + x^8 + x))" | $GP -qf 2>> config.log` + expected="x^8 + 2*x^7 + 6*x^6 + 9*x^5 + 18*x^4 + 27*x^3 + 54*x^2 + 54*x + 81" if test x"$bug_check" = x"$expected"; then AC_MSG_RESULT([yes]) else @@ -85,7 +78,7 @@ SAGE_SPKG_CONFIGURE([pari], [ sage_spkg_install_pari=yes fi AC_MSG_CHECKING([whether bnfisunit bug of pari 2.11.3 is fixed]) - bug_check=$(echo "bnf = bnfinit(y^4-y-1); bnfisunit(bnf,-y^3+2*y^2-1)" | $GP -qf 2>> config.log) + bug_check=`echo "bnf = bnfinit(y^4-y-1); bnfisunit(bnf,-y^3+2*y^2-1)" | $GP -qf 2>> config.log` expected="[[0, 2, Mod(0, 2)]]~" if test x"$bug_check" = x"$expected"; then AC_MSG_RESULT([yes]) @@ -139,8 +132,8 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_LANG_POP() ], [sage_spkg_install_pari=yes]) fi dnl end main PARI test - fi dnl end deps test - m4_popdef([SAGE_PARI_MINVER]) + ]) + m4_popdef([SAGE_PARI_MINVER]) ], [], [], [ if test x$sage_spkg_install_pari = xyes; then AC_SUBST(SAGE_PARI_PREFIX, ['$SAGE_LOCAL']) diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 index d42e9cd5c54..e88f3393a91 100644 --- a/build/pkgs/r/spkg-configure.m4 +++ b/build/pkgs/r/spkg-configure.m4 @@ -1,16 +1,22 @@ SAGE_SPKG_CONFIGURE([r], [ m4_pushdef([SAGE_R_MINVER],["3.4.4"]) SAGE_SPKG_DEPCHECK([atlas openblas iconv readline bzip2 xz pcre curl], [ - PKG_CHECK_MODULES([R], [libR >= $SAGE_R_MINVER], [ - AC_PATH_PROG([R], [R]) - AS_IF([test "x$R" = x], [ - AC_MSG_NOTICE([R is not found]) - sage_spkg_install_r=yes - ], [ - dnl TODO: check that versions of R and libR match - sage_spkg_install_r=no - ]) - ], [sage_spkg_install_r=yes]) + AS_CASE([$host], + [*-*-cygwin*], [ + dnl #29486: rpy2 2.8.x does not build against system R on cygwin. + sage_spkg_install_r=yes + ], [ + PKG_CHECK_MODULES([R], [libR >= $SAGE_R_MINVER], [ + AC_PATH_PROG([R], [R]) + AS_IF([test "x$R" = x], [ + AC_MSG_NOTICE([R is not found]) + sage_spkg_install_r=yes + ], [ + dnl TODO: check that versions of R and libR match + sage_spkg_install_r=no + ]) + ], [sage_spkg_install_r=yes]) + ]) ]) m4_popdef([SAGE_R_MINVER]) ]) diff --git a/build/pkgs/sage_numerical_backends_coin/dependencies b/build/pkgs/sage_numerical_backends_coin/dependencies index 4e5cceca5be..2717db3a34f 100644 --- a/build/pkgs/sage_numerical_backends_coin/dependencies +++ b/build/pkgs/sage_numerical_backends_coin/dependencies @@ -1,4 +1,4 @@ -cbc cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython +cbc cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython ipywidgets ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sage_numerical_backends_cplex/dependencies b/build/pkgs/sage_numerical_backends_cplex/dependencies index 0fb0bf5ae3a..bb56aad17be 100644 --- a/build/pkgs/sage_numerical_backends_cplex/dependencies +++ b/build/pkgs/sage_numerical_backends_cplex/dependencies @@ -1,4 +1,4 @@ -cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython +cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython ipywidgets ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sage_numerical_backends_gurobi/dependencies b/build/pkgs/sage_numerical_backends_gurobi/dependencies index 0fb0bf5ae3a..bb56aad17be 100644 --- a/build/pkgs/sage_numerical_backends_gurobi/dependencies +++ b/build/pkgs/sage_numerical_backends_gurobi/dependencies @@ -1,4 +1,4 @@ -cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython +cysignals $(SAGE_SRC)/sage/numerical/backends/generic_backend.pxd $(SAGE_SRC)/sage/cpython/string.pxd $(SAGE_SRC)/sage/cpython/string_impl.h | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) cython ipywidgets ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagetex/spkg-check.in b/build/pkgs/sagetex/spkg-check.in index 91d965b87f7..b502aed9df8 100644 --- a/build/pkgs/sagetex/spkg-check.in +++ b/build/pkgs/sagetex/spkg-check.in @@ -31,6 +31,10 @@ fi cd src +# Make sure that we use the version of sagetex.py and example.tex in +# the current working directory. +unset TEXINPUTS + typeset example.tex checkdotsage example typeset example.tex diff --git a/build/pkgs/symmetrica/spkg-configure.m4 b/build/pkgs/symmetrica/spkg-configure.m4 index 019256e01a5..96f8e003489 100644 --- a/build/pkgs/symmetrica/spkg-configure.m4 +++ b/build/pkgs/symmetrica/spkg-configure.m4 @@ -3,20 +3,24 @@ SAGE_SPKG_CONFIGURE([symmetrica], [ AC_CHECK_HEADER([symmetrica/def.h], [ dnl check for one of its many functions AC_SEARCH_LIBS([zykelind_tetraeder_edges_extended], [symmetrica], [ - AC_MSG_CHECKING([that we have a properly patched Symmetrica version... ]) + AC_MSG_CHECKING([whether we have a properly patched Symmetrica version]) AC_RUN_IFELSE([AC_LANG_PROGRAM([dnl this crashes on unpatched Symmetrica [#include "symmetrica/def.h"] [#include "symmetrica/macro.h"]], [[OP b,n;] + [int i;] [anfang();] - [n = callocobject();] - [b = callocobject();] - [sscan_integer("4",n);] - [kostka_tafel(n, b);] - [println(b);] + [for (i=1; i<6; i++) {] + [n = callocobject();] + [b = callocobject();] + [M_I_I(i, n);] + [kostka_tafel(n, b);] + [println(b);] + [freeall(n);] + [freeall(b);}] [ende();]])], [AC_MSG_RESULT([appears to be a well-patched version.])], - [AC_MSG_RESULT([buggy version. Sage will buld its own.]) + [AC_MSG_RESULT([buggy version. Sage will build its own.]) sage_spkg_install_symmetrica=yes]) ], [sage_spkg_install_symmetrica=yes]) ], [sage_spkg_install_symmetrica=yes]) diff --git a/m4/sage_spkg_collect.m4 b/m4/sage_spkg_collect.m4 index fdd22128053..27de8d52161 100644 --- a/m4/sage_spkg_collect.m4 +++ b/m4/sage_spkg_collect.m4 @@ -268,11 +268,6 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do ORDER_ONLY_DEPS='pip' ;; esac - case "$SPKG_TYPE" in - optional|experimental) - ORDER_ONLY_DEPS="$ORDER_ONLY_DEPS"' $(STANDARD_PACKAGES)' - ;; - esac if test -n "$ORDER_ONLY_DEPS"; then DEPS="| $ORDER_ONLY_DEPS" else @@ -318,9 +313,10 @@ AC_DEFUN([SAGE_SYSTEM_PACKAGE_NOTICE], [ AS_IF([test $SYSTEM != unknown], [ SYSTEM_PACKAGES=$(build/bin/sage-get-system-packages $SYSTEM $SAGE_NEED_SYSTEM_PACKAGES) AS_IF([test -n "$SYSTEM_PACKAGES"], [ - COMMAND=$(SAGE_ROOT=$SAGE_ROOT build/bin/sage-print-system-package-command $SYSTEM install $SYSTEM_PACKAGES) + PRINT_SYS="build/bin/sage-print-system-package-command $SYSTEM --verbose --prompt --sudo" + COMMAND=$($PRINT_SYS update && $PRINT_SYS install $SYSTEM_PACKAGES && SAGE_ROOT=$SAGE_ROOT $PRINT_SYS setup-build-env ) AC_MSG_NOTICE([hint: installing the following system packages is recommended and may avoid building some of the above SPKGs from source:]) - AC_MSG_NOTICE([ \$ $COMMAND]) + AC_MSG_NOTICE([$COMMAND]) AC_MSG_NOTICE([After installation, re-run configure using:]) AC_MSG_NOTICE([ \$ ./config.status --recheck && ./config.status]) ], [ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index dfba16a8a5e..188ce40c02e 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.1.rc1' -SAGE_RELEASE_DATE='2020-04-22' -SAGE_VERSION_BANNER='SageMath version 9.1.rc1, Release Date: 2020-04-22' +SAGE_VERSION='9.1.rc2' +SAGE_RELEASE_DATE='2020-04-25' +SAGE_VERSION_BANNER='SageMath version 9.1.rc2, Release Date: 2020-04-25' diff --git a/src/doc/bootstrap b/src/doc/bootstrap index c6cfea87529..eaefc1c83f4 100755 --- a/src/doc/bootstrap +++ b/src/doc/bootstrap @@ -16,15 +16,15 @@ fi cd "$SAGE_ROOT" -PROMPT=' $ ' STRIP_COMMENTS="sed s/#.*//;" OUTPUT_DIR="src/doc/en/installation" mkdir -p "$OUTPUT_DIR" shopt -s extglob -for SYSTEM in arch debian fedora cygwin; do +for SYSTEM in arch debian fedora cygwin homebrew; do SYSTEM_PACKAGES=$(echo $(${STRIP_COMMENTS} build/pkgs/$SYSTEM.txt)) + OPTIONAL_SYSTEM_PACKAGES= for PKG_SCRIPTS in build/pkgs/*; do if [ -d $PKG_SCRIPTS ]; then PKG_BASE=$(basename $PKG_SCRIPTS) @@ -46,6 +46,6 @@ for SYSTEM in arch debian fedora cygwin; do fi done echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/$SYSTEM.txt and "$OUTPUT_DIR"/$SYSTEM-optional.txt - echo "$PROMPT$(sage-print-system-package-command $SYSTEM install $(echo $(echo $SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM.txt - echo "$PROMPT$(sage-print-system-package-command $SYSTEM install $(echo $(echo $OPTIONAL_SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM-optional.txt + echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM.txt + echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $OPTIONAL_SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM-optional.txt done diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst index 1db03045974..5899c0993e3 100644 --- a/src/doc/en/developer/portability_testing.rst +++ b/src/doc/en/developer/portability_testing.rst @@ -916,7 +916,8 @@ Automatic parallel tox runs on GitHub Actions The Sage source tree includes a default configuration for GitHub Actions that runs tox on a multitude of platforms on every pull -request to a repository for which GitHub Actions are enabled. +request and on every push of a tag (but not of a branch) to a +repository for which GitHub Actions are enabled. This is defined in the file ``$SAGE_ROOT/.github/workflows/tox.yml``. @@ -936,8 +937,29 @@ workflow have finished. Each job generates one tarball. "Annotations" highlight certain top-level errors or warnings issued during the build. -The following procedure seems to work well for testing branches during -development: +The following procedure triggers a run of tests with the default set of +system configurations. Let's assume that ``github`` is the name of +the remote corresponding to your GitHub fork of the Sage repository:: + + $ git remote -v | grep /my-github + my-github https://github.com/mkoeppe/sage.git (fetch) + my-github https://github.com/mkoeppe/sage.git (push) + +- Create a ("lightweight", not "annotated") tag with an arbitrary + name, say ``ci`` (for "Continuous Integration"):: + + git tag -f ci + +- Then push the tag to your GitHub repository:: + + git push -f my-github ci + +(In both commands, the "force" option (``-f``) allows overwriting a +previous tag of that name.) + +For testing branches against a custom set of system configurations +during development, the following procedure seems to work well. It +avoids changing the CI configuration on your development branch: - Create a branch from a recent beta release that contains the default GitHub Actions configuration; name it ``TESTER``, say. @@ -951,6 +973,9 @@ development: pull request against the ``TESTER`` branch. This will trigger the GitHub Actions workflow. +You will find a workflow status page in the "Actions" tab of your +repository. + Here is how to read it. Each of the items in the left pane represents a full build of Sage on a particular system configuration. A test item in the left pane is marked with a green checkmark in the left diff --git a/src/doc/en/installation/launching.rst b/src/doc/en/installation/launching.rst index 2d8616e7b93..042414895a0 100644 --- a/src/doc/en/installation/launching.rst +++ b/src/doc/en/installation/launching.rst @@ -9,13 +9,46 @@ Notebook from the command line. If you did install the Windows version or the macOS application you should have icons available on your desktops or launching menus. Otherwise -you are strongly advised to create shortcuts for Sage as indicated as the end +you are strongly advised to create shortcuts for Sage as indicated at the end of the "Linux" Section in :ref:`sec-installation-from-binaries`. Assuming -that you have this shortcut, running ``sage`` in a console starts a Sage -session. To quit the session enter ``quit`` and then press ````. To -start a Jupyter Notebook instead of a Sage console, run the command -``sage -n jupyter`` instead of just ``sage``. To quit the Jupyter Notebook -press `` + `` twice in the console where you launched the command. +that you have this shortcut, running + +.. CODE-BLOCK:: bash + + sage + +in a console starts a Sage session. To quit the session enter ``quit`` and +then press ````. + +To start a Jupyter Notebook instead of a Sage console, run the command + +.. CODE-BLOCK:: bash + + sage -n jupyter + +instead of just ``sage``. To quit the Jupyter Notebook press `` + `` +twice in the console where you launched the command. + +Using a Jupyter Notebook remotely +--------------------------------- + +If Sage is installed on a remote machine to which you have ``ssh`` access, you +can launch a Jupyter Notebook using a command such as + +.. CODE-BLOCK:: bash + + ssh -L localhost:8888:localhost:8888 -t USER@REMOTE sage -n jupyter --no-browser --port=8888 + +where ``USER@REMOTE`` needs to be replaced by the login details to the remote +machine. This uses local port forwarding to connect your local machine to the +remote one. The command will print a URL to the console which you can copy and +paste in a web browser. + +Note that this assumes that a firewall which might be present between server +and client allows connections on port 8888. See details on port forwarding on +the internet, e.g. https://www.ssh.com/ssh/tunneling/example. + +------------------------------------------------------------------------ For further reading you can have a look at the other documents in the SageMath documentation at http://doc.sagemath.org/. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index de771342505..6ecceaa60a1 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -228,6 +228,13 @@ On Debian ("buster" or newer) or Ubuntu ("bionic" or newer): .. literalinclude:: debian.txt +.. WARNING:: + + Note: in this documentation, commands like these are + autogenerated. They may as such include duplications. The + duplications are certainly not necessary for the commands to + function properly, but they don't cause any harm, either. + On Fedora / Redhat / CentOS: .. literalinclude:: fedora.txt @@ -297,6 +304,43 @@ a registration. to Command Line Tools. + +macOS recommended installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although Sage can in theory build its own version of gfortran, this +can take a while, and the process fails on some recent versions of +OS X. So instead you can install your own copy. One advantage of this +is that you can install it once, and it will get used every time you +build Sage, rather than building gfortran every time. + +One way to do that is with the `Homebrew package manager +`_. Install Homebrew as their web page describes, and +then the command :: + + $ brew install gcc + +will install Homebrew's gcc package, which includes gfortran. Sage +will also use other Homebrew packages, if they are present. You can +install the following: + +.. literalinclude:: homebrew.txt + +Some Homebrew packages are installed "keg-only," meaning that they are +not available in standard paths. To make them accessible when building +Sage, run :: + + $ source SAGE_ROOT/.homebrew-build-env + +(replacing ``SAGE_ROOT`` by Sage's home directory). You can add a +command like this to your shell profile if you want the settings to +persist between shell sessions. + +Some additional optional packages are taken care of by: + +.. literalinclude:: homebrew-optional.txt + + .. _section_cygwinprereqs: Cygwin prerequisite installation @@ -310,7 +354,7 @@ Although it is possible to install Sage's dependencies using the Cygwin graphical installer, it is recommended to install the `apt-cyg `_ command-line package installer, which is used for the remainder of these instructions. To -run `apt-cyg`, you must have already installed (using the graphical +run ``apt-cyg``, you must have already installed (using the graphical installer) the following packages at a minimum:: bzip2 coreutils gawk gzip tar wget @@ -575,8 +619,10 @@ Running Sage from a directory with spaces in its name will also fail. #. Optional: Set various other environment variables that influence the build process; see :ref:`section_envvar`. - Some environment variables deserve a special mention: `CC`, `CXX` and `FC`; - and on macOS, `OBJC` and `OBJCXX`. Those variables defining your compilers + Some environment variables deserve a special mention: :envvar:`CC`, + :envvar:`CXX` and :envvar:`FC`; + and on macOS, :envvar:`OBJC` and :envvar:`OBJCXX`. Those variables + defining your compilers can be set at configuration time and their values will be recorded for further use at runtime. Those initial values are over-ridden if Sage builds its own compiler or they are set to a different value again before calling @@ -1192,30 +1238,6 @@ Here are some of the more commonly used variables affecting the build process: supports :envvar:`SAGE_SUDO`, into a root-owned installation hierarchy (:envvar:`SAGE_LOCAL`). -Variables to set if you're trying to build Sage with an unusual setup, e.g., -an unsupported machine or an unusual compiler: - -- :envvar:`SAGE_PORT` - if you try to build Sage on a platform which is - recognized as being unsupported (e.g. AIX, or HP-UX), or with a compiler - which is unsupported (anything except GCC), you will see a message saying - something like: - - .. CODE-BLOCK:: text - - You are attempting to build Sage on IBM's AIX operating system, - which is not a supported platform for Sage yet. Things may or - may not work. If you would like to help port Sage to AIX, - please join the sage-devel discussion list -- see - https://groups.google.com/group/sage-devel - The Sage community would also appreciate any patches you submit. - - To get past this message and try building Sage anyway, - export the variable SAGE_PORT to something non-empty. - - If this is case and you want to try to build Sage anyway, follow the - directions: set :envvar:`SAGE_PORT` to something non-empty (and expect to - run into problems). - Environment variables dealing with specific Sage packages: - :envvar:`SAGE_MP_LIBRARY` - to use an alternative library in place of ``MPIR`` @@ -1469,4 +1491,4 @@ the directory where you want to install Sage. -**This page was last updated in August 2019 (Sage 9.0).** +**This page was last updated in April 2020 (Sage 9.1).** diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index e09e3e24e4d..c9ae6fcf673 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -234,8 +234,8 @@ Examples of parents using categories sage/categories/examples/facade_sets sage/categories/examples/finite_coxeter_groups sage/categories/examples/finite_dimensional_algebras_with_basis - sage/categories/examples/finite_enumerated_sets sage/categories/examples/finite_dimensional_lie_algebras_with_basis + sage/categories/examples/finite_enumerated_sets sage/categories/examples/finite_monoids sage/categories/examples/finite_semigroups sage/categories/examples/finite_weyl_groups @@ -244,13 +244,14 @@ Examples of parents using categories sage/categories/examples/graphs sage/categories/examples/hopf_algebras_with_basis sage/categories/examples/infinite_enumerated_sets - sage/categories/examples/manifolds sage/categories/examples/lie_algebras sage/categories/examples/lie_algebras_with_basis + sage/categories/examples/magmas + sage/categories/examples/manifolds sage/categories/examples/monoids sage/categories/examples/posets - sage/categories/examples/semigroups_cython sage/categories/examples/semigroups + sage/categories/examples/semigroups_cython sage/categories/examples/sets_cat sage/categories/examples/sets_with_grading sage/categories/examples/with_realizations diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 27bfdc2263f..f488f0a8cbb 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1625,6 +1625,13 @@ REFERENCES: and Intersection Algorithms. SIAM Journal on Computing 1986 15:4, 948-957. +.. [CVV2019] Xavier Caruso, Tristan Vaccon and Thibaut Verron, + *Gröbner bases over Tate algebras*, :arxiv:`1901.09574` (2019) + +.. [CVV2020] Xavier Caruso, Tristan Vaccon and Thibaut Verron, + *Signature-based algorithms for Gröbner bases over Tate algebras*, + :arxiv:`2002.04491` (2020) + .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. @@ -1745,7 +1752,7 @@ REFERENCES: .. [DGH2020] \C. Donnot, A. Genitrini and Y. Herida. Unranking Combinations Lexicographically: an efficient new strategy compared with others, 2020, - https://hal.archives-ouvertes.fr/hal-02462764v1 + https://hal.archives-ouvertes.fr/hal-02462764v1 .. [DGMPPS2019] \N. Datta, A. Ghoshal, D. Mukhopadhyay, S. Patranabis, S. Picek, R. Sashukhan. "TRIFLE" @@ -2015,6 +2022,10 @@ REFERENCES: **F** +.. [Fag1983] Fagin, Ronald. *Degrees of acyclicity for hypergraphs and + relational database schemes.* Journal of the ACM (JACM) 30.3 + (1983): 514-550. + .. [Fayers2010] Matthew Fayers. *An LLT-type algorithm for computing higher-level canonical bases*. J. Pure Appl. Algebra **214** (2010), no. 12, 2186-2198. :arxiv:`0908.1749v3`. @@ -4074,6 +4085,9 @@ REFERENCES: Normaliz, http://www.mathematik.uni-osnabrueck.de/normaliz/ +.. [NormalizMan] Winfried Bruns, Max Horn, *Normaliz 3.8.5*, + 2020, https://github.com/Normaliz/Normaliz/blob/master/doc/Normaliz.pdf. + .. [NoThWi08] J.-C. Novelli, J.-Y. Thibon, L. K. Williams, *Combinatorial Hopf algebras, noncommutative Hall-Littlewood functions, and permutation tableaux*. @@ -4897,9 +4911,9 @@ REFERENCES: .. [St2011b] \W. Stein, *Toward a Generalization of the Gross-Zagier Conjecture*, Int Math Res Notices (2011), :doi:`10.1093/imrn/rnq075` - -.. [St2007] \W. Stein. *Modular Forms, a Computational Approach*. - With an appendix by Paul E. Gunnells. + +.. [St2007] \W. Stein. *Modular Forms, a Computational Approach*. + With an appendix by Paul E. Gunnells. AMS Graduate Studies in Mathematics, Volume 79, 2007. :doi:`10.1090/gsm/079` @@ -5380,11 +5394,11 @@ REFERENCES: .. [Wu2004] Wuthrich, Christian. *On p-adic heights in families of elliptic curves*. Journal of the London Mathematical Society, 70(1), 23-40, (2004). - + .. [Wu2018] Wuthrich, Christian. - *Numerical modular symbols for elliptic curves*. - Math. Comp. 87 (2018), no. 313, 2393–2423. - + *Numerical modular symbols for elliptic curves*. + Math. Comp. 87 (2018), no. 313, 2393–2423. + .. [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. diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index 4189018fff6..868b8a96e12 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -72,6 +72,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.f_vector` | the `f`-vector (number of faces of each dimension) :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.flag_f_vector` | the flag-`f`-vector (number of chains of faces) :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.neighborliness` | highest cardinality for which all `k`-subsets of the vertices are faces of the polyhedron + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.simpliciality` | highest cardinality for which all `k`-faces are simplices + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.simplicity` | highest cardinality for which the polar is `k`-simplicial **Implementation properties** @@ -161,6 +163,7 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.boundary_complex` | returns the boundary complex of simplicial compact polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.center` | returns the average of the vertices of the polyhedron + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.centroid` | returns the center of the mass :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.representative_point` | returns the sum of the center and the rays :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.a_maximal_chain` | returns a maximal chain of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_fan` | returns the fan spanned by the faces of the polyhedron diff --git a/src/module_list.py b/src/module_list.py index c74615f75db..d7b5f459cf0 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -302,20 +302,28 @@ def uname_specific(name, value, alternative): sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx']), Extension('sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx']), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.bit_vector_operations.cc', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc'], + sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx'], + depends = ['sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc'], + language="c++", extra_compile_args=['-std=c++11']), Extension('sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx']), + sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx'], + depends = ['sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc'], + language="c++", + extra_compile_args=['-std=c++11']), Extension('sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx']), + sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx'], + depends = ['sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc'], + language="c++", + extra_compile_args=['-std=c++11']), Extension('sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx']), + sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx'], + depends = ['sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc'], + language="c++", + extra_compile_args=['-std=c++11']), Extension('sage.geometry.polyhedron.combinatorial_polyhedron.conversions', sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx']), diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 5258fd4fdd0..17cd57f2e1f 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -720,6 +720,28 @@ def simple_projections(self, side='right', length_increasing=True): from sage.sets.family import Family return Family(self.index_set(), lambda i: self.simple_projection(i, side=side, length_increasing=length_increasing)) + def sign_representation(self, base_ring=None, side="twosided"): + r""" + Return the sign representation of ``self`` over ``base_ring``. + + INPUT: + + - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` + - ``side`` -- ignored + + EXAMPLES:: + + sage: W = WeylGroup(["A", 1, 1]) + sage: W.sign_representation() + Sign representation of Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space) over Integer Ring + + """ + if base_ring is None: + from sage.rings.all import ZZ + base_ring = ZZ + from sage.modules.with_basis.representation import SignRepresentationCoxeterGroup + return SignRepresentationCoxeterGroup(self, base_ring) + def demazure_product(self, Q): r""" Return the Demazure product of the list ``Q`` in ``self``. diff --git a/src/sage/categories/examples/magmas.py b/src/sage/categories/examples/magmas.py new file mode 100644 index 00000000000..5139041cef0 --- /dev/null +++ b/src/sage/categories/examples/magmas.py @@ -0,0 +1,158 @@ +r""" +Examples of magmas +""" +# **************************************************************************** +# Copyright (C) 2020 Markus Wageringel +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.all import Magmas +from sage.sets.family import Family + + +class FreeMagma(UniqueRepresentation, Parent): + r""" + An example of magma. + + The purpose of this class is to provide a minimal template for + implementing a magma. + + EXAMPLES:: + + sage: M = Magmas().example(); M + An example of a magma: the free magma generated by ('a', 'b', 'c', 'd') + + This is the free magma generated by:: + + sage: M.magma_generators() + Family ('a', 'b', 'c', 'd') + sage: a, b, c, d = M.magma_generators() + + and with a non-associative product given by:: + + sage: a * (b * c) * (d * a * b) + '((a*(b*c))*((d*a)*b))' + sage: a * (b * c) == (a * b) * c + False + + TESTS:: + + sage: TestSuite(M).run() + """ + + def __init__(self, alphabet=('a', 'b', 'c', 'd')): + r""" + The free magma. + + INPUT: + + - ``alphabet`` -- a tuple of strings; the generators of the magma + + EXAMPLES:: + + sage: from sage.categories.examples.magmas import FreeMagma + sage: F = FreeMagma(('a', 'b', 'c')); F + An example of a magma: the free magma generated by ('a', 'b', 'c') + + TESTS:: + + sage: F == loads(dumps(F)) + True + """ + if any('(' in x or ')' in x or '*' in x for x in alphabet): + raise ValueError("alphabet must not contain characters " + "'(', ')' or '*'") + self.alphabet = alphabet + Parent.__init__(self, category=Magmas().FinitelyGenerated()) + + def _repr_(self): + r""" + EXAMPLES:: + + sage: from sage.categories.examples.magmas import FreeMagma + sage: FreeMagma(('a', 'b', 'c'))._repr_() + "An example of a magma: the free magma generated by ('a', 'b', 'c')" + """ + return ("An example of a magma: the free magma generated by %s" + % (self.alphabet,)) + + def product(self, x, y): + r""" + Return the product of ``x`` and ``y`` in the magma, as per + :meth:`Magmas.ParentMethods.product`. + + EXAMPLES:: + + sage: F = Magmas().example() + sage: F('a') * F.an_element() + '(a*(((a*b)*c)*d))' + """ + assert x in self + assert y in self + return self("(%s*%s)" % (x.value, y.value)) + + @cached_method + def magma_generators(self): + r""" + Return the generators of the magma. + + EXAMPLES:: + + sage: F = Magmas().example() + sage: F.magma_generators() + Family ('a', 'b', 'c', 'd') + """ + return Family([self(i) for i in self.alphabet]) + + def an_element(self): + r""" + Return an element of the magma. + + EXAMPLES:: + + sage: F = Magmas().example() + sage: F.an_element() + '(((a*b)*c)*d)' + """ + gens = self.magma_generators() + x = gens.first() + for y in gens.iterator_range(1): + x *= y + return x + + def _element_constructor_(self, x): + r""" + Construct an element of this magma from the data ``x``. + + INPUT: + + - ``x`` -- a string + + EXAMPLES:: + + sage: F = Magmas().example(); F + An example of a magma: the free magma generated by ('a', 'b', 'c', 'd') + sage: F._element_constructor_('a') + 'a' + sage: F._element_constructor_('(a*(b*c))') + '(a*(b*c))' + """ + return self.element_class(self, x) + + class Element(ElementWrapper): + r""" + The class for elements of the free magma. + """ + wrapped_class = str + + +Example = FreeMagma diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 4ea4edd4017..1a0c7b95fc3 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1188,6 +1188,40 @@ def incidence_graph(self,labels=False): A = self.incidence_matrix() return BipartiteGraph(A) + def is_berge_cyclic(self): + r""" + Check whether ``self`` is a Berge-Cyclic uniform hypergraph. + + A `k`-uniform Berge cycle (named after Claude Berge) of length `\ell` + is a cyclic list of distinct `k`-sets `F_1,\ldots,F_\ell`, `\ell>1`, + and distinct vertices `C = \{v_1,\ldots,v_\ell\}` such that for each + `1\le i\le \ell`, `F_i` contains `v_i` and `v_{i+1}` (where `v_{l+1} = + v_1`). + + A uniform hypergraph is Berge-cyclic if its incidence graph is cyclic. + It is called "Berge-acyclic" otherwise. + + For more information, see [Fag1983]_ and :wikipedia:`Hypergraph`. + + EXAMPLES:: + + sage: Hypergraph(5, [[1, 2, 3], [2, 3 ,4]]).is_berge_cyclic() + True + sage: Hypergraph(6, [[1, 2, 3], [3 ,4, 5]]).is_berge_cyclic() + False + + TESTS:: + + sage: Hypergraph(5, [[1, 2, 3], [2, 3]]).is_berge_cyclic() + Traceback (most recent call last): + ... + TypeError: Berge cycles are defined for uniform hypergraphs only + """ + if not self.is_uniform(): + raise TypeError("Berge cycles are defined for uniform hypergraphs only") + + return not self.incidence_graph().is_forest() + def complement(self,uniform=False): r""" Return the complement of the incidence structure. diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index f55c24f61f1..0c4db0d7bb4 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -3143,6 +3143,15 @@ def plot_crystal(self, crystal, sage: C = crystals.Tableaux(['B',3], shape=[2,1]) sage: L.plot_crystal(C, plot_labels='circles', edge_labels=True) # long time Graphics3d Object + + TESTS: + + Check that :trac:`29548` is fixed:: + + sage: LS = crystals.LSPaths(['A',2], [1,1]) + sage: L = RootSystem(['A',2]).ambient_space() + sage: L.plot_crystal(LS) + Graphics object consisting of 16 graphics primitives """ from sage.plot.arrow import arrow from sage.plot.circle import circle @@ -3189,13 +3198,13 @@ def plot_crystal(self, crystal, G += plot_options.text(elt, positions[wt], rgbcolor=label_color) for h,t,i in g.edges(): - G += arrow(positions[h.weight()], positions[t.weight()], + G += arrow(positions[self(h.weight())], positions[self(t.weight())], zorder=1, rgbcolor=plot_options.color(i), arrowsize=plot_options._arrowsize) if edge_labels: - mid = (positions[h.weight()] + positions[t.weight()]) / QQ(2) + mid = (positions[self(h.weight())] + positions[self(t.weight())]) / QQ(2) if plot_options.dimension >= 2: - diff = (positions[h.weight()] - positions[t.weight()]).normalized() + diff = (positions[self(h.weight())] - positions[self(t.weight())]).normalized() if plot_options.dimension >= 3: from copy import copy diff2 = copy(diff) diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 5c03f5d2a71..01cc7e73d5f 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -1,8 +1,7 @@ """ Monomial symmetric functions """ -from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen # 2010 Anne Schilling (addition) # 2012 Mike Zabrocki @@ -16,13 +15,15 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** +from __future__ import absolute_import from . import classical import sage.libs.symmetrica.all as symmetrica from sage.rings.integer import Integer -from sage.combinat.partition import Partition +from sage.combinat.partition import Partition, _Partitions + class SymmetricFunctionAlgebra_monomial(classical.SymmetricFunctionAlgebra_classical): def __init__(self, Sym): @@ -46,7 +47,7 @@ def __init__(self, Sym): def _dual_basis_default(self): """ - Returns the default dual basis to ``self`` when no scalar product is specified + Return the default dual basis to ``self`` when no scalar product is specified This method returns the dual basis of the monomial basis with respect to the standard scalar product, which is the @@ -104,35 +105,36 @@ def _multiply(self, left, right): sage: a^2 x^2*m[] + 2*x*m[2, 1] + 4*m[2, 2, 1, 1] + 6*m[2, 2, 2] + 2*m[3, 2, 1] + 2*m[3, 3] + 2*m[4, 1, 1] + m[4, 2] """ - #Use symmetrica to do the multiplication - #A = left.parent() + # Use symmetrica to do the multiplication + # A = left.parent() - #Hack due to symmetrica crashing when both of the - #partitions are the empty partition - #if R is ZZ or R is QQ: - # return symmetrica.mult_monomial_monomial(left, right) + # Hack due to symmetrica crashing when both of the + # partitions are the empty partition + # if R is ZZ or R is QQ: + # return symmetrica.mult_monomial_monomial(left, right) z_elt = {} - for (left_m, left_c) in six.iteritems(left._monomial_coefficients): - for (right_m, right_c) in six.iteritems(right._monomial_coefficients): + for left_m, left_c in left._monomial_coefficients.items(): + for right_m, right_c in right._monomial_coefficients.items(): - #Hack due to symmetrica crashing when both of the - #partitions are the empty partition - if left_m == [] and right_m == []: - z_elt[ left_m ] = left_c*right_c + # Hack due to symmetrica crashing when both of the + # partitions are the empty partition + if not left_m and not right_m: + z_elt[left_m] = left_c * right_c continue - d = symmetrica.mult_monomial_monomial({left_m:Integer(1)}, {right_m:Integer(1)}).monomial_coefficients() + d = symmetrica.mult_monomial_monomial({left_m: Integer(1)}, + {right_m: Integer(1)}).monomial_coefficients() for m in d: if m in z_elt: - z_elt[ m ] = z_elt[m] + left_c * right_c * d[m] + z_elt[m] += left_c * right_c * d[m] else: - z_elt[ m ] = left_c * right_c * d[m] + z_elt[m] = left_c * right_c * d[m] return z_elt def from_polynomial(self, f, check=True): """ - Returns the symmetric function in the monomial basis corresponding to the polynomial ``f``. + Return the symmetric function in the monomial basis corresponding to the polynomial ``f``. INPUT: @@ -158,7 +160,7 @@ def from_polynomial(self, f, check=True): sage: f = x[0]**2+x[1]**2+x[2]**2 sage: m.from_polynomial(f) m[2] - sage: f=x[0]^2+x[1] + sage: f = x[0]^2+x[1] sage: m.from_polynomial(f) Traceback (most recent call last): ... @@ -171,11 +173,17 @@ def from_polynomial(self, f, check=True): m[1, 1] + 2*m[2, 1] + 3*m[3] """ assert self.base_ring() == f.base_ring() - out = self.sum_of_terms((Partition(e), c) - for (e,c) in six.iteritems(f.dict()) - if tuple(sorted(e)) == tuple(reversed(e))) - if check and out.expand(f.parent().ngens(),f.parent().variable_names()) != f: - raise ValueError("%s is not a symmetric polynomial"%f) + + def is_partition(e): + if not e: + return True + return all(x >= y for x, y in zip(e[:-1], e[1:])) + out = self._from_dict({_Partitions.element_class(_Partitions, list(e)): c + for e, c in f.dict().items() + if is_partition(e)}, remove_zeros=False) + if check and f != out.expand(f.parent().ngens(), + f.parent().variable_names()): + raise ValueError("%s is not a symmetric polynomial" % f) return out def from_polynomial_exp(self, p): @@ -225,7 +233,7 @@ def from_polynomial_exp(self, p): """ assert self.base_ring() == p.parent().base_ring() return self.sum_of_terms((Partition(exp=monomial), coeff) - for (monomial, coeff) in six.iteritems(p.dict())) + for monomial, coeff in p.dict().items()) def antipode_by_coercion(self, element): r""" @@ -299,13 +307,14 @@ def expand(self, n, alphabet='x'): sage: (3*m([])).expand(0) 3 """ - condition = lambda part: len(part) > n + + def condition(part): + return len(part) > n return self._expand(condition, n, alphabet) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -import six - -register_unpickle_override('sage.combinat.sf.monomial', 'SymmetricFunctionAlgebraElement_monomial', SymmetricFunctionAlgebra_monomial.Element) +register_unpickle_override('sage.combinat.sf.monomial', 'SymmetricFunctionAlgebraElement_monomial', SymmetricFunctionAlgebra_monomial.Element) diff --git a/src/sage/env.py b/src/sage/env.py index f1d96ecda2b..18d86fe6c49 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -386,22 +386,27 @@ def cython_aliases(): 'FFLASFFPACK_CFLAGS', 'FFLASFFPACK_INCDIR', 'FFLASFFPACK_LIBDIR', + 'FFLASFFPACK_LIBEXTRA', 'FFLASFFPACK_LIBRARIES', 'GIVARO_CFLAGS', 'GIVARO_INCDIR', 'GIVARO_LIBDIR', + 'GIVARO_LIBEXTRA', 'GIVARO_LIBRARIES', 'GSL_CFLAGS', 'GSL_INCDIR', 'GSL_LIBDIR', + 'GSL_LIBEXTRA', 'GSL_LIBRARIES', 'LINBOX_CFLAGS', 'LINBOX_INCDIR', 'LINBOX_LIBDIR', + 'LINBOX_LIBEXTRA', 'LINBOX_LIBRARIES', 'SINGULAR_CFLAGS', 'SINGULAR_INCDIR', 'SINGULAR_LIBDIR', + 'SINGULAR_LIBEXTRA', 'SINGULAR_LIBRARIES'] """ import pkgconfig @@ -416,6 +421,7 @@ def cython_aliases(): # passed in CFLAGS aliases[var + "INCDIR"] = pc['include_dirs'] aliases[var + "LIBDIR"] = pc['library_dirs'] + aliases[var + "LIBEXTRA"] = list(filter(lambda s: not s.startswith(('-l','-L')), pkgconfig.libs(lib).split())) aliases[var + "LIBRARIES"] = pc['libraries'] # LinBox needs special care because it actually requires C++11 with diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 202ba3bb3d1..1570a9a6c0c 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1319,19 +1319,26 @@ def _volume_normaliz(self, measure='euclidean'): INPUT: - - ``measure`` -- (default: 'euclidean') the measure to take. 'euclidean' - correspond to ``EuclideanVolume`` in normaliz and 'induced_lattice' - correspond to ``Volume`` in normaliz. + - ``measure`` -- string. The measure to use. Allowed values are: + + * ``'euclidean'`` (default): corresponds to ``'EuclideanVolume`` in normaliz + * ``'induced_lattice'``: corresponds to ``'Volume'`` in normaliz + * ``'ambient'``: Lebesgue measure of ambient space (volume) OUTPUT: - A float value (when ``measure`` is 'euclidean') or a rational number - (when ``measure`` is 'induced_lattice'). + A float value (when ``measure`` is 'euclidean'), + a rational number (when ``measure`` is 'induced_lattice'), + a rational number or symbolic number otherwise (dependent on base ring). .. NOTE:: This function depends on Normaliz (i.e., the ``pynormaliz`` optional - package). See the Normaliz documentation for further details. + package). + + REFERENCES: + + See section 6.1.1 of [NormalizMan]_. EXAMPLES: @@ -1342,7 +1349,7 @@ def _volume_normaliz(self, measure='euclidean'): sage: s._volume_normaliz() # optional - pynormaliz 0.3333333333333333 - The other possibility is to compute the scaled volume where a unimodual + One other possibility is to compute the scaled volume where a unimodular simplex has volume 1:: sage: s._volume_normaliz(measure='induced_lattice') # optional - pynormaliz @@ -1354,6 +1361,14 @@ def _volume_normaliz(self, measure='euclidean'): sage: cube._volume_normaliz(measure='induced_lattice') # optional - pynormaliz 6 + Or one can can calculate the ambient volume, which is the above multiplied by the + volume of the unimodular simplex (or zero if not full-dimensional):: + + sage: cube._volume_normaliz(measure='ambient') # optional - pynormaliz + 1 + sage: s._volume_normaliz(measure='ambient') # optional - pynormaliz + 0 + TESTS: Check that :trac:`28872` is fixed:: @@ -1361,6 +1376,27 @@ def _volume_normaliz(self, measure='euclidean'): sage: P = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz sage: P.volume(measure='induced_lattice') # optional - pynormaliz -1056*sqrt5 + 2400 + + Some sanity checks that the ambient volume works correctly:: + + sage: (2*cube)._volume_normaliz(measure='ambient') # optional - pynormaliz + 8 + sage: (1/2*cube)._volume_normaliz(measure='ambient') # optional - pynormaliz + 1/8 + sage: s._volume_normaliz(measure='ambient') # optional - pynormaliz + 0 + + sage: P = polytopes.regular_polygon(3, backend='normaliz') # optional - pynormaliz + sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # optional - pynormaliz + True + + sage: P = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz + sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # optional - pynormaliz + True + + sage: P = Polyhedron(rays=[[1]], backend='normaliz') # optional - pynormaliz + sage: P.volume() # optional - pynormaliz + +Infinity """ cone = self._normaliz_cone assert cone @@ -1371,6 +1407,20 @@ def _volume_normaliz(self, measure='euclidean'): return self._nmz_result(cone, 'Volume') else: return self._nmz_result(cone, 'RenfVolume') + elif measure == 'ambient': + if self.dim() < self.ambient_dim(): + return self.base_ring().zero() + if not self.is_compact(): + from sage.rings.infinity import infinity + return infinity + + from sage.functions.other import factorial + volume = self._volume_normaliz('induced_lattice')/factorial(self.dim()) + + return volume + + else: + raise TypeError("the measure should be `ambient`, `euclidean`, or `induced_lattice`") def _triangulate_normaliz(self): r""" diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d8609bdfe4a..01989bbd80e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -35,7 +35,6 @@ from sage.functions.other import sqrt, floor, ceil, binomial from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.graphs.graph import Graph -from sage.graphs.digraph import DiGraph from .constructor import Polyhedron from sage.categories.sets_cat import EmptySetError @@ -725,24 +724,14 @@ def vertex_facet_graph(self, labels=True): sage: G._immutable True - """ - # We construct the edges and remove the columns that have all 1s; - # those correspond to faces, that contain all vertices (which happens - # if the polyhedron is not full-dimensional) - G = DiGraph() - if labels: - edges = [[v, f] for f in self.Hrep_generator() - if any(not(f.is_incident(v)) for v in self.Vrep_generator()) - for v in self.vertices() if f.is_incident(v)] - else: - # here we obtain this incidence information from the incidence matrix - M = self.incidence_matrix() - edges = [[i, M.ncols()+j] for i, column in enumerate(M.columns()) - if any(entry != 1 for entry in column) - for j in range(M.nrows()) if M[j, i] == 1] - G.add_edges(edges) - return G.copy(immutable=True) + Check that :trac:`29188` is fixed:: + + sage: P = polytopes.cube() + sage: P.vertex_facet_graph().is_isomorphic(P.vertex_facet_graph(False)) + True + """ + return self.combinatorial_polyhedron().vertex_facet_graph(names=labels) def plot(self, point=None, line=None, polygon=None, # None means unspecified by the user @@ -2790,6 +2779,98 @@ def center(self): vertex_sum.set_immutable() return vertex_sum / self.n_vertices() + @cached_method(do_pickle=True) + def centroid(self, engine='auto', **kwds): + r""" + Return the center of the mass of the polytope. + + The mass is taken with respect to the induced Lebesgue measure, + see :meth:`volume`. + + If the polyhedron is not compact, a ``NotImplementedError`` is + raised. + + INPUT: + + - ``engine`` -- either 'auto' (default), 'internal', + 'TOPCOM', or 'normaliz'. The 'internal' and 'TOPCOM' instruct + this package to always use its own triangulation algorithms + or TOPCOM's algorithms, respectively. By default ('auto'), + TOPCOM is used if it is available and internal routines otherwise. + + - ``**kwds`` -- keyword arguments that are passed to the + triangulation engine (see :meth:`triangulate`). + + OUTPUT: The centroid as vector. + + ALGORITHM: + + We triangulate the polytope and find the barycenter of the simplices. + We add the individual barycenters weighted by the fraction of the total + mass. + + EXAMPLES:: + + sage: P = polytopes.hypercube(2).pyramid() + sage: P.centroid() + (1/4, 0, 0) + + sage: P = polytopes.associahedron(['A',2]) + sage: P.centroid() + (2/21, 2/21) + + sage: P = polytopes.permutahedron(4, backend='normaliz') # optional - pynormaliz + sage: P.centroid() # optional - pynormaliz + (5/2, 5/2, 5/2, 5/2) + + The method is not implemented for unbounded polyhedra:: + + sage: P = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) + sage: P.centroid() + Traceback (most recent call last): + ... + NotImplementedError: the polyhedron is not compact + + The centroid of an empty polyhedron is not defined:: + + sage: Polyhedron().centroid() + Traceback (most recent call last): + ... + ZeroDivisionError: rational division by zero + + TESTS:: + + sage: Polyhedron(vertices=[[0,1]]).centroid() + (0, 1) + """ + if not self.is_compact(): + raise NotImplementedError("the polyhedron is not compact") + if self.n_vertices() == self.dim() + 1: + # The centroid of a simplex is its center. + return self.center() + + triangulation = self.triangulate(engine=engine, **kwds) + + if self.ambient_dim() == self.dim(): + pc = triangulation.point_configuration() + else: + from sage.geometry.triangulation.point_configuration import PointConfiguration + A,b = self.affine_hull_projection(as_affine_map=True, orthogonal=True, orthonormal=True, extend=True) + pc = PointConfiguration((A(v.vector()) for v in self.Vrep_generator())) + + barycenters = [sum(self.Vrepresentation(i).vector() for i in simplex)/(self.dim() + 1) for simplex in triangulation] + volumes = [pc.volume(simplex) for simplex in triangulation] + + centroid = sum(volumes[i]*barycenters[i] for i in range(len(volumes)))/sum(volumes) + if self.ambient_dim() != self.dim(): + # By the affine hull projection, the centroid has base ring ``AA``, + # we try return the centroid in a reasonable ring. + try: + return centroid.change_ring(self.base_ring().fraction_field()) + except ValueError: + pass + return centroid + @cached_method def representative_point(self): """ @@ -3124,6 +3205,40 @@ def combinatorial_polyhedron(self): from sage.geometry.polyhedron.combinatorial_polyhedron.base import CombinatorialPolyhedron return CombinatorialPolyhedron(self) + def simplicity(self): + r""" + Return the largest integer `k` such that the polytope is `k`-simple. + + A polytope `P` is `k`-simple, if every `(d-1-k)`-face + is contained in exactly `k+1` facets of `P` for `1 \leq k \leq d-1`. + Equivalently it is `k`-simple if the polar/dual polytope is `k`-simplicial. + If `self` is a simplex, it returns its dimension. + + EXAMPLES:: + + sage: polytopes.hypersimplex(4,2).simplicity() + 1 + sage: polytopes.hypersimplex(5,2).simplicity() + 2 + sage: polytopes.hypersimplex(6,2).simplicity() + 3 + sage: polytopes.simplex(3).simplicity() + 3 + sage: polytopes.simplex(1).simplicity() + 1 + + The method is not implemented for unbounded polyhedra:: + + sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) + sage: p.simplicity() + Traceback (most recent call last): + ... + NotImplementedError: this function is implemented for polytopes only + """ + if not(self.is_compact()): + raise NotImplementedError("this function is implemented for polytopes only") + return self.combinatorial_polyhedron().simplicity() + def is_simple(self): """ Test for simplicity of a polytope. @@ -3143,6 +3258,38 @@ def is_simple(self): if not self.is_compact(): return False return self.combinatorial_polyhedron().is_simple() + def simpliciality(self): + r""" + Return the largest interger `k` such that the polytope is `k`-simplicial. + + A polytope is `k`-simplicial, if every `k`-face is a simplex. + If `self` is a simplex, returns its dimension. + + EXAMPLES:: + + sage: polytopes.cyclic_polytope(10,4).simpliciality() + 3 + sage: polytopes.hypersimplex(5,2).simpliciality() + 2 + sage: polytopes.cross_polytope(4).simpliciality() + 3 + sage: polytopes.simplex(3).simpliciality() + 3 + sage: polytopes.simplex(1).simpliciality() + 1 + + The method is not implemented for unbounded polyhedra:: + + sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) + sage: p.simpliciality() + Traceback (most recent call last): + ... + NotImplementedError: this function is implemented for polytopes only + """ + if not(self.is_compact()): + raise NotImplementedError("this function is implemented for polytopes only") + return self.combinatorial_polyhedron().simpliciality() + def is_simplicial(self): """ Tests if the polytope is simplicial @@ -6244,6 +6391,14 @@ def vertex_graph(self): Return a graph in which the vertices correspond to vertices of the polyhedron, and edges to edges. + ..NOTE:: + + The graph of a polyhedron with lines has no vertices, + as the polyhedron has no vertices (`0`-faces). + + The method :meth:`Polyhedron_base:vertices` returns + the defining points in this case. + EXAMPLES:: sage: g3 = polytopes.hypercube(3).vertex_graph(); g3 @@ -6254,37 +6409,22 @@ def vertex_graph(self): Graph on 5 vertices sage: s4.is_eulerian() True - """ - from itertools import combinations - inequalities = self.inequalities() - vertices = self.vertices() - # Associated to 'v' the inequalities in contact with v - vertex_ineq_incidence = [frozenset([i for i, ineq in enumerate(inequalities) if self._is_zero(ineq.eval(v))]) - for i, v in enumerate(vertices)] + The graph of an unbounded polyhedron + is the graph of the bounded complex:: - # the dual incidence structure - ineq_vertex_incidence = [set() for _ in range(len(inequalities))] - for v, ineq_list in enumerate(vertex_ineq_incidence): - for ineq in ineq_list: - ineq_vertex_incidence[ineq].add(v) + sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]], + ....: rays =[[1,1]]) + sage: open_triangle.vertex_graph() + Graph on 2 vertices - n = len(vertices) + The graph of a polyhedron with lines has no vertices:: - pairs = [] - for i, j in combinations(range(n), 2): - common_ineq = vertex_ineq_incidence[i] & vertex_ineq_incidence[j] - if not common_ineq: # or len(common_ineq) < d-2: - continue - - if len(set.intersection(*[ineq_vertex_incidence[k] for k in common_ineq])) == 2: - pairs.append((i, j)) - - from sage.graphs.graph import Graph - g = Graph() - g.add_vertices(vertices) - g.add_edges((vertices[i], vertices[j]) for i, j in pairs) - return g + sage: line = Polyhedron(lines=[[0,1]]) + sage: line.vertex_graph() + Graph on 0 vertices + """ + return self.combinatorial_polyhedron().vertex_graph() graph = vertex_graph @@ -7142,6 +7282,9 @@ def volume(self, measure='ambient', engine='auto', **kwds): except FeatureNotPresentError: raise RuntimeError("the induced rational measure can only be computed with the optional packages `latte_int`, or `pynormaliz`") + if engine == 'auto' and measure == 'ambient' and self.backend() == 'normaliz': + engine = 'normaliz' + if measure == 'ambient': if self.dim() < self.ambient_dim(): return self.base_ring().zero() @@ -7156,7 +7299,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): elif engine == 'latte': return self._volume_latte(**kwds) elif engine == 'normaliz': - return self._volume_normaliz(measure='euclidean') + return self._volume_normaliz(measure='ambient') triangulation = self.triangulate(engine=engine, **kwds) pc = triangulation.point_configuration() @@ -8288,12 +8431,11 @@ def combinatorial_automorphism_group(self, vertex_graph_only=False): sage: quadrangle.restricted_automorphism_group() Permutation Group with generators [()] - Permutations can only exchange vertices with vertices, rays - with rays, and lines with lines:: + Permutations of the vertex graph only exchange vertices with vertices:: - sage: P = Polyhedron(vertices=[(1,0,0), (1,1,0)], rays=[(1,0,0)], lines=[(0,0,1)]) + sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)]) sage: P.combinatorial_automorphism_group(vertex_graph_only=True) - Permutation Group with generators [(A vertex at (1,0,0),A vertex at (1,1,0))] + Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))] This shows an example of two polytopes whose vertex-edge graphs are isomorphic, but their face_lattices are not isomorphic:: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 969c9f86db1..d7d27efc5f0 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1399,6 +1399,90 @@ cdef class CombinatorialPolyhedron(SageObject): deprecation(28604, "the method ridge_graph of CombinatorialPolyhedron is deprecated; use facet_graph", 3) return Graph(self.ridges(names=names), format="list_of_edges") + @cached_method + def vertex_facet_graph(self, names=True): + r""" + Return the vertex-facet graph. + + This method constructs a directed bipartite graph. + The nodes of the graph correspond to elements of the Vrepresentation + and facets. There is a directed edge from Vrepresentation to facets + for each incidence. + + If ``names`` is set to ``False``, then the vertices (of the graph) are given by + integers. + + INPUT: + + - ``names`` -- boolean (default: ``True``); if ``True`` label the vertices of the + graph by the corresponding names of the Vrepresentation resp. Hrepresentation; + if ``False`` label the vertices of the graph by integers + + EXAMPLES:: + + sage: P = polytopes.hypercube(2).pyramid() + sage: C = CombinatorialPolyhedron(P) + sage: G = C.vertex_facet_graph(); G + Digraph on 10 vertices + sage: C.Vrepresentation() + (A vertex at (0, -1, -1), + A vertex at (0, -1, 1), + A vertex at (0, 1, -1), + A vertex at (0, 1, 1), + A vertex at (1, 0, 0)) + sage: G.neighbors_out(C.Vrepresentation()[4]) + [An inequality (-1, 0, -1) x + 1 >= 0, + An inequality (-1, 0, 1) x + 1 >= 0, + An inequality (-1, -1, 0) x + 1 >= 0, + An inequality (-1, 1, 0) x + 1 >= 0] + + If ``names`` is ``True`` (the default) but the combinatorial polyhedron + has been initialized without specifying names to + ``Vrepresentation`` and ``Hrepresentation``, + then indices of the Vrepresentation and the facets will be used along + with a string 'H' or 'V':: + + sage: C = CombinatorialPolyhedron(P.incidence_matrix()) + sage: C.vertex_facet_graph().vertices() + [('H', 0), + ('H', 1), + ('H', 2), + ('H', 3), + ('H', 4), + ('V', 0), + ('V', 1), + ('V', 2), + ('V', 3), + ('V', 4)] + + If ``names`` is ``False`` then the vertices of the graph are given by integers:: + + sage: C.vertex_facet_graph(names=False).vertices() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + """ + # The face iterator will iterate through the facets in opposite order. + facet_iter = self.face_iter(self.dimension() - 1, dual=False) + n_facets = self.n_facets() + n_Vrep = self.n_Vrepresentation() + + if not names: + vertices = [i for i in range(n_facets + n_Vrep)] + edges = tuple((j, n_Vrep + n_facets - 1 - i) for i,facet in enumerate(facet_iter) for j in facet.ambient_V_indices()) + else: + facet_names = self.facet_names() + if facet_names is None: + # No names where provided at initializiation. + facet_names = [("H",i) for i in range(n_facets)] + + Vrep = self.Vrep() + if Vrep is None: + # No names where provided at initializiation. + Vrep = [("V",i) for i in range(n_Vrep)] + + vertices = Vrep + facet_names + edges = tuple((Vrep[j], facet_names[n_facets - 1 - i]) for i,facet in enumerate(facet_iter) for j in facet.ambient_V_indices()) + return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True) + def f_vector(self): r""" Compute the ``f_vector`` of the polyhedron. @@ -1739,7 +1823,7 @@ cdef class CombinatorialPolyhedron(SageObject): Return the dimension in case of a simplex. A polytope `P` is `k`-simple, if every `(d-1-k)`-face - is contained in exactly `k+1` facets of `P` for `1 <= k <= d-1`. + is contained in exactly `k+1` facets of `P` for `1 \leq k \leq d-1`. Equivalently it is `k`-simple if the polar/dual polytope is `k`-simplicial. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 777725378fa..b7131b58e65 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -70,10 +70,23 @@ import numbers from sage.rings.integer cimport smallInteger from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron -from .bit_vector_operations cimport count_atoms, bit_rep_to_coatom_rep from .polyhedron_face_lattice cimport PolyhedronFaceLattice from libc.string cimport memcpy +cdef extern from "bit_vector_operations.cc": + cdef size_t count_atoms(uint64_t *A, size_t face_length) +# Return the number of atoms/vertices in A. +# This is the number of set bits in A. +# ``face_length`` is the length of A in terms of uint64_t. + + cdef size_t bit_rep_to_coatom_rep( + uint64_t *face, uint64_t **coatoms, size_t n_coatoms, + size_t face_length, size_t *output) +# Write the coatom-representation of face in output. Return length. +# ``face_length`` is the length of ``face`` and ``coatoms[i]`` +# in terms of uint64_t. +# ``n_coatoms`` length of ``coatoms``. + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 8bd8121ee2e..4e56cb205f2 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -161,7 +161,57 @@ from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check, sig_on, sig_off from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron -from .bit_vector_operations cimport get_next_level, count_atoms, bit_rep_to_coatom_rep + +cdef extern from "bit_vector_operations.cc": + cdef size_t get_next_level( + uint64_t **faces, const size_t n_faces, uint64_t **nextfaces, + uint64_t **nextfaces2, uint64_t **visited_all, + size_t n_visited_all, size_t face_length) +# Set ``newfaces`` to be the facets of ``faces[n_faces -1]`` +# that are not contained in a face of ``visited_all``. + +# INPUT: + +# - ``maybe_newfaces`` -- quasi of type ``uint64_t[n_faces -1][face_length]``, +# needs to be ``chunksize``-Bit aligned +# - ``newfaces`` -- quasi of type ``*uint64_t[n_faces -1] +# - ``visited_all`` -- quasi of type ``*uint64_t[n_visited_all] +# - ``face_length`` -- length of the faces + +# OUTPUT: + +# - return number of ``newfaces`` +# - set ``newfaces`` to point to the new faces + +# ALGORITHM: + +# To get all facets of ``faces[n_faces-1]``, we would have to: +# - Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. +# - Add all the intersection of ``visited_all`` with the last face +# - Out of both the inclusion-maximal ones are of codimension 1, i.e. facets. + +# As we have visited all faces of ``visited_all``, we alter the algorithm +# to not revisit: +# Step 1: Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. +# Step 2: Out of thosse the inclusion-maximal ones are some of the facets. +# At least we obtain all of those, that we have not already visited. +# Maybe, we get some more. +# Step 3: Only keep those that we have not already visited. +# We obtain exactly the facets of ``faces[n_faces-1]`` that we have +# not visited yet. + + cdef size_t count_atoms(uint64_t *A, size_t face_length) +# Return the number of atoms/vertices in A. +# This is the number of set bits in A. +# ``face_length`` is the length of A in terms of uint64_t. + + cdef size_t bit_rep_to_coatom_rep( + uint64_t *face, uint64_t **coatoms, size_t n_coatoms, + size_t face_length, size_t *output) +# Write the coatom-representation of face in output. Return length. +# ``face_length`` is the length of ``face`` and ``coatoms[i]`` +# in terms of uint64_t. +# ``n_coatoms`` length of ``coatoms``. cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index d93d3ac2254..920f297a89a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -11,7 +11,7 @@ to bit-representations of vertices stored in :class:`ListOfFaces`. Moreover, :class:`ListOfFaces` calculates the dimension of a polyhedron, assuming the faces are the facets of this polyhedron. -Each face is stored over-aligned according to :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.bit_vector_operations.chunktype`. +Each face is stored over-aligned according to the ``chunktype``. .. SEEALSO:: @@ -83,9 +83,54 @@ AUTHOR: from sage.structure.element import is_Matrix from cysignals.signals cimport sig_on, sig_off -from .bit_vector_operations cimport chunksize, get_next_level, count_atoms from libc.string cimport memcpy +cdef extern from "bit_vector_operations.cc": + # Any Bit-representation is assumed to be `chunksize`-Bit aligned. + cdef const size_t chunksize + + cdef size_t get_next_level( + uint64_t **faces, const size_t n_faces, uint64_t **nextfaces, + uint64_t **nextfaces2, uint64_t **visited_all, + size_t n_visited_all, size_t face_length) +# Set ``newfaces`` to be the facets of ``faces[n_faces -1]`` +# that are not contained in a face of ``visited_all``. + +# INPUT: + +# - ``maybe_newfaces`` -- quasi of type ``uint64_t[n_faces -1][face_length]``, +# needs to be ``chunksize``-Bit aligned +# - ``newfaces`` -- quasi of type ``*uint64_t[n_faces -1] +# - ``visited_all`` -- quasi of type ``*uint64_t[n_visited_all] +# - ``face_length`` -- length of the faces + +# OUTPUT: + +# - return number of ``newfaces`` +# - set ``newfaces`` to point to the new faces + +# ALGORITHM: + +# To get all facets of ``faces[n_faces-1]``, we would have to: +# - Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. +# - Add all the intersection of ``visited_all`` with the last face +# - Out of both the inclusion-maximal ones are of codimension 1, i.e. facets. + +# As we have visited all faces of ``visited_all``, we alter the algorithm +# to not revisit: +# Step 1: Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. +# Step 2: Out of thosse the inclusion-maximal ones are some of the facets. +# At least we obtain all of those, that we have not already visited. +# Maybe, we get some more. +# Step 3: Only keep those that we have not already visited. +# We obtain exactly the facets of ``faces[n_faces-1]`` that we have +# not visited yet. + + cdef size_t count_atoms(uint64_t *A, size_t face_length) +# Return the number of atoms/vertices in A. +# This is the number of set bits in A. +# ``face_length`` is the length of A in terms of uint64_t. + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython 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 aeed515b5c6..f7af68746e5 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -68,7 +68,21 @@ from libc.string cimport memcmp, memcpy, memset from .conversions cimport Vrep_list_to_bit_rep, bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator -from .bit_vector_operations cimport intersection, bit_rep_to_coatom_rep + +cdef extern from "bit_vector_operations.cc": + cdef void intersection(uint64_t *A, uint64_t *B, uint64_t *C, + size_t face_length) +# Return ``A & ~B == 0``. +# A is not subset of B, iff there is a vertex in A, which is not in B. +# ``face_length`` is the length of A and B in terms of uint64_t. + + cdef size_t bit_rep_to_coatom_rep( + uint64_t *face, uint64_t **coatoms, size_t n_coatoms, + size_t face_length, size_t *output) +# Write the coatom-representation of face in output. Return length. +# ``face_length`` is the length of ``face`` and ``coatoms[i]`` +# in terms of uint64_t. +# ``n_coatoms`` length of ``coatoms``. cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 72b35f9bf83..7d2a1b8de20 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -563,7 +563,7 @@ def regular_polygon(self, n, exact=True, base_ring=None, backend=None): sage: octagon.n_vertices() # optional - pynormaliz 8 sage: octagon.volume() # optional - pynormaliz - 2.828427124746190? + 2*a """ n = ZZ(n) if n <= 2: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 23ed1da5068..e00570bc2cc 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -430,7 +430,7 @@ from copy import copy from sage.graphs.views import EdgesView -from .generic_graph_pyx import GenericGraph_pyx, spring_layout_fast +from .generic_graph_pyx import GenericGraph_pyx, spring_layout_fast, layout_split from .dot2tex_utils import assert_have_dot2tex from sage.misc.decorators import options @@ -5296,6 +5296,24 @@ def layout_planar(self, set_embedding=False, on_embedding=None, sage: g.layout(layout='planar', external_face=(3,1)) {0: [2, 1], 1: [0, 2], 2: [1, 1], 3: [1, 0]} + Choose the embedding: + + sage: H = graphs.LadderGraph(4) + sage: em = {0:[1,4], 4:[0,5], 1:[5,2,0], 5:[4,6,1], 2:[1,3,6], 6:[7,5,2], 3:[7,2], 7:[3,6]} + sage: p = H.layout_planar(on_embedding=em) + sage: p # random + {2: [8.121320343559642, 1], + 3: [2.1213203435596424, 6], + 7: [3.1213203435596424, 0], + 0: [5.121320343559642, 3], + 1: [3.1213203435596424, 5], + 4: [4.121320343559642, 3], + 5: [4.121320343559642, 2], + 6: [3.1213203435596424, 1], + 9: [9.698670612749268, 1], + 8: [8.698670612749268, 1], + 10: [9.698670612749268, 0]} + TESTS:: sage: G = Graph([[0, 1, 2, 3], [[0, 1], [0, 2], [0, 3]]]) @@ -5316,11 +5334,84 @@ def layout_planar(self, set_embedding=False, on_embedding=None, sage: pos2 = G.layout('planar', save_pos=True) sage: pos1 == pos2 False + + Check that the function handles disconnected graphs and small + graphs (:trac:`29522`):: + + sage: G = graphs.CycleGraph(4) + graphs.CycleGraph(5) + sage: G.layout_planar() # random + {1: [3.0, 1], + 0: [1.0, 2], + 2: [2.0, 0], + 3: [2.0, 1], + 5: [6.0, 1], + 4: [4.0, 2], + 6: [5.0, 0], + 7: [5.0, 1]} + sage: K1 = graphs.CompleteGraph(1) + sage: K1.layout_planar() + {0: [0, 0]} + sage: K2 = graphs.CompleteGraph(2) + sage: K2.layout_planar() + {0: [0, 0], 1: [0, 1]} + + Check that the embedding can be specified for disconnected + graphs (:trac:`29522`):: + + sage: H = graphs.LadderGraph(4) + graphs.CompleteGraph(3) + sage: em = {0:[1,4], 4:[0,5], 1:[5,2,0], 5:[4,6,1], 2:[1,3,6], 6:[7,5,2], 3:[7,2], 7:[3,6], 8:[10,9], 9:[8,10], 10:[8,9]} + sage: p = H.layout_planar(on_embedding=em) + + Check that an exception is raised if the input graph is not planar:: + + sage: G = graphs.PetersenGraph() + sage: G.layout(layout='planar') + Traceback (most recent call last): + ... + ValueError: Petersen graph is not a planar graph + """ from sage.graphs.graph import Graph from sage.graphs.schnyder import _triangulate, _normal_label, _realizer, _compute_coordinates G = Graph(self) + + # Trivial cases + if len(G) <= 2: + verts = G.vertex_iterator() + pos = dict() + embedding = dict() + if len(G) >= 1: + v1 = next(verts) + pos[v1] = [0,0] + embedding[v1] = [] + if len(G) == 2: + v2 = next(verts) + pos[v2] = [0,1] + embedding[v1] = [v2] + embedding[v2] = [v1] + if set_embedding: + self.set_embedding(embedding) + return pos + + if not self.is_connected(): + if external_face: + raise NotImplementedError('cannot fix the external face for a' + 'disconnected graph') + # Compute the layout component by component + pos = layout_split(G.__class__.layout_planar, + G, + set_embedding=set_embedding, + on_embedding=on_embedding, + external_face=None, + test=test, + **options) + if set_embedding: + self.set_embedding(G.get_embedding()) + return pos + + # Now the graph is connected and has at least 3 vertices + try: G._embedding = self._embedding except AttributeError: @@ -5335,6 +5426,7 @@ def layout_planar(self, set_embedding=False, on_embedding=None, G._check_embedding_validity(on_embedding,boolean=False) if not G.is_planar(on_embedding=on_embedding): raise ValueError('provided embedding is not a planar embedding for %s'%self ) + G.set_embedding(on_embedding) else: if hasattr(G,'_embedding'): if G._check_embedding_validity(): @@ -5344,7 +5436,8 @@ def layout_planar(self, set_embedding=False, on_embedding=None, else: raise ValueError('provided embedding is not a valid embedding for %s. Try putting set_embedding=True'%self) else: - G.is_planar(set_embedding=True) + if not G.is_planar(set_embedding=True): + raise ValueError('%s is not a planar graph'%self) if external_face: if not self.has_edge(external_face): diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index fdfd9694e41..0611477abd8 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -42,14 +42,16 @@ from sage.graphs.base.static_sparse_graph cimport out_degree, has_edge cdef class GenericGraph_pyx(SageObject): pass -def spring_layout_fast_split(G, **options): + +def layout_split(layout_function, G, **options): """ - Graph each component of G separately, placing them adjacent to - each other. + Graph each component of ``G`` separately with ``layout_function``, + placing them adjacent to each other. - This is done because on a disconnected graph, the spring layout - will push components further and further from each other without - bound, resulting in very tight clumps for each component. + This is done because several layout methods need the input graph to + be connected. For instance, on a disconnected graph, the spring + layout will push components further and further from each other + without bound, resulting in very tight clumps for each component. .. NOTE:: @@ -61,8 +63,8 @@ def spring_layout_fast_split(G, **options): sage: G = graphs.DodecahedralGraph() sage: for i in range(10): G.add_cycle(list(range(100*i, 100*i+3))) - sage: from sage.graphs.generic_graph_pyx import spring_layout_fast_split - sage: D = spring_layout_fast_split(G); D # random + sage: from sage.graphs.generic_graph_pyx import layout_split, spring_layout_fast + sage: D = layout_split(spring_layout_fast, G); D # random {0: [0.77..., 0.06...], ... 902: [3.13..., 0.22...]} @@ -71,12 +73,21 @@ def spring_layout_fast_split(G, **options): Robert Bradshaw """ + from copy import copy Gs = G.connected_components_subgraphs() pos = {} left = 0 buffer = 1/sqrt(len(G)) + for g in Gs: - cur_pos = spring_layout_fast(g, **options) + if options.get('on_embedding', None): + em = options['on_embedding'] + options_g = copy(options) + # Restriction of `on_embedding` to `g` + options_g['on_embedding'] = {v: em[v] for v in g} + cur_pos = layout_function(g, **options_g) + else: + cur_pos = layout_function(g, **options) xmin = min(x[0] for x in cur_pos.values()) xmax = max(x[0] for x in cur_pos.values()) if len(g) > 1: @@ -85,9 +96,40 @@ def spring_layout_fast_split(G, **options): loc[0] += left - xmin + buffer pos[v] = loc left += xmax - xmin + buffer + + + if options.get('set_embedding', None): + embedding = dict() + for g in Gs: + embedding.update(g.get_embedding()) + G.set_embedding(embedding) return pos +def spring_layout_fast_split(G, **options): + """ + Graph each component of G separately, placing them adjacent to + each other. + + In ticket :trac:`29522` the function was modified so that it can + work with any layout method and renamed ``layout_split``. + Please use :func:`layout_split` from now on. + + TESTS:: + + sage: from sage.graphs.generic_graph_pyx import spring_layout_fast_split + sage: G = Graph(4) + sage: _ = spring_layout_fast_split(G) + doctest:...: DeprecationWarning: spring_layout_fast_split is deprecated, please use layout_split instead + See https://trac.sagemath.org/29522 for details. + + """ + from sage.misc.superseded import deprecation + deprecation(29522, ('spring_layout_fast_split is deprecated, please use ' + 'layout_split instead'), stacklevel=3) + return layout_split(spring_layout_fast, G, **options) + + def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True, bint height=False, by_component = False, **options): """ Spring force model layout @@ -134,9 +176,9 @@ def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True True """ if by_component: - return spring_layout_fast_split(G, iterations=iterations, dim = dim, - vpos = vpos, rescale = rescale, height = height, - **options) + return layout_split(spring_layout_fast, G, iterations=iterations, + dim = dim, vpos = vpos, rescale = rescale, + height = height, **options) G = G.to_undirected() vlist = list(G) # this defines a consistent order diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index aa3d5e39885..a4925add972 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -251,7 +251,7 @@ def UniformRandomUniform(self, n, k, m): nverts = Integer(n) except TypeError: raise ValueError("number of vertices should be an integer") - vertices = range(nverts) + vertices = list(range(nverts)) # Construct the edge set if k < 0: @@ -262,7 +262,7 @@ def UniformRandomUniform(self, n, k, m): raise ValueError("the uniformity should be an integer") all_edges = Subsets(vertices, uniformity) try: - edges = sample(all_edges, m) + edges = [all_edges[t] for t in sample(range(len(all_edges)), m)] except OverflowError: raise OverflowError("binomial({}, {}) too large to be treated".format(n, k)) except ValueError: diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index 241288a4de7..879cd0e0e8c 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -64,12 +64,32 @@ def _triangulate(g, comb_emb): sage: new_edges = _triangulate(g, g._embedding) sage: [sorted(e) for e in new_edges] [[0, 2]] + + TESTS: + + :trac:`29522` is fixed:: + + sage: g = Graph(2) + sage: _triangulate(g, {}) + Traceback (most recent call last): + ... + NotImplementedError: _triangulate() only knows how to handle connected graphs + sage: g = Graph([(0, 1)]) + sage: _triangulate(g, {}) + Traceback (most recent call last): + ... + ValueError: a Graph with less than 3 vertices doesn't have any triangulation + sage: g = Graph(3) + sage: _triangulate(g, {}) + Traceback (most recent call last): + ... + NotImplementedError: _triangulate() only knows how to handle connected graphs """ # first make sure that the graph has at least 3 vertices, and that it is connected - if g.order() < 3: - raise ValueError("A Graph with less than 3 vertices doesn't have any triangulation.") if not g.is_connected(): - raise NotImplementedError("_triangulate() only knows how to handle connected graphs.") + raise NotImplementedError("_triangulate() only knows how to handle connected graphs") + if g.order() < 3: + raise ValueError("a Graph with less than 3 vertices doesn't have any triangulation") # At this point we know that the graph is connected, has at least 3 # vertices. This is where the real work starts. diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 434e662ca15..2c90da94514 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -337,7 +337,40 @@ def _latex_(self): gens = ', '.join([latex(x) for x in self.gens()]) return '\\left\\langle %s \\right\\rangle'%gens + def sign_representation(self, base_ring=None, side="twosided"): + r""" + Return the sign representation of ``self`` over ``base_ring``. + + WARNING: assumes ``self`` is a matrix group over a field which has embedding over real numbers. + + INPUT: + + - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` + - ``side`` -- ignored + + EXAMPLES:: + sage: G = GL(2, QQ) + sage: V = G.sign_representation() + sage: e = G.an_element() + sage: e + [1 0] + [0 1] + sage: V._default_sign(e) + 1 + sage: m2 = V.an_element() + sage: m2 + 2*B['v'] + sage: m2*e + 2*B['v'] + sage: m2*e*e + 2*B['v'] + """ + if base_ring is None: + from sage.rings.all import ZZ + base_ring = ZZ + from sage.modules.with_basis.representation import SignRepresentationMatrixGroup + return SignRepresentationMatrixGroup(self, base_ring) ################################################################### # diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 8512b011221..29b5ec917e9 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -4671,6 +4671,28 @@ def upper_central_series(self): return [self.subgroup(gap_group=group) for group in UCS] from sage.groups.generic import structure_description + def sign_representation(self, base_ring=None, side="twosided"): + r""" + Return the sign representation of ``self`` over ``base_ring``. + + INPUT: + + - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` + - ``side`` -- ignored + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.sign_representation() + Sign representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + if base_ring is None: + from sage.rings.all import ZZ + base_ring = ZZ + from sage.modules.with_basis.representation import SignRepresentationPermgroup + return SignRepresentationPermgroup(self, base_ring) + class PermutationGroup_subgroup(PermutationGroup_generic): diff --git a/src/sage/libs/linbox/fflas.pxd b/src/sage/libs/linbox/fflas.pxd index b685af1f27d..fe475eb4a7c 100644 --- a/src/sage/libs/linbox/fflas.pxd +++ b/src/sage/libs/linbox/fflas.pxd @@ -1,6 +1,7 @@ # distutils: extra_compile_args = FFLASFFPACK_CFLAGS # distutils: libraries = FFLASFFPACK_LIBRARIES # distutils: library_dirs = FFLASFFPACK_LIBDIR +# distutils: extra_link_args = FFLASFFPACK_LIBEXTRA # distutils: language = c++ from .givaro cimport Modular_double, Modular_float, Dense, Sparse diff --git a/src/sage/libs/linbox/linbox.pxd b/src/sage/libs/linbox/linbox.pxd index fb0e0dcf9f9..8c6ba898927 100644 --- a/src/sage/libs/linbox/linbox.pxd +++ b/src/sage/libs/linbox/linbox.pxd @@ -1,6 +1,7 @@ # distutils: extra_compile_args = LINBOX_CFLAGS # distutils: libraries = LINBOX_LIBRARIES # distutils: library_dirs = LINBOX_LIBDIR +# distutils: extra_link_args = LINBOX_LIBEXTRA # distutils: language = c++ from libc.stdint cimport uint32_t, uint64_t diff --git a/src/sage/libs/linbox/linbox_flint_interface.pxd b/src/sage/libs/linbox/linbox_flint_interface.pxd index 6491cf52f8c..3cee66657f0 100644 --- a/src/sage/libs/linbox/linbox_flint_interface.pxd +++ b/src/sage/libs/linbox/linbox_flint_interface.pxd @@ -1,5 +1,6 @@ # distutils: libraries = LINBOX_LIBRARIES # distutils: library_dirs = LINBOX_LIBDIR +# distutils: extra_link_args = LINBOX_LIBEXTRA from sage.libs.flint.types cimport fmpz_t, fmpz_mat_t, fmpz_poly_t diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 607c0cb12ae..545c7f52942 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -334,6 +334,7 @@ cdef extern from "singular/Singular/libsingular.h": bint *pairtest void *R int *S_2_R + bint noTailReduction ctypedef struct data_union: ring *uring diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 8488fa8a387..a14df93ad42 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -93,6 +93,12 @@ cdef class GroebnerStrategy(SageObject): ... NotImplementedError: Only coefficient fields are implemented so far. + Check that :trac:`27508` is fixed:: + + sage: R2. = PolynomialRing(QQ, 2, order="lex") + sage: I2 = R2.ideal(["x^2 - x", "y^2 - y"]) + sage: R2("x^2 + y").mod(I2), R2("x + y^2").mod(I2) + (x + y, x + y) """ if not isinstance(L, MPolynomialIdeal): raise TypeError("First parameter must be a multivariate polynomial ideal.") @@ -127,6 +133,7 @@ cdef class GroebnerStrategy(SageObject): self._strat.sl = -1 #- init local data struct initS(i, NULL, self._strat) + self._strat.noTailReduction = False cdef int j cdef bint base_ring_is_field = R.base_ring().is_field() diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index b631876de13..a507ab01419 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -1762,51 +1762,6 @@ def exterior_power(self, p): self._exterior_powers[p] = MultivectorFreeModule(self, p) return self._exterior_powers[p] - def alternating_form(self, degree, name=None, latex_name=None): - r""" - Construct an alternating form on the free vector field module - ``self``. - - An alternating form on ``self`` is actually a differential form - along the differentiable manifold `U` over which ``self`` is - defined. - - INPUT: - - - ``degree`` -- the degree of the alternating form - (i.e. its tensor rank) - - ``name`` -- (string; optional) name given to the alternating - form - - ``latex_name`` -- (string; optional) LaTeX symbol to denote - the alternating form; if none is provided, the LaTeX symbol is - set to ``name`` - - OUTPUT: - - - instance of - :class:`~sage.manifolds.differentiable.diff_form.DiffFormParal` - - EXAMPLES:: - - sage: M = Manifold(2, 'M') - sage: X. = M.chart() # makes M parallelizable - sage: XM = M.vector_field_module() - sage: XM.alternating_form(2, name='a') - 2-form a on the 2-dimensional differentiable manifold M - sage: XM.alternating_form(1, name='a') - 1-form a on the 2-dimensional differentiable manifold M - - .. SEEALSO:: - - :class:`~sage.manifolds.differentiable.diff_form.DiffFormParal` - for more examples and documentation. - - """ - if degree == 0: - return self._domain.scalar_field(name=name, latex_name=latex_name) - return self.dual_exterior_power(degree).element_class(self, - degree, name=name, latex_name=latex_name) - def dual_exterior_power(self, p): r""" Return the `p`-th exterior power of the dual of ``self``. diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 1a0c8a1e2aa..3057de93f6c 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -4,6 +4,7 @@ AUTHORS: - Travis Scrimshaw (2015-11-21): Initial version +- Siddharth Singh (2020-03-21): Signed Representation """ #################################################################################### @@ -549,3 +550,230 @@ def _acted_upon_(self, scalar, self_on_left=False): _rmul_ = _lmul_ = _acted_upon_ + +class SignRepresentation_abstract(Representation_abstract): + """ + Generic implementation of a sign representation. + + The sign representation of a semigroup `S` over a commutative ring + `R` is the `1`-dimensional `R`-module on which every element of `S` + acts by 1 if order of element is even (including 0) or -1 if order of element if odd. + + This is simultaneously a left and right representation. + + INPUT: + + - ``permgroup`` -- a permgroup + - ``base_ring`` -- the base ring for the representation + - ``sign_function`` -- a function which returns 1 or -1 depending on the elements sign + + REFERENCES: + + - :wikipedia:`Representation_theory_of_the_symmetric_group` + """ + + def __init__(self, group, base_ring, sign_function=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.PGL(2, 3) + sage: V = G.sign_representation() + sage: TestSuite(V).run() + """ + self.sign_function = sign_function + if sign_function is None: + try: + self.sign_function = self._default_sign + except AttributeError: + raise TypeError("a sign function must be given") + + cat = Modules(base_ring).WithBasis().FiniteDimensional() + + Representation_abstract.__init__(self, group, base_ring, ["v"], category=cat) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.sign_representation() + Sign representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + return "Sign representation of {} over {}".format( + self._semigroup, self.base_ring() + ) + + def side(self): + """ + Return that ``self`` is a two-sided representation. + + OUTPUT: + + - the string ``"twosided"`` + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.sign_representation() + sage: R.side() + 'twosided' + """ + return "twosided" + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: G = PermutationGroup(gens=[(1,2,3), (1,2)]) + sage: S = G.sign_representation() + sage: x = S.an_element(); x + 2*B['v'] + sage: s,c = G.gens(); c + (1,2,3) + sage: s*x + -2*B['v'] + sage: s*x*s + 2*B['v'] + sage: s*x*s*s*c + -2*B['v'] + sage: A = G.algebra(ZZ) + sage: s,c = A.algebra_generators() + sage: c + (1,2,3) + sage: s + (1,2) + sage: c*x + 2*B['v'] + sage: c*c*x + 2*B['v'] + sage: c*x*s + -2*B['v'] + sage: c*x*s*s + 2*B['v'] + sage: (c+s)*x + 0 + sage: (c-s)*x + 4*B['v'] + """ + if isinstance(scalar, Element): + P = self.parent() + if not self: + return self + if scalar.parent() is P._semigroup: + return self if P.sign_function(scalar) > 0 else -self + + if scalar.parent() is P._semigroup_algebra: + sum_scalar_coeff = 0 + for ms, cs in scalar: + sum_scalar_coeff += P.sign_function(ms) * cs + return sum_scalar_coeff * self + + return CombinatorialFreeModule.Element._acted_upon_( + self, scalar, self_on_left + ) + + _rmul_ = _lmul_ = _acted_upon_ + + +class SignRepresentationPermgroup(SignRepresentation_abstract): + """ + The sign representation for a permutation group. + + EXAMPLES:: + + sage: G = groups.permutation.PGL(2, 3) + sage: V = G.sign_representation() + sage: TestSuite(V).run() + """ + + def _default_sign(self, elem): + """ + Return the sign of the element + + INPUT: + + - ``elem`` -- the element of the group + + EXAMPLES:: + + sage: G = groups.permutation.PGL(2, 3) + sage: V = G.sign_representation() + sage: elem = G.an_element() + sage: elem + (1,2,4,3) + sage: V._default_sign(elem) + -1 + """ + + return elem.sign() + + +class SignRepresentationMatrixGroup(SignRepresentation_abstract): + """ + The sign representation for a matrix group. + + EXAMPLES:: + + sage: G = groups.permutation.PGL(2, 3) + sage: V = G.sign_representation() + sage: TestSuite(V).run() + """ + + def _default_sign(self, elem): + """ + Return the sign of the element + + INPUT: + + - ``elem`` -- the element of the group + + EXAMPLES:: + + sage: G = GL(2, QQ) + sage: V = G.sign_representation() + sage: m = G.an_element() + sage: m + [1 0] + [0 1] + sage: V._default_sign(m) + 1 + """ + return 1 if elem.matrix().det() > 0 else -1 + + +class SignRepresentationCoxeterGroup(SignRepresentation_abstract): + """ + The sign representation for a Coxeter group. + + EXAMPLES:: + + sage: G = WeylGroup(["A", 1, 1]) + sage: V = G.sign_representation() + sage: TestSuite(V).run() + """ + + def _default_sign(self, elem): + """ + Return the sign of the element + + INPUT: + + - ``elem`` -- the element of the group + + EXAMPLES:: + + sage: G = WeylGroup(["A", 1, 1]) + sage: elem = G.an_element() + sage: V = G.sign_representation() + sage: V._default_sign(elem) + 1 + """ + return -1 if elem.length() % 2 == 1 else 1 diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index 212932203f6..5e9e91da179 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -63,6 +63,7 @@ def is_LaurentSeriesRing(x): """ return isinstance(x, LaurentSeriesRing) + class LaurentSeriesRing(UniqueRepresentation, CommutativeRing): r""" Univariate Laurent Series Ring. @@ -85,12 +86,11 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing): sage: Frac(GF(5)['y']) Fraction Field of Univariate Polynomial Ring in y over Finite Field of size 5 - Here the fraction field is not just the Laurent series ring, so you - can't use the ``Frac`` notation to make the Laurent - series ring:: + When the base ring is a domain, the fraction field is the + Laurent series ring over the fraction field of the base ring:: sage: Frac(ZZ[['t']]) - Fraction Field of Power Series Ring in t over Integer Ring + Laurent Series Ring in t over Rational Field Laurent series rings are determined by their variable and the base ring, and are globally unique:: @@ -365,7 +365,7 @@ def _repr_(self): sage: LaurentSeriesRing(ZZ,'t',sparse=True) Sparse Laurent Series Ring in t over Integer Ring """ - s = "Laurent Series Ring in %s over %s"%(self.variable_name(), self.base_ring()) + s = "Laurent Series Ring in %s over %s" % (self.variable_name(), self.base_ring()) if self.is_sparse(): s = 'Sparse ' + s return s @@ -493,7 +493,7 @@ def _element_constructor_(self, x, n=0, prec=infinity): return (self(self.polynomial_ring()(x)) << n).add_bigoh(prec) elif (is_FractionFieldElement(x) and (x.base_ring() is self.base_ring() or x.base_ring() == self.base_ring()) - and (is_Polynomial(x.numerator()) or is_MPolynomial(x.numerator())) ): + and (is_Polynomial(x.numerator()) or is_MPolynomial(x.numerator()))): x = self(x.numerator()) / self(x.denominator()) return (x << n).add_bigoh(prec) return self.element_class(self, x, n).add_bigoh(prec) @@ -615,16 +615,16 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): sage: R._is_valid_homomorphism_(R, [2*x]) False """ - ## NOTE: There are no ring homomorphisms from the ring of - ## all formal power series to most rings, e.g, the p-adic - ## field, since you can always (mathematically!) construct - ## some power series that doesn't converge. - ## NOTE: The above claim is wrong when the base ring is Z. - ## See trac 28486. + # NOTE: There are no ring homomorphisms from the ring of + # all formal power series to most rings, e.g, the p-adic + # field, since you can always (mathematically!) construct + # some power series that does not converge. + # NOTE: The above claim is wrong when the base ring is Z. + # See trac 28486. if base_map is None and not codomain.has_coerce_map_from(self.base_ring()): return False - ## Note that 0 is not a *ring* homomorphism, and you cannot map to a power series ring + # Note that 0 is not a *ring* homomorphism, and you cannot map to a power series ring if is_LaurentSeriesRing(codomain): return im_gens[0].valuation() > 0 and im_gens[0].is_unit() return False @@ -698,7 +698,7 @@ def gen(self, n=0): """ if n != 0: raise IndexError("generator {} not defined".format(n)) - return self.element_class(self, [0,1]) + return self.element_class(self, [0, 1]) def uniformizer(self): """ diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index e12a7540b4c..3d9e268f96b 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -35,7 +35,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2018 Julian Rüth +# Copyright (C) 2013-2020 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -285,6 +285,26 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): sage: GaussianIntegers().valuation(GaussianIntegers().ideal(2)) # indirect doctest 2-adic valuation + TESTS: + + Verify that :trac:`28976` has been resolved:: + + sage: R. = QQ[] + sage: K. = NumberField(x^6 - 18*x^4 - 24*x^3 + 27*x^2 + 36*x - 6) + sage: I = K.fractional_ideal((2, -7/44*a^5 + 19/44*a^4 + 87/44*a^3 - 87/44*a^2 - 5/2*a + 39/22)) + sage: I.norm() + 2 + sage: I in K.primes_above(2) + True + sage: K.valuation(I) + [ 2-adic valuation, v(x + 1) = 1/2 ]-adic valuation + + :: + + sage: K. = NumberField([x^2 - 2, x^2 + x + 1]) + sage: K.valuation(2) + 2-adic valuation + """ K, L, G = self._normalize_number_field_data(R) @@ -297,14 +317,33 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): if len(F) != 1: raise ValueError("%r does not lie over a single prime of %r"%(I, K)) vK = K.valuation(F[0][0]) - candidates = vK.mac_lane_approximants(G, require_incomparability=True) + approximants = vK.mac_lane_approximants(G, require_incomparability=True) - candidates_for_I = [c for c in candidates if all(c(g.polynomial()) > 0 for g in I.gens())] - assert(len(candidates_for_I) > 0) # This should not be possible, unless I contains a unit - if len(candidates_for_I) > 1: - raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) - else: - return (R, candidates_for_I[0]), {'approximants': candidates} + candidates = approximants[:] + + # The correct approximant has v(g) > 0 for all g in the ideal. + # Unfortunately, the generators of I, even though defined over K have + # their polynomial() defined over the rationals so we need to turn them + # into polynomials over K[x] explicitly. + from sage.rings.all import PolynomialRing + gens = I.gens() + gens = [PolynomialRing(K, 'x')(list(g.vector())) for g in gens] + + # Refine candidates until we can detect which valuation corresponds to the ideal I + while True: + assert any(candidates), "the defining polynomial of the extension factored but we still could not figure out which valuation corresponds to the given ideal" + + match = [i for (i, v) in enumerate(candidates) if v and all(v(g) > 0 for g in gens)] + + if len(match) > 1: + raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) + if len(match) == 1: + return (R, approximants[match[0]]), {'approximants': approximants} + + # We refine candidates which increases v(g) for all g in I; + # however, we cannot augment the valuations which are already at + # v(G) = +∞ which we ignore by setting them to None. + candidates = [v.mac_lane_step(G)[0] if v and v.is_discrete_valuation() else None for v in candidates] def _normalize_number_field_data(self, R): r""" diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 962205f4de7..cc587596dd3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2888,38 +2888,70 @@ def hilbert_numerator(self, grading = None, algorithm = 'sage'): raise ValueError("'algorithm' must be one of 'sage' or 'singular'") @require_field - def _normal_basis_libsingular(self): + def _normal_basis_libsingular(self, degree): r""" - Returns the normal basis for a given groebner basis. It will use - the Groebner Basis as computed by + Return the normal basis for a given Groebner basis. + + This will use the Groebner basis as computed by ``MPolynomialIdeal._groebner_basis_libsingular()``. + INPUT: + + - ``degree`` -- ``None`` or integer + + OUTPUT: + + If ``degree`` is an integer, only the monomials of the given degree in + the normal basis. + EXAMPLES:: sage: R. = PolynomialRing(QQ) sage: I = R.ideal(x^2-2*x*z+5, x*y^2+y*z+1, 3*y^2-8*x*z) sage: I.normal_basis() #indirect doctest [z^2, y*z, x*z, z, x*y, y, x, 1] + sage: J = R.ideal(x^2-2*x*z+5) + sage: J.normal_basis(3) # indirect doctest + [z^3, y*z^2, x*z^2, y^2*z, x*y*z, y^3, x*y^2] + + TESTS: + + Check that zero is not included in trivial results:: + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal(x^2-2*x*z+5, x*y^2+y*z+1, 3*y^2-8*x*z) + sage: I._normal_basis_libsingular(5) + [] """ from sage.rings.polynomial.multi_polynomial_ideal_libsingular import kbase_libsingular from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence gb = self._groebner_basis_libsingular() - - return PolynomialSequence(self.ring(), kbase_libsingular(self.ring().ideal(gb)), immutable=True) + res = kbase_libsingular(self.ring().ideal(gb), degree) + if len(res) == 1 and res[0].is_zero(): + res = [] + return PolynomialSequence(self.ring(), res, immutable=True) @require_field @handle_AA_and_QQbar - def normal_basis(self, algorithm='libsingular', singular=singular_default): + def normal_basis(self, degree=None, algorithm='libsingular', + singular=singular_default): """ - Returns a vector space basis (consisting of monomials) of the - quotient ring by the ideal, resp. of a free module by the module, - in case it is finite dimensional and if the input is a standard - basis with respect to the ring ordering. + Return a vector space basis of the quotient ring of this ideal. INPUT: - ``algorithm`` - defaults to use libsingular, if it is anything - else we will use the ``kbase()`` command + - ``degree`` -- integer (default: ``None``) + + - ``algorithm`` -- string (default: ``"libsingular"``); if not the + default, this will use the ``kbase()`` command from Singular + + - ``singular`` -- the singular interpreter to use when ``algorithm`` is + not ``"libsingular"`` (default: the default instance) + + OUTPUT: + + Monomials in the basis. If ``degree`` is given, only the monomials of + the given degree are returned. EXAMPLES:: @@ -2930,6 +2962,18 @@ def normal_basis(self, algorithm='libsingular', singular=singular_default): sage: I.normal_basis(algorithm='singular') [y*z^2, z^2, y*z, z, x*y, y, x, 1] + The result can be restricted to monomials of a chosen degree, which is + particularly useful when the quotient ring is not finite-dimensional as + a vector space. :: + + sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) + sage: J.dimension() + 1 + sage: [J.normal_basis(d) for d in (0..3)] + [[1], [z, y, x], [z^2, y*z, x*z, x*y], [z^3, y*z^2, x*z^2, x*y*z]] + sage: [J.normal_basis(d, algorithm='singular') for d in (0..3)] + [[1], [z, y, x], [z^2, y*z, x*z, x*y], [z^3, y*z^2, x*z^2, x*y*z]] + TESTS: Check that this method works over QQbar (:trac:`25351`):: @@ -2938,15 +2982,35 @@ def normal_basis(self, algorithm='libsingular', singular=singular_default): sage: I = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5, x*z-1) sage: I.normal_basis() [y*z^2, z^2, y*z, z, x*y, y, x, 1] + sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) + sage: [J.normal_basis(d) for d in (0..3)] + [[1], [z, y, x], [z^2, y*z, x*z, x*y], [z^3, y*z^2, x*z^2, x*y*z]] + + Check the deprecation:: + + sage: R. = PolynomialRing(QQ) + sage: _ = R.ideal(x^2+y^2, x*y+2*y).normal_basis('singular') + doctest:...: DeprecationWarning: "algorithm" should be used as keyword argument + See https://trac.sagemath.org/29543 for details. """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence + if isinstance(degree, str): + from sage.misc.superseded import deprecation + deprecation(29543, + '"algorithm" should be used as keyword argument') + algorithm = degree + degree = None if algorithm == 'libsingular': - return self._normal_basis_libsingular() + return self._normal_basis_libsingular(degree) else: gb = self.groebner_basis() R = self.ring() - return PolynomialSequence(R, [R(f) for f in singular.kbase(R.ideal(gb))], immutable=True) + if degree is None: + res = singular.kbase(R.ideal(gb)) + else: + res = singular.kbase(R.ideal(gb), int(degree)) + return PolynomialSequence(R, [R(f) for f in res], immutable=True) class MPolynomialIdeal_macaulay2_repr: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx index c36c9c2fa4c..6f884ea2c12 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx @@ -146,7 +146,7 @@ cdef ideal *sage_ideal_to_singular_ideal(I) except NULL: raise TypeError("All generators must be of type MPolynomial_libsingular.") return i -def kbase_libsingular(I): +def kbase_libsingular(I, degree=None): """ SINGULAR's ``kbase()`` algorithm. @@ -154,6 +154,9 @@ def kbase_libsingular(I): - ``I`` -- a groebner basis of an ideal + - ``degree`` -- integer (default: ``None``); if not ``None``, return + only the monomials of the given degree + OUTPUT: Computes a vector space basis (consisting of monomials) of the quotient @@ -162,12 +165,19 @@ def kbase_libsingular(I): the ring ordering. If the input is not a standard basis, the leading terms of the input are used and the result may have no meaning. + With two arguments: computes the part of a vector space basis of the + respective quotient with degree of the monomials equal to the second + argument. Here, the quotient does not need to be finite dimensional. + EXAMPLES:: sage: R. = PolynomialRing(QQ, order='lex') sage: I = R.ideal(x^2-2*y^2, x*y-3) - sage: I.normal_basis() + sage: I.normal_basis() # indirect doctest [y^3, y^2, y, 1] + sage: J = R.ideal(x^2-2*y^2) + sage: [J.normal_basis(d) for d in (0..4)] # indirect doctest + [[1], [y, x], [y^2, x*y], [y^3, x*y^2], [y^4, x*y^3]] """ global singular_options @@ -179,8 +189,10 @@ def kbase_libsingular(I): cdef ideal *result singular_options = singular_options | Sy_bit(OPT_REDSB) + degree = -1 if degree is None else int(degree) + sig_on() - result = scKBase(-1, i, q) + result = scKBase(degree, i, q) sig_off() id_Delete(&i, r) diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 5632233bcd9..d42578a4fe9 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -827,7 +827,7 @@ def construction(self): True """ from sage.categories.pushout import CompletionFunctor - return CompletionFunctor(self._names[0], self.default_prec()), self._poly_ring() + return CompletionFunctor(self._names[0], self.default_prec()), self._poly_ring() def _coerce_impl(self, x): """ @@ -1247,7 +1247,25 @@ def laurent_series_ring(self): return self.__laurent_series_ring class PowerSeriesRing_domain(PowerSeriesRing_generic, ring.IntegralDomain): - pass + def fraction_field(self): + """ + Return the fraction field of this power series ring, which is + defined since this is over a domain. + + This fraction field is just the Laurent series ring over the + fraction field of the base ring. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(ZZ) + sage: R.fraction_field() + Laurent Series Ring in t over Rational Field + sage: Frac(R) + Laurent Series Ring in t over Rational Field + """ + laurent = self.laurent_series_ring() + return laurent.change_ring(self.base_ring().fraction_field()) + class PowerSeriesRing_over_field(PowerSeriesRing_domain): _default_category = CompleteDiscreteValuationRings() diff --git a/src/sage/rings/tate_algebra_element.pxd b/src/sage/rings/tate_algebra_element.pxd index 23d31f02bc4..20aff3a7fc1 100644 --- a/src/sage/rings/tate_algebra_element.pxd +++ b/src/sage/rings/tate_algebra_element.pxd @@ -14,18 +14,18 @@ cdef class TateAlgebraTerm(MonoidElement): cdef ETuple _exponent cpdef _mul_(self, other) + cdef TateAlgebraTerm _floordiv_c(self, TateAlgebraTerm other) cpdef _floordiv_(self, other) cdef TateAlgebraTerm _new_c(self) cdef long _valuation_c(self) - cdef long _cmp_c(self, TateAlgebraTerm other) + cdef long _cmp_c(self, TateAlgebraTerm other) except? 300 cdef Element _call_c(self, list arg) cpdef TateAlgebraTerm monomial(self) cpdef TateAlgebraTerm monic(self) cdef TateAlgebraTerm _gcd_c(self, TateAlgebraTerm other) cdef TateAlgebraTerm _lcm_c(self, TateAlgebraTerm other) cdef bint _divides_c(self, TateAlgebraTerm other, bint integral) - cdef TateAlgebraTerm _floordiv_c(self, TateAlgebraTerm other) cdef class TateAlgebraElement(CommutativeAlgebraElement): diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 64ba29c77f6..6a293fff3d3 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -348,7 +348,7 @@ cdef class TateAlgebraTerm(MonoidElement): # """ # raise NotImplementedError("fraction fields of Tate algebras are not implemented; try inverse_of_unit()") - cdef long _cmp_c(self, TateAlgebraTerm other): + cdef long _cmp_c(self, TateAlgebraTerm other) except? 300: r""" Compare the Tate algebra term with ``other``. @@ -937,7 +937,7 @@ cdef class TateAlgebraTerm(MonoidElement): """ return self._divides_c(other, integral) - + cdef bint _divides_c(self, TateAlgebraTerm other, bint integral): r""" Return ``True`` if this term divides ``other``. @@ -1002,6 +1002,7 @@ cdef class TateAlgebraTerm(MonoidElement): raise ValueError("the division is not exact") return (self)._floordiv_c(other) + cdef TateAlgebraTerm _floordiv_c(self, TateAlgebraTerm other): r""" Return the result of the exact division of this term by ``other``. @@ -1979,6 +1980,11 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): ...0000000001*x^3 + ...0000000001*x + ...00000000010*x^2 sage: f << 2 # indirect doctest ...000000000100*x^3 + ...000000000100*x + ...0000000001000*x^2 + sage: Ao = A.integer_ring() + sage: g = Ao(f).add_bigoh(5); g + ...00001*x^3 + ...00001*x + ...00010*x^2 + O(2^5 * ) + sage: g << 2 + ...0000100*x^3 + ...0000100*x + ...0001000*x^2 + O(2^7 * ) """ cdef dict coeffs = { } @@ -1997,7 +2003,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): for (e,c) in self._poly.__repn.items(): minval = ZZ(e.dotprod(parent._log_radii)).ceil() coeffs[e] = field(base(c) >> (minval-n)) << minval - ans._prec = max(ZZ(0), self._prec - n) + ans._prec = max(ZZ(0), self._prec + n) ans._poly = PolyDict(coeffs, None) return ans @@ -2227,6 +2233,74 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): self._normalize() return dict(self._poly.__repn) + def coefficient(self, exponent): + r""" + Return the coefficient corresponding to the given exponent + + INPUT: + + - ``exponent`` -- a tuple of integers + + EXAMPLES:: + + sage: R = Zp(2, prec=10, print_mode='terse') + sage: A. = TateAlgebra(R) + sage: f = 2*x^2 + 53*x*y + y^3 + + sage: f.coefficient((2,0)) # coeff in x^2 + 2 + O(2^11) + sage: f.coefficient((1,1)) # coeff in x*y + 53 + O(2^10) + sage: f.coefficient((3,0)) # coeff in x^3 + 0 + + sage: g = f.add_bigoh(5) + sage: g.coefficient((2,0)) # coeff in x^2 + 2 + O(2^5) + sage: g.coefficient((1,1)) # coeff in x*y + 21 + O(2^5) + sage: g.coefficient((3,0)) # coeff in x^3 + O(2^5) + """ + if not self._is_normalized: + self._normalize() + try: + e = ETuple(exponent) + except TypeError: + raise IndexError("%s is not a correct exponent" % exponent) + if len(e) != self.parent().ngens(): + raise IndexError("lengths do not match") + if e in self._poly.__repn: + return self._poly.__repn[e] + else: + return self.base_ring()(0, self.precision_absolute()) + + def __getitem__(self, exponent): + r""" + Return the coefficient corresponding to the given exponent + + INPUT: + + - ``exponent`` -- a tuple of integers + + TESTS:: + + sage: R = Zp(2, prec=10, print_mode='terse') + sage: A. = TateAlgebra(R) + sage: f = 2*x^2 + 53*x*y + y^3 + + sage: f['hello'] + Traceback (most recent call last): + ... + IndexError: hello is not a correct exponent + + sage: f[1,2,3] + Traceback (most recent call last): + ... + IndexError: lengths do not match + """ + return self.coefficient(exponent) + def coefficients(self): r""" Return the list of coefficients of this series. diff --git a/src/sage/rings/tate_algebra_ideal.pxd b/src/sage/rings/tate_algebra_ideal.pxd index 2bc81ac6514..e5581e06b8c 100644 --- a/src/sage/rings/tate_algebra_ideal.pxd +++ b/src/sage/rings/tate_algebra_ideal.pxd @@ -1,4 +1,6 @@ +from sage.rings.tate_algebra_element cimport TateAlgebraTerm from sage.rings.tate_algebra_element cimport TateAlgebraElement -cdef _groebner_basis_buchberger(I, prec, bint integral_first) - +cdef Jpair(p1, p2) +cdef TateAlgebraElement regular_reduce(sgb, TateAlgebraTerm s, TateAlgebraElement v, stopval) +cdef TateAlgebraElement reduce(gb, TateAlgebraElement v, stopval) diff --git a/src/sage/rings/tate_algebra_ideal.pyx b/src/sage/rings/tate_algebra_ideal.pyx index e4f7aef34be..8869c99609d 100644 --- a/src/sage/rings/tate_algebra_ideal.pyx +++ b/src/sage/rings/tate_algebra_ideal.pyx @@ -7,8 +7,9 @@ algorithm in this context. AUTHORS: -- Xavier Caruso, Thibaut Verron (2018-09) +- Xavier Caruso, Thibaut Verron (2018-09): Buchberger algorithm +- Xavier Caruso, Thibaut Verron (2019-04): F5-type algorithms (PoTe and VaPoTe) """ # *************************************************************************** @@ -28,198 +29,73 @@ from sage.misc.cachefunc import cached_method from sage.structure.richcmp import op_EQ, op_NE, op_LT, op_GT, op_LE, op_GE from sage.structure.element cimport Element +from sage.rings.polynomial.polydict cimport PolyDict + from sage.rings.tate_algebra_element cimport TateAlgebraTerm from sage.rings.tate_algebra_element cimport TateAlgebraElement from heapq import heappush, heappop +from cysignals.signals cimport sig_check -cdef _groebner_basis_buchberger(I, prec, bint integral): +class TateAlgebraIdeal(Ideal_generic): r""" - Compute a Groebner basis of the Tate algebra ideal I using Buchberger's algorithm - - INPUT: - - - ``I`` - an ideal in a Tate series algebra - - - ``prec`` - the related precision at which the initial generators - are truncated - - - ``integral`` -- a boolean; if ``True``, first compute a - Grobner basis of the ideal generated by the same generators over - the ring over the ring of integers - - NOTE:: - - This function is not meant to be called directly, but through the - ``groebner_basis`` method of Tate algebra ideals. + Initialize a class for ideals in a Tate series algebra EXAMPLES:: - sage: R = Zp(3, prec=10, print_mode="digits"); + sage: R = Zp(3, prec=10, print_mode="digits") sage: A. = TateAlgebra(R) sage: f = 3*x^2 + 5*x*y^2 sage: g = 5*x^2*y + 3 sage: I = A.ideal([f,g]); I Ideal (...0000000012*x*y^2 + ...00000000010*x^2, ...0000000012*x^2*y + ...00000000010) of Tate Algebra in x (val >= 0), y (val >= 0) over 3-adic Field with capped relative precision 10 - sage: I.groebner_basis() # indirect doctest - [...000000001*x^3 + ...222222222*y + O(3^9 * ), - ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), - ...000000001*y^2 + ...210121020*x + O(3^9 * )] """ - cdef list gb, rgb, indices, ts, S = [ ] - cdef int i, j, l - cdef TateAlgebraTerm ti, tj, t - cdef TateAlgebraElement f, g, r, s - cdef bint reduce = True - gb = [ ] - l = 0 - for f in I.gens(): - if f == 0: - continue - g = f.add_bigoh(f.valuation() + prec) - if g == 0: - continue - gb.append(g) - l += 1 - indices = list(range(l)) - - # We minimize the family of generators - rgb = gb[:] - i = 0 - while i < len(rgb): - ti = (rgb[i])._terms_c()[0] - for j in range(len(rgb)): - tj = (rgb[j])._terms_c()[0] - if j != i and tj._divides_c(ti, integral): - del rgb[i] - del indices[i] - break - else: - i += 1 - - # We compute the initial S-polynomials - for i in range(l): - ti = (gb[i])._terms_c()[0] - for j in range(i+1, l): - tj = (gb[j])._terms_c()[0] - if not ti.is_coprime_with(tj): - s = (gb[i])._Spoly_c(gb[j]) - if not s.is_zero(): - t = s._terms_c()[0] - heappush(S, (t._valuation_c(), t._exponent, i, j, s)) - - # Main loop of Buchberger algorithm - # Loop invariant: - # the S-polynomials of pairs of elements in rgb - # all reduce to zero modulo (rgb,S) - while S: - # We reduce the Grobner basis if needed - if reduce: - reduce = False - for i in range(len(rgb)-1, -1, -1): - g = rgb[i] - rgb[i] = g._positive_lshift_c(1) - _, rgb[i] = g._quo_rem_c(rgb, False, True, True) - gb[indices[i]] = rgb[i] + #@cached_method + def groebner_basis(self, prec=None, algorithm='VaPoTe', **options): + r""" + Compute a Groebner basis of the ideal - # We pop a new S-polynomial - _, _, i, j, f = heappop(S) - if i >= 0 and (gb[i] is None or gb[j] is None): - continue - _, r = f._quo_rem_c(rgb, False, True, integral) - if r.is_zero(): - continue + INPUT: - # We add it to our Grobner basis - tj = r._terms_c()[0] - j = len(gb) - for i in range(j): - g = gb[i] - if g is None: continue - ti = g._terms_c()[0] - if not ti.is_coprime_with(tj): # first Buchberger criterium - s = g._Spoly_c(r) - if not s.is_zero(): - t = s._terms_c()[0] - heappush(S, (t._valuation_c(), t._exponent, i, j, s)) - gb.append(r) + - ``prec`` -- an integer or ``None`` (default: ``None``), the precision + at which the computations are carried. If ``None``, defaults to the + algebra precision cap - # We minimize the Grobner basis - i = 0 - while i < len(rgb): - ti = (rgb[i])._terms_c()[0] - if tj._divides_c(ti, integral): - if indices[i] >= l: - heappush(S, (ti._valuation_c(), ti._exponent, -1, -1, rgb[i])) - gb[indices[i]] = None - del rgb[i] - del indices[i] - else: - i += 1 - rgb.append(r) - indices.append(j) - # and reduce it - reduce = True + - ``algorithm`` -- a string (default: ``VaPoTe``), the algorithm to + use in the calculations; available algorithms are: - base = I.ring().base_ring() - if base.is_field(): - if integral: - # We need to minimize and reduce the Groebner basis again - i = 0 - while i < len(rgb): - ti = (rgb[i])._terms_c()[0] - for j in range(len(rgb)): - tj = (rgb[j])._terms_c()[0] - if j != i and tj._divides_c(ti, False): - del rgb[i] - break - else: - rgb[i] = rgb[i].monic() - i += 1 - for i in range(len(rgb)): - g = rgb[i] - rgb[i] = g._positive_lshift_c(1) - _, rgb[i] = g._quo_rem_c(rgb, False, True, True) - else: - rgb = [ g.monic() for g in rgb ] - else: - rgb = [ g * base(g.leading_coefficient().unit_part()).inverse_of_unit() for g in rgb ] + - ``buchberger``: classical Buchberger algorithm - rgb.sort(reverse=True) - return rgb + - ``buchberger-integral``: first computes a Groebner basis of the + ideal generated by the same generators over the ring of integers + (provides better numerical stability) + - ``PoTe``: a F5-type algorithm where signatures are ordered by + position over term -class TateAlgebraIdeal(Ideal_generic): - r""" - Initialize a class for ideals in a Tate series algebra + - ``VaPoTe``: a F5-type algorithm where signatures are ordered + by valuation over position over term - EXAMPLES:: + We refer to [CVV2019]_ and [CVV2020]_ for a detailed description + of these algorithms. - sage: R = Zp(3, prec=10, print_mode="digits") - sage: A. = TateAlgebra(R) - sage: f = 3*x^2 + 5*x*y^2 - sage: g = 5*x^2*y + 3 - sage: I = A.ideal([f,g]); I - Ideal (...0000000012*x*y^2 + ...00000000010*x^2, ...0000000012*x^2*y + ...00000000010) of Tate Algebra in x (val >= 0), y (val >= 0) over 3-adic Field with capped relative precision 10 + - ``options`` -- extra arguments that are passed in to the + algorithm; this notably include the keyword ``verbose`` (only + available for ``PoTe`` and ``VaPoTe``) which is an integer + defining the verbosity level: - """ + - ``0``: no verbosity (quiet) - @cached_method - def groebner_basis(self, prec=None, algorithm='buchberger-integral'): - r""" - Compute a Groebner basis of the ideal + - ``1``: print each new generator and a notification each time a + J-pair is popped - INPUT: + - ``2``: in addition, print the outcome of the treatment of a J-pair - - ``prec`` -- an integer or ``None`` (default: ``None``), the precision - at which the computations are carried. If ``None``, defaults to the - algebra precision cap + - ``3``: in addition, print all added J-pairs - - ``algorithm`` -- a string (default: ``buchberger-integral``), the - algorithm to use in the calculations; currently, only ``buchberger`` - and ``buchberger-integral`` are available. + - ``4``: print entire series instead of only their leading terms OUTPUT: @@ -240,10 +116,7 @@ class TateAlgebraIdeal(Ideal_generic): - it is sorted, in the sense that the leading term of `g_i` is greater than the leading of `g_{i+1}` for all `i`. - NOTE:: - - The algorithm ``buchberger-integral`` first computes a Groebner basis - of the ideal generated by the same generators over the ring of integers. + NOTE: The result of this method is cached. @@ -268,7 +141,7 @@ class TateAlgebraIdeal(Ideal_generic): sage: g = x^4*y^5 + x^5*y^2 + x^4 + 5*x^2*y + 2*x^5*y^4 + 2*x^6*y + 6*x^3*y^3 sage: h = 2*x^6*y^4 + 2*x^4 + 4*x^5*y^2 + 8*x^8*y^2 + 8*x^7*y^3 + 8*x^6*y sage: I = A.ideal([f,g,h]) - sage: I.groebner_basis() + sage: I.groebner_basis(algorithm="buchberger-integral") [...0001*x^4 + O(2^4 * ), ...0001*x^2*y + O(2^4 * ), ...0001*y^2 + O(2^4 * )] @@ -282,17 +155,21 @@ class TateAlgebraIdeal(Ideal_generic): sage: I.groebner_basis(algorithm="F4") Traceback (most recent call last): ... - NotImplementedError: only Buchberger algorithm is implemented so far + NotImplementedError: available algorithms are 'buchberger', 'buchberger-integral', 'PoTe' and 'VaPoTe' """ if prec is None: prec = self.ring().precision_cap() if algorithm == "buchberger": - return _groebner_basis_buchberger(self, prec, False) + return groebner_basis_buchberger(self, prec, False, **options) elif algorithm == "buchberger-integral": - return _groebner_basis_buchberger(self, prec, True) + return groebner_basis_buchberger(self, prec, True, **options) + elif algorithm == "pote" or algorithm == "PoTe": + return groebner_basis_pote(self, prec, **options) + elif algorithm == "vapote" or algorithm == "VaPoTe": + return groebner_basis_vapote(self, prec, **options) else: - raise NotImplementedError("only Buchberger algorithm is implemented so far") + raise NotImplementedError("available algorithms are 'buchberger', 'buchberger-integral', 'PoTe' and 'VaPoTe'") def _contains_(self, x): r""" @@ -526,3 +403,893 @@ class TateAlgebraIdeal(Ideal_generic): gb = self.groebner_basis() gens = [ g.monic() for g in gb ] return self.ring().ideal(gens) + + + +# Grobner bases computations +############################ + +# Buchberger algorithm + +def groebner_basis_buchberger(I, prec, py_integral): + r""" + Compute a Groebner basis of the Tate algebra ideal I using Buchberger's algorithm + + INPUT: + + - ``I`` - an ideal in a Tate series algebra + + - ``prec`` - the related precision at which the initial generators + are truncated + + - ``integral`` -- a boolean; if ``True``, first compute a + Grobner basis of the ideal generated by the same generators over + the ring over the ring of integers + + NOTE:: + + This function is not meant to be called directly, but through the + ``groebner_basis`` method of Tate algebra ideals. + + EXAMPLES:: + + sage: R = Zp(3, prec=10, print_mode="digits"); + sage: A. = TateAlgebra(R) + sage: f = 3*x^2 + 5*x*y^2 + sage: g = 5*x^2*y + 3 + sage: I = A.ideal([f,g]); I + Ideal (...0000000012*x*y^2 + ...00000000010*x^2, ...0000000012*x^2*y + ...00000000010) of Tate Algebra in x (val >= 0), y (val >= 0) over 3-adic Field with capped relative precision 10 + sage: I.groebner_basis() # indirect doctest + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + + """ + cdef list gb, rgb, indices, ts, S = [ ] + cdef int i, j, l + cdef TateAlgebraTerm ti, tj, t + cdef TateAlgebraElement f, g, r, s + cdef bint do_reduce = True + cdef bint integral = py_integral + + gb = [ ] + l = 0 + for f in I.gens(): + if f == 0: + continue + g = f.add_bigoh(f.valuation() + prec) + if g == 0: + continue + gb.append(g) + l += 1 + indices = list(range(l)) + + # We minimize the family of generators + rgb = gb[:] + i = 0 + while i < len(rgb): + ti = (rgb[i])._terms_c()[0] + for j in range(len(rgb)): + tj = (rgb[j])._terms_c()[0] + if j != i and tj._divides_c(ti, integral): + del rgb[i] + del indices[i] + break + else: + i += 1 + + # We compute the initial S-polynomials + for i in range(l): + ti = (gb[i])._terms_c()[0] + for j in range(i+1, l): + tj = (gb[j])._terms_c()[0] + if not ti.is_coprime_with(tj): + s = (gb[i])._Spoly_c(gb[j]) + if not s.is_zero(): + t = s._terms_c()[0] + heappush(S, (t._valuation_c(), t._exponent, i, j, s)) + + # Main loop of Buchberger algorithm + # Loop invariant: + # the S-polynomials of pairs of elements in rgb + # all reduce to zero modulo (rgb,S) + while S: + sig_check() + # We reduce the Grobner basis if needed + if do_reduce: + do_reduce = False + for i in range(len(rgb)-1, -1, -1): + g = rgb[i] + rgb[i] = g._positive_lshift_c(1) + _, rgb[i] = g._quo_rem_c(rgb, False, True, True) + gb[indices[i]] = rgb[i] + + # We pop a new S-polynomial + _, _, i, j, f = heappop(S) + if i >= 0 and (gb[i] is None or gb[j] is None): + continue + _, r = f._quo_rem_c(rgb, False, True, integral) + if r.is_zero(): + continue + + # We add it to our Grobner basis + tj = r._terms_c()[0] + j = len(gb) + for i in range(j): + g = gb[i] + if g is None: continue + ti = g._terms_c()[0] + if not ti.is_coprime_with(tj): # first Buchberger criterium + s = g._Spoly_c(r) + if not s.is_zero(): + t = s._terms_c()[0] + heappush(S, (t._valuation_c(), t._exponent, i, j, s)) + gb.append(r) + + # We minimize the Grobner basis + i = 0 + while i < len(rgb): + ti = (rgb[i])._terms_c()[0] + if tj._divides_c(ti, integral): + if indices[i] >= l: + heappush(S, (ti._valuation_c(), ti._exponent, -1, -1, rgb[i])) + gb[indices[i]] = None + del rgb[i] + del indices[i] + else: + i += 1 + rgb.append(r) + indices.append(j) + # and reduce it + do_reduce = True + + base = I.ring().base_ring() + if base.is_field(): + if integral: + # We need to minimize and reduce the Groebner basis again + i = 0 + while i < len(rgb): + ti = (rgb[i])._terms_c()[0] + for j in range(len(rgb)): + tj = (rgb[j])._terms_c()[0] + if j != i and tj._divides_c(ti, False): + del rgb[i] + break + else: + rgb[i] = rgb[i].monic() + i += 1 + for i in range(len(rgb)): + g = rgb[i] + rgb[i] = g._positive_lshift_c(1) + _, rgb[i] = g._quo_rem_c(rgb, False, True, True) + else: + rgb = [ g.monic() for g in rgb ] + else: + rgb = [ g * base(g.leading_coefficient().unit_part()).inverse_of_unit() for g in rgb ] + + rgb.sort(reverse=True) + return rgb + + +# F5 algorithms + +cdef Jpair(p1, p2): + r""" + Return the J-pair of ``p1`` and ``p2`` + + INPUT: + + - ``p1`` -- a pair (signature, series) + + - ``p2`` -- a pair (signature, series) + + TESTS:: + + + + """ + cdef TateAlgebraTerm s1, s2 + cdef TateAlgebraElement v1, v2 + cdef TateAlgebraTerm t, t1, t2, su1, su2, sv1, sv2 + s1, v1 = p1 # we assume that s1 is not None + s2, v2 = p2 + if v1 == 0 or v2 == 0: + return + sv1 = v1.leading_term() + sv2 = v2.leading_term() + # TODO: Maybe can be made more efficient when we know the elements are "monic" + t = sv1.lcm(sv2) + t1 = t // sv1 + t2 = t // sv2 + su1 = t1*s1 + if s2 is None: + return su1, t1*v1 + su2 = t2*s2 + if su1 > su2: + return su1, t1*v1 + elif su2 > su1: + return su2, t2*v2 + + +cdef TateAlgebraElement regular_reduce(sgb, TateAlgebraTerm s, TateAlgebraElement v, stopval): + r""" + Return the result of the regular reduction of the pair ``(s,v)`` by ``sgb`` + + INPUT: + + - ``sgb`` -- a list of pairs (signature, series), candidate to be a strong + Groebner basis; all leading coefficients are assumed to be a power of the + uniformizer + + - ``s`` -- a term (the signature) + + - ``v`` -- a series + + - ``stopval`` -- the valuation until which reductions are performed + + TESTS:: + + sage: cython(''' + ....: from sage.rings.tate_algebra_ideal cimport regular_reduce + ....: def python_regular_reduce(gb, s, v, stopval): + ....: return regular_reduce(gb, s, v, stopval) + ....: ''') + + sage: R = Zp(3, prec=8) + sage: A. = TateAlgebra(R) + sage: tx = x.leading_term() # the term x + sage: ty = y.leading_term() # the term y + sage: v = (x + y + 2*x^2*y - x^3).add_bigoh(8) + sage: p1 = (tx, x^3 + 9*x*y) + sage: p2 = (ty, x*y + 3*x^2*y) + + sage: python_regular_reduce([p1,p2], tx*ty, v, 8) # indirect doctest + (2 + O(3^8))*x^2*y + (1 + O(3^8))*x + (1 + O(3^8))*y + O(3^8 * ) + + sage: python_regular_reduce([p1,p2], tx, v, 8) # indirect doctest + (2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + O(3^8))*x^3 + (2 + O(3^8))*x^2*y + (1 + O(3^8))*x + (1 + O(3^8))*y + O(3^8 * ) + """ + # We assume that the elements of the sgb are such that lt(g) = p^v lm(g) to + # avoid performing divisions + cdef dict coeffs = { } + cdef TateAlgebraElement f + cdef TateAlgebraTerm lt, factor + cdef list ltds = [ ((d[1]))._terms_c()[0] for d in sgb ] + cdef list terms = v._terms_c() + cdef int index = 0 + cdef int i + cdef bint in_rem + + f = v._new_c() + f._poly = PolyDict(v._poly.__repn, None) + f._prec = v._prec + factor = (terms[0])._new_c() + while len(terms) > index: + sig_check() + lt = terms[index] + if lt._valuation_c() >= stopval: + break + for i in range(len(sgb)): + sig_check() + # The comparison below does not perform a division, it only compares valuation and exponents + if (ltds[i])._divides_c(lt, integral=True): + # Here we need the elements of sgb to be "monic" + factor._exponent = lt._exponent.esub((ltds[i])._exponent) + factor._coeff = lt._coeff >> (ltds[i])._valuation_c() + if sgb[i][0] is None or factor * sgb[i][0] < s: + f = f - (sgb[i][1])._term_mul_c(factor) + terms = f._terms_c() + index = 0 + break + else: + if coeffs.has_key(lt._exponent): + coeffs[lt._exponent] += lt._coeff + else: + coeffs[lt._exponent] = lt._coeff + del f._poly.__repn[lt._exponent] + index += 1 + f._poly += PolyDict(coeffs, None) + f._terms = None + return f + + +cdef TateAlgebraElement reduce(gb, TateAlgebraElement v, stopval): + r""" + Return the result of the reduction of ``v`` by ``gb`` + + INPUT: + + - ``gb`` -- a list of reductors + + - ``v`` -- a series + + - ``stopval`` -- the valuation until which reductions are performed + + TESTS:: + + sage: cython(''' + ....: from sage.rings.tate_algebra_ideal cimport reduce + ....: def python_reduce(gb, v, stopval): + ....: return reduce(gb, v, stopval) + ....: ''') + + sage: R = Zp(3, prec=8) + sage: A. = TateAlgebra(R) + sage: v = (x + y + 2*x^2*y - x^3*y^2).add_bigoh(8) + sage: g1 = x*y + 3*x^2*y + sage: g2 = x^3 + 9*y + sage: python_reduce([g1,g2], v, 8) # indirect doctest + (1 + O(3^8))*x + (1 + O(3^8))*y + O(3^8 * ) + + sage: python_reduce([g1,g2], v, 5) # indirect doctest + (1 + O(3^8))*x + (1 + O(3^8))*y + (3^5 + O(3^8))*x^8*y^2 + (3^5 + 2*3^6 + 2*3^7 + O(3^8))*x^7*y + O(3^8 * ) + """ + cdef dict coeffs = { } + cdef TateAlgebraElement f + cdef TateAlgebraTerm lt, factor + cdef list ltds = [ (d)._terms_c()[0] for d in gb ] + cdef list terms = v._terms_c() + cdef int index = 0 + cdef int i + + f = v._new_c() + f._poly = PolyDict(v._poly.__repn, None) + f._prec = v._prec + while len(terms) > index: + lt = terms[index] + if lt._valuation_c() >= stopval: + break + for i in range(len(gb)): + if (ltds[i])._divides_c(lt, integral=True): + factor = lt._floordiv_c(ltds[i]) + f = f - (gb[i])._term_mul_c(factor) + terms = f._terms_c() + index = 0 + break + else: + if coeffs.has_key(lt._exponent): + coeffs[lt._exponent] += lt._coeff + else: + coeffs[lt._exponent] = lt._coeff + del f._poly.__repn[lt._exponent] + index += 1 + f._poly += PolyDict(coeffs, None) + f._terms = None + return f + + +def print_pair(p, verbose): + r""" + Return a string representation of the pair ``p`` + + INPUT: + + - ``p`` -- a pair (signature, series) + + - ``verbose`` -- an integer + + TESTS:: + + sage: from sage.rings.tate_algebra_ideal import print_pair + sage: R = Zp(3, prec=10, print_mode="digits") + sage: A. = TateAlgebra(R) + sage: v = 3*x^2 + 5*x*y^2 + sage: s = v.leading_term() + sage: p = (s,v) + + When ``verbose`` is less than 4, only the leading term of + the series is printed:: + + sage: print_pair(p, 0) + '(sign = ...0000000012*x*y^2, series = ...0000000012*x*y^2 + ...)' + + When ``verbose`` is at least 4, the entire series is printed:: + + sage: print_pair(p, 4) + '(sign = ...0000000012*x*y^2, series = ...0000000012*x*y^2 + ...00000000010*x^2)' + """ + if verbose > 3: + return "(sign = %s, series = %s)" % p + else: + s, v = p + return "(sign = %s, series = %s + ...)" % (s, v.leading_term()) + +def groebner_basis_pote(I, prec, verbose=0): + r""" + Run the PoTe algorithm to compute the Groebner basis of ``I`` + and return it + + INPUT: + + - ``I`` -- an ideal + + - ``prec`` -- the precision at which the Groebner basis is computed + + - ``verbose`` -- an integer (default: ``0``) + + TESTS:: + + sage: R = Zp(3, prec=10, print_mode="digits") + sage: A. = TateAlgebra(R) + sage: f = 3*x^2 + 5*x*y^2 + sage: g = 5*x^2*y + 3 + sage: I = A.ideal([f,g]) + sage: I.groebner_basis(algorithm="PoTe") # indirect doctest + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + + sage: I.groebner_basis(algorithm="PoTe", verbose=2) # indirect doctest + --- + new generator: ...0000000001*x*y^2 + ... + 0 initial J-pairs + 1 elements in GB before minimization + 1 elements in GB after minimization + grobner basis reduced + --- + new generator: ...0000000001*x^2*y + ... + 1 initial J-pairs + current J-pair: (sign = ...0000000001*y, series = ...0000000001*x^2*y^2 + ...) + 0 remaining J-pairs + | new element in SGB: (sign = ...0000000001*y, series = ...0000000010*x^3 + ...) + | add 2 J-pairs + current J-pair: (sign = ...000000001*y^2, series = ...0000000010*x^3*y + ...) + 1 remaining J-pairs + | new element in SGB: (sign = ...000000001*y^2, series = ...0000000010*y^2 + ...) + | add 3 J-pairs + current J-pair: (sign = ...000000001*y^3, series = ...0000000010*x^3*y^2 + ...) + 3 remaining J-pairs + | skip: cover by (sign = ...000000001*y^2, series = ...0000000010*y^2 + ...) + current J-pair: (sign = ...000000001*x*y^2, series = ...0000000010*x*y^2 + ...) + 2 remaining J-pairs + | skip: sygyzy criterium; signature = ...0000000001*x*y^2 + current J-pair: (sign = ...000000001*x^2*y^2, series = ...0000000010*x^2*y^2 + ...) + 1 remaining J-pairs + | skip: sygyzy criterium; signature = ...0000000001*x*y^2 + current J-pair: (sign = ...000000001*x^3*y^2, series = ...0000000010*x^3*y^2 + ...) + 0 remaining J-pairs + | skip: sygyzy criterium; signature = ...0000000001*x*y^2 + 4 elements in GB before minimization + 3 elements in GB after minimization + grobner basis reduced + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + """ + cdef TateAlgebraElement g, v + cdef TateAlgebraTerm s, sv, S, ti, tj + cdef TateAlgebraTerm term_one = I.ring().monoid_of_terms().one() + cdef bint integral = not I.ring().base_ring().is_field() + + gb = [ ] + + for f in sorted(I.gens()): + sig_check() + if f == 0: # Maybe reduce first? + continue + + # TODO: this should probably be a single function call + f = f.monic() << f.valuation() + + if verbose > 0: + print("---") + print("new generator: %s + ..." % f.leading_term()) + # Initial strong Grobner basis: + # we add signatures + sgb = [ (None, g) for g in gb if g != 0 ] + # We compute initial J-pairs + l = len(sgb) + p = (term_one, f.add_bigoh(prec)) + Jpairs = [ ] + for P in sgb: + sig_check() + J = Jpair(p, P) + if J is not None: + heappush(Jpairs, J) + sgb.append(p) + + # For the syzygy criterium + gb0 = [ g.leading_term() for g in gb ] + + if verbose > 1: + print("%s initial J-pairs" % len(Jpairs)) + if verbose > 2: + for s,v in Jpairs: + print("| " + print_pair((s,v),verbose)) + + while Jpairs: + sig_check() + s, v = heappop(Jpairs) + sv = v.leading_term() + + if verbose == 1: + print("pop one J-pair; %s remaining J-pairs" % len(Jpairs)) + if verbose > 1: + print("current J-pair: " + print_pair((s,v), verbose)) + print("%s remaining J-pairs" % len(Jpairs)) + + # The syzygy criterium + syzygy = None + for S in gb0: + sig_check() + if S.divides(s): + syzygy = S + break + if syzygy is not None: + if verbose > 1: + print("| skip: sygyzy criterium; signature = %s" % syzygy) + continue + + # We check if (s,v) is covered by + # the current strong Grobner basis + cover = None + for S, V in sgb: + sig_check() + if S is not None and S.divides(s): + sV = V.leading_term() + if (s // S)*sV < sv: + cover = (S,V) + break + if cover is not None: + if verbose > 1: + print("| skip: cover by " + print_pair(cover, verbose)) + continue + + # We perform regular top-reduction + sgb.append((None, v._positive_lshift_c(1))) + v = regular_reduce(sgb, s, v, prec) + del sgb[-1] + + if v.valuation() >= prec: + # We have a new element in (I0:f) whose signature + # could be useful to strengthen the syzygy criterium + #print ("| add signature for syzygy criterium: %s" % s) + gb0.append(s) + else: + # We update the current strong Grobner basis + # and the J-pairs accordingly + vv = v.monic() << v.valuation() + p = (s,vv) + if verbose > 1: + print("| new element in SGB: " + print_pair(p, verbose)) + count = 0 + for P in sgb: + sig_check() + J = Jpair(p, P) + if J is not None: + count += 1 + if verbose > 2: + print("| add J-pair: " + print_pair(J, verbose)) + heappush(Jpairs, J) + if verbose > 1: + print("| add %s J-pairs" % count) + sgb.append(p) + + # We forget signatures + gb = [g for (s,g) in sgb] + if verbose > 1: + print("%s elements in GB before minimization" % len(gb)) + if verbose > 3: + for g in gb: + print("| %s" % g) + + # We minimize the Grobner basis + i = 0 + while i < len(gb): + ti = (gb[i])._terms_c()[0] + for j in range(len(gb)): + sig_check() + tj = (gb[j])._terms_c()[0] + if j != i and tj._divides_c(ti, integral): + del gb[i] + break + else: + i += 1 + if verbose > 0: + if verbose > 1: + aft = " after minimization" + else: + aft = "" + print("%s elements in GB%s" % (len(gb), aft)) + if verbose > 3: + for g in gb: + print("| %s" % g) + # and reduce it + for i in range(len(gb)-1, -1, -1): + sig_check() + g = gb[i] + gb[i] = g._positive_lshift_c(1) + _, gb[i] = g._quo_rem_c(gb, False, True, True) + if verbose > 1: + print("grobner basis reduced") + if verbose > 3: + for g in gb: + print("| %s" % g) + + if not integral: + gb = [ f.monic() for f in gb ] + gb.sort(reverse=True) + return gb + + +def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, interrupt_interred_with_val=False): + r""" + Run the VaPoTe algorithm to compute the Groebner basis of ``I`` + and return it + + INPUT: + + - ``I`` -- an ideal + + - ``prec`` -- the precision at which the Groebner basis is computed + + - ``verbose`` -- an integer (default: ``0``) + + - ``interrupt_red_with_val`` -- a boolean (default: ``False``); if + regular reductions have to be interrupted as soon as the valuation + raises + + - ``interrupt_interred_with_val`` -- a boolean (default: ``False''); + if intermediate interreductions of the Groebner basis have to be + interrupted as soon as the valuation raises + + TESTS:: + + sage: R = Zp(3, prec=10, print_mode="digits") + sage: A. = TateAlgebra(R) + sage: f = 3*x^2 + 5*x*y^2 + sage: g = 5*x^2*y + 3 + sage: I = A.ideal([f,g]) + sage: I.groebner_basis(algorithm="VaPoTe") # indirect doctest + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + + sage: I.groebner_basis(algorithm="VaPoTe", interrupt_red_with_val=True) # indirect doctest + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + + sage: I.groebner_basis(algorithm="VaPoTe", verbose=2) # indirect doctest + grobner basis reduced + --- + new generator: ...0000000012*x*y^2 + ... + generator reduced + 0 initial J-pairs + 1 elements in GB before minimization + 1 elements in GB after minimization + grobner basis reduced + --- + new generator: ...0000000012*x^2*y + ... + generator reduced + 1 initial J-pairs + pop one J-pair; 0 remaining J-pairs + | add signature for syzygy criterium: ...0000000001*y + 2 elements in GB before minimization + 2 elements in GB after minimization + grobner basis reduced + --- + new generator: ...0000000010*x^3 + ... + generator reduced + 2 initial J-pairs + pop one J-pair; 1 remaining J-pairs + | new element is SGB: (sign = ...000000001*y, series = ...0000000010*y^2 + ...) + | add 3 J-pairs + pop one J-pair; 3 remaining J-pairs + | skip: cover by (sign = ...000000001*y, series = ...0000000010*y^2 + ...) + pop one J-pair; 2 remaining J-pairs + | add signature for syzygy criterium: ...000000001*x*y + pop one J-pair; 1 remaining J-pairs + | skip: sygyzy criterium; signature = ...0000000001*x^2*y + pop one J-pair; 0 remaining J-pairs + | skip: sygyzy criterium; signature = ...0000000001*x^2*y + 4 elements in GB before minimization + 3 elements in GB after minimization + grobner basis reduced + [...000000001*x^3 + ...222222222*y + O(3^9 * ), + ...0000000001*x^2*y + ...1210121020 + O(3^10 * ), + ...000000001*y^2 + ...210121020*x + O(3^9 * )] + """ + cdef TateAlgebraElement g, v + cdef TateAlgebraTerm s, S, sv, ti, tj + cdef bint do_reduce, integral + term_one = I.ring().monoid_of_terms().one() + gb = [ ] + + gens = [ ] + for f in I.gens(): + val = f.valuation() + if val < prec: + heappush(gens, (val, f.add_bigoh(prec))) + + do_reduce = False + integral = not I.ring().base_ring().is_field() + + while gens: + sig_check() + val, f = heappop(gens) + if val > prec: + break + + # We reduce the current Gröbner basis + if val == 0 or do_reduce: + for i in range(len(gb)-1, -1, -1): + sig_check() + g = gb[i] + gb[i] = g._positive_lshift_c(1) + tgtval = val + 1 if interrupt_interred_with_val else prec + gb[i] = reduce(gb, g, tgtval) + if verbose > 1: + print("grobner basis reduced") + if verbose > 3: + for g in gb: + print("| %s" % g) + do_reduce = False + + if verbose > 0: + print("---") + print("new generator: %s + ..." % f.leading_term()) + + tgtval = val + 1 if interrupt_red_with_val else prec + f = reduce(gb, f, tgtval) + if verbose > 1: + print("generator reduced") + + if f == 0: + if verbose > 0: + print("reduction to zero") + continue + + f = f.monic() << f.valuation() + + if f.valuation() > val: + if verbose > 0: + print("reduction increases the valuation") + heappush(gens, (f.valuation(), f)) + continue + + # Initial strong Grobner basis: + # we add signatures + sgb = [ (None, g) for g in gb if g != 0 ] + # We compute initial J-pairs + l = len(sgb) + p = (term_one, f.add_bigoh(prec)) + Jpairs = [ ] + for P in sgb: + J = Jpair(p, P) + if J is not None: + heappush(Jpairs, J) + sgb.append(p) + + # For the syzygy criterium + gb0 = [ g.leading_term() for g in gb ] + + if verbose > 1: + print("%s initial J-pairs" % len(Jpairs)) + if verbose > 2: + for s,v in Jpairs: + print("| " + print_pair((s,v),verbose)) + + while Jpairs: + sig_check() + s, v = heappop(Jpairs) + sv = v.leading_term() + + if verbose == 2: + print("pop one J-pair; %s remaining J-pairs" % len(Jpairs)) + if verbose > 2: + print("current J-pair: " + print_pair((s,v), verbose)) + print("%s remaining J-pairs" % len(Jpairs)) + + # The syzygy criterium + syzygy = None + for S in gb0: + sig_check() + if S.divides(s): + syzygy = S + break + if syzygy is not None: + if verbose > 1: + print("| skip: sygyzy criterium; signature = %s" % syzygy) + continue + + # We check if (s,v) is covered by + # the current strong Grobner basis + cover = None + for S, V in sgb: + sig_check() + if S is not None and S.divides(s): + sV = V.leading_term() + # FIXME: should be a monic division + if (s // S)*sV < sv: + cover = (S,V) + break + if cover is not None: + if verbose > 1: + print("| skip: cover by " + print_pair(cover, verbose)) + continue + + # We perform regular top-reduction + sgb.append((None, v._positive_lshift_c(1))) + tgtval = val + 1 if interrupt_red_with_val else prec + v = regular_reduce(sgb, s, v, tgtval) + del sgb[-1] + + if v != 0: + v = v.monic() << v.valuation() + # if v == 0: + if v.valuation() > val: + # We have a new element in (I0:f) whose signature + # could be useful to strengthen the syzygy criterium + if verbose > 1: + print ("| add signature for syzygy criterium: %s" % s) + gb0.append(s) + if v != 0: + heappush(gens, (v.valuation(), v)) + else: + # We update the current strong Grobner basis + # and the J-pairs accordingly + p = (s,v) + if verbose > 1: + print("| new element is SGB: " + print_pair(p, verbose)) + count = 0 + for P in sgb: + sig_check() + J = Jpair(p, P) + if J is not None: + count += 1 + if verbose > 2: + print("| add J-pair: " + print_pair(J, verbose)) + heappush(Jpairs, J) + if verbose > 1: + print("| add %s J-pairs" % count) + sgb.append(p) + + # We forget signatures + # gb = [ v.monic() for (s,v) in sgb ] + gb = [ v for (s,v) in sgb ] + if verbose > 1: + print("%s elements in GB before minimization" % len(gb)) + if verbose > 3: + for g in gb: + print("| %s" % g) + # we minimize the Grobner basis + i = 0 + while i < len(gb): + ti = (gb[i])._terms_c()[0] + for j in range(len(gb)): + sig_check() + tj = (gb[j])._terms_c()[0] + if j != i and tj._divides_c(ti, integral): + del gb[i] + break + else: + i += 1 + if verbose > 0: + if verbose > 1: + aft = " after minimization" + else: + aft = "" + print("%s elements in GB%s" % (len(gb), aft)) + if verbose > 3: + for g in gb: + print("| %s" % g) + # and reduce it + do_reduce = True + + # We normalize the final Gröbner basis + for i in range(len(gb)-1, -1, -1): + sig_check() + g = gb[i] + gb[i] = g._positive_lshift_c(1) + gb[i] = reduce(gb, g, prec) + if verbose > 1: + print("grobner basis reduced") + if verbose > 3: + for g in gb: + print("| %s" % g) + if not integral: + gb = [ f.monic() for f in gb ] + gb.sort(reverse=True) + + return gb diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index fa4ff530e1d..6b661bd14aa 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -560,7 +560,8 @@ cdef class _CuspsForModularSymbolNumerical: It is to only to be used internally. """ - cdef public llong _a, _m, _width, _N + cdef public llong _a, _m, _width + cdef public llong _N_level # trac 29290 renamed cdef public Rational _r def __init__(self, Rational r, llong N): @@ -592,7 +593,7 @@ cdef class _CuspsForModularSymbolNumerical: self._width = N / B self._a = a self._m = m - self._N = N + self._N_level = N # we could make it inherit from general cusps # but there is no need for this here # from sage.modular.cusps import Cusp @@ -605,7 +606,7 @@ cdef class _CuspsForModularSymbolNumerical: Lehner operator that brings it to i`\infty`. """ cdef llong B - B = llgcd(self._m, self._N) + B = llgcd(self._m, self._N_level) return llgcd(self._width, B) == 1 cdef public int atkin_lehner(self, llong* res) except -1: @@ -620,7 +621,7 @@ cdef class _CuspsForModularSymbolNumerical: #verbose(" enter atkin_lehner for cusp r=%s"%self._r, level=5) Q = self._width - B = llgcd(self._m, self._N) + B = llgcd(self._m, self._N_level) c = self._m / B if llgcd(Q, B) != 1: raise ValueError("This cusp is not in the Atkin-Lehner " @@ -628,7 +629,7 @@ cdef class _CuspsForModularSymbolNumerical: g = llxgcd( self._a * Q, self._m, &x, &y) res[0] = Q * x res[1] = y - res[2] = -c * self._N + res[2] = -c * self._N_level res[3] = Q * self._a #verbose(" leaving atkin_lehner with w_Q = " # "[%s, %s, %s, %s]"%(res[0], res[1], res[2], res[3]), @@ -717,7 +718,7 @@ cdef class ModularSymbolNumerical: -4/5 """ cdef: - llong _N, _cut_val, _t_plus, _t_minus + llong _N_E, _cut_val, _t_plus, _t_minus llong _t_unitary_minus, _t_unitary_plus int _lans int * _ans @@ -775,7 +776,7 @@ cdef class ModularSymbolNumerical: self._E = E self._Epari= E.pari_mincurve() self._global_sign = sign - self._N = ( E.conductor() ) + self._N_E = ( E.conductor() ) self._D = -Integer(1) self._set_epsQs() self._initialise_an_coefficients() @@ -1002,7 +1003,7 @@ cdef class ModularSymbolNumerical: self._epsQs = dict( [d,prod(self._E.root_number(p) for p in d.prime_divisors() )] - for d in Integer( self._N ).divisors()) + for d in Integer( self._N_E ).divisors()) def _set_den_bounds(self): r""" @@ -1030,7 +1031,7 @@ cdef class ModularSymbolNumerical: RealNumber E0om1, E0om2, q #verbose(" enter _set_bounds", level=5) - N = Integer( self._N ) + N = Integer( self._N_E ) E = self._E L = E.period_lattice().basis() self._om1 = L[0] @@ -1933,7 +1934,7 @@ cdef class ModularSymbolNumerical: llong * wQ = [0L, 0L, 0L, 0L] object ka - rc = _CuspsForModularSymbolNumerical(r, self._N) + rc = _CuspsForModularSymbolNumerical(r, self._N_E) Q = rc._width oi = rc.atkin_lehner(wQ) m = rc._m @@ -1952,7 +1953,7 @@ cdef class ModularSymbolNumerical: if m == 1: use_partials = 0 if use_partials == 2: - use_partials = (prec==53) and ( m**4 < self._N or m < PARTIAL_LIMIT) + use_partials = (prec==53) and ( m**4 < self._N_E or m < PARTIAL_LIMIT) if not use_partials and prec > 53: CC = ComplexField(prec) @@ -2057,11 +2058,11 @@ cdef class ModularSymbolNumerical: #verbose(" enter _from_r_to_rr_approx_direct with r=%s," # " rr=%s,..."%(r,rr), level=5) - rc = _CuspsForModularSymbolNumerical(r, self._N) + rc = _CuspsForModularSymbolNumerical(r, self._N_E) m = rc._m a = rc._a Q = rc._width - rrc = _CuspsForModularSymbolNumerical(rr,self._N) + rrc = _CuspsForModularSymbolNumerical(rr,self._N_E) mm = rrc._m aa = rrc._a QQ = rrc._width @@ -2075,7 +2076,7 @@ cdef class ModularSymbolNumerical: D = Q * QQ D /= g D *= llabs(a*mm-aa*m) - use_partials = (prec==53) and ( D**4 < self._N or D < PARTIAL_LIMIT) + use_partials = (prec==53) and ( D**4 < self._N_E or D < PARTIAL_LIMIT) CC = ComplexField(prec) if not use_partials and prec > 53: @@ -2235,7 +2236,7 @@ cdef class ModularSymbolNumerical: #verbose(" enter _from_r_to_rr_approx with r=%s," # " rr=%s, "%(r,rr), level=5) - rc = _CuspsForModularSymbolNumerical(r, self._N) + rc = _CuspsForModularSymbolNumerical(r, self._N_E) m = rc._m a = rc._a Q = rc._width @@ -2243,7 +2244,7 @@ cdef class ModularSymbolNumerical: epsQ = self._epsQs[Q] r = rc._r - rrc = _CuspsForModularSymbolNumerical(rr,self._N) + rrc = _CuspsForModularSymbolNumerical(rr,self._N_E) mm = rrc._m aa = rrc._a QQ = rrc._width @@ -2405,22 +2406,22 @@ cdef class ModularSymbolNumerical: #this finds a gamma with smallest |c| from sage.modular.cusps import Cusp rc = Cusp(r) - boo, ga = rc.is_gamma0_equiv(rr, self._N, "matrix") + boo, ga = rc.is_gamma0_equiv(rr, self._N_E, "matrix") if not boo: raise ValueError("The cusps %s and %s are not " - "Gamma_0(%s)-equivalent"%(r, rr, self._N)) + "Gamma_0(%s)-equivalent"%(r, rr, self._N_E)) # now find the same for the move to 0 c = ga[1][0] - r0 = - Rational( (c/self._N, ga[0][0]) ) + r0 = - Rational( (c/self._N_E, ga[0][0]) ) rc0 = Cusp(r0) - _, ga0 = rc0.is_gamma0_equiv(0, self._N, "matrix") + _, ga0 = rc0.is_gamma0_equiv(0, self._N_E, "matrix") if c.abs() > ga0[1][0].abs(): # better at 0 ga = ga0 c = ga[1][0] - eN = -self._epsQs[self._N] + eN = -self._epsQs[self._N_E] else: #better at i oo eN = 1 @@ -2695,7 +2696,7 @@ cdef class ModularSymbolNumerical: #verbose(" enter _symbol_non_unitary with r=%s," # " sign=%s"%(r,sign), level=5) cdef: - llong a, m, B, Q, N_ell, aell, u, N = self._N + llong a, m, B, Q, N_ell, aell, u, N = self._N_E Integer ell Rational r2, res @@ -2776,7 +2777,7 @@ cdef class ModularSymbolNumerical: -1 """ cdef: - llong c, d, x, y, N = self._N, Mu, Mv, Qu, Qv, du=1, dv=1 + llong c, d, x, y, N = self._N_E, Mu, Mv, Qu, Qv, du=1, dv=1 Rational r, rr, res int oi @@ -2800,7 +2801,7 @@ cdef class ModularSymbolNumerical: Qv = N/Mv isunitary = ( llgcd(Qu,Mu) == 1 and llgcd(Qv,Mv) == 1 ) if isunitary: # unitary case - oi = best_proj_point(u, v, self._N, &c, &d) + oi = best_proj_point(u, v, self._N_E, &c, &d) else: # at least one of the two cusps is not unitary du = llgcd(Qu,Mu) dv = llgcd(Qv,Mv) @@ -2905,7 +2906,7 @@ cdef class ModularSymbolNumerical: if sign == 0: sign = self._global_sign - oi = proj_normalise(self._N, u, v, &un, &vn) + oi = proj_normalise(self._N_E, u, v, &un, &vn) #verbose(" normalized representant on P^1: " # "(%s :%s)"%(un, vn), level=3) @@ -2929,58 +2930,58 @@ cdef class ModularSymbolNumerical: c = self.__cached_methods["_manin_symbol_with_cache"] # (-v:u) = - (u:v) - oi = proj_normalise(self._N, -v, u, &un, &vn) + oi = proj_normalise(self._N_E, -v, u, &un, &vn) c.set_cache(-res, un, vn, sign) # (v:u) = -1 * sign * (u:v) - oi = proj_normalise(self._N, v, u, &un, &vn) + oi = proj_normalise(self._N_E, v, u, &un, &vn) c.set_cache(-sign*res, un, vn, sign) # (-u:v) = sign * (u:v) - oi = proj_normalise(self._N, -u, v, &un, &vn) + oi = proj_normalise(self._N_E, -u, v, &un, &vn) c.set_cache(sign*res, un, vn, sign) # (u:v) + ( u+v:-u) +(v,-u-v) = 0 # is ( u+v:-u) already computed, we set the third - oi = proj_normalise(self._N, u+v, -u, &un, &vn) + oi = proj_normalise(self._N_E, u+v, -u, &un, &vn) if c.is_in_cache(un,vn,sign): r2 = - res - c(un,vn,sign) # (v:-u-v) = r2 - oi = proj_normalise(self._N, v, -u-v, &un, &vn) + oi = proj_normalise(self._N_E, v, -u-v, &un, &vn) c.set_cache(r2, un, vn, sign) # (u+v:v) = -r2 - oi = proj_normalise(self._N, u+v, v, &un, &vn) + oi = proj_normalise(self._N_E, u+v, v, &un, &vn) c.set_cache(-r2, un, vn, sign) # (-u-v:v) = -1 * sign * r2 - oi = proj_normalise(self._N, -u-v, v, &un, &vn) + oi = proj_normalise(self._N_E, -u-v, v, &un, &vn) c.set_cache(-sign*r2, un, vn, sign) # (-v:-u-v) = sign * r2 - oi = proj_normalise(self._N, -v, -u-v, &un, &vn) + oi = proj_normalise(self._N_E, -v, -u-v, &un, &vn) c.set_cache(sign*r2, un, vn, sign) # is ( v,-u-v) already computed, we set ( u+v:-u) - oi = proj_normalise(self._N, v, -u-v, &un, &vn) + oi = proj_normalise(self._N_E, v, -u-v, &un, &vn) if c.is_in_cache(un,vn,sign): r2 = - res - c(un,vn,sign) # (u+v:-u) = r2 - oi = proj_normalise(self._N, u+v, -u, &un, &vn) + oi = proj_normalise(self._N_E, u+v, -u, &un, &vn) c.set_cache(r2, un, vn, sign) # (u:u+v) = -r2 - oi = proj_normalise(self._N, u, u+v, &un, &vn) + oi = proj_normalise(self._N_E, u, u+v, &un, &vn) c.set_cache(-r2, un, vn, sign) # (-u:u+v) = -1 * sign * r2 - oi = proj_normalise(self._N, -u, u+v, &un, &vn) + oi = proj_normalise(self._N_E, -u, u+v, &un, &vn) c.set_cache(-sign*r2, un, vn, sign) # (-u-v:-u) = sign * r2 - oi = proj_normalise(self._N, -u-v, -u, &un, &vn) + oi = proj_normalise(self._N_E, -u-v, -u, &un, &vn) c.set_cache(sign*r2, un, vn, sign) return res @@ -3052,7 +3053,7 @@ cdef class ModularSymbolNumerical: #verbose(" enter _evaluate with r=%s, sign=%s"%(r,sign), # level=5) cdef: - llong N = self._N, u, v + llong N = self._N_E, u, v Rational r2, res Integer a, m, B, Q, y, x, M, uu, vv @@ -3180,7 +3181,7 @@ cdef class ModularSymbolNumerical: sign = self._global_sign RR = RealField(53) - N = self._N + N = self._N_E Q = N / llgcd(m,N) if llgcd(m,Q) > 1: raise NotImplementedError("Only implemented for cusps that are " @@ -3314,7 +3315,7 @@ cdef class ModularSymbolNumerical: #verbose(" enter _evaluate_approx with r=%s, eps=%s"%(r,eps), # level=5) cdef: - llong N = self._N + llong N = self._N_E ComplexNumber res Rational r2 Integer a, m, Q, x, y, B, M @@ -3408,7 +3409,7 @@ cdef class ModularSymbolNumerical: #verbose(" enter _symbol_nonunitary_approx with r=%s," # " eps=%s"%(r,eps), level=5) cdef: - llong a, m, B, Q, N_ell, aell, u, N = self._N + llong a, m, B, Q, N_ell, aell, u, N = self._N_E Integer ell Rational r2 ComplexNumber res diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 90f15e039b0..028c7cbbc99 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1625,11 +1625,6 @@ def alternating_form(self, degree, name=None, latex_name=None): sage: a = M.alternating_form(1, 'a') ; a Linear form a on the Rank-3 free module M over the Integer Ring - An alternating form of degree 0 is an element of the base ring:: - - sage: s = M.alternating_form(0) ; s - 1 - To construct such a form, it is preferable to call the method :meth:`linear_form` instead:: @@ -1642,8 +1637,12 @@ def alternating_form(self, degree, name=None, latex_name=None): """ if degree == 0: - # Return preferably a non-zero element of the ring: - return self._ring._an_element_() + try: + return self._ring.element_class(self._ring, name=name, + latex_name=latex_name) + except (KeyError, AttributeError): + raise NotImplementedError('{} apparently '.format(self._ring) + + 'does not provide generic elements') return self.dual_exterior_power(degree).element_class(self, degree, name=name, latex_name=latex_name) diff --git a/src/sage/version.py b/src/sage/version.py index cad2ca3e8c8..657d5c2f15f 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.1.rc1' -date = '2020-04-22' -banner = 'SageMath version 9.1.rc1, Release Date: 2020-04-22' +version = '9.1.rc2' +date = '2020-04-25' +banner = 'SageMath version 9.1.rc2, Release Date: 2020-04-25' diff --git a/tox.ini b/tox.ini index 26eb7983858..05eee95cfa0 100644 --- a/tox.ini +++ b/tox.ini @@ -364,7 +364,7 @@ commands = docker: done' # pathpy checksuite needs tox. #28728: gap fails its test suite. # linbox/cysignals testsuites fail. ppl takes very long. - local: bash -c 'export PATH={env:PATH} && {env:SETENV} && {env:BOOTSTRAP:./bootstrap} && ./configure --prefix={envdir}/local {env:CONFIGURE_ARGS} && make -k V=0 base-toolchain && make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl" {env:TARGETS_PRE:} {posargs:build} && (make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' + local: bash -c 'export PATH={env:PATH} && {env:SETENV} && {env:BOOTSTRAP:./bootstrap} && ./configure --prefix={envdir}/local {env:CONFIGURE_ARGS} && make -k V=0 base-toolchain && make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl,!cmake" {env:TARGETS_PRE:} {posargs:build} && (make -k V=0 SAGE_SPKG="sage-spkg -y -o" SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!python2,!nose,!pathpy,!gap,!cysignals,!linbox,!git,!ppl,!cmake" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' [testenv:check_configure] ## Test that configure behaves properly